2014-06-25 12 views
5

Deserialisieren ich Message mit C++ bin mit und ich versuche, das Äquivalent dieser Python Karte deserialisieren:eine heterogene Karte mit Message in C++

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

Das Objekt der obersten Ebene ist eine Karte mit String-Schlüssel , aber die Werte sind von ganz anderen Arten. Mein Code weiß im Voraus, wie die Struktur des Objekts aussehen sollte. Ich sollte in der Lage sein, eine Ganzzahl zu deklarieren und dann meinen Deserialisierungscode zu sagen, "Der Wert des Schlüssels version ist eine ganze Zahl, also setze den Wert dieser ganzen Zahl in diese Speicheradresse."

Das Problem ist, dass ich ' Ich bin mir nicht einmal sicher, wie ich zu dem Punkt kommen kann, an dem mein C++ - Code diese Struktur als Map behandeln kann. Ich würde erwarten, etwas zu tun, wie

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"); 

Gibt es eine mögliche Art (anything), die hier funktionieren würde? Die MessagePack-Dokumentation enthält nur triviale Beispiele und this blog post ist besser, deckt diesen Anwendungsfall jedoch nicht ab.

+0

'boost :: any' oder' boost :: variant' könnte die Aufgabe erledigen, aber dieses Problem ist eine größere Designfrage als Sie denken, Ihre Karte könnte eine andere Karte als eines ihrer Mitglieder haben, also werden Sie letztendlich brauche eine komplett rekursive Map, die mehr oder weniger ein Baum ist. Die meisten JSON-Parsing-Bibliotheken erstellen ihren eigenen benutzerdefinierten Typ zum Speichern dieser Art von Daten – Drax

Antwort

1

Sie können das mit boost :: variant tun. Um rekursive Struktur zu implementieren, können Sie oost :: make_recursive_variant wie folgt verwenden:

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; 

Hier ist eine Dokumentation: http://www.boost.org/doc/libs/1_55_0/doc/html/variant/tutorial.html#variant.tutorial.recursive.recursive-variant

Sie müssen auch einen Konverter schreiben, die von msgpack :: Objekt variant_t konvertieren und umgekehrt wie folgt:

// 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 

Sie können einen vollständigen Beispielcode aus Kern erhalten: https://gist.github.com/redboltz/672c5af16b2907488977 ich die C++ 11-Funktion verwendet habe In diesem Beispiel müssen Sie die Option -std = C++ 11 hinzufügen.