c++ - boost::property_tree 的通用枚举转换器

标签 c++ boost enums boost-propertytree

我正在使用 boost::property_tree 从/向文件加载/保存一组参数。其中许多参数是枚举(不同类型)。所以我需要一种从 boost::property_tree 获取枚举的方法(即将字符串转换为枚举),反之亦然。例如

const Enum_1 position = params.get<Enum_1>("Test.position");

我已经 checkout this answer ,这涉及为每个枚举创建一个翻译器。由于我有几十个枚举,看起来有点不知所措。

当涉及许多枚举时,是否有更通用的方法来做到这一点?

PS:我将我当前的解决方案发布在一个答案中,因为我无法找到更容易/更简单的东西。我很高兴听到更好的选择。

最佳答案

我当前的解决方案包括一个依赖 boost::bimap 的模板化翻译器简化 std::string/enum 转换。

// Generic translator for enums
template<typename T>
struct EnumTranslator {
  typedef std::string internal_type;
  typedef T external_type;
  typedef boost::bimap<internal_type, external_type> map_type;

  boost::optional<external_type> get_value(const internal_type& str) {
    // If needed, 'str' can be transformed here so look-up is case insensitive
    const auto it = s_map.left.find(str);
    if (it == s_map.left.end()) return boost::optional<external_type>(boost::none);
    return boost::optional<external_type>(it->get_right());
  }

  boost::optional<internal_type> put_value(const external_type& value) {
    const auto it = s_map.right.find(value);
    if (it == s_map.right.end()) return boost::optional<internal_type>(boost::none);
    return boost::optional<internal_type>(it->get_left());
  }

private:
  static const map_type s_map;
};

然后为每个枚举定义这样的字典:

// Dictionaries for string<-->enum conversion
typedef EnumTranslator<Enum_1> Enum_1_Translator;
const Enum_1_Translator::map_type Enum_1_Translator::s_map =
  boost::assign::list_of<Enum_1_Translator::map_type::relation>
  ("first", Enum_1::first)
  ("second", Enum_1::second)
  ("third", Enum_1::third);

typedef EnumTranslator<Enum_2> Enum_2_Translator;
const Enum_2_Translator::map_type Enum_2_Translator::s_map =
  boost::assign::list_of<Enum_2_Translator::map_type::relation>
  ("foo", Enum_2::foo)
  ("bar", Enum_2::bar)
  ("foobar", Enum_2::foobar);

最后,转换器必须注册,这样它们才能被 boost::property_tree 使用。

// Register translators
namespace boost {
  namespace property_tree {
    template<typename Ch, typename Traits, typename Alloc>
    struct translator_between<std::basic_string<Ch, Traits, Alloc>, Enum_1> {
      typedef Enum_1_Translator type;
    };

    template<typename Ch, typename Traits, typename Alloc>
    struct translator_between<std::basic_string<Ch, Traits, Alloc>, Enum_2> {
      typedef Enum_2_Translator type;
    };
  }
}

最后一个使用示例(params 是一个 boost::property_tree::ptree):

const Enum_1 position = params.get<Enum_1>("Test.position");
const Enum_2 foo_or_bar = params.get<Enum_2>("Test.foo_or_bar");

也许有人更愿意添加一些宏来减少代码困惑,例如:

#define DECLARE_ENUM_TRANSLATOR(E) \
  typedef EnumTranslator<E> E##EnumTranslator; \
  const E##EnumTranslator::map_type E##EnumTranslator::s_map = \
    boost::assign::list_of<E##EnumTranslator::map_type::relation>

#define REGISTER_ENUM_TRANSLATOR(E) \
  namespace boost { namespace property_tree { \
  template<typename Ch, typename Traits, typename Alloc> \
  struct translator_between<std::basic_string<Ch, Traits, Alloc>, E> { \
    typedef E##EnumTranslator type; \
  }; } }

通过这种方式,可以通过以下方式注册新的枚举:

DECLARE_ENUM_TRANSLATOR(Enum_1)
  ("first", Enum_1::first)
  ("second", Enum_1::second)
  ("third", Enum_1::third);
REGISTER_ENUM_TRANSLATOR(Enum_1);

DECLARE_ENUM_TRANSLATOR(Enum_2)
  ("foo", Enum_2::foo)
  ("bar", Enum_2::bar)
  ("foobar", Enum_2::foobar);
REGISTER_ENUM_TRANSLATOR(Enum_2);

注意:由于双冒号 (a_namespace::the_enum),这些宏与命名空间或类中的枚举不兼容。作为解决方法,可以执行 typedef 以重命名 枚举,或者在这些情况下不使用宏 ;)。

关于c++ - boost::property_tree 的通用枚举转换器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52206926/

相关文章:

c++ - 处理不完整的类型

c++ - [boost.asio] 在与 I/O 线程不同的线程中关闭 tcp::socket 或 tcp::acceptor

javascript - 根据 TypeScript 中的字符串值属性对对象进行排序

c++ - C++代码中的错误

c++ - gvim :make command does not work

c++ - 在同一个函数/程序中使用 WebSocket++ 服务器和客户端

c++ - 理解 boost::variant

enums - 枚举没有编译时 TypeScript 错误

swift - Swift 枚举的非详尽模式匹配

c++ - Qt/C++ Exited with code -1073741819(程序崩溃,异常代码为 c0000005)