Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can boost::serialization be used with std::shared_ptr from C++11?

I know that there is a Boost module for serialization of boost::shared_ptr, but I cannot find anything for std::shared_ptr.

Also, I don't know how to implement it easily. I'm afraid that the following code

namespace boost{namespace serialization{
template<class Archive, class T>
inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version)
{
  if(Archive::is_loading::value) {T*r;ar>>r;t=r;}
  else {ar<<t.get();}
}
}}//namespaces

doesn't work. Indeed, if some object was referred multiple times, it would be loaded with first run of ar>>r, and after that just a pointer will be copied. However we would create multiple shared_ptr objects pointing to it, and therefore would destruct it more than one time.

Any ideas on that?

Some technical details about the system I'm using:

  • OS: Ubuntu 11.10 (x64)
  • Compiler: g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
  • boost version: 1.46.1 (installed with sudo apt-get install libboost-dev)
like image 899
fiktor Avatar asked Nov 13 '11 22:11

fiktor


4 Answers

As of Boost 1.56, the serialization library has built-in support for std::shared_ptr. You do not need to implement your own serialization helper functions if you can use a more recent version of the library.

like image 76
joshuanapoli Avatar answered Nov 14 '22 06:11

joshuanapoli


I finally found a solution on how to serialize the std::shared_ptr using boost serialization. All you need is the following piece of code (explanation follows):

#include <boost/serialization/split_free.hpp>
#include <boost/unordered_map.hpp>

//---/ Wrapper for std::shared_ptr<> /------------------------------------------

namespace boost { namespace serialization {

template<class Archive, class Type>
void save(Archive & archive, const std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
    Type *data = value.get();
    archive << data;
}

template<class Archive, class Type>
void load(Archive & archive, std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
    Type *data;
    archive >> data;

    typedef std::weak_ptr<Type> WeakPtr;
    static boost::unordered_map<void*, WeakPtr> hash;

    if (hash[data].expired())
    {
         value = std::shared_ptr<Type>(data);
         hash[data] = value;
    }
    else value = hash[data].lock();
}

template<class Archive, class Type>
inline void serialize(Archive & archive, std::shared_ptr<Type> & value, const unsigned int version)
{
    split_free(archive, value, version);
}

}}

This code simply serializes the object managed by the std::shared_ptr in the function save(). If multiple std::shared_ptr instances point to same object boost serialization will take automatically care to store it only once. The magic happens in load() where boost serialization returns a raw pointer to the object (data). This raw pointer is looked up in a hash that holds a weak_ptr for each raw pointer. In case that the weak_ptr in the hash is expired we can safely create a new shared_ptr instance, let it manage the raw pointer and store a weak_ptr in the hash. In case that the weak_ptr is not expired we simply lock it to return a shared_ptr. This way the reference counting is correct.

like image 29
denim Avatar answered Nov 14 '22 06:11

denim


Serialisation is provided by boost and not by the standard library and although shared_ptr is included in the standard it is part of TR1 (technical report 1).

TR1 as of now does not have serialization. So I would recommend that you use boost's shared pointer.

like image 3
ken Avatar answered Nov 14 '22 07:11

ken


You haven't said what "doesn't work" means; it doesn't compile? It doesn't load/store the value properly? It doesn't..what?

There are two problems I can identify here, one may be part of your intentional design though.

The first, you have not made a correct pointer in the load procedure. Let's break it down:

inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version) {
    if (1) { //unimportant
        T* r;
        ar >> r;
        t = r;
    }
}

When you make an object of std::shared_ptr, you are instantiating a class template to provide pointer-like capability (as you know). If you made with an int, it will work as an int pointer. However, simply passing the type as T does NOT mean a pointer created of that type will automatically use that template; indeed, you're creating a bare pointer with T* r. It may as well be int *r. You then fail to initialize it with new; r could be pointing anywhere. If it were intialized properly with a new, you MAY get correct reference counting for creation/deletion of that object; this is one area where std::shared_ptr doesn't seem worth the effort to me. I think the assignment from a bare pointer counts as the second reference, not the first, but I may be wrong? Anyhow, that's not the problem. You're probably corrupting the heap; a compiler should spit out a warning about using an uninitialized pointer, it's a wonder it hasn't. I hope you don't have warnings turned off.

If I remember correctly, that declaration of r needs to be replaced with:

std::shared_ptr<T> r = new std::shared_ptr<T>;

Although it may be

std::shared_ptr<T> r = new std::shared_ptr<T>(r());

I haven't used shared_ptr for a while.

TR1, by the way, has been out for at least 2 years. It is based off of boost's shared_ptr. I don't know why you're using Boost 1.46, but I think that it was out by the time shared_ptr became part of the standard? So it should be compatible...?

Anyhow, the second potential error comes with

t = r;

I'm assuming - incorrectly? - that you WISH to decrement the reference count to t by reassigning it (and possibly destroying the object t points to). If you meant to copy it, you would of course use:

*t = *r;

and make sure your copy constructor works properly.

like image 2
std''OrgnlDave Avatar answered Nov 14 '22 06:11

std''OrgnlDave