2010-05-14 21 views
7

Voglio essere in grado di effettuare le seguenti operazioni:Dichiarare un tipo di dati in modo dinamico in C++

Ho un array di stringhe che contengono i tipi di dati:

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

Poi, più tardi, mi piacerebbe creare una variabile di uno dei tipi di dati in fase di esecuzione. Non saprò in fase di compilazione quale dovrebbe essere il tipo di dati corretto.

Così, per esempio, se in fase di esecuzione che ha determinato una variabile x necessarie per essere di tipo int:

DataTypeValues[3] x = 100; 

Ovviamente questo non funziona, così come potrei fare qualcosa di simile?

+4

Sono contento che non sia possibile farlo in C++. –

+0

Perché non vuoi scrivere 'int x = 100'? Descrivi il tuo problema più chiaro. –

+7

Dato che non puoi farlo in C++, ti suggerisco di aprire una nuova domanda in cui dichiari il problema che ti fa venir voglia di fare questo e chiedere come questo possa essere risolto invece in C++. – sbi

risposta

13

La risposta semplice è che non è possibile - i tipi devono essere conosciuti in fase di compilazione in C++. Puoi fare qualcosa di simile usando cose come boost :: any o unions, ma non sarà carino.

+4

In realtà, poiché il set di tipi è limitato e noto al momento della compilazione, la classe appropriata qui sarà 'boost :: variant '. Ancora non carina, ma molto meglio. Non è più possibile riempire una stringa. – MSalters

3

Non è possibile. Questo tipo di metaprogrammazione di runtime non è supportato in C++.

1

L'unica cosa che puoi fare è scorrere manualmente i tipi e confrontare ogni singolo. C'è anche il potenziale per utilizzare un oggetto factory qui - ma ciò implicherebbe l'heap.

+1

Dato che i tipi non sono correlati per ereditarietà, l'uso di una fabbrica sarebbe difficile, per non dire altro. –

+0

C'è quello. Boost :: Variant è probabilmente la soluzione più semplice qui. – Puppy

12

si dovrà utilizzare i sindacati per ottenere qualcosa di simile, ma la gestione sindacati è una questione molto difficile, così si dovrebbe scegliere una classe contenitore che avvolge la logica dell'unione dietro un'interfaccia simile Boost.Variant o Qts QVariant

+0

La variante è sicuramente la strada da percorrere, è quasi gratis (rispetto a quelli che controllano il tipo usando RTTI). –

+0

Sono d'accordo con Matthieu e smerlin, i tipi Variant sono decisamente la strada da percorrere. Funzionano molto bene L'unica cosa di cui preoccuparsi è quando estrai i dati da Variant, devi chiedere all'oggetto che tipo è memorizzato in esso in modo che tu possa memorizzarlo correttamente. –

2

Tutti dicendo che non puoi farlo in C++ manca una soluzione ovvia. È qui che è possibile utilizzare una classe base, è necessario definire l'interfaccia comunemente utilizzata lì e quindi tutte le classi derivate sono di qualsiasi tipo siano necessarie. Mettilo in un puntatore intelligente appropriato per un contenitore e eccoti. Potresti dover usare un'inferenza di tipo dinamico se non riesci a mettere abbastanza dell'interfaccia nella classe base, il che è sempre disapprovato perché è brutto, ma è lì per un motivo. E allocare dinamicamente i tuoi tipi probabilmente non è la cosa più efficiente, ma, come sempre, dipende da cosa lo stai usando.

+1

I tipi di dati citati dall'OP sono integrati e non fanno parte di alcuna classe. Sarebbe necessario avvolgere i built-in in classi, con membri virtuali e passare il comportamento. Potrebbe essere fatto, ma in realtà non è bello, e deve esserci un modo migliore per gestire il vero problema dell'OP. –

1

Penso che tu stia davvero cercando un linguaggio tipizzato dinamicamente. Incorporare un interprete se si deve bastone con C++!

Oppure è possibile implementare qualcosa di simile al modello di componente utilizzando le interfacce per lavorare con i dati avvolti. Inizia con la classe base cosmica - IObject, quindi implementa le interfacce per IInteger, IDouble, IString, ecc. Gli oggetti stessi verrebbero quindi creati da una factory.

