Clase de almacenamiento de tipo -- ++ campo con object-oriented campo con type-safety camp codereview Relacionados El problema

Type Storage Class


1
vote

problema

Español

Entonces, he diseñado una "tienda tipo" en C ++ que puede contener una única instancia de cualquier subclase de un tipo definido.

Aquí hay un ejemplo de cómo se usaría:

  class BaseClass { }; class FirstSubClass : public BaseClass { }; class SecondSubClass : public BaseClass { };  TypeStore<BaseClass> store;  // Each of these returns a pointer to the newly created instance store.Create<BaseClass>(); store.Create<FirstSubClass>(); store.Create<SecondSubClass>();  // Each of these fails (returning nullptr) as an instance already exists for the type store.Create<BaseClass>(); store.Create<FirstSubClass>(); store.Create<SecondSubClass>();  // Returns pointers to the instances for each type or nullptr if it is not present store.Get<BaseClass>(); store.Get<FirstSubClass>(); store>Get<SecondSubClass>();   

He diseñado la clase de una manera que maneja toda la asignación de la memoria y la desasignación, lo que facilita la utilización.

Si se está preguntando por qué usaría esto, actualmente estoy desarrollando un sistema de componentes de entidad que usaría esto para almacenar componentes; Solo puede haber una sola instancia de cada tipo de componente por entidad.

Hay una cosa que realmente no me gusta de mi implementación, y así es como me veo obligado a escribir el cuerpo de mis métodos en el encabezado. Sé que esto se requiere porque tengo una clase plantelada, y podría extraerlos en otro archivo para ser incluido por type_store.h , sin embargo, todavía siento que esto es bastante desordenado solución.

y aquí está la fuente:

identifier.h

  #ifndef IDENTIFIER_H #define IDENTIFIER_H  #include <stddef.h>  typedef size_t Identifier;  #endif   

type_identifier.h

  #ifndef TYPE_IDENTIFIER_H #define TYPE_IDENTIFIER_H  class TypeIdentifier { public:     template<typename T>     static Identifier GetIdentifier() {         static Identifier identifier = nextIdentifier++;          return identifier;     }  private:     static Identifier nextIdentifier; };  #endif   

type_identifier.cpp

  9988776655544336  

type_store.h

  #ifndef TYPE_STORE_H #define TYPE_STORE_H  #include <map>  template<typename T> class TypeStore { public:     typedef std::map<Identifier, T *> Map;      typedef typename Map::iterator Iterator;     typedef typename Map::const_iterator ConstIterator;      ~TypeStore() {         for (auto iterator = Begin(); iterator != End(); /* unused */) {             delete iterator->second;              store.erase(iterator++);         }     }      template<typename V>     bool Contains() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          return store.find(TypeIdentifier::GetIdentifier<T>()) != store.end();     }      template<typename V>     V *Get() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          if (!Contains<V>()) {             return nullptr;         }          Identifier identifier = TypeIdentifier::GetIdentifier<T>();          return static_cast<V *>(store.at(identifier));     }      template<typename V, typename ...A>     V *Create(A &&... args) {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          if (Contains<V>()) {             return nullptr;         }          Identifier identifier = TypeIdentifier::GetIdentifier<T>();         V *instance = new V(std::forward(args)...);          if (!store.insert(std::make_pair(identifier, instance)).second){             delete instance;              return nullptr;         }          return instance;     }      template<typename V>     bool Remove() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          Identifier identifier = TypeIdentifier::GetIdentifier<T>();          delete Get<V>();          return store.erase(identifier) == 1;     }      typename Map::size_type Size() {         return store.size();     }      Iterator Begin() {         return store.begin();     }      Iterator End() {         return store.end();     }      ConstIterator Begin() const {         return store.begin();     }      ConstIterator End() const {         return store.end();     }      ConstIterator CBegin() const {         return store.cbegin();     }      ConstIterator CEnd() const {         return store.cend();     } private:     Map store; };  #endif   
Original en ingles

So I have designed a "type store" in C++ that can hold a single instance of any subclass of a defined type.

Here is an example of how it would be used:

