Domanda
Io sono nel processo di creazione di una classe che memorizza i metadati relativi a una particolare fonte di dati.I metadati è strutturato in un albero, molto simile a come XML è strutturato.I valori dei metadati può essere un numero intero o decimale, o valori di stringa.
Sono curioso di sapere se c'è un buon modo in C++ per memorizzare i dati delle varianti per una situazione come questa.Mi piacerebbe per la variante di usare librerie standard, quindi sto evitando di COM, Ole e SQL tipi di VARIANTE di che sono disponibili.
La mia soluzione attuale è qualcosa di simile a questo:
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;
};
Il MetaValue classe ha diverse funzioni Get per ottenere attualmente memorizzati variante di valore, ma si finisce per fare ogni query per un valore di un grosso blocco di if/else, se le istruzioni per capire il valore che sto cercando.
Ho esplorato anche memorizzare il valore come solo una stringa, e di eseguire conversioni per ottenere diversi tipi di variante, ma per quanto ho visto questo porta ad un sacco di stringhe interno l'analisi e la gestione degli errori che non è abbastanza, si apre un grande vecchio grado di precisione e di problemi di perdita di dati con valori a virgola mobile, e ancora non elimina la query if/else se il problema sopra indicato.
Qualcuno ha implementato o visto qualcosa che il detergente da utilizzare per un C++ tipo di dati variant utilizzando librerie standard?
Soluzione
Come di C++17, c'è std::variant
.
Se non è possibile utilizzare che di sicurezza, si potrebbe desiderare Boost.Variante.Simili, ma distinti, tipo per la modellazione di polimorfismo è fornito dalla std::any
(e, pre-C++17, Boost.Qualsiasi).
Come puntatore, è possibile cercare “tipo di cancellazione”.
Altri suggerimenti
Mentre Konrad risposta (usare una soluzione standardizzata) è sicuramente preferibile scrivere la propria soggetta a bug della versione, il boost variante ha alcune spese generali, in particolare in costruzione per copia e di memoria.
Un comune approccio personalizzato è modificata seguente Modello Factory:
- Creare una interfaccia di Base di un generico oggetto che incapsula anche il tipo di oggetto (come un enum), oppure l'uso di typeid' (preferibile).
- Ora implementare l'interfaccia utilizzando un modello
Derived
classe. - Creare una classe factory con un templateized
create
con funzione di firma:
template <typename _T> Base * Factory::create ();
Questo internamente crea un Derived<_T>
oggetto sullo heap, e retuns un cast dinamico puntatore.Specializzati questo per ogni classe che si desidera implementare.
Infine, la definizione di una Variant
wrapper che contiene questo Base *
puntatore e definisce il modello le funzioni get e set.Funzioni di utilità come getType()
, isEmpty()
, di cessione e operatori di uguaglianza, ecc possono essere opportunamente implementato qui.
A seconda delle funzioni di utilità e la fabbrica di attuazione, supportato classi sarà necessario il supporto di alcune funzioni di base come l'assegnazione o la copia di costruzione.
Si può anche andare giù per un C-ish soluzione, che avrebbe un void* la dimensione di un doppio nel sistema, oltre a una enum per il tipo che si sta utilizzando.È ragionevolmente pulito, ma sicuramente una soluzione per qualcuno che si sente del tutto confortevole, con il raw byte del sistema.
C++17 ora ha std::variant
che è esattamente quello che stai cercando.
Anche se la questione era stata risolta per un lungo periodo di tempo, per la cronaca, vorrei ricordare che QVariant fa anche questo.