O si potrebbe semplicemente utilizzare buffer void con una factory ... Questo è il metodo vecchio di evitare la tipizzazione statica in C/C++ (senza l'uso di polimorfismo basato sull'eredità). Quindi cospargere abbondante quantità di reinterpret_cast.

1

Il tipo di dati "Variant" di Visual Basic è quello di cui si sta parlando. Può contenere qualsiasi cosa, tipi di dati primari, matrici, oggetti, ecc.

"La classe Collection in OLE Automation può archiviare elementi di tipi di dati diversi. Poiché il tipo di dati di questi elementi non può essere conosciuto in fase di compilazione, i metodi per aggiungere elementi e recuperare elementi da una collezione usa le varianti: se in Visual Basic viene utilizzato il costrutto For Each, la variabile iterator deve essere di tipo oggetto o variante.. "- da http://en.wikipedia.org/wiki/Variant_type

La pagina di cui sopra dà alcune intuizioni su come vengono utilizzate le varianti e mostra come OLE viene utilizzato in C++ per trattare con le varianti

1

Il più vicino si può ottenere è con i modelli:

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; 

Se avete bisogno di qualcosa di molto più complesso, Boost ha un C++ -. ponte Python

+1

Questo deve essere risolto in fase di compilazione, non in fase di runtime. La domanda specifica che il tipo non è noto al momento della compilazione, ma piuttosto al runtime, quindi questa non sarebbe una soluzione. –

1

nel vostro semplice esempio, non ci sarebbe poco vantaggio nel non semplicemente utilizzando il tipo più largo nella lista come un contenitore generico e casting a i tipi più piccoli quando necessario (o addirittura fare affidamento su cast impliciti).

È possibile ottenere elaborati con unioni, classi, polimorfismo, RTTI, varianti Boost ecc., Ma semplicemente per un elenco di interi di larghezza diversi non vale la pena.

Mi sembra che tu abbia un problema percepito per il quale hai inventato una soluzione poco pratica per la quale ora stai chiedendo aiuto. Probabilmente starai molto meglio descrivendo il tuo problema originale invece di rendere la soluzione il problema!

1

Inoltre, non dimenticare che tutte le funzioni che devono funzionare su questo misterioso tipo di dati. Molte funzioni sono progettate per utilizzare solo un tipo, ad esempio l'aggiunta. Le funzioni sono sovraccariche per gestire altri tipi.

Come si conosce in fase di esecuzione qual è il tipo di variabile?

0

L'unico modo che mi vengono in mente ora è il vecchio stile C dove puntatore a void è stato utilizzato come:

void *unkown; 

Leter on è possibile assegnare qualsiasi oggetto ad esso come di seguito:

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

Se si conosce il tipo in fase di esecuzione, allora si può eseguire la funzione specificata in tale variabile come di seguito:

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

In questo modo (casting void *) viene utilizzato, ad esempio, quando viene utilizzata la funzione malloc [, ecc.].

Non sto dicendo che è una buona pratica quando C++ è ora molto più sviluppato. Ancora d'accordo con le persone che dicono che non è la migliore soluzione per il tuo problema. Ma forse dopo qualche riprogettazione potresti trovarlo utile.

Potreste trovare anche interessanti tipi di auto dal C++ 11. http://en.cppreference.com/w/cpp/language/auto

0

Immagino che questa risposta sarebbe un ritardo di alcuni anni. Ma per le persone che potrebbero capitare di visualizzare questo thread, una possibile soluzione per questo sarebbe utilizzare modelli variabili. Ad esempio:

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; 
} 

L'unico problema con questo, è che si avrebbe bisogno di conoscere il tipo di variabile di ciò che è attualmente entro la variabile (ad esempio la memorizzazione in una matrice intera cosa "id" della variabile (l'ID darei), per un tipo specifico). Se non conosci o non hai una condizione per sapere cosa c'è all'interno di una specifica variabile o posizione dell'array, non ti suggerisco di usarlo.

Problemi correlati