[英]Generic enum translator for boost::property_tree
I'm loading/saving a set of parameters from/to a file using boost::property_tree
. 我正在使用boost::property_tree
从文件中加载/保存一组参数。 Many of those parameters are enumerations (different types). 这些参数中有许多是枚举(不同类型)。 So I need a way to get enums from a boost::property_tree
(ie, converting a string to enum), and viceversa. 所以我需要一种方法来从boost::property_tree
获取枚举(即,将字符串转换为枚举),反之亦然。 For example 例如
const Enum_1 position = params.get<Enum_1>("Test.position");
I've checked out this answer , which involves creating a translator for each enumeration. 我已经检查了这个答案 ,其中涉及为每个枚举创建一个翻译器。 As I have several dozens of enumerations, it looks like a bit overwhelming. 由于我有几十个枚举,因此看起来有点不知所措。
Is there any more generic way to do it when many enums are involved? 当涉及到许多枚举时,还有其他通用的方法吗?
PS: I'm posting my current solution in an answer since I haven't been able to find something easier/simpler. PS:由于无法找到更容易/更简单的方法,因此我将当前的解决方案发布在答案中。 I'll be glad to hear better options. 我很高兴听到更好的选择。
My current solution consists on a templated translator that relies on a boost::bimap
to ease the std::string
/enum conversion. 我当前的解决方案包括依赖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;
};
Such dictionaries are then defined for each enum: 然后为每个枚举定义此类字典:
// 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);
Finally, the translators must be registered so they can be used by boost::property_tree
. 最后,必须注册翻译器,以便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;
};
}
}
Final example of use ( params
is a boost::property_tree::ptree
): 最终使用示例( 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");
Maybe someone would prefer to add some macros to reduce the code cluttering, for example: 也许有人希望添加一些宏来减少代码混乱,例如:
#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; \
}; } }
In this way, new enums can be registered by: 这样,可以通过以下方式注册新的枚举:
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);
Note: these macros are not compatible with enums within a namespace or class, due to the double colons ( a_namespace::the_enum
). 注意:由于双冒号( a_namespace::the_enum
),这些宏与命名空间或类中的枚举不兼容。 As a workaround, a typedef can be done to rename the enumeration, or just do not use the macros in these cases ;). 解决方法是,可以使用typedef 重命名枚举,或者在这种情况下不要使用宏;)。
Looking at the header, a good point of customization is: 查看标题,自定义的一个好地方是:
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;
}
}
};
it has an Enabler field. 它具有一个启动器字段。
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
}
};
where you can put whatever code in // your code
and whatever test in /* some test */
. 在这里,您可以将任何代码放入// your code
并将任何测试放入/* some test */
。
The remaining part is to (a) associate bob::a
with "a"
, and (b) connect this to the above. 其余部分是(a)将bob::a
与"a"
关联,并且(b)将此连接到上面。
I like doing these associations in the namespace of bob
, then finding them via ADL. 我喜欢在bob
的命名空间中进行这些关联,然后通过ADL查找它们。
Create a template<class T> struct tag_t {}
. 创建一个template<class T> struct tag_t {}
。 If you pass a tag_t<foo>
to a function, ADL will find functions in both the namespace of tag_t
and in the namespace of foo
. 如果将tag_t<foo>
传递给函数,则ADL将在tag_t
的命名空间和foo
的命名空间中找到函数。
Create a function that gets the mapping from enum value to string (and back). 创建一个函数,该函数获取从枚举值到字符串(以及向后)的映射。 Suppose your mapping is: 假设您的映射是:
std::vector< std::pair< E, std::string > >
and you just do a linear search. 然后您就可以进行线性搜索。 Then: 然后:
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;
}
}
we can find this mapping wherever we have a type T=bob
by doing get_string_mapping( tag_t<T>{} )
. 我们可以通过执行get_string_mapping( tag_t<T>{} )
在类型为T=bob
的任何地方找到此映射。
Use something like can_apply
to detect if get_string_mapping( tag_t<T>{} )
can be found, use that to enable your custom customize_stream
to use get_string_mapping
to load/save the data to/from streams. 使用类似can_apply
检测是否get_string_mapping( tag_t<T>{} )
可以发现,用它来使您的自定义customize_stream
使用get_string_mapping
加载/保存数据到/从流。
Now all we have to do is reduce the pain of writing get_string_mapping
. 现在,我们要做的就是减轻编写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; }
use: 采用:
MAP_ENUM_TO_STRING( bob )
{
{bob::a, "a"},
{bob::b, "b"},
{bob::c, "c"},
}
END_ENUM_TO_STRING
within bob
's namespace. 在bob
的命名空间中。
If you want something fancier (ooo, sorted lists, or unordered maps), that can easily be done within get_string_mapping
or even by a get_efficient_string_mapping
that calls get_string_mapping
and does a one-off reprocessing of the flat data. 如果你想要的东西票友(000,排序的列表或无序地图),可以很容易地内完成get_string_mapping
甚至由get_efficient_string_mapping
调用get_string_mapping
和不平坦的数据的一次性再加工。
The big advantage of this is that we don't have to do this in the global namespace; 这样做的最大好处是,我们不必在全局名称空间中执行此操作。 we can put it naturally right under an enum or in the enum's namespace. 我们可以自然地将其放在枚举下或枚举的命名空间中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.