Изменить способ чтения Boost :: property_tree для перевода строк в bool - PullRequest
14 голосов
/ 17 марта 2012

Я заблудился в заголовочных файлах для boost property_tree и, учитывая отсутствие документации по нижним уровням, я решил спросить, как проще всего переопределить потоковый транслятор, чтобы изменить значения Boolean анализируются.

Проблема в том, что на входной стороне дерева свойств есть пользователи, которые могут изменять файлы конфигурации. Логическое значение может быть указано несколькими способами, например:

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

Поведение по умолчанию - проверить 0 или 1, а затем использовать

std::ios_base::boolalpha 

чтобы поток попытался проанализировать значение соответствующим образом для текущей локали ... что может быть безумно, если мы попытаемся отправить файл конфигурации международным клиентам.

Так, каков самый простой способ переопределить это поведение или только bool? Не только самый простой в реализации, но и самый простой в использовании - так что пользователям моего класса, производным от iptree, не нужно делать что-то особенное для логических значений.

Спасибо!

Ответы [ 2 ]

21 голосов
/ 18 марта 2012

Вы можете специализировать boost::property_tree::translator_between, чтобы в дереве свойств использовался собственный переводчик для типа значения bool. Эта специализация должна быть видна (т. Е. #includ ed) клиентами, желающими настроить поведение. Вот рабочий пример:

#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 шаблоном и обеспечить специализации для широких строк и сравнения с учетом регистра.

1 голос
/ 07 февраля 2017

Есть также хороший пример на 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;
    }
} 

Это только для bool и std :: string, но легко расширяется.

Тогда

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