Given that std::chrono::duration
can represent the signed difference between two times, it would seem a very common case to need the absolute value of such a duration. For example, the following code outputs diff: -5
as expected:
using namespace std;
using namespace std::chrono;
auto now = system_clock::now();
auto then = now - seconds(5);
auto diff = then - now;
cout << "diff: " << duration_cast<seconds>(diff).count() << endl;
It would be nice to be able to do something like:
auto diff = abs(then - now);
However, I can't see any specialization of std::abs
for the chrono
templates in the standard, nor can I see any appropriate member function in std::chrono::duration
.
How should I be transforming a std::chrono::duration
into it's absolute value?
Class template std::chrono::duration represents a time interval. It consists of a count of ticks of type Rep and a tick period, where the tick period is a compile-time rational fraction representing the time in seconds from one tick to the next. The only data stored in a duration is a tick count of type Rep .
C++ includes support for two types of time manipulation: The chrono library, a flexible collection of types that track time with varying degrees of precision (e.g. std::chrono::time_point). C-style date and time library (e.g. std::time)
I would do it like this:
template <class Rep, class Period>
std::chrono::duration<Rep, Period>
abs(std::chrono::duration<Rep, Period> d)
{
Rep x = d.count();
return std::chrono::duration<Rep, Period>(x >= 0 ? x : -x);
}
See my list of <chrono>
Utilities for other <chrono>
utilities I wish were standard. Please feel free to use them, recommend them to your standards body representatives, or propose them yourself.
Update
I was off my game when I wrote the above. I'm not deleting it, as it serves as a good lesson for both myself and others on how not to write chrono
utilities.
Things I don't like about it:
It needlessly drops type safety by directly manipulating the Rep
.
It assumes that the literal 0 is implicitly convertible to, or at least comparable with Rep
.
There is no reason for this not to be constexpr
.
I'm not happy about the behavior for unsigned Rep
. If one says:
auto d = abs(t1 - t0);
and t1
and t0
are based on unsigned durations, then this is likely a logic bug in the code. If t1 < t0
, then you are likely to get an incorrect, very large duration. If that is what you really want, then you shouldn't be using abs
, and instead just code the simpler:
auto d = t1 - t0;
To address these concerns I've rewritten abs
for durations as:
template <class Rep, class Period,
class = typename std::enable_if
<
std::chrono::duration<Rep, Period>::min() <
std::chrono::duration<Rep, Period>::zero()
>::type
>
constexpr
inline
std::chrono::duration<Rep, Period>
abs(std::chrono::duration<Rep, Period> d)
{
return d >= d.zero() ? d : -d;
}
duration
has unary -
, just use it.
duration
has a customizable zero
trait just so that one doesn't have to assume an intimate cooperation with 0
from Rep
. Just use it.
All of the operations used are constexpr
, marked abs
with constexpr
.
The static functions min
and zero
are constexpr
. Using these to determine if Rep
is signed is more general than using a trait such as !std::is_unsigned
. I.e. Rep
might be a BigNum
, or a C11 timespec
(augmented with overloaded arithmetic operators). So the question "is signed" is answered with min() < zero()
. And now this version of abs
will not accept a duration<unsigned, milli>
(for example).
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