2010-05-14 17 views
7

Quiero ser capaz de hacer lo siguiente:Declarar un tipo de datos de forma dinámica en C++

Tengo una matriz de cadenas que contiene los tipos de datos:

string DataTypeValues[20] = {"char", "unsigned char", "short", "int"}; 

Luego, más tarde, me gustaría crear una variable de uno de los tipos de datos en tiempo de ejecución. No sabré en tiempo de compilación cuál debería ser el tipo de datos correcto.

Así por ejemplo, si en tiempo de ejecución determiné una variable x necesarios para ser de tipo int:

DataTypeValues[3] x = 100; 

Obviamente, esto no va a funcionar, así que ¿cómo podría hacer algo como esto?

+4

Estoy contento de que no puedas hacer eso en C++. –

+0

¿Por qué no quieres escribir 'int x = 100'? Describe tu problema más claro. –

+7

Dado que no puede hacer esto en C++, le sugiero que abra una nueva pregunta donde indique el problema que hace que desee hacer esto y pregunte cómo se puede resolver en C++. – sbi

Respuesta

13

La respuesta simple es que no se puede: los tipos deben conocerse en tiempo de compilación en C++. Puedes hacer algo parecido usando cosas como boost :: any o uniones, pero no será bonito.

+4

En realidad, dado que el conjunto de tipos es limitado y conocido en tiempo de compilación, la clase apropiada aquí sería 'boost :: variant '. Todavía no es bonita, pero mucho mejor. Ya no puede rellenar una cadena. – MSalters

3

No puede. Este tipo de metaprogramación en tiempo de ejecución no es compatible con C++.

1

Lo único que puede hacer es recorrer manualmente los tipos y comparar cada uno. También existe el potencial de usar un objeto de fábrica aquí, pero eso involucraría el montón.

+1

Como los tipos no están relacionados por herencia, usar una fábrica sería difícil, por decir lo menos. –

+0

Eso es eso. Boost :: Variant es probablemente la solución más fácil aquí. – Puppy

12

tendría que utilizar los sindicatos para lograr algo por el estilo, pero el manejo de los sindicatos es un asunto muy difficile, lo que debe elegir una clase de contenedor que envuelve la lógica de unión detrás de una interfaz como Boost.Variant o Qts QVariant

+0

Variante es definitivamente el camino a seguir, es casi gratis también (en comparación con cualquiera que verifique el tipo usando RTTI). –

+0

Estoy de acuerdo con Matthieu y smerlin, los tipos de variantes son definitivamente el camino a seguir. Funcionan muy bien. Lo único que debe tenerse en cuenta es que al extraer los datos de la Variante, debe preguntar al objeto qué tipo está almacenado en él para que pueda almacenarlo correctamente. –

2

Todo el mundo diciendo que no se puede hacer esto en C++, falta una solución obvia. Aquí es donde puede usar una clase base, necesita definir allí la interfaz que se usa comúnmente y, a continuación, todas las clases derivadas son de cualquier tipo que necesite. Ponlo en un puntero inteligente apropiado para un contenedor y ahí lo tienes. Puede que tenga que usar la inferencia de tipo dinámico si no puede poner suficiente de la interfaz en la clase base, lo cual siempre es desaprobado porque es feo, pero está ahí por algún motivo. Y la asignación dinámica de sus tipos probablemente no sea lo más eficiente, pero, como siempre, depende de para qué lo utilice.

+1

Los tipos de datos que menciona OP están integrados y no forman parte de ninguna clase. Sería necesario envolver los integrables en clases, con miembros virtuales, y pasar el comportamiento. Podría hacerse, pero realmente, realmente no es bonito, y tiene que haber una mejor manera de manejar el problema real del PO. –

1

Creo que realmente está buscando un lenguaje de tipado dinámico. Incruste un intérprete si debe ¡quédese con C++!

O podría implementar algo similar al modelo de componentes utilizando interfaces para trabajar con datos envueltos. Comience con la clase base cósmica - IObject, luego implemente interfaces para IInteger, IDouble, IString, etc. Los objetos en sí mismos serían creados por una fábrica.

O simplemente podría usar los búferes vacíos con una fábrica ... Esa es la antigua forma de evitar el tipado estático en C/C++ (sin el uso de polimorfismo basado en la herencia). Luego espolvorea en cantidades generosas de reinterpret_cast.

1

El tipo de datos 'Variant' de Visual Basic es de lo que está hablando. Puede contener cualquier cosa, tipos de datos primarios, matrices, objetos, etc.

