2014-06-25 10 views
5

sto usando MessagePack con C++ e sto cercando di deserializzare l'equivalente di questo Python mappa:deserializzazione una mappa eterogenea con MessagePack in C++

{'metadata': {'date': '2014-06-25', 'user_id': 501}, 
'values': [3.0, 4.0, 5.0], 
'version': 1} 

L'oggetto di livello superiore è una mappa con le chiavi di stringa , ma i valori sono di tipi completamente diversi. Il mio codice sa in anticipo quale dovrebbe essere la struttura dell'oggetto; Dovrei essere in grado di dichiarare un numero intero e poi dire al mio codice di deserializzazione, "Il valore della chiave version è un numero intero, quindi metti il ​​valore di tale numero intero in questo indirizzo di memoria."

Il problema è che I ' Non sono nemmeno sicuro di come arrivare al punto in cui il mio codice C++ può trattare questa struttura come una mappa. Mi aspetto di fare qualcosa di simile

msgpack::unpacker unpacker; 
// ...copy the data into unpacker's buffer... 

msgpack::unpacked message; 
std::map<std::string, anything> output_map; 

unpacker.next(&message); 
msgpack::object obj = message.get(); 
obj.convert(&output_map); 

int version_number = output_map.at("version"); 

C'è qualche tipo di possibile (anything) che avrebbe funzionato qui? La documentazione di MessagePack ha solo esempi banali, e this blog post è migliore ma non copre questo caso d'uso.

+0

'boost :: any' o' boost :: variant' potrebbe fare il lavoro, ma questo problema è una domanda di progettazione più grande di quanto pensi, la tua mappa potrebbe avere un'altra mappa come uno dei suoi membri, quindi alla fine serve una mappa completamente ricorsiva, che è più o meno un albero. La maggior parte delle librerie di analisi JSON finisce per creare il proprio tipo personalizzato per archiviare questo tipo di dati – Drax

risposta

1

È possibile farlo utilizzando boost :: variant. Per implementare struttura ricorsiva, è possibile utilizzare Oost :: make_recursive_variant come segue:

typedef boost::make_recursive_variant< 
    std::string, 
    std::map<boost::recursive_variant_, boost::recursive_variant_>, 
    std::vector<boost::recursive_variant_>, 
    int, 
    double 
    >::type variant_t; 

Ecco una documentazione: http://www.boost.org/doc/libs/1_55_0/doc/html/variant/tutorial.html#variant.tutorial.recursive.recursive-variant

È inoltre necessario scrivere un convertitore che convertire da msgpack :: oggetto variant_t e viceversa nel modo seguente:

// Custom converter for variant_t 
namespace msgpack { 

// Convert from msgpacl::object to variant_t. 
inline variant_t& operator>>(object const& o, variant_t& v) { 
    switch(o.type) { 
    case type::MAP: 
     v = std::map<variant_t, variant_t>(); 
     o.convert(boost::get<std::map<variant_t, variant_t> >(&v)); 
     break; 
    case type::ARRAY: 
     v = std::vector<variant_t>(); 
     o.convert(boost::get<std::vector<variant_t> >(&v)); 
     break; 
    case type::POSITIVE_INTEGER: 
     v = int(); 
     o.convert(boost::get<int>(&v)); 
     break; 
    case type::DOUBLE: 
     v = double(); 
     o.convert(boost::get<double>(&v)); 
     break; 
    case type::RAW: 
     v = std::string(); 
     o.convert(boost::get<std::string>(&v)); 
     break; 
    default: 
     break; 
    } 
    return v; 
} 


// Convert from variant_t to msgpacl::object. 
template <typename Stream> 
struct packer_imp:boost::static_visitor<void> { 
    template <typename T> 
    void operator()(T const& value) const { 
     o_.pack(value); 
    } 
    packer_imp(packer<Stream>& o):o_(o) {} 
    packer<Stream>& o_; 
}; 

template <typename Stream> 
inline packer<Stream>& operator<< (packer<Stream>& o, const variant_t& v) 
{ 
    boost::apply_visitor(packer_imp<Stream>(o), v); 
    return o; 
} 

} // namespace msgpack 

È possibile ottenere un codice completo esempio dalla sostanza: https://gist.github.com/redboltz/672c5af16b2907488977 ho usato la funzione C++ 11 nell'esempio, quindi è necessario aggiungere l'opzione -std = C++ 11.

Problemi correlati