13

As far as I understand, there is no serialization (boost::serialization, actually) support for boost::any placeholder.

Does someone know if there is a way to serialize a custom boost::any entity?

The problem here is obvious: boost::any uses template-based placeholders to store objects and typeid to check if boost::any_cast is appropriate.

So, there is a custom abstract superclass placeholder and custom template-based derived classes, which are created the following way:

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

Obviously, this brings some troubles when even thinking about serializing this stuff. Maybe someone knows some trick to make such kind of serialization (and of course, proper deserialization)?

Thank you

Yippie-Ki-Yay
  • 22,026
  • 26
  • 90
  • 148

4 Answers4

6

If you want to stick with boost::any i am not sure but you can write your own "boost::any". I'm using this code for proxy methods to pass the parameters.

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

It is not possible at all, at least for arbitrary types. Note that maybe you could serialize using some tricky code (like finding the size of the elements contained in the any), but the any code relies on the compiler statically putting the any type_code and the proper types inside the placeholder. You surely cannot do that in deserialization in C++, as the type that you'd get from the deserialization is not known at compile time (as required by the newly formed boost::any).

The best solution is to build some kind of specialized any type for the exact types of elements you're going to serialize. Then, you can have special cases for the actual type of element being deserialized, but note that each element type serialization/deserialization has to be phisically written as static C++ code.

PD. Some others suggested using boost::variant as a representation of this specialized type holding the exact types you're going to serialize. You need a way of discerning the exact type on deserialization, though (maybe assigning identifiers to types in the variant).

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
  • A specialized type for a know set of types would be called a variant like e.g. [`boost::variant`](http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html) (which obviously only gives the foundation). – Georg Fritzsche Sep 08 '10 at 22:48
  • @Georg: Yes, thanks for the hint. I edited the response to reflect that. – Diego Sevilla Sep 08 '10 at 22:54
  • boost::variant is already serializable https://www.boost.org/doc/libs/1_74_0/boost/serialization/variant.hpp – olq_plo Sep 21 '20 at 19:06
1

There is no need to create new class. Try to use xany https://sourceforge.net/projects/extendableany/?source=directory xany class allows to add new methods to any's existing functionality. By the way there is a example in documentation which does exactly what you want.

ArmanHunanyan
  • 905
  • 3
  • 11
  • 24
1

Assuming you have to use boost::any and you cannot switch to variant, a map<type_info const*, string(*)(any)> based solution could get you done.

You have to initialize at runtime such a map with all the types you plan to use. Of course, you can use something along the lines of

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

and populate the map with addresses of any_serializer<T>::perform under the key &typeid(T). You can specialize the class any_serializer and use some (ugly) macros to populate the map.

More difficult is of course the deserialization. I haven't had a look at boost::lexical_cast for a while, perhaps it can provide some help. I am afraid that this is totally problem-dependant. However, you only need one function, which takes a string and returns one any. You may also want to prepend your output string with a custom type identifier.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 1
    This schema is rather tricky, and it wouldn't work if, say, you send those serializations over the net to other process or computer. Note that `&typeid(T)` would be different for different architectures (and maybe even programs), so I don't see this one very reliable... After response edit: Yes, this has more sense using a custom type identifier. Using variant with a set of restricted types is also a good idea. – Diego Sevilla Sep 08 '10 at 22:41
  • @Diego: I don't see the problem. The map is local to the program and is only a mean to dispatch the various serialization routines. What I'd be more concerned about if whether `typeid(T)` always yields objects which can be compared by address. This can be solved with a simple wrapper class which calls `type_info::before` as `operator<`. – Alexandre C. Sep 08 '10 at 22:47
  • @Alexandre: I was refering to transfering the map (using typeid's as keys) through a network channel, for example, or being used to be read by different applications running in different architectures or operating systems. This key for the map would be program-dependant, and you'd need a system-independent type identifier, as you added in your edit to the response. – Diego Sevilla Sep 08 '10 at 22:51
  • @Diego: Of course, you don't transfer the map... One more point: the serialization routines *should not* be implemented in shared libraries, since this too will likely mess up the `type_info` stuff... – Alexandre C. Sep 08 '10 at 22:56