"La clase Collection en Automatización OLE puede almacenar elementos de diferentes tipos de datos. Como el tipo de datos de estos elementos no se puede conocer en tiempo de compilación, los métodos para agregar elementos ay recuperar elementos de una colección use variantes. Si en Visual Basic se utiliza el constructo For Each, la variable del iterador debe ser de tipo de objeto o una variante.. "- de http://en.wikipedia.org/wiki/Variant_type

La página anterior da algunas ideas sobre cómo se utilizan las variantes y muestra cómo se utiliza OLE en C++ para hacer frente a las variantes

1

Lo más cerca que se puede conseguir es con plantillas:

template<int i> class Data { }; 
template<> class Data<0> { typedef char type; } 
template<> class Data<1> { typedef unsigned char type; } 
template<> class Data<2 { typedef short type; } 
template<> class Data<3> { typedef int type; } 
Data<3>::Type x; 

Si necesita algo mucho más complejo, Boost tiene un C++ -. puente Python

+1

Esto debe resolverse en tiempo de compilación, no en tiempo de ejecución. La pregunta dice específicamente que el tipo no se conoce en tiempo de compilación, sino más bien en tiempo de ejecución, por lo que esta no sería una solución. –

1

en el ejemplo sencillo, habría poco beneficio en no simplemente utilizando el tipo más amplia en la lista como un contenedor genérico y lanzando a los tipos más pequeños cuando sea necesario (o incluso depender de conversiones implícitas).

Puede obtener detalles con uniones, clases, polimorfismo, RTTI, variantes Boost, etc., pero simplemente para una lista de enteros de ancho diferente, no vale la pena el esfuerzo.

Me parece que tiene un problema percibido para el que ha inventado una solución poco práctica para la que ahora está pidiendo ayuda. ¡Probablemente sea mucho mejor que describa su problema original en lugar de hacer de su solución el problema!

1

Además, no olvide que todas las funciones que deben operar en este misterioso tipo de datos. La mayoría de las funciones están diseñadas para usar solo un tipo, como la suma. Las funciones están sobrecargadas para manejar tipos adicionales.

¿Cómo se sabe en tiempo de ejecución cuál es el tipo de variable?

0

La única manera de que vienen a la mente ahora es el viejo estilo C, donde el puntero para anular se utilizó como:

void *unkown; 

Leterme en que puede asignar cualquier objeto a ella, como a continuación:

unkown = (void *)new int(4); 

Si conoce el tipo en el tiempo de ejecución a continuación, puede ejecutar la función especificada en dicha variable, como a continuación:

if(type==0) { // int 
    printf("%d\n", * ((int*)unkown)); 
} else { 
    // other type 
} 

De esta forma (casting void *) se usa, por ejemplo, cuando se usa la función malloc [, etc.].

No digo que sea una buena práctica cuando C++ ahora está mucho más desarrollado. Aún estoy de acuerdo con las personas que dicen que no es la mejor solución para su problema. Pero tal vez después de un nuevo diseño, puede ser útil.

También puede encontrar tipo de auto interesante desde C++ 11. http://en.cppreference.com/w/cpp/language/auto

0

Supongo que esta respuesta sería unos años tarde. Pero para las personas que puedan ver este hilo, una posible solución para esto sería usar plantillas variables. Por ejemplo:

template<typename T> 
T var; 

template<typename T> 
T arr[10]; 

int main() { 
    int temp; 
    var<int> = 2; 
    cout << var<int> << ' '; // this would output 2 
    var<char> = 'a'; 
    cout << var<int> << ' '; // var<int> value would be a space character 
    cout << var<char> << ' '; // this would output 'a' 
    for(int i = 0; i < 10; i++) { 
     switch(i % 2) { 
      case 0: 
       arr<int>[i] = ++temp; 
       break; 
      case 1: 
       arr<char>[i] = 'a' + ++temp; 
       break; 
    } 
    cout << endl; 
    for(int i = 0; i < 10; i++) { 
     switch(i % 2) { 
      case 0: 
       cout << arr<int>[i] << ' '; 
       break; 
      case 1: 
       cout << arr<char>[i] << ' '; 
       break; 
     } 
    } 
    return 0; 
} 

El único problema con esto, es que se necesita saber el tipo de variable de lo que es actualmente dentro de la variable (por ejemplo, almacenar en una matriz de enteros qué "id" de la variable (el ID que lo daría), para un tipo específico). Si no sabe o no tiene una condición para saber qué hay dentro de una variable o ubicación de matriz específica, no sugiero usar esto.

Cuestiones relacionadas