简体   繁体   English

如何使用谷物序列化枚举类型?

[英]How to use cereal to serialize enum types?

For example例如

enum Color {RED, BLUE, YELLOW};

And I want to serialize the type with cereal我想用谷物序列化类型

Color c = RED;
JSONOutputArchive archive(std::cout);
archive(c);

Output looks like输出看起来像

"value0" : "RED"

or或者

"value0" : RED

Your desired output is something that no serialization library would be able to perform automatically, because the names you provide for the enum values are known only to the compiler. 所需的输出是任何序列化库都无法自动执行的输出,因为您为枚举值提供的名称仅是编译器知道的。

Generally in this type of situation you need to provide code that can translate between a string representation and the numerical value for the enum (eg, Enum to String C++ ), or use a library that does this automatically for you. 通常,在这种情况下,您需要提供可以在字符串表示形式和枚举的数值之间转换的代码(例如, Enum到String C ++ ),或使用自动为您执行此操作的

Let's assume you have this functionality. 假设您具有此功能。 You would then need to write ta pair of minimal serialization functions specialized for your enum. 然后,您需要编写一对专门针对您的枚举的最小序列化函数。

Here is a fully working example. 这是一个完整的示例。 How you choose to go from enum to string is up to you, I went with two maps because it required no real effort on my part, but you could easily hide all of this behind a macro that would generate all of the required code for you: 如何选择从枚举到字符串由您决定,我选择了两张地图,因为这不需要我付出任何实际的努力,但是您可以轻松地将所有这些隐藏在一个可以为您生成所有所需代码的宏后面:

#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;
}

Gives as output: 给出作为输出:

{
    "value0": "RED",
    "value1": "BLUE",
    "value2": "GREEN",
    "value3": 0
}
{
    "value0": "RED",
    "value1": "BLUE",
    "value2": "GREEN",
    "value3": 0
}

You can cast the enum to an int and back, like this: 您可以将枚举转换为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;
};

I recommend combining cereal with magic_enum ( https://github.com/Neargye/magic_enum ).我建议将cerealmagic_enum ( https://github.com/Neargye/magic_enum ) 结合使用。 It uses compiler-specific code to get the job done but works with MSVC and GCC (also on the macOS version of it).它使用特定于编译器的代码来完成工作,但适用于 MSVC 和 GCC(也在它的 macOS 版本上)。

In my case, I created the following code which replaces the enum conversion code with magic_enum:就我而言,我创建了以下代码,用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();
}

Insert this and from now on all enum s are converted, it works like a charm.插入这个,从现在开始所有enum都被转换,它就像一个魅力。 magic_enum lib itself is header-only, so very easy to integrate. magic_enum lib 本身仅包含头文件,因此非常容易集成。

However, I just assume that enum_cast returns a valid value, you might add some checking there, throw an exception, etc.但是,我只是假设enum_cast返回一个有效值,您可以在那里添加一些检查、抛出异常等。

Anyways, in this way, you can just serialise any of your structures that might contain enum s or enum class es and it will just work.无论如何,通过这种方式,您可以序列化任何可能包含enumenum class结构,并且它会正常工作。

However, you need to make sure that these templates are within the namespace of your enum s.但是,您需要确保这些模板在enum的命名空间内。 So, some duplicated code is maybe necessary if you want to serialise enum s of various namespaces (or you put it into a header and include that header into your namespaces, anyways).因此,如果您想序列化各种命名空间的enum ,则可能需要一些重复的代码(或者将其放入标头并将该标头包含在命名空间中)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM