Это ограничение способа реализации сериализации Boost.Вместо того, чтобы просто копировать адрес указателя, он отменяет ссылки на указатели и копирует весь объект.Это сделано для всех контейнеров STL.При десериализации создается новый объект с использованием стандартного распределителя.
Есть два способа обойти это: путем создания пользовательского класса карты или с помощью pool_allocator.
Использование оболочки для std::map
Вы можете обойтиэто не используя контейнеры STL.Напишите свою собственную карту (обертку).Например,
#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
В качестве альтернативы вы можетеобойти это, не используя стандартный распределитель, а вместо этого использовать распределитель пула.Например,
#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