2010-09-08 11 views
10

Por lo que yo entiendo, no hay serialización (boost::serialization, en realidad) soporte para boost::any marcador de posición.C++ - boost :: cualquier serialización

¿Alguien sabe si hay una forma de serializar una entidad personalizada boost::any?

El problema aquí es obvio: boost::any utiliza marcadores de posición basados ​​en plantillas para almacenar objetos y typeid para verificar si boost::any_cast es apropiado.

Por lo tanto, hay una costumbre abstractos clases derivadas basadas en plantillas superclase placeholder y costumbre, que se crean de la siguiente manera:

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

Obviamente, esto trae algunos problemas cuando ni siquiera pensar en la serialización de estas cosas. Quizás alguien sepa algún truco para hacer ese tipo de serialización (y, por supuesto, deserialización adecuada)?

Gracias

Respuesta

5

No es posible en absoluto, al menos para los tipos arbitrarios. Tenga en cuenta que tal vez podría serializar usando algún código complicado (como encontrar el tamaño de los elementos contenidos en el cualquiera), pero el código depende del compilador que coloca estáticamente el type_code y los tipos adecuados dentro del marcador de posición. Seguramente no puedes hacer eso en la deserialización en C++, ya que el tipo que obtendrías de la deserialización no se conoce en tiempo de compilación (como es requerido por el recién formado boost::any).

La mejor solución es crear algún tipo de tipo especializado para los tipos exactos de elementos que va a serializar. Entonces, puede tener casos especiales para el tipo real de elemento que se está deserializando, pero tenga en cuenta que cada serialización/deserialización del tipo de elemento debe escribirse físicamente como código estático de C++.

PD. Algunos otros sugirieron usar boost::variant como una representación de este tipo especializado que contiene los tipos exactos que va a serializar. Sin embargo, se necesita una forma de discernir el tipo exacto de deserialización (tal vez asignar identificadores a los tipos en la variante).

+0

Un tipo especializado para un conjunto conocido de tipos se denominaría una variante, como p. Ej. ['boost :: variant'] (http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html) (que obviamente solo da la base). –

+0

@Georg: Sí, gracias por la pista. Edité la respuesta para reflejar eso. –

1

Suponiendo que tiene que usar boost::any y no puede cambiar a variant, una solución basada en map<type_info const*, string(*)(any)> podría hacerlo.

Tiene que inicializar en tiempo de ejecución como map con todos los tipos que planea usar. Por supuesto, se puede usar algo en la línea de

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

y poblar el mapa con las direcciones de any_serializer<T>::perform en la clave &typeid(T). Puede especializar la clase any_serializer y usar algunas macros (feas) para rellenar el mapa.

Más difícil es, por supuesto, la deserialización. No he echado un vistazo a boost::lexical_cast por un tiempo, tal vez pueda brindar alguna ayuda. Me temo que esto es totalmente dependiente del problema. Sin embargo, solo necesita una función, que toma un string y devuelve un any. Es posible que también desee anteponer su cadena de salida con un identificador de tipo personalizado.

+1

Este esquema es bastante complicado, y no funcionaría si, por ejemplo, envía esas serializaciones a otro proceso o computadora a través de la red. Tenga en cuenta que '& typeid (T)' sería diferente para diferentes arquitecturas (y tal vez incluso para programas), así que no veo que este sea muy confiable ... Después de la edición de respuesta: Sí, tiene más sentido usar un identificador de tipo personalizado. Usar una variante con un conjunto de tipos restringidos también es una buena idea. –

+0

@Diego: no veo el problema. El mapa es local al programa y solo sirve para enviar las diversas rutinas de serialización. Lo que más me preocuparía sería si 'typeid (T)' siempre produce objetos que pueden compararse por dirección. Esto se puede resolver con una clase contenedora simple que llama 'type_info :: before' como' operator <'. –

+0

@Alexandre: me estaba refiriendo a la transferencia del mapa (usando typeid's como teclas) a través de un canal de red, por ejemplo, o para ser leído por diferentes aplicaciones que se ejecutan en diferentes arquitecturas o sistemas operativos. Esta clave para el mapa dependería del programa, y ​​necesitaría un identificador de tipo independiente del sistema, como lo agregó en su edición a la respuesta. –

6

Si quieres quedarte con boost :: any no estoy seguro pero puedes escribir tu propio "boost :: any". Estoy usando este código para métodos proxy para pasar los parámetros.

#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; 
} 
Cuestiones relacionadas