代码之家  ›  专栏  ›  技术社区  ›  cbuchart

boost::property\u树的泛型枚举转换器

  •  0
  • cbuchart  · 技术社区  · 6 年前

    boost::property_tree . 其中许多参数是枚举(不同类型)。所以我需要一种从 boost::属性树

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

    我退房了 this answer ,这涉及为每个枚举创建一个转换器。因为我已经列举了几十次了,看起来有点让人不知所措。

    当涉及许多枚举时,是否有更通用的方法来执行此操作?

    附言:我把我目前的解决方案贴在一个答案里,因为我还没有找到更简单/更简单的方法。我很高兴听到更好的选择。

    2 回复  |  直到 6 年前
        1
  •  1
  •   cbuchart    6 年前

    我目前的解决方案是一个依赖于 boost::bimap std::string

    // 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可以 重命名 枚举,或者在这些情况下不使用宏;)。

        2
  •  1
  •   Yakk - Adam Nevraumont    6 年前

    看看页眉,定制的一个好方法是:

    namespace boost { namespace property_tree
    {
    
      template <typename Ch, typename Traits, typename E, typename Enabler = void>
      struct customize_stream
      {
        static void insert(std::basic_ostream<Ch, Traits>& s, const E& e) {
            s << e;
        }
        static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
            s >> e;
            if(!s.eof()) {
                s >> std::ws;
            }
        }
      };
    

    它有一个启用码字段。

    namespace boost { namespace property_tree {
      template <typename Ch, typename Traits, typename E>
      struct customize_stream<Ch, Traits, E,
        std::enable_if_t< /* some test */ >
      >
      {
        static void insert(std::basic_ostream<Ch, Traits>& s, const E& e) {
          // your code
        }
        static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
          // your code
        }
      };
    

    你可以把任何代码放进去 // your code /* some test */ .

    剩下的部分是(a)联系 bob::a 具有 "a" ,和(b)将此连接到上面。

    bob ,然后通过ADL找到他们。

    创建 template<class T> struct tag_t {} . 如果你通过考试 tag_t<foo> 对于一个函数,ADL将在 tag_t foo .

    创建一个函数,用于获取从枚举值到字符串的映射。假设您的映射是:

    std::vector< std::pair< E, std::string > >
    

    namespace string_mapping {
      template<class Enum>
      using mapping = std::vector<std::pair< Enum, std::string > >;
    }
    namespace some_ns {
      enum bob { a, b, c };
      string_mapping::mapping<bob> const& get_string_mapping( tag_t<bob> ) {
        static string_mapping::mapping<bob> retval = {
          {bob::a, "a"},
          {bob::b, "b"},
          {bob::c, "c"},
        };
        return retval;
      }
    }
    

    我们可以在任何有类型的地方找到这个映射 T=bob get_string_mapping( tag_t<T>{} )

    使用类似 can_apply 获取字符串映射(标记{}) 可以找到,使用它来启用您的自定义 customize_stream 使用 get_string_mapping 在流中加载/保存数据。

    获取字符串映射 .

    #define MAP_ENUM_TO_STRING( ENUM ) \
      string_mapping::mapping<ENUM> const& get_string_mapping( tag_t<ENUM> ) { \
        static string_mapping::mapping<ENUM> retval =
    
    #define END_ENUM_TO_STRING ; return retval; }
    

    用途:

     MAP_ENUM_TO_STRING( bob )
        {
          {bob::a, "a"},
          {bob::b, "b"},
          {bob::c, "c"},
        }
     END_ENUM_TO_STRING
    

    鲍勃 的命名空间。

    如果你想要更华丽的东西(ooo,排序列表,或无序地图),那可以在 甚至是通过 get_efficient_string_mapping 这叫 获取字符串映射 并对平面数据进行一次性的重新处理。