Pergunta

Eu estou no processo de criação de uma classe que armazena metadados sobre uma fonte de dados particular. Os metadados são estruturados em uma árvore, muito semelhante à forma como XML é estruturado. Os valores de metadados pode ser inteiro, decimal ou valores de cadeia.

Estou curioso para saber se há uma boa maneira em C ++ para armazenar dados variantes para uma situação como esta. Eu gostaria que a variante de usar bibliotecas padrão, então eu estou evitando a COM, Ole, e SQL VARIANT tipos que estão disponíveis.

O meu atual solução é algo como isto:

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

A classe metavalue tem várias funções GET para obter o valor variante atualmente armazenado, mas acaba tornando cada consulta para um valor um grande bloco de if / else if para descobrir qual valor que eu estou procurando.

Eu também explorou armazenar o valor como apenas uma corda, e realizando conversões para obter diferentes tipos de variantes, mas, tanto quanto eu vi isso leva a um monte de análise de cadeia interna e tratamento de erros que não é bonita, abre-se uma grande lata velha de questões de precisão e de perda de dados com valores de ponto flutuante, e ainda não elimina a consulta if / else if problema indicado acima.

Tem alguém implementado ou visto algo que é mais limpo para usar um tipo de C ++ dados variante usando bibliotecas padrão?

Foi útil?

Solução

Como de C ++ 17 anos, há std::variant .

Se você não pode usar isso ainda, você pode querer Boost.Variant . Um semelhante, mas distinta, tipo de polimorfismo de modelagem é fornecido por std::any (e , pré-C ++ 17, Boost.Any ).

Assim como um ponteiro adicional, você pode olhar para “ digite apagamento ”.

Outras dicas

Enquanto a resposta de Konrad (utilizando uma solução padronizada existente) é certamente preferível escrever a sua própria versão bug-propensa, a variante impulso tem algumas despesas gerais, especialmente na construção cópia e memória.

Uma abordagem comum personalizado é o seguinte padrão de fábrica modificado:

  1. criar uma interface de base de dados para um objecto genérico que também engloba o tipo de objecto (quer como uma enumeração), ou utilizando 'typeid' (preferível).
  2. Agora implementar a interface usando uma classe de modelo Derived.
  3. Criar uma classe de fábrica com uma função create templateized com a assinatura:

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

Isso cria internamente um objeto Derived<_T> na pilha, e retuns um ponteiro dinâmica elenco. Especialize isso para cada classe que você deseja implementar.

Finalmente, definir um invólucro Variant que contém esse ponteiro Base * e define modelo get e funções definidas. funções de serviços públicos como getType(), isEmpty(), atribuição e operadores de igualdade, etc pode ser adequadamente implementado aqui.

Dependendo das funções de serviço público e a implementação da fábrica, aulas apoiadas terão de suportar algumas funções básicas, como a atribuição ou copiar construção.

Você também pode ir para baixo para uma solução mais C-ish, que teria um void * do tamanho de um casal em seu sistema, além de um enum para que tipo você está usando. É razoavelmente limpo, mas definitivamente uma solução para alguém que se sente totalmente confortável com os bytes brutos do sistema.

C ++ 17 agora tem std::variant que é exatamente o que você está procurando.

std :: variante

Embora a pergunta tinha sido respondida por um longo tempo, para o registro eu gostaria de mencionar que QVariant também faz isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top