2010-09-08 7 views
10

Per quanto ho capito, non esiste alcuna serializzazione (boost::serialization, in realtà) supporto per segnaposto boost::any.C++ - boost :: qualsiasi serializzazione

Qualcuno sa se esiste un modo per serializzare un'entità personalizzata boost::any?

Il problema qui è ovvio: boost::any utilizza segnaposto basati su modello per memorizzare oggetti e typeid per verificare se boost::any_cast è appropriato.

Quindi, c'è un astratti personalizzato classi derivate basate su template superclasse placeholder e personalizzati, che vengono creati nel seguente modo:

template <T> custom_placeholder : public placeholder { 
    virtual std::type_info type() const { return typeid(T); } 
    virtual ... 
}; 

Ovviamente, questo porta qualche problema quando nemmeno pensarci serializzazione questa roba. Forse qualcuno conosce qualche trucco per realizzare un tale tipo di serializzazione (e, naturalmente, una deserializzazione appropriata)?

Grazie

risposta

5

Non è possibile a tutti, almeno per i tipi arbitrari. Nota che potresti serializzare usando un codice complicato (come trovare la dimensione degli elementi contenuti in qualsiasi), ma il codice si basa sul compilatore che inserisce staticamente qualsiasi tipo_code e il tipo corretto all'interno del segnaposto. Sicuramente non è possibile farlo in deserializzazione in C++, poiché il tipo che si otterrebbe dalla deserializzazione non è noto in fase di compilazione (come richiesto dal neo formato boost::any).

La soluzione migliore consiste nel creare un tipo di specialista di qualsiasi tipo per i tipi esatti di elementi che verranno serializzati. Quindi, è possibile avere casi speciali per il tipo effettivo di elemento che viene deserializzato, ma si noti che ogni serializzazione/deserializzazione di ogni elemento deve essere scritta fisicamente come codice C++ statico.

PD. Alcuni altri hanno suggerito di utilizzare boost::variant come una rappresentazione di questo tipo specializzato che contiene i tipi esatti da serializzare. Tuttavia, è necessario un modo per discernere il tipo esatto sulla deserializzazione (magari assegnando identificatori ai tipi nella variante).

+0

Un tipo specializzato per un set noto di tipi si chiamerebbe una variante come ad es. ['boost :: variant'] (http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html) (che ovviamente fornisce solo le basi). –

+0

@Georg: Sì, grazie per il suggerimento. Ho modificato la risposta per riflettere questo. –

1

Supponendo che sia necessario utilizzare boost::any e non è possibile passare a variant, una soluzione basata su potrebbe farvi fare.

È necessario inizializzare in fase di esecuzione tale map con tutti i tipi che si prevede di utilizzare. Naturalmente, è possibile utilizzare qualcosa sulla falsariga di

template <typename T> 
struct any_serializer 
{ 
    static string perform(any a) 
    { 
     T const& x = any_cast<T const&>(a); 
     stringstream out; 
     out << x; 
     return out.str(); 
    } 
}; 

e popolare la mappa con gli indirizzi di any_serializer<T>::perform sotto la chiave &typeid(T). È possibile specializzare la classe any_serializer e utilizzare alcuni (brutti) macro per popolare la mappa.

Più difficile è ovviamente la deserializzazione. Non ho dato un'occhiata a boost::lexical_cast per un po ', forse può fornire un aiuto. Temo che questo sia totalmente dipendente dal problema. Tuttavia, è necessaria solo una funzione, che prende uno string e restituisce uno any. Si potrebbe anche voler anteporre la stringa di output con un identificatore di tipo personalizzato.

+1

Questo schema è piuttosto complicato e non funzionerebbe se, per esempio, si inviasse quelle serializzazioni sulla rete ad altri processi o computer. Si noti che '& typeid (T)' sarebbe diverso per architetture diverse (e forse anche programmi), quindi non vedo questo molto affidabile ... Dopo la modifica della risposta: Sì, questo ha più senso usare un identificatore di tipo personalizzato. Anche l'utilizzo della variante con un insieme di tipi ristretti è una buona idea. –

