2012-03-16 6 views
11

Zgubiłem się w plikach nagłówkowych dla boost property_tree i biorąc pod uwagę brak dokumentacji wokół niższych warstw, zdecydowałem się zapytać, w jaki prosty sposób over-ride translatora strumienia, aby zmienić sposób analizowania wartości logicznych.Zmień sposób pobierania :: property_tree tłumaczy ciągi znaków na bool

Problem polega na tym, że po stronie wejściowej drzewa właściwości znajdują się użytkownicy i mogą modyfikować pliki konfiguracyjne. Wartość logiczna może być określona na wiele sposobów, takich jak:

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

Domyślnym zachowaniem jest, aby sprawdzić, 0 lub 1, a następnie użyć

std::ios_base::boolalpha 

aby uzyskać strumień próbować zanalizować wartość w odpowiedni sposób dla bieżących ustawień narodowych ... co może być szalone, jeśli spróbujemy wysłać plik konfiguracyjny do klientów międzynarodowych.

Jaki jest najprostszy sposób na zastąpienie tego zachowania lub tylko bool? Jest to nie tylko najłatwiejsze do wdrożenia, ale i najłatwiejsze w użyciu - aby użytkownicy mojej klasy, którzy pochodzą z programu iptree, nie musieli wykonywać żadnych specjalnych czynności dla wartości typu Boolean.

Dzięki!

Odpowiedz

18

Można specjalizować boost::property_tree::translator_between, aby drzewo właściwości używało niestandardowego translatora dla typu wartości bool. Ta specjalizacja musi być widoczna (tzn. #includ ed) przez klientów, którzy chcą niestandardowego zachowania. Oto przykład praca:

#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" 
} 

wyjściowa:

i=42 b=1 

Należy pamiętać, że w tym przykładzie założono, że drzewo nieruchomość nie uwzględnia wielkości liter i wykorzystuje std::string. Jeśli chcesz, aby numer BoolTranslator był bardziej ogólny, musisz utworzyć szablon i zapewnić specjalizację dla szerokich ciągów znaków i rozróżniania wielkości liter.

+0

No wow. Dzięki @Emile - to działa. Obecnie jest nie do odróżnienia od magii. Nie próbowałem jeszcze wyjścia, ale wygląda na to, że "fałsz" działa tylko przypadkowo; nie powinno być cytatów wokół fałszu? – Arunas

+0

haha, i oczywiście od kiedy piszę Boost test przypadków dla kodu, jest również oczywiste, że 'str ==" 0 "' nie należy do tego samego zdania, co 'iequals (str," yes ")' – Arunas

+1

@Arunas: Tak, powinny być cytaty w okolicy "false". Jestem zaskoczony, że nawet skompilowałem. Jeśli przejdziesz do studiowania * częściowej specjalizacji szablonów *, ta odpowiedź nie okaże się tak magiczna. :-) –

1

Istnieje również dobry przykład w theboostcpplibraries.com.

Na tej podstawie napisałem dla niestandardowego parsera (deklaracja pominięta):

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; 
    } 
} 

To tylko dla bool i std :: string, ale łatwo rozszerzalny.

Następnie

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

Co jest właściwie tym, co @ f4 zasugerowało w komentarze do wcześniejszej odpowiedzi. +1 dla pełnego przykładu. Zauważ, że skoro musisz określić tłumacza, to jest to coś, o czym ktoś może zapomnieć, co może siać zamęt. – Arunas

+0

Prawda. Domyślam się, że to zapewni translator_between, ale przeoczyłem to i jeszcze go nie wypróbowałem. – sebkraemer

Powiązane problemy