【问题标题】:How to use cereal to serialize enum types?如何使用谷物序列化枚举类型?
【发布时间】:2017-02-19 13:04:35
【问题描述】:

例如

enum Color {RED, BLUE, YELLOW};

而且我想用谷物序列化类型

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

输出看起来像

"value0" : "RED"

"value0" : RED

【问题讨论】:

    标签: c++ serialization enums cereal


    【解决方案1】:

    您想要的输出是任何序列化库都无法自动执行的,因为您为枚举值提供的名称只有编译器知道。

    通常在这种情况下,您需要提供可以在字符串表示和枚举的数值之间进行转换的代码(例如,Enum to String C++),或者使用自动为您执行此操作的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
    }
    

    【讨论】:

      【解决方案2】:

      您可以将枚举转换为 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;
      };
      

      【讨论】:

        【解决方案3】:

        我建议将cerealmagic_enum (https://github.com/Neargye/magic_enum) 结合使用。它使用特定于编译器的代码来完成工作,但也适用于 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();
        }
        

        插入这个,从现在开始所有enums 都被转换了,它就像一个魅力。 magic_enum lib 本身是header-only,所以很容易集成。

        但是,我只是假设 enum_cast 返回一个有效值,您可能会在那里添加一些检查、抛出异常等。

        无论如何,通过这种方式,您只需序列化任何可能包含enums 或enum classes 的结构,它就可以正常工作。

        但是,您需要确保这些模板位于您的 enums 的命名空间内。所以,如果你想序列化各种命名空间的enums(或者你把它放到一个头文件中并将该头文件包含到你的名称空间中),可能需要一些重复的代码。

        【讨论】:

          猜你喜欢
          • 2017-02-03
          • 1970-01-01
          • 2017-04-10
          • 1970-01-01
          • 1970-01-01
          • 2021-07-22
          • 2021-11-28
          • 2019-05-31
          • 2021-12-01
          相关资源
          最近更新 更多