C ++ Boost包含从Boost object_pool构造的指针的std :: map的二进制序列化

时间:2018-05-24 07:34:09

标签: c++ pointers serialization boost stdmap

我的应用程序有一个类" MyClass"。它的对象是从Boost Object_pool构建的。

我需要通过Boost Binary Serialization序列化/反序列化包含这些对象的std :: map作为值。

对于序列化 -

我从池中取一个指针,做一些操作,将它插入std :: map并通过Boost二进制序列化序列化它。

用于反序列化 -

我获取该序列化缓冲区并使用Boost二进制序列化对其进行反序列化。反序列化成功发生,但在此过程中,通过Boost Serialization机制为指针分配新内存,该机制不是从对象池构建的。

因此,我无法重用由boost序列化机制分配的内存,因为它不能返回到对象池,因为它不是从池中构造的。

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/pool/object_pool.hpp>
#include <boost/serialization/map.hpp>
#include <map>
#include <iostream>
#include <sstream>
#include <string>
#include <functional>
#include <stdint.h>

class MyClass
{
  public :
   friend class boost::serialization::access;
   MyClass():data(0)
   {
     std::cout << std::endl << "MyClass()" << std::endl ;
   }

   MyClass( uint32_t val):data(val)
   {
     std::cout << std::endl << "Parameterized MyClass()" << std::endl ;
   }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
      ar & data;
    }

   ~MyClass(){}

    friend std::ostream &operator<<( std::ostream &output, const MyClass &D )
    {
      output << "Data : " << D.print() ;
      return output;
    }

   void print()
   {
      std::cout << std::endl << "Data : " << data << std::endl ;
   }


   private :
   uint32_t data ;
};

int main()
{ 
  try
  {
    typedef std::map<int, MyClass *> ObjectMap ;

    ObjectMap map;

     boost::object_pool<MyClass> pool ;

     map[1] = pool.construct(6) ;
     map[2] = pool.construct(7) ;
     map[3] = pool.construct(8) ;
     map[4] = pool.construct(9) ;

     // Serialization
     std::stringbuf strbuf;
      boost::archive::binary_oarchive oa( strbuf ) ;
      oa << map;


     // Deserialzation
      ObjectMap mapRoundTrip;

      boost::archive::binary_iarchive ia( strbuf ) ;
      ia >> mapRoundTrip ;
    }
    catch ( boost::archive::archive_exception &e )
    {
      std::cout << std::endl << e.what() << std::endl ;
    }
 }  

我的要求是在反序列化期间使用从object_pool获取的指针填充地图。

1 个答案:

答案 0 :(得分:0)

这是Boost序列化实现方式的限制。它不是仅复制指针地址,而是取消引用指针并复制整个对象。这是针对所有STL容器完成的。反序列化时,使用标准分配器创建新对象。

有两种方法可以避免这种情况:通过构建自定义地图类或使用pool_allocator。

使用std::map

的包装器

您可以通过不使用STL容器来避免这种情况。编写自己的地图(包装)。 E.g。

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/pool/object_pool.hpp>
#include <map>
#include <iostream>
#include <sstream>

class MyClass
{
public:
    friend class boost::serialization::access;
    MyClass() { std::cout << "MyClass()\n"; }
    MyClass(int val) :data(val) { std::cout << "MyClass(" << val << ")\n";  }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) { ar & data; }

    void print() { std::cout << "Data : " << data << "\n"; }
private:
    int data;
};

template<class Key, class T>
class MyMap
{
public:
    MyMap(boost::object_pool<T> &pool) : mr_pool(pool) {}

    ~MyMap()
    {
        for (auto& kv : m_map)
        {
            if (kv.second != nullptr) mr_pool.destroy(kv.second);
            kv.second = nullptr;
        }
    }

    typename std::map<Key, T*>::iterator begin() { return m_map.begin(); }
    typename std::map<Key, T*>::iterator end() { return m_map.end(); }

    template<class ... Types>
    void construct(const Key& key, Types ... args)
    {
        m_map[key] = mr_pool.construct(args...);
    }

    template<class Archive>
    void save(Archive & ar, const unsigned int version) const
    {
        ar << m_map.size();
        for (auto& kv : m_map)
        {
            ar << kv.first;
            ar << boost::serialization::binary_object(kv.second, sizeof(T));
        }
    }

    template<class Archive>
    void load(Archive & ar, const unsigned int version)
    {
        size_t size;
        ar >> size;
        for (size_t i = 0; i<size; i++)
        {
            Key key;
            ar >> key;
            T* prt = mr_pool.construct();
            ar >> boost::serialization::make_binary_object(prt, sizeof(T));
            m_map[key] = prt;
        }
    }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int file_version)
    {
        boost::serialization::split_member(ar, *this, file_version);
    }
private:
    boost::object_pool<T>& mr_pool;
    std::map<Key, T*> m_map;
};

int main()
{
    try
    {
        using ObjectMap = MyMap<int, MyClass>;

        boost::object_pool<MyClass> pool;
        ObjectMap map(pool);

        map.construct(1, 6);
        map.construct(2, 7);
        map.construct(3, 8);
        map.construct(4, 9);

        // Serialization
        std::stringbuf strbuf;
        boost::archive::binary_oarchive oa(strbuf);
        oa << map;

        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", ";
            kv.second->print();
        }
        std::cout << "pre destory\n";
        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", data addr: " << kv.second << "\n";
        }

        for (auto& kv : map) {
            if (kv.second != nullptr) pool.destroy(kv.second);
            kv.second = nullptr;
        }

        std::cout << "post destroy\n";
        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", data addr: "  << kv.second << "\n";
        }

        MyClass* temp = pool.construct(10); // to create memory offset
        // Deserialzation
        ObjectMap mapRoundTrip(pool);

        boost::archive::binary_iarchive ia(strbuf);
        ia >> mapRoundTrip;

        for (auto& kv : mapRoundTrip) {
            std::cout << "mapRoundTrip: " << kv.first << ", data addr: " << kv.second << "\n";
        }
        for (auto& kv : mapRoundTrip) {
            std::cout << "mapRoundTrip: " << kv.first << ", ";
            kv.second->print();
        }

        pool.destroy(temp);
        temp = nullptr;
    }
    catch (boost::archive::archive_exception &e)
    {
        std::cout << std::endl << e.what() << std::endl;
    }
    return 0;
}

输出:

MyClass(6)
MyClass(7)
MyClass(8)
MyClass(9)
map: 1, Data : 6
map: 2, Data : 7
map: 3, Data : 8
map: 4, Data : 9
pre destory
map: 1, data addr: 0x24ad720
map: 2, data addr: 0x24ad728
map: 3, data addr: 0x24ad730
map: 4, data addr: 0x24ad738
post destroy
map: 1, data addr: 0
map: 2, data addr: 0
map: 3, data addr: 0
map: 4, data addr: 0
MyClass(10)
MyClass()
MyClass()
MyClass()
MyClass()
mapRoundTrip: 1, data addr: 0x24ad728
mapRoundTrip: 2, data addr: 0x24ad730
mapRoundTrip: 3, data addr: 0x24ad738
mapRoundTrip: 4, data addr: 0x24ad740
mapRoundTrip: 1, Data : 6
mapRoundTrip: 2, Data : 7
mapRoundTrip: 3, Data : 8
mapRoundTrip: 4, Data : 9

DEMO

使用pool_allocator

或者,您可以通过不使用标准分配器来避免这种情况,而是使用池分配器。 E.g。

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/pool/object_pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/serialization/map.hpp>
#include <map>
#include <iostream>
#include <sstream>

class MyClass
{
public:
    friend class boost::serialization::access;
    MyClass() { std::cout << "MyClass empty construct\n"; }
    MyClass(MyClass const& src) :data(src.data) { std::cout << "MyClass copy construct\n"; }
    void swap(MyClass& src) noexcept { std::swap(data, src.data); }
    MyClass(MyClass&& src) :MyClass() { src.swap(*this); std::cout << "MyClass move construct\n"; }

    MyClass(int val) :data(val) { std::cout << "MyClass data construct (" << val << ")\n"; }

    MyClass& operator=(MyClass src) { src.swap(*this); return *this; }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) { ar & data; }

    void print() { std::cout << "Data : " << data << "\n"; }
private:
    int data;
};

int main()
{
    using ObjectMap = std::map<int, MyClass, std::less<int>, boost::pool_allocator<MyClass>>;
    using Pool = boost::singleton_pool<boost::pool_allocator_tag, sizeof(ObjectMap::value_type)>;

    try
    {
        ObjectMap map;

        map[1] = MyClass(6);
        map[2] = MyClass(7);
        map[3] = MyClass(8);
        map[4] = MyClass(9);

        // Serialization
        std::stringbuf strbuf;
        boost::archive::binary_oarchive oa(strbuf);
        oa << map;

        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", ";
            kv.second.print();
        }
        std::cout << "pre destory\n";
        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", data addr: " << &kv.second << "\n";
        }

        map.clear();
        Pool::purge_memory();

        map[5] = MyClass(10);

        std::cout << "post destroy and reassign\n";
        for (auto& kv : map) {
            std::cout << "map: " << kv.first << ", data addr: " << &kv.second << "\n";
        }

        // Deserialzation
        ObjectMap mapRoundTrip;

        boost::archive::binary_iarchive ia(strbuf);
        ia >> mapRoundTrip;

        for (auto& kv : mapRoundTrip) {
            std::cout << "mapRoundTrip: " << kv.first << ", data addr: " << &kv.second << "\n";
        }
        for (auto& kv : mapRoundTrip) {
            std::cout << "mapRoundTrip: " << kv.first << ", ";
            kv.second.print();
        }

        mapRoundTrip.clear();
    }
    catch (boost::archive::archive_exception &e)
    {
        std::cout << std::endl << e.what() << std::endl;
    }
    Pool::purge_memory();

    return 0;
}

输出:

MyClass data construct (6)
MyClass empty construct
MyClass data construct (7)
MyClass empty construct
MyClass data construct (8)
MyClass empty construct
MyClass data construct (9)
MyClass empty construct
map: 1, Data : 6
map: 2, Data : 7
map: 3, Data : 8
map: 4, Data : 9
pre destory
map: 1, data addr: 0x118e604
map: 2, data addr: 0x118e62c
map: 3, data addr: 0x118e654
map: 4, data addr: 0x118e67c
MyClass data construct (10)
MyClass empty construct
post destroy and reassign
map: 5, data addr: 0x118e604
MyClass empty construct
MyClass empty construct
MyClass move construct
MyClass empty construct
MyClass empty construct
MyClass move construct
MyClass empty construct
MyClass empty construct
MyClass move construct
MyClass empty construct
MyClass empty construct
MyClass move construct
mapRoundTrip: 1, data addr: 0x118e62c
mapRoundTrip: 2, data addr: 0x118e654
mapRoundTrip: 3, data addr: 0x118e67c
mapRoundTrip: 4, data addr: 0x118e6a4
mapRoundTrip: 1, Data : 6
mapRoundTrip: 2, Data : 7
mapRoundTrip: 3, Data : 8
mapRoundTrip: 4, Data : 9

您可以通过检查地址来查看内存池的重用方式。

DEMO