2010-09-08 16 views
10

Pour autant que je sache, il n'y a pas de support de sérialisation (boost::serialization, en fait) pour boost::any placeholder.C++ - boost :: toute sérialisation

Est-ce que quelqu'un sait s'il existe un moyen de sérialiser une entité boost::any personnalisée?

Le problème ici est évident: boost::any utilise des espaces réservés basés sur un modèle pour stocker des objets et typeid pour vérifier si boost::any_cast est approprié.

Donc, il y a un cours, qui sont créés de la manière suivante dérivés basés sur des modèles superclasse placeholder et la coutume coutume abstraite:

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

De toute évidence, cela apporte des ennuis quand même de penser à sérialisation ce genre de choses. Peut-être que quelqu'un connaît un truc pour faire ce genre de sérialisation (et bien sûr, la désérialisation appropriée)?

Merci

Répondre

5

Il est impossible du tout, au moins pour les types arbitraires. Notez que vous pourriez peut-être sérialiser en utilisant un code difficile (comme trouver la taille des éléments contenus dans le any), mais tout le code repose sur le compilateur statiquement mettre le any type_code et le dans l'espace réservé. Vous ne pouvez certainement pas le faire en désérialisation en C++, car le type que vous obtiendriez de la désérialisation n'est pas connu au moment de la compilation (comme l'exige le nouveau formulaire boost::any).

La meilleure solution est de construire un type de n'importe quel type spécialisé pour les types exacts d'éléments que vous allez sérialiser. Ensuite, vous pouvez avoir des cas spéciaux pour le type réel d'élément désérialisé, mais notez que chaque type d'élément sérialisation/désérialisation doit être physiquement écrit en tant que code C++ statique.

PD. D'autres suggèrent d'utiliser boost::variant comme représentation de ce type spécialisé contenant les types exacts que vous allez sérialiser. Cependant, vous avez besoin d'un moyen de discerner le type exact de désérialisation (en assignant des identifiants aux types de la variante).

+0

Un type spécialisé pour un ensemble connu de types serait appelé une variante, par ex. ['boost :: variant'] (http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html) (qui, évidemment, ne donne que la fondation). –

+0

@Georg: Oui, merci pour l'indice. J'ai édité la réponse pour refléter cela. –

1

En supposant que vous devez utiliser boost::any et vous ne pouvez pas passer à variant, une solution basée map<type_info const*, string(*)(any)> pourrait vous aider à faire.

Vous devez initialiser à l'exécution un map avec tous les types que vous prévoyez d'utiliser. Bien sûr, vous pouvez utiliser quelque chose le long des lignes 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(); 
    } 
}; 

et remplir la carte avec des adresses de any_serializer<T>::perform sous la &typeid(T) clé. Vous pouvez spécialiser la classe any_serializer et utiliser des macros (moche) pour peupler la carte.

Plus difficile est bien sûr la désérialisation. Je n'ai pas jeté un coup d'oeil à boost::lexical_cast pendant un moment, peut-être qu'il peut fournir de l'aide. J'ai peur que cela dépende totalement du problème. Cependant, vous n'avez besoin que d'une seule fonction, qui prend un string et renvoie une any. Vous pouvez également ajouter votre chaîne de sortie avec un identifiant de type personnalisé.

+1

Ce schéma est plutôt compliqué, et cela ne fonctionnerait pas si, disons, vous envoyez ces sérialisations sur le réseau à un autre processus ou ordinateur. Notez que '& typeid (T)' serait différent pour différentes architectures (et peut-être même des programmes), donc je ne vois pas celui-ci très fiable ... Après l'édition de la réponse: Oui, cela a plus de sens. Utiliser une variante avec un ensemble de types restreints est également une bonne idée. –

+0

@Diego: Je ne vois pas le problème. La carte est locale au programme et est seulement un moyen d'expédier les différentes routines de sérialisation. Ce qui m'inquiète le plus est de savoir si 'typeid (T)' donne toujours des objets qui peuvent être comparés par adresse. Cela peut être résolu avec une classe wrapper simple qui appelle 'type_info :: before' comme' operator <'. –

+0

@Alexandre: Je me suis référé au transfert de la carte (en utilisant les clés typeid) à travers un canal réseau, par exemple, ou être utilisé pour être lu par différentes applications fonctionnant dans différentes architectures ou systèmes d'exploitation. Cette clé pour la carte dépendrait du programme et vous auriez besoin d'un identificateur de type indépendant du système, comme vous l'avez ajouté dans votre modification de la réponse. –

6

Si vous voulez coller avec boost :: je ne suis pas sûr, mais vous pouvez écrire votre propre "boost :: any". J'utilise ce code pour les méthodes proxy pour passer les paramètres.

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