如何使用谷物序列化枚举类型?
How to use cereal to serialize enum types?
例如
enum Color {RED, BLUE, YELLOW};
而且我想用麦片连载类型
Color c = RED;
JSONOutputArchive archive(std::cout);
archive(c);
输出看起来像
"value0" : "RED"
或
"value0" : RED
您想要的输出是任何序列化库都无法自动执行的,因为您为枚举值提供的名称只有编译器知道。
通常在这种情况下,您需要提供可以在字符串表示形式和枚举数值之间进行转换的代码(例如,Enum to String C++), or use a library 会自动为您完成此操作。
假设您拥有此功能。然后,您需要编写一对专门用于您的枚举的最小序列化函数。
这是一个完整的示例。你如何选择从枚举到字符串取决于你,我选择了两个地图,因为它不需要我真正的努力,但你可以轻松地将所有这些隐藏在一个宏后面,这个宏会为你生成所有需要的代码:
#include <cereal/archives/json.hpp>
#include <iostream>
#include <sstream>
#include <map>
enum Color {RED, BLUE, GREEN};
enum AnotherEnum {HELLO_WORLD};
std::map<Color, std::string> ColorMapForward = {{RED, "RED"}, {BLUE, "BLUE"}, {GREEN, "GREEN"}};
std::map<std::string, Color> ColorMapReverse = {{"RED", RED}, {"BLUE", BLUE}, {"GREEN", GREEN}};
std::string Color_tostring( Color c )
{
return ColorMapForward[c];
}
Color Color_fromstring( std::string const & s )
{
return ColorMapReverse[s];
}
namespace cereal
{
template <class Archive> inline
std::string save_minimal( Archive const &, Color const & t )
{
return Color_tostring( t );
}
template <class Archive> inline
void load_minimal( Archive const &, Color & t, std::string const & value )
{
t = Color_fromstring( value );
}
}
int main()
{
std::stringstream ss;
{
cereal::JSONOutputArchive ar(ss);
ar( RED );
ar( BLUE );
ar( GREEN );
ar( HELLO_WORLD ); // uses standard cereal implementation
}
std::cout << ss.str() << std::endl;
std::stringstream ss2;
{
cereal::JSONInputArchive ar(ss);
cereal::JSONOutputArchive ar2(ss2);
Color r, b, g;
AnotherEnum a;
ar( r, b, g, a );
ar2( r, b, g, a );
}
std::cout << ss2.str() << std::endl;
}
给出输出:
{
"value0": "RED",
"value1": "BLUE",
"value2": "GREEN",
"value3": 0
}
{
"value0": "RED",
"value1": "BLUE",
"value2": "GREEN",
"value3": 0
}
您可以将枚举转换为 int 并返回,如下所示:
struct Camera {
enum Mode { MODE_ORTHOGRAPHIC, MODE_PERSPECTIVE, MODE_COUNT };
template<class Archive>
void serialize( Archive &archive )
{
auto mode = static_cast<int>( mMode );
archive( cereal::make_nvp( "mode", mode ) );
mMode = static_cast<Mode>( mode );
}
Mode mMode = MODE_PERSPECTIVE;
};
我建议结合使用 cereal
和 magic_enum
(https://github.com/Neargye/magic_enum)。它使用 compiler-specific 代码来完成工作,但也适用于 MSVC 和 GCC(也在它的 macOS 版本上)。
在我的例子中,我创建了以下代码,将枚举转换代码替换为 magic_enum:
template <class Archive,
cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
= cereal::traits::sfinae, class T>
std::enable_if_t<std::is_enum_v<T>, std::string> save_minimal( Archive &, const T& h )
{
return std::string(magic_enum::enum_name(h));
}
template <class Archive, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
= cereal::traits::sfinae, class T> std::enable_if_t<std::is_enum_v<T>, void> load_minimal( Archive const &, T& enumType, std::string const& str)
{
enumType = magic_enum::enum_cast<T>(str).value();
}
插入这个,从现在开始所有 enum
s 都被转换,它就像一个魅力。 magic_enum
lib 本身是 header-only,所以很容易集成。
但是,我只是假设 enum_cast
returns 是一个有效值,您可以在那里添加一些检查、抛出异常等。
无论如何,通过这种方式,您可以序列化任何可能包含 enum
或 enum class
的结构,它就可以正常工作。
但是,您需要确保这些模板在您的 enum
的命名空间内。因此,如果您想序列化各种命名空间的 enum
(或者将其放入 header 并将 header 包含到您的命名空间中,则可能需要一些重复的代码)。
例如
enum Color {RED, BLUE, YELLOW};
而且我想用麦片连载类型
Color c = RED;
JSONOutputArchive archive(std::cout);
archive(c);
输出看起来像
"value0" : "RED"
或
"value0" : RED
您想要的输出是任何序列化库都无法自动执行的,因为您为枚举值提供的名称只有编译器知道。
通常在这种情况下,您需要提供可以在字符串表示形式和枚举数值之间进行转换的代码(例如,Enum to String C++), or use a library 会自动为您完成此操作。
假设您拥有此功能。然后,您需要编写一对专门用于您的枚举的最小序列化函数。
这是一个完整的示例。你如何选择从枚举到字符串取决于你,我选择了两个地图,因为它不需要我真正的努力,但你可以轻松地将所有这些隐藏在一个宏后面,这个宏会为你生成所有需要的代码:
#include <cereal/archives/json.hpp>
#include <iostream>
#include <sstream>
#include <map>
enum Color {RED, BLUE, GREEN};
enum AnotherEnum {HELLO_WORLD};
std::map<Color, std::string> ColorMapForward = {{RED, "RED"}, {BLUE, "BLUE"}, {GREEN, "GREEN"}};
std::map<std::string, Color> ColorMapReverse = {{"RED", RED}, {"BLUE", BLUE}, {"GREEN", GREEN}};
std::string Color_tostring( Color c )
{
return ColorMapForward[c];
}
Color Color_fromstring( std::string const & s )
{
return ColorMapReverse[s];
}
namespace cereal
{
template <class Archive> inline
std::string save_minimal( Archive const &, Color const & t )
{
return Color_tostring( t );
}
template <class Archive> inline
void load_minimal( Archive const &, Color & t, std::string const & value )
{
t = Color_fromstring( value );
}
}
int main()
{
std::stringstream ss;
{
cereal::JSONOutputArchive ar(ss);
ar( RED );
ar( BLUE );
ar( GREEN );
ar( HELLO_WORLD ); // uses standard cereal implementation
}
std::cout << ss.str() << std::endl;
std::stringstream ss2;
{
cereal::JSONInputArchive ar(ss);
cereal::JSONOutputArchive ar2(ss2);
Color r, b, g;
AnotherEnum a;
ar( r, b, g, a );
ar2( r, b, g, a );
}
std::cout << ss2.str() << std::endl;
}
给出输出:
{
"value0": "RED",
"value1": "BLUE",
"value2": "GREEN",
"value3": 0
}
{
"value0": "RED",
"value1": "BLUE",
"value2": "GREEN",
"value3": 0
}
您可以将枚举转换为 int 并返回,如下所示:
struct Camera {
enum Mode { MODE_ORTHOGRAPHIC, MODE_PERSPECTIVE, MODE_COUNT };
template<class Archive>
void serialize( Archive &archive )
{
auto mode = static_cast<int>( mMode );
archive( cereal::make_nvp( "mode", mode ) );
mMode = static_cast<Mode>( mode );
}
Mode mMode = MODE_PERSPECTIVE;
};
我建议结合使用 cereal
和 magic_enum
(https://github.com/Neargye/magic_enum)。它使用 compiler-specific 代码来完成工作,但也适用于 MSVC 和 GCC(也在它的 macOS 版本上)。
在我的例子中,我创建了以下代码,将枚举转换代码替换为 magic_enum:
template <class Archive,
cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
= cereal::traits::sfinae, class T>
std::enable_if_t<std::is_enum_v<T>, std::string> save_minimal( Archive &, const T& h )
{
return std::string(magic_enum::enum_name(h));
}
template <class Archive, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
= cereal::traits::sfinae, class T> std::enable_if_t<std::is_enum_v<T>, void> load_minimal( Archive const &, T& enumType, std::string const& str)
{
enumType = magic_enum::enum_cast<T>(str).value();
}
插入这个,从现在开始所有 enum
s 都被转换,它就像一个魅力。 magic_enum
lib 本身是 header-only,所以很容易集成。
但是,我只是假设 enum_cast
returns 是一个有效值,您可以在那里添加一些检查、抛出异常等。
无论如何,通过这种方式,您可以序列化任何可能包含 enum
或 enum class
的结构,它就可以正常工作。
但是,您需要确保这些模板在您的 enum
的命名空间内。因此,如果您想序列化各种命名空间的 enum
(或者将其放入 header 并将 header 包含到您的命名空间中,则可能需要一些重复的代码)。