c++17c++-chronoboost-propertytree

Is there a way to parse a std::chrono::duration with a string literal as a field in an XML file using boost::property_tree?


I would like to be able to write my config file as:

<?xml version="1.0" encoding="UTF-8"?>
<Config>  
  <DurationValue>20ns</DurationValue>
</Config>

or as:

<?xml version="1.0" encoding="UTF-8"?>
<Config>  
  <DurationValue>15s</DurationValue>
</Config>

Then parse the DurationValue field the same way in both cases. I'm running into an issue where I don't know what type to tell boost::property_tree that the field is. Internally, the value is stored as a std::chrono::nanoseconds.

My attempt at parsing:

boost::property_tree::ptree property_tree;
boost::property_tree::read_xml("config.xml", property_tree);
auto config = property_tree.get_child("Config");
std::chrono::nanoseconds duration_value = std::chrono::duration_cast<std::chrono::nanoseconds>(config.get<std::common_type<std::chrono::duration>>("DurationValue"));

Is there a way to do this that I am missing? I want to be flexible enough to support using any std::chrono literal. Thanks in advance.


Solution

  • How about telling boost::property_tree to parse the field as a string and then pass that string off to a custom post-processor to turn it into the desired duration?

    std::chrono::nanoseconds
    parse_duration(std::string s)
    {
        using namespace std;
        using namespace chrono;
        istringstream in{std::move(s)};
        in.exceptions(ios::failbit);
        int v;
        string unit;
        in >> v >> unit;
        if (unit == "h")
            return hours{v};
        else if (unit == "min")
            return minutes{v};
        else if (unit == "s")
            return seconds{v};
        else if (unit == "ms")
            return milliseconds{v};
        else if (unit == "us")
            return microseconds{v};
        else if (unit == "ns")
            return nanoseconds{v};
        throw std::runtime_error("bad unit");
    }