标记枚举的非侵入式Boost序列化
Non-intruisive Boost serialization of labelled enums C++
我想序列化结构体A,在那里我可以以一种非侵入性的方式保存枚举表达式的标记名,而不是它的整数(而不必改变结构体A)。
enum e_fruit {
apple,
banana,
coconut
};
struct A {
e_fruit fruit;
int num;
};
namespace boost { namespace serialization {
template<class Archive>
void serialize(Archive & ar, A &a, const unsigned int version)
{
ar & boost::serialization::make_nvp("FruitType", a.fruit); // this will store an integer
ar & boost::serialization::make_nvp("Number", a.num);
}
}}
我已经尝试在本地引入一个查找表到序列化函数:
template<class Archive>
void serialize(Archive & ar, A &a, const unsigned int version)
{
static const char* const labels[] = { "Apple", "Banana", "Coconut" };
ar & boost::serialization::make_nvp("FruitType", labels[a.fruit]); // this won't work
ar & boost::serialization::make_nvp("Number", a.num);
}
不幸的是,我得到了错误:
错误78错误C2228: left of '。序列化'必须有类/结构/联盟
,因为make_nvp
的原型是
nvp< T > make_nvp(const char * name, T & t){
return nvp< T >(name, t);
}
所以T应该是一个推导出来的模板参数。然后我考虑创建一个包含这些标签的结构体但是我必须在结构体a中添加这个这是我想要避免的。
那么我们如何才能以最不具侵入性的方式实现这一点呢?
我认为你需要将加载与保存分开。这个编译,我想知道它是否值得这个游戏…
struct fruit_serializer
{
e_fruit &a_;
fruit_serializer(e_fruit &a) : a_(a) {}
template<class Archive>
void save(Archive & ar, const unsigned int version) const
{
std::string label = labels[static_cast<int>(a_)];
ar & boost::serialization::make_nvp("label", label);
}
template<class Archive>
void load(Archive & ar, const unsigned int version)
{
std::string label ;
ar & boost::serialization::make_nvp("label", label);
a_ = static_cast<e_fruit>(std::find(labels.begin(), labels.end(), label) - labels.begin());
}
BOOST_SERIALIZATION_SPLIT_MEMBER();
static std::vector<std::string> labels ;
};
std::vector<std::string> fruit_serializer::labels({ "Apple", "Banana", "Coconut" });
template<class Archive>
void serialize(Archive & ar, A &a, const unsigned int version)
{
fruit_serializer a1(a.fruit);
ar & boost::serialization::make_nvp("FruitType", a1);
}
尽管我不愿意重提一个老问题,但我想做同样的事情,但需要使枚举在没有任何装饰符的情况下可字符串序列化。由于我没有找到更多关于这个主题的其他内容,所以我发布了我的hack解决方案,作为任何需要将其枚举序列化为字符串的人的选择。
首先,一些(最终)序列化的示例类型:
#include <iostream>
#include <sstream>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_oarchive.hpp>
// A few dummy enum types to test the solution
enum MyEnum00 {
Barb, Sarah, Lucy,
};
enum MyEnum01 {
Corey, Trevor, Jacob = 99,
};
const char* const to_cstring(const MyEnum01 e) {
switch (e) {
case Corey: return "Corey";
case Trevor: return "Trevor";
case Jacob: return "Jacob";
default: return "UNKNOWN";
}
}
inline std::ostream& operator<<(std::ostream& o, const MyEnum01 e) { return o << to_cstring(e); }
enum class MyEnumClass00 {
Ricky, Julian, Bubbles
};
enum class MyEnumClass01 {
Jim, Randy, Cyrus
};
const char* const to_cstring(const MyEnumClass01 e) {
switch (e) {
case MyEnumClass01::Jim: return "I let the liquor do the thinking, bud!";
case MyEnumClass01::Randy: return "Got any cheeeeeseburgers?";
case MyEnumClass01::Cyrus: return "I got work to do";
default: return "UNKNOWN";
}
}
inline std::ostream& operator<<(std::ostream& o, const MyEnumClass01 e) { return o << to_cstring(e); }
在boost_1_63_0/boost/archive/detail/oserializer.hpp中,函数save_enum_type::invoke()是将枚举转换为int的地方。
Boost使用了模板结构和单独的模板成员的笨拙组合,因此很难在使用我们想要的枚举类型时应用我们的更改。作为一种解决方法,我们可以针对我们正在使用的归档类型专门化boost::archive::detail::save_enum_type。然后,我们可以重载它的invoke()函数,以防止它破坏我们需要作为字符串存档的枚举类型。
save_enum_type::invoke大致在boost的调用堆栈的中间被调用,它最终向下进入basic_text_primitive类。在这里,最后使用插入操作符将值保存到目标存档底层的ostream中。通过专门化save_enum_type并为目标类型实现插入操作符,可以利用该实现细节将枚举类型存档为字符串。
namespace boost {
namespace archive {
namespace detail {
using xml_oarchive_ = boost::archive::xml_oarchive;
using save_non_pointer_type_ = detail::save_non_pointer_type<xml_oarchive_>;
template<>
struct save_enum_type<xml_oarchive_>
{
// This is boost's stock function that converts enums to ints before serializing them.
// We've added a copy to our specialized version of save_enum_type to maintain the exisitng behavior for any
// enum types we don't care to have archived in string form
template<class T>
static void invoke(xml_oarchive_& ar, const T& t) {
const int i = static_cast<int>(t);
ar << boost::serialization::make_nvp(NULL, i);
}
///// specialized enum types /////
// You could probably reduce all the repeated code with some type-trait magic...
static void invoke(xml_oarchive_& ar, const MyEnum00 &e) {
save_non_pointer_type_::invoke(ar, e);
}
static void invoke(xml_oarchive_& ar, const MyEnum01 &e) {
save_non_pointer_type_::invoke(ar, e);
}
// Won't work -- MyEnumClass00 doesn't have an insertion operator, so the underlying ostream won't know
// how to handle it
//static void invoke(xml_oarchive_& ar, const MyEnumClass00 &e) {
// save_non_pointer_type_::invoke(ar, e);
//}
static void invoke(xml_oarchive_& ar, const MyEnumClass01 &e) {
save_non_pointer_type_::invoke(ar, e);
}
};
} // namespace detail
} // namespace archive
} // namespace boost
最后是测试所有内容的代码:
int main()
{
std::stringstream outstream;
boost::archive::xml_oarchive ar(outstream);
MyEnum00 e00_0 = Barb;
MyEnum00 e00_1 = Sarah;
MyEnum00 e00_2 = Lucy;
MyEnum01 e01_0 = Corey;
MyEnum01 e01_1 = Trevor;
MyEnum01 e01_2 = Jacob;
MyEnumClass00 ec00_0 = MyEnumClass00::Ricky;
MyEnumClass00 ec00_1 = MyEnumClass00::Julian;
MyEnumClass00 ec00_2 = MyEnumClass00::Bubbles;
MyEnumClass01 ec01_0 = MyEnumClass01::Jim;
MyEnumClass01 ec01_1 = MyEnumClass01::Randy;
MyEnumClass01 ec01_2 = MyEnumClass01::Cyrus;
ar
// regular enums get passed down as int even if you don't explicitly convert them
<< BOOST_SERIALIZATION_NVP(e00_0)
<< BOOST_SERIALIZATION_NVP(e00_1)
<< BOOST_SERIALIZATION_NVP(e00_2)
// regular enums can also get special treatment
<< BOOST_SERIALIZATION_NVP(e01_0)
<< BOOST_SERIALIZATION_NVP(e01_1)
<< BOOST_SERIALIZATION_NVP(e01_2)
// enum classes that aren't specialized pass down as ints
<< BOOST_SERIALIZATION_NVP(ec00_0)
<< BOOST_SERIALIZATION_NVP(ec00_1)
<< BOOST_SERIALIZATION_NVP(ec00_2)
// enum classes can also get special treatment
<< BOOST_SERIALIZATION_NVP(ec01_0)
<< BOOST_SERIALIZATION_NVP(ec01_1)
<< BOOST_SERIALIZATION_NVP(ec01_2)
;
std::cout << outstream.str() << std::endl;
return 0;
}
- C++boost序列化多态性问题
- 将 boost 序列化对象的 asio::streambuf 表示转换为 Beast 的 DynamicBody req.body()
- 如何使用 Boost 序列化mersenne_twister_engine?
- boost::序列化中的派生类偏移量计算.有效吗?
- C++ Boost - 序列化错误 - 将"const B"作为"this"参数
- 序列化模式类似于boost::序列化
- 使用 boost::序列化代码将 *this 传递给模板函数会产生错误
- 使用 boost::序列化序列化 std::vector of unique_ptr 在 Linux 上失败
- 使用 boost::序列化将派生类指针序列化为向量时出现问题
- 跳过层次结构中的中间类,并使用boost ::序列化
- 使用Boost序列化std ::乘坐
- Boost::序列化存储结构时的堆栈溢出错误
- 2D 矢量未正确保存并加载 boost::序列化库
- 使用Boost序列化保存和检索多个对象
- 使用boost::序列化递归图结构时,如何防止堆栈溢出
- 特征矩阵+Boost::序列化/C++17
- C++Boost序列化:输入流错误
- 将Boost序列化文本存档转换为结构化格式
- Microsoft Guid guid serialization使用Boost序列化
- Boost::序列化 - 如何序列化需要数据分配但没有默认构造函数的数据结构