尽管我讨厌复活一个老问题,但我想做同样的事情,但需要在没有任何装饰器的情况下使枚举字符串可序列化。由于我没有找到有关该主题的其他内容,因此我将发布我的 hacky 解决方案作为其他需要将其枚举序列化为字符串的人的选择。
首先,一些要(最终)序列化的样本类型:
#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() 将枚举转换为整数。
Boost 使用模板结构与单独模板成员的有点笨拙的组合,因此很难在使用我们所需的枚举类型时应用我们的更改。作为一种解决方法,我们可以将 boost::archive::detail::save_enum_type 专门用于我们正在使用的存档类型。然后,我们可以重载它的 invoke() 函数,以防止它破坏我们需要归档为字符串的枚举类型。
save_enum_type::invoke 大致在 boost 的调用堆栈中间被调用,最终进入 basic_text_oprimitive 类。在那里,插入运算符最终用于将值保存到目标存档下的 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;
}