class BaseClass { }; class FirstSubClass : public BaseClass { }; class SecondSubClass : public BaseClass { };  TypeStore<BaseClass> store;  // Each of these returns a pointer to the newly created instance store.Create<BaseClass>(); store.Create<FirstSubClass>(); store.Create<SecondSubClass>();  // Each of these fails (returning nullptr) as an instance already exists for the type store.Create<BaseClass>(); store.Create<FirstSubClass>(); store.Create<SecondSubClass>();  // Returns pointers to the instances for each type or nullptr if it is not present store.Get<BaseClass>(); store.Get<FirstSubClass>(); store>Get<SecondSubClass>(); 

I have designed the class in a way that it handles all memory allocation and deallocation, making it easier to use.

If you are wondering what I would use this for, I am currently developing an Entity Component System that would use this for storing components; there can only be a single instance of each component type per entity.

There is one thing I don't really like about my implementation, and that is how I am forced to write the body of my methods in the header. I know this is required because I have a templated class, and I could extract them out into another file to be included by type_store.h, however I still feel this is a rather messy solution.

And here is the source:

identifier.h

#ifndef IDENTIFIER_H #define IDENTIFIER_H  #include <stddef.h>  typedef size_t Identifier;  #endif 

type_identifier.h

#ifndef TYPE_IDENTIFIER_H #define TYPE_IDENTIFIER_H  class TypeIdentifier { public:     template<typename T>     static Identifier GetIdentifier() {         static Identifier identifier = nextIdentifier++;          return identifier;     }  private:     static Identifier nextIdentifier; };  #endif 

type_identifier.cpp

#include "type_identifier.h"  Identifier TypeIdentifier::nextIdentifier = 0; 

type_store.h

#ifndef TYPE_STORE_H #define TYPE_STORE_H  #include <map>  template<typename T> class TypeStore { public:     typedef std::map<Identifier, T *> Map;      typedef typename Map::iterator Iterator;     typedef typename Map::const_iterator ConstIterator;      ~TypeStore() {         for (auto iterator = Begin(); iterator != End(); /* unused */) {             delete iterator->second;              store.erase(iterator++);         }     }      template<typename V>     bool Contains() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          return store.find(TypeIdentifier::GetIdentifier<T>()) != store.end();     }      template<typename V>     V *Get() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          if (!Contains<V>()) {             return nullptr;         }          Identifier identifier = TypeIdentifier::GetIdentifier<T>();          return static_cast<V *>(store.at(identifier));     }      template<typename V, typename ...A>     V *Create(A &&... args) {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          if (Contains<V>()) {             return nullptr;         }          Identifier identifier = TypeIdentifier::GetIdentifier<T>();         V *instance = new V(std::forward(args)...);          if (!store.insert(std::make_pair(identifier, instance)).second){             delete instance;              return nullptr;         }          return instance;     }      template<typename V>     bool Remove() {         static_assert(std::is_base_of<T, V>::value, "incorrect type for container");          Identifier identifier = TypeIdentifier::GetIdentifier<T>();          delete Get<V>();          return store.erase(identifier) == 1;     }      typename Map::size_type Size() {         return store.size();     }      Iterator Begin() {         return store.begin();     }      Iterator End() {         return store.end();     }      ConstIterator Begin() const {         return store.begin();     }      ConstIterator End() const {         return store.end();     }      ConstIterator CBegin() const {         return store.cbegin();     }      ConstIterator CEnd() const {         return store.cend();     } private:     Map store; };  #endif 
        

Lista de respuestas

1
 
vote
vote
La mejor respuesta
 

Unique_PTR:

Puede considerar usar al menos algunas administraciones de memoria automatizada internamente. Si el #ifndef HEADER 8 conservará la propiedad de todos los objetos que crea, en lugar de esto:

  #ifndef HEADER 9  

Puede simplemente almacenar un 99887766655443330 en el mapa y eliminar completamente el HEADER1 destructor:

  HEADER2  

El HEADER33 S A TUDES DISPARSE ABAJO A TODO SE VA A LLEGAR. Una solución mucho más limpia, imo.

Uso de NULL como indicador de error:

Elegió devolver HEADER4 para indicar errores. Desafortunadamente, eso excluye la devolución de una referencia al objeto. Una referencia sería mejor porque transmite más claramente la idea de que el objeto es propiedad de HEADER5 , por lo que no habría riesgo de intentar eliminar accidentalmente un puntero devuelto por la tienda.

Puede considerar otras formas de indicar los errores, para que pueda devolver las referencias. Uno podría ser excepciones, pero esa no es una gran estrategia si se espera que el 998877766554433336 obtenga solicitudes inválidas a menudo. La otra estrategia que también sería un gran ajuste en este caso es un tipo opcional . Lamentablemente, actualmente no hay tal tipo en el estándar, pero la siguiente mejor cosa es boost.optional .

