Domanda

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?

È stato utile?

Soluzione

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:

  1. It needlessly drops type safety by directly manipulating the Rep.

  2. It assumes that the literal 0 is implicitly convertible to, or at least comparable with Rep.

  3. There is no reason for this not to be constexpr.

  4. 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;
}
  1. duration has unary -, just use it.

  2. 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.

  3. All of the operations used are constexpr, marked abs with constexpr.

  4. 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).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top