2010-09-08 13 views
10

O ile rozumiem, nie ma potrzeby serializacji (boost::serialization, faktycznie) obsługi dla elementu zastępczego .C++ - boost :: any serialization

Czy ktoś wie, czy istnieje sposób na serializację niestandardowej jednostki boost::any?

Problem tutaj jest oczywisty: boost::any używa symboli zastępczych opartych na szablonach do przechowywania obiektów i typeid, aby sprawdzić, czy odpowiedni jest boost::any_cast.

Tak, tam jest zwyczaj abstrakcyjne nadklasą placeholder i niestandardowych opartych na szablonie klasy pochodne, które są tworzone w następujący sposób:

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

Oczywiście, ten przynosi pewne problemy, gdy nawet myślenia o szeregowania te rzeczy. Może ktoś zna jakąś sztuczkę, aby dokonać takiej serializacji (i oczywiście właściwej deserializacji)?

Dziękujemy

Odpowiedz

5

Nie jest to możliwe, przynajmniej dla typów arbitralnych. Zauważ, że być może mógłbyś serializować używając trudnego kodu (jak znalezienie rozmiaru elementów zawartych w dowolnym), ale każdy kod opiera się na kompilatorze statycznie umieszczającym każdy typ_klucza i właściwy typy wewnątrz elementu zastępczego. Na pewno nie możesz tego zrobić w deserializacji w C++, ponieważ typ, który otrzymasz z deserializacji, nie jest znany w czasie kompilacji (zgodnie z wymogiem nowo utworzonego boost::any).

Najlepszym rozwiązaniem jest zbudowanie jakiegoś wyspecjalizowanego dowolnego rodzaju dla dokładnych typów elementów, które zamierzasz serializować. Następnie możesz mieć specjalne przypadki dla rzeczywistego typu deserializacji elementu, ale pamiętaj, że każda seria/deserializacja typu elementu musi być fizycznie zapisana jako statyczny kod C++.

PD. Niektórzy inni sugerowali używanie boost::variant jako reprezentacji tego wyspecjalizowanego typu z dokładnie tymi typami, które zamierzacie serializować. Potrzebny jest jednak sposób rozpoznawania dokładnego typu przy deserializacji (może przypisać identyfikatory do typów w wariancie).

+0

Wyspecjalizowany typ dla znanego zestawu typów byłby nazywany wariantem np. ['boost :: variant'] (http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html) (co oczywiście daje tylko podstawę). –

+0

@Georg: Tak, dzięki za podpowiedź. Zmieniłem odpowiedź, aby to odzwierciedlić. –

1

Zakładając, trzeba użyć boost::any i nie można przełączyć się variant, map<type_info const*, string(*)(any)> rozwiązanie oparte mógłby Ci zrobić.

Należy uruchomić w czasie wykonywania taki map ze wszystkimi typami, których planujesz użyć. Oczywiście, można użyć coś wzdłuż linii

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

i wypełnić mapę z adresami any_serializer<T>::perform pod klucz &typeid(T). Możesz specjalizować klasę any_serializer i używać niektórych (brzydkich) makr do zapełniania mapy.

Bardziej skomplikowana jest oczywiście deserializacja. Przez jakiś czas nie patrzyłem na boost::lexical_cast, może to może pomóc. Obawiam się, że jest to całkowicie zależne od problemu. Jednak potrzebujesz tylko jednej funkcji, która pobiera string i zwraca jedną any. Możesz także dodać ciąg wyjściowy z niestandardowym identyfikatorem typu.

+1

Ten schemat jest dość skomplikowany i nie zadziałałby, gdyby, powiedzmy, wysłać te serializacje przez sieć do innego procesu lub komputera. Zauważ, że '& typeid (T)' byłoby inne dla różnych architektur (a może nawet programów), więc nie widzę tego bardzo wiarygodnego ... Po edycji odpowiedzi: Tak, to ma więcej sensu przy użyciu niestandardowego identyfikatora typu. Używanie wariantu z zestawem ograniczonych typów jest również dobrym pomysłem. –

+0

@Diego: Nie widzę problemu. Mapa jest lokalna dla programu i jest tylko środkiem do wysyłania różnych procedur serializacji. Co bardziej mnie martwiłoby, czy 'typeid (T)' zawsze daje obiekty, które mogą być porównywane przez adres. Można to rozwiązać za pomocą prostej klasy wrapper, która wywołuje 'type_info :: before' jako' operator <'. –

+0

@Alexandre: Miałem na myśli przeniesienie mapy (używając klawiszy typeid jako kluczy) przez kanał sieciowy lub na przykład odczytanie przez różne aplikacje działające w różnych architekturach lub systemach operacyjnych. Ten klucz do mapy byłby zależny od programu i potrzebny byłby niezależny od systemu identyfikator typu, dodany podczas edycji do odpowiedzi. –

6

Jeśli chcesz pozostać przy boost :: nie jestem pewien, ale możesz napisać własne "boost :: any". Używam tego kodu do metod proxy, aby przekazać parametry.

#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; 
} 
Powiązane problemy