+0

@Diego: non vedo il problema. La mappa è locale al programma ed è solo un mezzo per inviare le varie routine di serializzazione. Quello che mi preoccuperei di più se 'typeid (T)' produce sempre oggetti che possono essere confrontati per indirizzo. Questo può essere risolto con una semplice classe wrapper che chiama 'type_info :: before' come' operator <'. –

+0

@Alexandre: mi riferivo al trasferimento della mappa (utilizzando le chiavi di tipo as come chiavi) attraverso un canale di rete, ad esempio, o essere utilizzato per essere letto da diverse applicazioni in esecuzione in diverse architetture o sistemi operativi. Questa chiave per la mappa sarebbe dipendente dal programma e avresti bisogno di un identificatore di tipo indipendente dal sistema, come hai aggiunto nella tua modifica alla risposta. –

6

Se si desidera attenersi a boost :: any non sono sicuro, ma è possibile scrivere il proprio "boost :: any". Sto usando questo codice per i metodi proxy per passare i parametri.

#include <iostream> 
#include <boost\smart_ptr\scoped_ptr.hpp> 
#include <boost/shared_ptr.hpp> 
#include <boost/serialization/access.hpp> 
#include <boost/serialization/shared_ptr.hpp> 
#include <boost/archive/text_oarchive.hpp> 
#include <boost/archive/text_iarchive.hpp> 
#include <boost/serialization/export.hpp> 
#include <sstream> 
class my_placeholder 
{ 
public: 
    virtual ~my_placeholder(){} 
    my_placeholder(){} 
private: 
    friend class boost::serialization::access; 
    template<class Archive> 
    void serialize(Archive & ar, const unsigned int version) 
    { 
     // serialize base class information 
     //ar & boost::serialization::base_object<bus_stop>(*this); 
     //ar & m_placeholder; 

    } 

}; 




template<typename T> 
class my_derivedplaceholder: 
    public my_placeholder 
{ 
    public: 
     my_derivedplaceholder() 
     { 

     } 
     my_derivedplaceholder(T &value) 
     { 
      m_value=value; 
     } 
    T m_value; 

private: 
    friend class boost::serialization::access; 
    template<class Archive> 
    void serialize(Archive & ar, const unsigned int version) 
    { 
     // serialize base class information 
     ar & boost::serialization::base_object<my_placeholder>(*this); 
     ar & m_value; 

    } 
}; 


BOOST_CLASS_EXPORT_GUID(my_derivedplaceholder<int>, "p<int>"); 


class my_any 
{ 
public: 

    my_any() 
    { 

    } 

    template<typename T> 
    my_any(const T &value) 
    { 
     m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value))); 
    } 

    template<typename T> 
    void operator=(const T &value) 
    { 
     m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value))); 
    } 



protected: 

    friend class boost::serialization::access; 
    template<class Archive> 
    void serialize(Archive & ar, const unsigned int version) 
    { 
     // serialize base class information 
     //ar & boost::serialization::base_object<bus_stop>(*this); 
     ar & m_placeholder; 

    } 

    template<typename T> 
    friend T my_anycast(my_any &val); 

    boost::shared_ptr<my_placeholder> m_placeholder; 
}; 

template<typename T> 
T my_anycast(my_any &val) 
{ 
    boost::shared_ptr<my_derivedplaceholder<T>> concrete=boost::dynamic_pointer_cast<my_derivedplaceholder<T>>(val.m_placeholder); 
    if (concrete.get()==NULL) 
     throw std::invalid_argument("Not convertible"); 

    return concrete->m_value; 
} 

void main() 
{ 
    my_any m=10; 

    int a=my_anycast<int>(m); 

    std::cout << a << std::endl; 

    std::stringstream ss,ss2; 
    boost::archive::text_oarchive oa(ss); 

    oa << m; 

    boost::archive::text_iarchive ia(ss); 

    my_any m2; 
    ia >> m2; 

    std::cout << my_anycast<int>(m2) << std::endl; 
}