En cualquier caso, esto es más de una sugerencia, si está cómodo con su diseño actual, por todos los medios, mantenlo. Solo asegúrese de documentar claramente que los punteros devueltos no deben eliminarse fuera HEADER7 .

 

unique_ptr:

You might consider using at least some automated memory management internally. If the TypeStore will retain ownership of all objects it creates, instead of this:

typedef std::map<Identifier, T *> Map;  ~TypeStore() {     for (auto iterator = Begin(); iterator != End(); /* unused */) {         delete iterator->second;         store.erase(iterator++);     } } 

You could simply store a unique_ptr in the map and completely remove the TypeStore destructor:

typedef std::map<Identifier, std::unique_ptr<T>> Map; 

The deletes you have scattered around would also go away. A much cleaner solution, IMO.

Use of null as an error indicator:

You chose to return nullptr to indicate errors. Unfortunately that precludes returning a reference to the object. A reference would be better because it more clearly conveys the notion that the object is owned by TypeStore, so there'd be no risk of accidentally attempting to delete a pointer returned by the store.

You might consider other ways of indicating the errors instead, so you could return references. One might be exceptions, but that's not a great strategy if the TypeStore is expected to get invalid requests often. The other strategy which would also be a great fit in this case is an optional type. Sadly there's currently no such type in the Standard, but the next best thing is Boost.Optional.

In any case, this is more of a suggestion, if you're comfortable with your current design, by all means, keep it. Just be sure to document clearly that the pointers returned are not to be deleted outside TypeStore.

 
 

Relacionados problema

3  Robustez de esta primera función de diferencia  ( Robustness of this first difference function ) 
Aquí hay una función de primera diferencia que tiraré para un cheque rápido en otro programa: use std::ops::Sub; fn first_difference<T>(input: &Vec<T>) ->...

48  Lista <T> Implementación para VB6 / VBA  ( Listt implementation for vb6 vba ) 
Recientemente, decidí que el 998877665555544330 no fue suficiente para mis necesidades, así que decidí implementar algo como C # 's List<T> . Aquí está la ...

28  Diccionario de tipo seguro para varios tipos  ( Type safe dictionary for various types ) 
Asume la siguiente situación: tiene un objeto que puede almacenar cualquier objeto basado en una tecla (básicamente, 99887766555443312 ). Desea almacenar obj...

2  Añadir 5 tipos fuertes a un objeto como propiedades  ( Add 5 strong types to an object as properties ) 
Este es un objeto pequeño que le permite pasar un obj y un nombre de propiedad usando sus funciones de ayuda para crear un VAR escribió que no guardará un val...

3  Alias ​​de tipo restringido  ( Constrained type alias ) 
¿Qué piensa en la siguiente sintaxis para algunas validaciones muy simples e intensivas? hace esto: string name = (SomeText)"Hm…"; ¿Significa para us...

2  Tipo Creador Servicio y Marco  ( Type creator service framework ) 
Necesitaba un mecanismo para crear tipos dinamicalmente de cadenas y bytes, etc. Intenté muy difícil de usar el sistema 99887765555443339 , ya que parecía ...

-1  Implementar el borrador de estado para el NIST DRBG y otros primitivos de Crypto usando Macro  ( Implement state eraser for nist drbg and other crypto primitives using macro ) 
Como proyecto personal de tiempo libre, estoy implementando un conjunto de primitivas criptográficas clave simétricas. Una cosa que me he dejado vacante dur...

2  Marco de conversor de tipo (v2)  ( Type converter framework v2 ) 
Esta es la segunda versión de My tipo de marco de convertidor. El anterior se puede encontrar aquí: Type Creator Service & AMP; Framework En esta vers...

5  Seguro, totalmente estándar, siguiendo el entero ABS en C  ( Safe fully standards following integer abs in c ) 
Aquí está un poco complicado, porque AFAIK no existe una plataforma existente, donde la parte recopilada condicional del Código realmente se incluiría por el ...

3  Cargando un objeto desde el archivo con acceso de tipo de seguridad y roscas  ( Loading an object from file with type safety and thread safe access ) 
Estoy intentando escribir un poco de código que permite un acceso fácil y seguro a los objetos en un archivo. Parece que funciona muy bien, pero tenía curiosi...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos