I'm attempting to allow setting the units that a std::chrono::duration is reported in by modifying an xml config file parsed by boost::property_tree. My current, non-compiling solution attempts to do this using std::variant.
In .hpp class declaration
using TimestampVariant = std::variant<
std::chrono::nanoseconds,
std::chrono::microseconds,
std::chrono::milliseconds,
std::chrono::seconds
>;
TimestampVariant _timestamp_v;
in .cpp
auto GetTimestampVisitor = [](const auto& t) -> decltype(auto) {
return std::chrono::duration_cast<std::remove_reference_t<decltype(t)>>(std::chrono::system_clock::now().time_since_epoch()).count();
};
void SetupFunction()
{
boost::property_tree::ptree property_tree;
boost::property_tree::read_xml(filepath, property_tree);
auto config = property_tree.get_child("Config");
std::string timestamp_type = config.get<std::string>("ReportingUnits");
if(!timestamp_type.compare("seconds") || !timestamp_type.compare("s"))
{
_timestamp_v = std::chrono::seconds();
}
else if(!timestamp_type.compare("milliseconds") || !timestamp_type.compare("ms"))
{
_timestamp_v = std::chrono::milliseconds();
}
else if(!timestamp_type.compare("microseconds") || !timestamp_type.compare("us"))
{
_timestamp_v = std::chrono::microseconds();
}
else if(!timestamp_type.compare("nanoseconds") || !timestamp_type.compare("ns"))
{
_timestamp_v = std::chrono::nanoseconds();
}
}
void OutputFunction()
{
std::cout << std::visit(GetTimestampVisitor, _timestamp_v) << std::endl;
}
I'm admittedly not skilled at metaprogramming. Is there a simpler way to do this? Essentially I can only guarantee at compile time that the type of the duration will be one of a subset of duration types.
Meta-programming is not required because there is a common representation.
Just always store a plain duration
with enough precision. Then on output scale to the desired presentation format:
#include <boost/property_tree/xml_parser.hpp>
#include <chrono>
#include <iostream>
#include <map>
struct Program {
using duration = std::chrono::steady_clock::duration;
auto ReportingDuration(duration d) const {
using namespace std::chrono_literals;
using unit = decltype(1.ns); // or `duration` for integral
static std::map<std::string_view, unit> const units = {
{"ns", 1ns}, {"nanoseconds", 1ns}, //
{"us", 1us}, {"microseconds", 1us}, //
{"ms", 1ms}, {"milliseconds", 1ms}, //
{"s", 1s}, {"seconds", 1s}, //
{"m", 1min}, {"minutes", 1min}, //
{"h", 1h}, {"hours", 1h}, //
{"d", 24h}, {"days", 24h},
};
return d / units.at(reporting_unit_);
};
bool ReportingUnitValid() const try {
return ReportingDuration(std::chrono::hours(24)), true;
} catch (...) {
return false;
}
void SetupFunction(std::string const& filepath = "config.xml") {
boost::property_tree::ptree pt;
read_xml(filepath, pt);
reporting_unit_ = pt.get<std::string>("Config.ReportingUnits");
if (!ReportingUnitValid())
throw std::runtime_error("Invalid reporting unit");
}
void OutputFunction() {
using namespace std::chrono_literals;
for (duration v : std::vector<duration> //
{ //
1ns, 1us, 1ms, 1s, //
1min, //
1h, 1h + 1min, //
1h + 1min + 1s, //
24h, 24h + 1min, 24h + 1min + 1s})
std::cout << std::setw(16) << v << ": " << ReportingDuration(v) << " " << reporting_unit_
<< std::endl;
}
private:
std::string reporting_unit_ = "s";
};
int main() {
std::cout << std::fixed;
Program p;
p.SetupFunction();
p.OutputFunction();
}
With all tests:
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp
for unit in d h m s ms us ns; do cat > config.xml <<< "<Config><ReportingUnits>$unit</ReportingUnits></Config>"; ./a.out; done
Printing:
1ns: 0.000000 d
1000ns: 0.000000 d
1000000ns: 0.000000 d
1000000000ns: 0.000012 d
60000000000ns: 0.000694 d
3600000000000ns: 0.041667 d
3660000000000ns: 0.042361 d
3661000000000ns: 0.042373 d
86400000000000ns: 1.000000 d
86460000000000ns: 1.000694 d
86461000000000ns: 1.000706 d
1ns: 0.000000 h
1000ns: 0.000000 h
1000000ns: 0.000000 h
1000000000ns: 0.000278 h
60000000000ns: 0.016667 h
3600000000000ns: 1.000000 h
3660000000000ns: 1.016667 h
3661000000000ns: 1.016944 h
86400000000000ns: 24.000000 h
86460000000000ns: 24.016667 h
86461000000000ns: 24.016944 h
1ns: 0.000000 m
1000ns: 0.000000 m
1000000ns: 0.000017 m
1000000000ns: 0.016667 m
60000000000ns: 1.000000 m
3600000000000ns: 60.000000 m
3660000000000ns: 61.000000 m
3661000000000ns: 61.016667 m
86400000000000ns: 1440.000000 m
86460000000000ns: 1441.000000 m
86461000000000ns: 1441.016667 m
1ns: 0.000000 s
1000ns: 0.000001 s
1000000ns: 0.001000 s
1000000000ns: 1.000000 s
60000000000ns: 60.000000 s
3600000000000ns: 3600.000000 s
3660000000000ns: 3660.000000 s
3661000000000ns: 3661.000000 s
86400000000000ns: 86400.000000 s
86460000000000ns: 86460.000000 s
86461000000000ns: 86461.000000 s
1ns: 0.000001 ms
1000ns: 0.001000 ms
1000000ns: 1.000000 ms
1000000000ns: 1000.000000 ms
60000000000ns: 60000.000000 ms
3600000000000ns: 3600000.000000 ms
3660000000000ns: 3660000.000000 ms
3661000000000ns: 3661000.000000 ms
86400000000000ns: 86400000.000000 ms
86460000000000ns: 86460000.000000 ms
86461000000000ns: 86461000.000000 ms
1ns: 0.001000 us
1000ns: 1.000000 us
1000000ns: 1000.000000 us
1000000000ns: 1000000.000000 us
60000000000ns: 60000000.000000 us
3600000000000ns: 3600000000.000000 us
3660000000000ns: 3660000000.000000 us
3661000000000ns: 3661000000.000000 us
86400000000000ns: 86400000000.000000 us
86460000000000ns: 86460000000.000000 us
86461000000000ns: 86461000000.000000 us
1ns: 1.000000 ns
1000ns: 1000.000000 ns
1000000ns: 1000000.000000 ns
1000000000ns: 1000000000.000000 ns
60000000000ns: 60000000000.000000 ns
3600000000000ns: 3600000000000.000000 ns
3660000000000ns: 3660000000000.000000 ns
3661000000000ns: 3661000000000.000000 ns
86400000000000ns: 86400000000000.000000 ns
86460000000000ns: 86460000000000.000000 ns
86461000000000ns: 86461000000000.000000 ns
Live Demo:
Note how the video only recompiles for fractional durations at the very end.