2012-03-16 87 views
11

我在boost文件的property_tree头文件中迷失了方向,并且由于缺少关于较低层的文档,我决定询问简单的方法是重载流转换器以更改如何分析布尔值。更改boost :: property_tree的读取方式将字符串转换为bool

问题是在属性树的输入端有用户,他们可以修改配置文件。一个布尔值,可能会在许多方式来指定,如:

dosomething.enabled=true 
dosomething.enabled=trUE 
dosomething.enabled=yes 
dosomething.enabled=ON 
dosomething.enabled=1 

的默认行为是检查0或1,然后用

std::ios_base::boolalpha 

获得流尝试解析以适当的方式为当前语言环境提供价值...如果我们尝试向国际客户发送配置文件,这可能是疯狂的。

那么覆盖这种行为或布尔只有最简单的方法是什么?不仅最容易实现,而且最容易使用 - 这样我的类的用户从iptree派生出来就不需要为布尔值做特殊的事情。

谢谢!

回答

18

您可以专注于​​,以便属性树将为bool值类型使用自定义翻译器。客户需要自定义行为时,该专业化必须可见(即#includ)。这里有一个工作示例:

#include <iostream> 
#include <boost/property_tree/ptree.hpp> 
#include <boost/property_tree/json_parser.hpp> 
#include <boost/algorithm/string/predicate.hpp> 

// Custom translator for bool (only supports std::string) 
struct BoolTranslator 
{ 
    typedef std::string internal_type; 
    typedef bool  external_type; 

    // Converts a string to bool 
    boost::optional<external_type> get_value(const internal_type& str) 
    { 
     if (!str.empty()) 
     { 
      using boost::algorithm::iequals; 

      if (iequals(str, "true") || iequals(str, "yes") || str == "1") 
       return boost::optional<external_type>(true); 
      else 
       return boost::optional<external_type>(false); 
     } 
     else 
      return boost::optional<external_type>(boost::none); 
    } 

    // Converts a bool to string 
    boost::optional<internal_type> put_value(const external_type& b) 
    { 
     return boost::optional<internal_type>(b ? "true" : "false"); 
    } 
}; 

/* Specialize translator_between so that it uses our custom translator for 
    bool value types. Specialization must be in boost::property_tree 
    namespace. */ 
namespace boost { 
namespace property_tree { 

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

} // namespace property_tree 
} // namespace boost 

int main() 
{ 
    boost::property_tree::iptree pt; 

    read_json("test.json", pt); 
    int i = pt.get<int>("number"); 
    int b = pt.get<bool>("enabled"); 
    std::cout << "i=" << i << " b=" << b << "\n"; 
} 

test.json:

{ 
    "number" : 42, 
    "enabled" : "Yes" 
} 

输出:

i=42 b=1 

请注意,这个例子假定属性树是不区分大小写,并使用std::string。如果你想BoolTranslator更一般,你必须制作BoolTranslator模板,并提供专业化的宽字符串和区分大小写的比较。

+0

好了,哇。谢谢@Emile - 这有效。目前它与魔法无法区分。我还没有尝试输出,但它看起来像'假'输出只是偶然的工作;不应该有虚假的引号吗? – Arunas 2012-03-19 01:41:48

+0

哈哈,当然,因为我正在为代码编写Boost测试用例,所以'str ==“0”'不属于'iequals(str,“yes”)' – Arunas 2012-03-19 02:03:04

+1

@Arunas:是的,应该引用“false”的引号。我很惊讶,即使编译。如果你开始学习*部分模板专业化*,这个答案不会显得如此神奇。 :-) – 2012-03-19 02:52:07

1

theboostcpplibraries.com也有一个很好的例子。

此基础上,我写了一个自定义的解析器(声明中省略):

boost::optional<bool> string_to_bool_translator::get_value(const std::string &s) { 
    auto tmp = boost::to_lower_copy(s); 
    if (tmp == "true" || tmp == "1" || tmp == "y" || tmp == "on") { 
     return boost::make_optional(true); 
    } else if (tmp == "false" || tmp == "0" || tmp == "n" || tmp == "off") { 
     return boost::make_optional(false); 
    } else { 
     return boost::none; 
    } 
} 

这只是针对布尔和std ::字符串,但易于扩展。

然后,

boost::property_tree::ptree pt; 
... 
string_to_bool_translator tr; 
auto optional_value = pt.get_optional<bool>(key, tr); 
+0

这几乎是@ f4在先前答复的评论。 +1为一个完整的例子。请注意,由于您必须指定翻译员,因此可能会被某人遗忘,并可能造成混淆。 – Arunas 2017-02-08 07:14:41

+0

是的。我想这就是translator_between会确保的,但我忽略了这一点,并没有尝试。 – sebkraemer 2017-02-08 14:35:18