This is what I'd like to do in C++11: given two time points (such as a timing class) as std::chrono::steady_clock::now()
, print their time difference elegantly, for instance:
1 day 4 hours 3 minutes 45 seconds
or
32 minutes 54 seconds 345 milliseconds
Please note that I'm not interested in simply using put_time
, since I want to print starting from the most significant unit of time. I know, it's a solution to just print the difference, but it's not pretty: I'm looking for an elegant solution :)
Cheers!
Here's an easily extensible solution using variadic templates and recursion. It defines ostream& operator<<(ostream&, const duration&)
for ease of use.
#include <chrono>
#include <iostream>
#include <tuple>
using day_t = std::chrono::duration<long long, std::ratio<3600 * 24>>;
template<typename> struct duration_traits {};
#define DURATION_TRAITS(Duration, Singular, Plural) \
template<> struct duration_traits<Duration> { \
constexpr static const char* singular = Singular; \
constexpr static const char* plural = Plural; \
}
DURATION_TRAITS(std::chrono::milliseconds, "millisecond", "milliseconds");
DURATION_TRAITS(std::chrono::seconds, "second", "seconds");
DURATION_TRAITS(std::chrono::minutes, "minute", "minutes");
DURATION_TRAITS(std::chrono::hours, "hour", "hours");
DURATION_TRAITS(day_t, "day", "days");
using divisions = std::tuple<std::chrono::milliseconds,
std::chrono::seconds,
std::chrono::minutes,
std::chrono::hours,
day_t>;
namespace detail {
template<typename...> struct print_duration_impl_ {};
template<typename Head, typename... Tail>
struct print_duration_impl_<Head, Tail...> {
template <typename Duration>
static bool print(std::ostream& os, Duration& dur) {
const auto started_printing = print_duration_impl_<Tail...>::print(os, dur);
const auto n = std::chrono::duration_cast<Head>(dur);
const auto count = n.count();
if (count == 0) {
return started_printing;
}
if (started_printing) {
os << ' ';
}
using traits = duration_traits<Head>;
os << count << ' ' << (count == 1 ? traits::singular : traits::plural);
dur -= n;
return true;
}
};
template<>
struct print_duration_impl_<> {
template <typename Duration>
static bool print(std::ostream& os, Duration& dur) {
return false;
}
};
template<typename...> struct print_duration {};
template<typename... Args>
struct print_duration<std::tuple<Args...>> {
template<typename Duration>
static void print(std::ostream& os, Duration dur) {
print_duration_impl_<Args...>::print(os, dur);
}
};
}
template<typename Rep, typename Period>
std::ostream& operator<<(std::ostream& os, const std::chrono::duration<Rep, Period>& dur) {
detail::print_duration<divisions>::print(os, dur);
return os;
}
New durations are added by specializing duration_traits
and inserting the type at the correct position in divisions. For example, adding a 10 ms jiffy type would involve:
using jiffy_t = std::chrono::duration<long long, std::centi>;
DURATION_TRAITS(jiffy_t, "jiffy", "jiffies");
using divisions = std::tuple<std::chrono::milliseconds,
jiffy_t,
std::chrono::seconds,
std::chrono::minutes,
std::chrono::hours,
day_t>;
Not bad for three lines of code!
Live example on ideone.com.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With