问题
I wanted to use boosts property tree as handling the settings of my c++ app since it seems to be widely used in this scenario.
My question: when changing values in the property tree (through xml parsing or manually), is there a way to specify a list of allowed values of a key in advance? E.g. if I wanted to do a simple "Yes/No" setting, do I have to check the values with an if - condition or can I somehow teach my tree to only accept the two values "Yes" and "No" for the specific key in advance, so that it throws an exception on error.
回答1:
You can use translators for this. A nice blog post I remember that describes this to get custom date format parsing in an XML-backed property tree was here:
- Andzrej's blog
Let's take your example:
enum class YesNo { No, Yes };
In this case the calling code could look like:
static YesNoTranslator trans;
int main() {
std::istringstream iss(R"(
<?xml version="1.0"?>
<demo>
<positive>Yes</positive>
<negative>No</negative>
<invalid>Bogus</invalid>
</demo>
)");
ptree pt;
read_xml(iss, pt);
for (auto&& field : { "demo.positive", "demo.negative", "demo.invalid" })
{
try {
std::cout << "With 'No' default: '" << field << "':\t" << pt.get(field, YesNo::No, trans) << "\n";
std::cout << "Without default: '" << field << "':\t" << pt.get<YesNo>(field, trans) << "\n";
} catch(std::exception const& e) {
std::cout << "Error parsing '" << field << "':\t" << e.what() << "\n";
}
}
}
Full Demo
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <sstream>
#include <iostream>
using boost::property_tree::ptree;
enum class YesNo { No, Yes };
static inline std::ostream& operator<<(std::ostream& os, YesNo v) {
switch(v) {
case YesNo::Yes: return os << "Yes";
case YesNo::No: return os << "No";
}
return os << "??";
}
struct YesNoTranslator {
typedef std::string internal_type;
typedef YesNo external_type;
boost::optional<external_type> get_value(internal_type const& v) {
if (v == "Yes") return YesNo::Yes;
if (v == "No") return YesNo::No;
return boost::none;
}
boost::optional<internal_type> put_value(external_type const& v) {
switch(v) {
case YesNo::Yes: return std::string("Yes");
case YesNo::No: return std::string("No");
default: throw std::domain_error("YesNo");
}
}
};
static YesNoTranslator trans;
int main() {
std::istringstream iss(R"(
<?xml version="1.0"?>
<demo>
<positive>Yes</positive>
<negative>No</negative>
<invalid>Bogus</invalid>
</demo>
)");
ptree pt;
read_xml(iss, pt);
for (auto&& field : { "demo.positive", "demo.negative", "demo.invalid" })
{
try {
std::cout << "With 'No' default: '" << field << "':\t" << pt.get(field, YesNo::No, trans) << "\n";
std::cout << "Without default: '" << field << "':\t" << pt.get<YesNo>(field, trans) << "\n";
} catch(std::exception const& e) {
std::cout << "Error parsing '" << field << "':\t" << e.what() << "\n";
}
}
}
Which prints
With 'No' default: 'demo.positive': Yes
Without default: 'demo.positive': Yes
With 'No' default: 'demo.negative': No
Without default: 'demo.negative': No
With 'No' default: 'demo.invalid': No
Without default: 'demo.invalid': Error parsing 'demo.invalid': conversion of data to type "5YesNo" failed
来源:https://stackoverflow.com/questions/31277354/boost-property-tree-specify-allowed-values