Boost.Serialize:写一个通用的地图序列化函数
Boost.Serialize: writing a general map serialization function
Boost.Serialize 为 std::map / std::multimap 提供显式序列化,这不适用于其他类似地图的容器。
我想序列化那些而不需要每次都重写这些函数,但是 Boost 抱怨模棱两可。
这是我的代码:
/** Some sfinae to detect types that behave like std::map **/
template<typename T, typename = void>
struct is_map_ish : std::false_type { };
template<template<typename K, typename V> typename Map, typename K, typename V>
struct is_map_ish<
Map<K,V>,
std::void_t<
typename Map<K,V>::key_type,
typename Map<K,V>::value_type
>
> : std::true_type {
};
template<class Archive, class Map>
inline auto save(
Archive & ar,
const Map& t,
const unsigned int /* file_version */,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value>
{
boost_155::serialization::stl::save_collection<
Archive,
Map
>(ar, t);
}
template<class Archive, class Map>
inline auto load(
Archive & ar,
Map& t,
const unsigned int /* file_version */,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value> {
boost_155::serialization::stl::load_collection<
Archive,
Map,
boost_155::serialization::stl::archive_input_map<
Archive, Map >,
boost_155::serialization::stl::no_reserve_imp<Map>
>(ar, t);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive, class Map>
inline auto serialize(
Archive & ar,
Map &t,
const unsigned int file_version,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value> {
boost_155::serialization::split_free(ar, t, file_version);
}
serialize
函数与
发生冲突
// default implementation - call the member function "serialize"
template<class Archive, class T>
inline void serialize(
Archive & ar, T & t, const BOOST_155_PFTO unsigned int file_version
){
access::serialize(ar, t, static_cast<unsigned int>(file_version));
}
in boost,我显然无法更改。我尝试(如上面的代码所示)将一些 SFINAE 放入 return 类型和函数参数列表中,但这并不能帮助我避免重载歧义。
我有哪些选择?在 C++20 中我会尝试概念,但在这里我只能使用 C++17。
我的建议是使用 ADL,或者使用 Serialization Wrapper。
因为不知道什么时候停下来,所以做了下面的概念验证。一般往返测试允许:
- 允许切换存档类型
- 可选地提供包装函数(在我的示例中,我们将在下面定义
as_map
和 as_multimap
);默认是恒等函数
- 将包含 4 个条目的“map-ish 容器”序列化为字符串流
- 反序列化它
- 将副本与原件进行比较
template <typename OA, typename IA, template <typename...> class Map,
typename WrapFun = std::identity>
void roundtrip_impl(std::string_view archive_type, WrapFun apply_wrap = {})
{
using Container = Map<int, std::string>;
Container original{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
using namespace boost::archive;
std::stringstream ss;
{
OA oa(ss, archive_flags::no_header);
auto&& payload = apply_wrap(original);
oa << boost::make_nvp("data", payload);
}
std::cout << archive_type << " "
<< (typename boost::serialization::is_wrapper<decltype(
apply_wrap(original))>::type{}
? "Wrapped "
: "Builtin ")
<< std::setw(3) << ss.str().length() << " bytes" << std::flush;
{
IA ia(ss, archive_flags::no_header);
Container replica;
auto&& payload = apply_wrap(replica);
ia >> boost::serialization::make_nvp("data", payload);
std::cout << " - roundtrip verified: " << std::boolalpha
<< (original == replica) << std::endl;
}
}
现在为了系统化,我们将定义一个测试所有存档类型的测试入口点:
template <template <typename...> class Map, typename WrapFun = std::identity>
void roundtrips(WrapFun do_wrap = {})
{
roundtrip_impl<
boost::archive::binary_oarchive,
boost::archive::binary_iarchive,
Map>("Binary", do_wrap);
roundtrip_impl<
boost::archive::text_oarchive,
boost::archive::text_iarchive,
Map>("Text ", do_wrap);
roundtrip_impl<
boost::archive::xml_oarchive,
boost::archive::xml_iarchive,
Map>("XML ", do_wrap);
}
This specifically verifies that the wrapper works in the presence of NVP wrappers.
序列化包装器
对于没有内置库支持的 map-ish 容器,让我们创建一个包装器:
namespace Serialization {
template <typename Map, typename Unique = std::true_type> struct map_wrapper {
Map& ref;
map_wrapper(Map& m) : ref(m) {}
constexpr static bool is_unique = Unique::value;
};
添加一些方便的函数as_map
和as_multimap
:
struct {
constexpr auto operator()(auto& ref) const { return map_wrapper(ref); }
} static inline constexpr as_map;
struct {
constexpr auto operator()(auto& ref) const {
return map_wrapper<decltype(ref), std::false_type>(ref);
}
} static inline constexpr as_multimap;
现在我们可以为包装器实现泛型 save/load:
// ADL finds these
template <typename Ar, typename... Args>
void save(Ar& ar, map_wrapper<Args...> const& w, unsigned v) {
size_t n = std::size(w.ref);
ar & boost::make_nvp("size", n);
for (auto& element : w.ref) {
ar & boost::make_nvp("item", element);
}
}
这很简单。当然,反序列化更多样化一些:
template <typename Ar, typename Map, typename Unique>
void load(Ar& ar, map_wrapper<Map, Unique>& w, unsigned v)
{
using V = typename boost::range_value<Map>::type;
w.ref.clear(); // optionally?
size_t n;
ar & boost::make_nvp("size", n);
while (n--) {
V element;
ar & boost::make_nvp("item", element);
// insert and fix object tracking!
if constexpr (Unique()) {
auto [it, inserted] = w.ref.insert(std::move(element));
if (inserted)
ar.reset_object_address(std::addressof(*it), &element);
} else {
auto it = w.ref.insert(std::end(w.ref), std::move(element));
ar.reset_object_address(std::addressof(*it), &element);
}
}
}
最重要的一点是我们在何处固定对象地址以进行地址跟踪。参见 docs。
没有模板BOOST_SERIALIZATION_SPLIT_FREE
,所以我们写:
template <typename Ar, typename... Args>
void serialize(Ar& ar, map_wrapper<Args...>& m, unsigned v)
{
if constexpr (typename Ar::is_saving())
save(ar, m, v);
else
load(ar, m, v);
}
} // namespace Serialization
快完成了。我们只需要帮助图书馆意识到我们是一个包装器:
namespace boost::serialization {
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...>> {
using type = std::true_type;
};
// it's heinous, but we have to do it for const qualified too
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...> const> {
using type = std::true_type;
};
} // namespace boost::serialization
运行 一些测试
// bunch of tests with various containers
#include
#include
#include
#include
#include
#include
// including fantasy "maps"
template <typename K, typename V>
using FakeMap = boost::container::set<std::pair<K, V>>;
template <typename K, typename V>
using FakeMultiMap = std::vector<std::pair<K, V>>;
int main()
{
roundtrips<std::map>();
roundtrips<std::multimap>();
roundtrips<std::unordered_map>();
roundtrips<std::unordered_multimap>();
// no direct support, but we can wrap!
using Serialization::as_map;
using Serialization::as_multimap;
roundtrips<boost::container::map>(as_map);
roundtrips<boost::container::multimap>(as_multimap);
roundtrips<boost::unordered_map>(as_map);
roundtrips<boost::unordered_multimap>(as_multimap);
roundtrips<FakeMap>(as_map);
roundtrips<FakeMultiMap>(as_multimap);
}
版画
Binary Builtin 85 bytes - roundtrip verified: true
Text Builtin 47 bytes - roundtrip verified: true
XML Builtin 393 bytes - roundtrip verified: true
Binary Builtin 85 bytes - roundtrip verified: true
Text Builtin 47 bytes - roundtrip verified: true
XML Builtin 393 bytes - roundtrip verified: true
Binary Builtin 93 bytes - roundtrip verified: true
Text Builtin 49 bytes - roundtrip verified: true
XML Builtin 425 bytes - roundtrip verified: true
Binary Builtin 93 bytes - roundtrip verified: true
Text Builtin 49 bytes - roundtrip verified: true
XML Builtin 425 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
警告
请注意,在这些示例中,我假设 map-ish 容器将使用可默认构造的comparators/hash function/equality 比较器。否则你必须记住实现 load_/save_construct_data
并且在这一点上我相信你最好只编写特定于类型的实现。
完整列表
(反bitrot)
#include <boost/archive/basic_archive.hpp>
#include <boost/serialization/nvp.hpp>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cassert>
template <typename OA, typename IA, template <typename...> class Map,
typename WrapFun = std::identity>
void roundtrip_impl(std::string_view archive_type, WrapFun apply_wrap = {})
{
using Container = Map<int, std::string>;
Container original{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
using namespace boost::archive;
std::stringstream ss;
{
OA oa(ss, archive_flags::no_header);
auto&& payload = apply_wrap(original);
oa << boost::make_nvp("data", payload);
}
std::cout << archive_type << " "
<< (typename boost::serialization::is_wrapper<decltype(
apply_wrap(original))>::type{}
? "Wrapped "
: "Builtin ")
<< std::setw(3) << ss.str().length() << " bytes" << std::flush;
{
IA ia(ss, archive_flags::no_header);
Container replica;
auto&& payload = apply_wrap(replica);
ia >> boost::serialization::make_nvp("data", payload);
std::cout << " - roundtrip verified: " << std::boolalpha
<< (original == replica) << std::endl;
}
}
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
template <template <typename...> class Map, typename WrapFun = std::identity>
void roundtrips(WrapFun do_wrap = {})
{
roundtrip_impl<
boost::archive::binary_oarchive,
boost::archive::binary_iarchive,
Map>("Binary", do_wrap);
roundtrip_impl<
boost::archive::text_oarchive,
boost::archive::text_iarchive,
Map>("Text ", do_wrap);
roundtrip_impl<
boost::archive::xml_oarchive,
boost::archive::xml_iarchive,
Map>("XML ", do_wrap);
}
#include <boost/range/value_type.hpp>
namespace Serialization {
template <typename Map, typename Unique = std::true_type> struct map_wrapper {
Map& ref;
map_wrapper(Map& m) : ref(m) {}
constexpr static bool is_unique = Unique::value;
};
struct {
constexpr auto operator()(auto& ref) const { return map_wrapper(ref); }
} static inline constexpr as_map;
struct {
constexpr auto operator()(auto& ref) const {
return map_wrapper<decltype(ref), std::false_type>(ref);
}
} static inline constexpr as_multimap;
// ADL finds these
template <typename Ar, typename... Args>
void save(Ar& ar, map_wrapper<Args...> const& w, unsigned v) {
size_t n = std::size(w.ref);
ar & boost::make_nvp("size", n);
for (auto& element : w.ref) {
ar & boost::make_nvp("item", element);
}
}
template <typename Ar, typename Map, typename Unique>
void load(Ar& ar, map_wrapper<Map, Unique>& w, unsigned v)
{
using V = typename boost::range_value<Map>::type;
w.ref.clear(); // optionally?
size_t n;
ar & boost::make_nvp("size", n);
while (n--) {
V element;
ar & boost::make_nvp("item", element);
// insert and fix object tracking!
if constexpr (Unique()) {
auto [it, inserted] = w.ref.insert(std::move(element));
if (inserted)
ar.reset_object_address(std::addressof(*it), &element);
} else {
auto it = w.ref.insert(std::end(w.ref), std::move(element));
ar.reset_object_address(std::addressof(*it), &element);
}
}
}
template <typename Ar, typename... Args>
void serialize(Ar& ar, map_wrapper<Args...>& m, unsigned v)
{
if constexpr (typename Ar::is_saving())
save(ar, m, v);
else
load(ar, m, v);
}
} // namespace Serialization
namespace boost::serialization {
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...>> {
using type = std::true_type;
};
// it's heinous, but we have to do it for const qualified too
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...> const> {
using type = std::true_type;
};
} // namespace boost::serialization
// bunch of tests with various containers
#include <boost/serialization/map.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/container/map.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/set.hpp>
#include <boost/unordered_map.hpp>
// including fantasy "maps"
template <typename K, typename V>
using FakeMap = boost::container::set<std::pair<K, V>>;
template <typename K, typename V>
using FakeMultiMap = std::vector<std::pair<K, V>>;
int main()
{
roundtrips<std::map>();
roundtrips<std::multimap>();
roundtrips<std::unordered_map>();
roundtrips<std::unordered_multimap>();
// no direct support, but we can wrap!
using Serialization::as_map;
using Serialization::as_multimap;
roundtrips<boost::container::map>(as_map);
roundtrips<boost::container::multimap>(as_multimap);
roundtrips<boost::unordered_map>(as_map);
roundtrips<boost::unordered_multimap>(as_multimap);
roundtrips<FakeMap>(as_map);
roundtrips<FakeMultiMap>(as_multimap);
}
Boost.Serialize 为 std::map / std::multimap 提供显式序列化,这不适用于其他类似地图的容器。
我想序列化那些而不需要每次都重写这些函数,但是 Boost 抱怨模棱两可。
这是我的代码:
/** Some sfinae to detect types that behave like std::map **/
template<typename T, typename = void>
struct is_map_ish : std::false_type { };
template<template<typename K, typename V> typename Map, typename K, typename V>
struct is_map_ish<
Map<K,V>,
std::void_t<
typename Map<K,V>::key_type,
typename Map<K,V>::value_type
>
> : std::true_type {
};
template<class Archive, class Map>
inline auto save(
Archive & ar,
const Map& t,
const unsigned int /* file_version */,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value>
{
boost_155::serialization::stl::save_collection<
Archive,
Map
>(ar, t);
}
template<class Archive, class Map>
inline auto load(
Archive & ar,
Map& t,
const unsigned int /* file_version */,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value> {
boost_155::serialization::stl::load_collection<
Archive,
Map,
boost_155::serialization::stl::archive_input_map<
Archive, Map >,
boost_155::serialization::stl::no_reserve_imp<Map>
>(ar, t);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive, class Map>
inline auto serialize(
Archive & ar,
Map &t,
const unsigned int file_version,
std::enable_if_t<is_map_ish<Map>::value>* = 0
) -> std::enable_if_t<is_map_ish<Map>::value> {
boost_155::serialization::split_free(ar, t, file_version);
}
serialize
函数与
// default implementation - call the member function "serialize"
template<class Archive, class T>
inline void serialize(
Archive & ar, T & t, const BOOST_155_PFTO unsigned int file_version
){
access::serialize(ar, t, static_cast<unsigned int>(file_version));
}
in boost,我显然无法更改。我尝试(如上面的代码所示)将一些 SFINAE 放入 return 类型和函数参数列表中,但这并不能帮助我避免重载歧义。
我有哪些选择?在 C++20 中我会尝试概念,但在这里我只能使用 C++17。
我的建议是使用 ADL,或者使用 Serialization Wrapper。
因为不知道什么时候停下来,所以做了下面的概念验证。一般往返测试允许:
- 允许切换存档类型
- 可选地提供包装函数(在我的示例中,我们将在下面定义
as_map
和as_multimap
);默认是恒等函数 - 将包含 4 个条目的“map-ish 容器”序列化为字符串流
- 反序列化它
- 将副本与原件进行比较
template <typename OA, typename IA, template <typename...> class Map,
typename WrapFun = std::identity>
void roundtrip_impl(std::string_view archive_type, WrapFun apply_wrap = {})
{
using Container = Map<int, std::string>;
Container original{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
using namespace boost::archive;
std::stringstream ss;
{
OA oa(ss, archive_flags::no_header);
auto&& payload = apply_wrap(original);
oa << boost::make_nvp("data", payload);
}
std::cout << archive_type << " "
<< (typename boost::serialization::is_wrapper<decltype(
apply_wrap(original))>::type{}
? "Wrapped "
: "Builtin ")
<< std::setw(3) << ss.str().length() << " bytes" << std::flush;
{
IA ia(ss, archive_flags::no_header);
Container replica;
auto&& payload = apply_wrap(replica);
ia >> boost::serialization::make_nvp("data", payload);
std::cout << " - roundtrip verified: " << std::boolalpha
<< (original == replica) << std::endl;
}
}
现在为了系统化,我们将定义一个测试所有存档类型的测试入口点:
template <template <typename...> class Map, typename WrapFun = std::identity>
void roundtrips(WrapFun do_wrap = {})
{
roundtrip_impl<
boost::archive::binary_oarchive,
boost::archive::binary_iarchive,
Map>("Binary", do_wrap);
roundtrip_impl<
boost::archive::text_oarchive,
boost::archive::text_iarchive,
Map>("Text ", do_wrap);
roundtrip_impl<
boost::archive::xml_oarchive,
boost::archive::xml_iarchive,
Map>("XML ", do_wrap);
}
This specifically verifies that the wrapper works in the presence of NVP wrappers.
序列化包装器
对于没有内置库支持的 map-ish 容器,让我们创建一个包装器:
namespace Serialization {
template <typename Map, typename Unique = std::true_type> struct map_wrapper {
Map& ref;
map_wrapper(Map& m) : ref(m) {}
constexpr static bool is_unique = Unique::value;
};
添加一些方便的函数as_map
和as_multimap
:
struct {
constexpr auto operator()(auto& ref) const { return map_wrapper(ref); }
} static inline constexpr as_map;
struct {
constexpr auto operator()(auto& ref) const {
return map_wrapper<decltype(ref), std::false_type>(ref);
}
} static inline constexpr as_multimap;
现在我们可以为包装器实现泛型 save/load:
// ADL finds these
template <typename Ar, typename... Args>
void save(Ar& ar, map_wrapper<Args...> const& w, unsigned v) {
size_t n = std::size(w.ref);
ar & boost::make_nvp("size", n);
for (auto& element : w.ref) {
ar & boost::make_nvp("item", element);
}
}
这很简单。当然,反序列化更多样化一些:
template <typename Ar, typename Map, typename Unique>
void load(Ar& ar, map_wrapper<Map, Unique>& w, unsigned v)
{
using V = typename boost::range_value<Map>::type;
w.ref.clear(); // optionally?
size_t n;
ar & boost::make_nvp("size", n);
while (n--) {
V element;
ar & boost::make_nvp("item", element);
// insert and fix object tracking!
if constexpr (Unique()) {
auto [it, inserted] = w.ref.insert(std::move(element));
if (inserted)
ar.reset_object_address(std::addressof(*it), &element);
} else {
auto it = w.ref.insert(std::end(w.ref), std::move(element));
ar.reset_object_address(std::addressof(*it), &element);
}
}
}
最重要的一点是我们在何处固定对象地址以进行地址跟踪。参见 docs。
没有模板BOOST_SERIALIZATION_SPLIT_FREE
,所以我们写:
template <typename Ar, typename... Args>
void serialize(Ar& ar, map_wrapper<Args...>& m, unsigned v)
{
if constexpr (typename Ar::is_saving())
save(ar, m, v);
else
load(ar, m, v);
}
} // namespace Serialization
快完成了。我们只需要帮助图书馆意识到我们是一个包装器:
namespace boost::serialization {
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...>> {
using type = std::true_type;
};
// it's heinous, but we have to do it for const qualified too
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...> const> {
using type = std::true_type;
};
} // namespace boost::serialization
运行 一些测试
// bunch of tests with various containers
#include
// including fantasy "maps"
template <typename K, typename V>
using FakeMap = boost::container::set<std::pair<K, V>>;
template <typename K, typename V>
using FakeMultiMap = std::vector<std::pair<K, V>>;
int main()
{
roundtrips<std::map>();
roundtrips<std::multimap>();
roundtrips<std::unordered_map>();
roundtrips<std::unordered_multimap>();
// no direct support, but we can wrap!
using Serialization::as_map;
using Serialization::as_multimap;
roundtrips<boost::container::map>(as_map);
roundtrips<boost::container::multimap>(as_multimap);
roundtrips<boost::unordered_map>(as_map);
roundtrips<boost::unordered_multimap>(as_multimap);
roundtrips<FakeMap>(as_map);
roundtrips<FakeMultiMap>(as_multimap);
}
版画
Binary Builtin 85 bytes - roundtrip verified: true
Text Builtin 47 bytes - roundtrip verified: true
XML Builtin 393 bytes - roundtrip verified: true
Binary Builtin 85 bytes - roundtrip verified: true
Text Builtin 47 bytes - roundtrip verified: true
XML Builtin 393 bytes - roundtrip verified: true
Binary Builtin 93 bytes - roundtrip verified: true
Text Builtin 49 bytes - roundtrip verified: true
XML Builtin 425 bytes - roundtrip verified: true
Binary Builtin 93 bytes - roundtrip verified: true
Text Builtin 49 bytes - roundtrip verified: true
XML Builtin 425 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
Binary Wrapped 81 bytes - roundtrip verified: true
Text Wrapped 45 bytes - roundtrip verified: true
XML Wrapped 359 bytes - roundtrip verified: true
警告
请注意,在这些示例中,我假设 map-ish 容器将使用可默认构造的comparators/hash function/equality 比较器。否则你必须记住实现 load_/save_construct_data
并且在这一点上我相信你最好只编写特定于类型的实现。
完整列表
(反bitrot)
#include <boost/archive/basic_archive.hpp>
#include <boost/serialization/nvp.hpp>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cassert>
template <typename OA, typename IA, template <typename...> class Map,
typename WrapFun = std::identity>
void roundtrip_impl(std::string_view archive_type, WrapFun apply_wrap = {})
{
using Container = Map<int, std::string>;
Container original{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
using namespace boost::archive;
std::stringstream ss;
{
OA oa(ss, archive_flags::no_header);
auto&& payload = apply_wrap(original);
oa << boost::make_nvp("data", payload);
}
std::cout << archive_type << " "
<< (typename boost::serialization::is_wrapper<decltype(
apply_wrap(original))>::type{}
? "Wrapped "
: "Builtin ")
<< std::setw(3) << ss.str().length() << " bytes" << std::flush;
{
IA ia(ss, archive_flags::no_header);
Container replica;
auto&& payload = apply_wrap(replica);
ia >> boost::serialization::make_nvp("data", payload);
std::cout << " - roundtrip verified: " << std::boolalpha
<< (original == replica) << std::endl;
}
}
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
template <template <typename...> class Map, typename WrapFun = std::identity>
void roundtrips(WrapFun do_wrap = {})
{
roundtrip_impl<
boost::archive::binary_oarchive,
boost::archive::binary_iarchive,
Map>("Binary", do_wrap);
roundtrip_impl<
boost::archive::text_oarchive,
boost::archive::text_iarchive,
Map>("Text ", do_wrap);
roundtrip_impl<
boost::archive::xml_oarchive,
boost::archive::xml_iarchive,
Map>("XML ", do_wrap);
}
#include <boost/range/value_type.hpp>
namespace Serialization {
template <typename Map, typename Unique = std::true_type> struct map_wrapper {
Map& ref;
map_wrapper(Map& m) : ref(m) {}
constexpr static bool is_unique = Unique::value;
};
struct {
constexpr auto operator()(auto& ref) const { return map_wrapper(ref); }
} static inline constexpr as_map;
struct {
constexpr auto operator()(auto& ref) const {
return map_wrapper<decltype(ref), std::false_type>(ref);
}
} static inline constexpr as_multimap;
// ADL finds these
template <typename Ar, typename... Args>
void save(Ar& ar, map_wrapper<Args...> const& w, unsigned v) {
size_t n = std::size(w.ref);
ar & boost::make_nvp("size", n);
for (auto& element : w.ref) {
ar & boost::make_nvp("item", element);
}
}
template <typename Ar, typename Map, typename Unique>
void load(Ar& ar, map_wrapper<Map, Unique>& w, unsigned v)
{
using V = typename boost::range_value<Map>::type;
w.ref.clear(); // optionally?
size_t n;
ar & boost::make_nvp("size", n);
while (n--) {
V element;
ar & boost::make_nvp("item", element);
// insert and fix object tracking!
if constexpr (Unique()) {
auto [it, inserted] = w.ref.insert(std::move(element));
if (inserted)
ar.reset_object_address(std::addressof(*it), &element);
} else {
auto it = w.ref.insert(std::end(w.ref), std::move(element));
ar.reset_object_address(std::addressof(*it), &element);
}
}
}
template <typename Ar, typename... Args>
void serialize(Ar& ar, map_wrapper<Args...>& m, unsigned v)
{
if constexpr (typename Ar::is_saving())
save(ar, m, v);
else
load(ar, m, v);
}
} // namespace Serialization
namespace boost::serialization {
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...>> {
using type = std::true_type;
};
// it's heinous, but we have to do it for const qualified too
template <typename... Args>
struct is_wrapper<Serialization::map_wrapper<Args...> const> {
using type = std::true_type;
};
} // namespace boost::serialization
// bunch of tests with various containers
#include <boost/serialization/map.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/container/map.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/set.hpp>
#include <boost/unordered_map.hpp>
// including fantasy "maps"
template <typename K, typename V>
using FakeMap = boost::container::set<std::pair<K, V>>;
template <typename K, typename V>
using FakeMultiMap = std::vector<std::pair<K, V>>;
int main()
{
roundtrips<std::map>();
roundtrips<std::multimap>();
roundtrips<std::unordered_map>();
roundtrips<std::unordered_multimap>();
// no direct support, but we can wrap!
using Serialization::as_map;
using Serialization::as_multimap;
roundtrips<boost::container::map>(as_map);
roundtrips<boost::container::multimap>(as_multimap);
roundtrips<boost::unordered_map>(as_map);
roundtrips<boost::unordered_multimap>(as_multimap);
roundtrips<FakeMap>(as_map);
roundtrips<FakeMultiMap>(as_multimap);
}