c++ variant

Variante C++



(5)

Es posible que desee Boost.Variant o Boost.Any .

Solo como un puntero adicional, puede buscar "borrado de tipo".

Estoy en el proceso de crear una clase que almacena metadatos sobre una fuente de datos en particular. Los metadatos están estructurados en un árbol, muy similar a cómo está estructurado XML. Los valores de metadatos pueden ser valores enteros, decimales o de cadena.

Tengo curiosidad por saber si hay una buena forma en C ++ para almacenar datos variantes para una situación como esta. Me gustaría que la variante utilice bibliotecas estándar, así que estoy evitando los tipos VARIANT COM, Ole y SQL que están disponibles.

Mi solución actual se ve así:

enum MetaValueType { MetaChar, MetaString, MetaShort, MetaInt, MetaFloat, MetaDouble }; union MetaUnion { char cValue; short sValue; int iValue; float fValue; double dValue; }; class MetaValue { ... private: MetaValueType ValueType; std::string StringValue; MetaUnion VariantValue; };

La clase MetaValue tiene varias funciones Get para obtener el valor de la variante almacenada actualmente, pero termina haciendo que cada consulta para un valor sea un gran bloque de instrucciones if / else if para descubrir qué valor estoy buscando.

También exploré almacenar el valor como solo una cadena y realizar conversiones para obtener diferentes tipos de variantes, pero, por lo que he visto, esto conduce a un montón de análisis interno de cadenas y manejo de errores que no es bonito. una gran vieja lata de precisión y problemas de pérdida de datos con valores de coma flotante, y aún no elimina la consulta if / else si el problema se indicó anteriormente.

¿Alguien ha implementado o visto algo que sea más limpio para usar en un tipo de datos de variante C ++ utilizando bibliotecas estándar?


También puede ir a una solución C-ish más, que tendría un vacío * del tamaño de un doble en su sistema, más una enumeración para el tipo que está utilizando. Es bastante limpio, pero definitivamente es una solución para alguien que se siente totalmente cómodo con los bytes sin procesar del sistema.


Si bien la respuesta de Konrad (utilizando una solución estandarizada existente) es ciertamente preferible a la de escribir su propia versión propensa a errores, la variante de refuerzo tiene algunos gastos generales, especialmente en la construcción de copias y la memoria.

Un enfoque personalizado común es el siguiente patrón de fábrica modificado:

  1. Cree una interfaz base para un objeto genérico que también encapsule el tipo de objeto (ya sea como una enumeración) o usando ''typeid'' (preferible).
  2. Ahora implementa la interfaz usando una plantilla Clase Derived .
  3. Cree una clase de fábrica con una función de create con plantilla con firma:

template <typename _T> Base * Factory::create ();

Esto crea internamente un objeto Derived<_T> en el montón, y vuelve a colocar un puntero de lanzamiento dinámico. Especializa esto para cada clase que quieras implementar.

Finalmente, defina un contenedor Variant que contenga este puntero Base * y defina las funciones get y set de la plantilla. Las funciones de utilidad como getType() , isEmpty() , operadores de asignación e igualdad, etc. se pueden implementar de forma adecuada aquí.

Dependiendo de las funciones de utilidad y la implementación de fábrica, las clases admitidas deberán admitir algunas funciones básicas, como la construcción de asignaciones o copias.


Aunque la pregunta ha sido respondida durante mucho tiempo, quiero dejar constancia de que QVariant también hace esto.


C ++ 17 ahora tiene std::variant que es exactamente lo que estás buscando.

std :: variante