处理摘要类和序列化的unordered_map
Dealing with unordered_map of abstract classes and serialization
假设我有以下结构:
struct Base {
virtual int get_x() = 0;
}
struct A : Base {
int get_x() {
// ...
return 0;
}
}
struct B : Base {
int get_x() {
// ...
return 1;
}
}
这些我也有序列化方法类:
BOOST_SERIALIZATION_SPLIT_FREE(A);
namespace boost {
namespace serialization {
template<class Archive>
void save(Archive & ar, const A & a,
const unsigned int version) {
// ...
}
template<class Archive>
void load(Archive & ar, A & a,
const unsigned int version) {
// ...
}
}
}
// same for B
在我的代码中,我正在制作一个 unordered_map<int, A>
。我有保存和加载这张地图的功能:
inline void save_map(string filename, unordered_map<int, A>& a_dict) {
ofstream filestream(filename);
boost::archive::binary_oarchive archive(filestream,
boost::archive::no_codecvt);
archive << a_dict;
}
inline void load_map(string filename, unordered_map<int, A>* a_dict) {
ifstream filestream(filename);
boost::archive::binary_iarchive archive(filestream,
boost::archive::no_codecvt);
archive >> *a_dict;
}
我现在想以一种优雅的方式概括我的地图定义和这些函数,这样我的代码就不知道我的对象是 A
还是 B
。显然我 运行 进入对象切片问题,例如,如果我刚开始使用 unordered_map<int, Base>
,但此时我已经有点迷失在指针的杂草中并且无法弄清楚出解决方案。
我可能会推荐现成的指针容器。有
指针容器
这很简单:
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/ptr_container/serialize_ptr_unordered_map.hpp>
struct Base {
virtual ~Base() noexcept = default;
virtual int get_x() const = 0;
friend auto& operator<<(std::ostream& os, Base const& b) {
return os << typeid(b).name() << "[" << b.get_x() << "]";
}
};
struct A : Base { int get_x() const override { return 0; } };
struct B : Base { int get_x() const override { return 1; } };
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
namespace boost { namespace serialization {
template <class Ar> void serialize(Ar& ar, Base&, unsigned) { }
template <class Ar> void serialize(Ar& ar, A& a, unsigned) { ar& base_object<Base>(a); }
template <class Ar> void serialize(Ar& ar, B& b, unsigned) { ar& base_object<Base>(b); }
}} // namespace boost::serialization
template <typename Map>
inline void save_map(std::string const& filename, Map const& map) {
std::ofstream ofs(filename, std::ios::binary);
boost::archive::binary_oarchive archive(ofs, boost::archive::no_codecvt);
archive << map;
}
template <typename Map>
inline void load_map(std::string const& filename, Map& map) {
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive archive(ifs, boost::archive::no_codecvt);
archive >> map;
}
#include <iostream>
int main()
{
using Dict = boost::ptr_unordered_map<int, Base>;
{
Dict dict;
dict.insert(100, std::make_unique<A>());
dict.insert(200, std::make_unique<B>());
dict.insert(300, std::make_unique<B>());
dict.insert(400, std::make_unique<A>());
save_map("test.bin", dict);
}
{
Dict roundtrip;
load_map("test.bin", roundtrip);
for (auto const& [k,v]: roundtrip) {
std::cout << k << ": " << *v << "\n";
}
}
}
打印例如
100: 1A[0]
200: 1B[1]
300: 1B[1]
400: 1A[0]
并且没有内存泄漏。
自己动手
这有一些好处(更现代的界面有时在其他方面更“指针”界面):
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/boost_unordered_map.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
struct Base {
virtual ~Base() noexcept = default;
virtual int get_x() const = 0;
friend auto& operator<<(std::ostream& os, Base const& b) {
return os << typeid(b).name() << "[" << b.get_x() << "]";
}
};
struct A : Base { int get_x() const override { return 0; } };
struct B : Base { int get_x() const override { return 1; } };
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
namespace boost { namespace serialization {
template <class Ar> void serialize(Ar& ar, Base&, unsigned) { }
template <class Ar> void serialize(Ar& ar, A& a, unsigned) { ar& base_object<Base>(a); }
template <class Ar> void serialize(Ar& ar, B& b, unsigned) { ar& base_object<Base>(b); }
}} // namespace boost::serialization
template <typename Map>
inline void save_map(std::string const& filename, Map const& map) {
std::ofstream ofs(filename, std::ios::binary);
boost::archive::binary_oarchive archive(ofs, boost::archive::no_codecvt);
archive << map;
}
template <typename Map>
inline void load_map(std::string const& filename, Map& map) {
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive archive(ifs, boost::archive::no_codecvt);
archive >> map;
}
#include <iostream>
int main()
{
using Dict = boost::unordered_map<int, std::unique_ptr<Base> >;
{
Dict dict;
dict.emplace(100, std::make_unique<A>());
dict.emplace(200, std::make_unique<B>());
dict.emplace(300, std::make_unique<B>());
dict.emplace(400, std::make_unique<A>());
save_map("test.bin", dict);
}
{
Dict roundtrip;
load_map("test.bin", roundtrip);
for (auto const& [k,v]: roundtrip) {
std::cout << k << ": " << *v << "\n";
}
}
}
打印,同样没有内存泄漏,类似于:
100: 1A[0]
200: 1B[1]
300: 1B[1]
400: 1A[0]
假设我有以下结构:
struct Base {
virtual int get_x() = 0;
}
struct A : Base {
int get_x() {
// ...
return 0;
}
}
struct B : Base {
int get_x() {
// ...
return 1;
}
}
这些我也有序列化方法类:
BOOST_SERIALIZATION_SPLIT_FREE(A);
namespace boost {
namespace serialization {
template<class Archive>
void save(Archive & ar, const A & a,
const unsigned int version) {
// ...
}
template<class Archive>
void load(Archive & ar, A & a,
const unsigned int version) {
// ...
}
}
}
// same for B
在我的代码中,我正在制作一个 unordered_map<int, A>
。我有保存和加载这张地图的功能:
inline void save_map(string filename, unordered_map<int, A>& a_dict) {
ofstream filestream(filename);
boost::archive::binary_oarchive archive(filestream,
boost::archive::no_codecvt);
archive << a_dict;
}
inline void load_map(string filename, unordered_map<int, A>* a_dict) {
ifstream filestream(filename);
boost::archive::binary_iarchive archive(filestream,
boost::archive::no_codecvt);
archive >> *a_dict;
}
我现在想以一种优雅的方式概括我的地图定义和这些函数,这样我的代码就不知道我的对象是 A
还是 B
。显然我 运行 进入对象切片问题,例如,如果我刚开始使用 unordered_map<int, Base>
,但此时我已经有点迷失在指针的杂草中并且无法弄清楚出解决方案。
我可能会推荐现成的指针容器。有
指针容器
这很简单:
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/ptr_container/serialize_ptr_unordered_map.hpp>
struct Base {
virtual ~Base() noexcept = default;
virtual int get_x() const = 0;
friend auto& operator<<(std::ostream& os, Base const& b) {
return os << typeid(b).name() << "[" << b.get_x() << "]";
}
};
struct A : Base { int get_x() const override { return 0; } };
struct B : Base { int get_x() const override { return 1; } };
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
namespace boost { namespace serialization {
template <class Ar> void serialize(Ar& ar, Base&, unsigned) { }
template <class Ar> void serialize(Ar& ar, A& a, unsigned) { ar& base_object<Base>(a); }
template <class Ar> void serialize(Ar& ar, B& b, unsigned) { ar& base_object<Base>(b); }
}} // namespace boost::serialization
template <typename Map>
inline void save_map(std::string const& filename, Map const& map) {
std::ofstream ofs(filename, std::ios::binary);
boost::archive::binary_oarchive archive(ofs, boost::archive::no_codecvt);
archive << map;
}
template <typename Map>
inline void load_map(std::string const& filename, Map& map) {
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive archive(ifs, boost::archive::no_codecvt);
archive >> map;
}
#include <iostream>
int main()
{
using Dict = boost::ptr_unordered_map<int, Base>;
{
Dict dict;
dict.insert(100, std::make_unique<A>());
dict.insert(200, std::make_unique<B>());
dict.insert(300, std::make_unique<B>());
dict.insert(400, std::make_unique<A>());
save_map("test.bin", dict);
}
{
Dict roundtrip;
load_map("test.bin", roundtrip);
for (auto const& [k,v]: roundtrip) {
std::cout << k << ": " << *v << "\n";
}
}
}
打印例如
100: 1A[0]
200: 1B[1]
300: 1B[1]
400: 1A[0]
并且没有内存泄漏。
自己动手
这有一些好处(更现代的界面有时在其他方面更“指针”界面):
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/boost_unordered_map.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
struct Base {
virtual ~Base() noexcept = default;
virtual int get_x() const = 0;
friend auto& operator<<(std::ostream& os, Base const& b) {
return os << typeid(b).name() << "[" << b.get_x() << "]";
}
};
struct A : Base { int get_x() const override { return 0; } };
struct B : Base { int get_x() const override { return 1; } };
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
namespace boost { namespace serialization {
template <class Ar> void serialize(Ar& ar, Base&, unsigned) { }
template <class Ar> void serialize(Ar& ar, A& a, unsigned) { ar& base_object<Base>(a); }
template <class Ar> void serialize(Ar& ar, B& b, unsigned) { ar& base_object<Base>(b); }
}} // namespace boost::serialization
template <typename Map>
inline void save_map(std::string const& filename, Map const& map) {
std::ofstream ofs(filename, std::ios::binary);
boost::archive::binary_oarchive archive(ofs, boost::archive::no_codecvt);
archive << map;
}
template <typename Map>
inline void load_map(std::string const& filename, Map& map) {
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive archive(ifs, boost::archive::no_codecvt);
archive >> map;
}
#include <iostream>
int main()
{
using Dict = boost::unordered_map<int, std::unique_ptr<Base> >;
{
Dict dict;
dict.emplace(100, std::make_unique<A>());
dict.emplace(200, std::make_unique<B>());
dict.emplace(300, std::make_unique<B>());
dict.emplace(400, std::make_unique<A>());
save_map("test.bin", dict);
}
{
Dict roundtrip;
load_map("test.bin", roundtrip);
for (auto const& [k,v]: roundtrip) {
std::cout << k << ": " << *v << "\n";
}
}
}
打印,同样没有内存泄漏,类似于:
100: 1A[0]
200: 1B[1]
300: 1B[1]
400: 1A[0]