Illusion
Illusion

Reputation: 53

Casting double chrono::seconds into millisecond

I have simple function:

void foo(std::chrono::milliseconds ms) {
    std::cout << ms.count() << " milliseconds" << std::endl;
}

And next I'm calling them like this:

int main() {
    using namespace std::chrono_literals;

    boo(3s);
    boo(1h);
    boo(100ms);
}

Output is simple:

3000 milliseconds
3600000 milliseconds
100 milliseconds

But, what if I want to use this function like this:

boo(3.5s);
boo(0.5s);
boo(0.3days);

Then I have compile error. So, I can write function who receive chrono::duration:

template<class T>
void foo(std::chrono::duration<T> duration) {
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " milliseconds" << std::endl;
}

And then 3.5s will work, but 1h or 3.5h doesn't work. So, question, can I write universal function which convert any of 1s/1.s/1m/1.5m/1h/1.5h/etc into milliseconds? Maybe I can create overloaded operators for chrono::seconds/hours? Or just cast, cast, cast?

Upvotes: 5

Views: 1174

Answers (3)

Drew Dormann
Drew Dormann

Reputation: 63694

Converting a duration to a number is easier than you think!

If you divide a duration by a duration, you get a numeric value. The result, mathematically speaking and respected by std::chrono, has no unit.

In your case, to get the number of milliseconds, simply divide any duration by one millisecond.

template <class Rep, class Period>
void foo(std::chrono::duration<Rep, Period> duration) {
    std::cout << duration / 1ms << " milliseconds" << std::endl;
}

See it work in Compiler Explorer

It should be noted that the author of std::chrono explicitly recommends that .count() and duration_cast should only be used as a last resort.

Upvotes: 2

Muhammad Nizami
Muhammad Nizami

Reputation: 926

Based on this reference, we can include two template parameters for the function with the conversion. This is because we want to make the template function general so we need to tell it what type and ratio we're passing.

template<class R, class T>
void foo(std::chrono::duration<R, T> duration) {
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " milliseconds" << std::endl;
}

As per another reference for the literals, floating point only works on seconds, minutes, and hours. As for days and years, it only works using unsigned long long. So this should already work:

int main() {
    using namespace std::chrono_literals;

    foo(3.5s);
    foo(3.5min);
    foo(3.5h);
}

However, if we want to use the same notation for days and years, we need to create our own custom literal operator. However, note that we cannot do it with the same reserved operators. We need to add underscores to the operator name. They're more or less like this:

constexpr std::chrono::milliseconds operator ""_y(long double y) noexcept
{
    return std::chrono::milliseconds((unsigned long long) (y * 31556952000));
}

constexpr std::chrono::milliseconds operator ""_d(long double d) noexcept
{
    return std::chrono::milliseconds((unsigned long long) (d * 86400000));
}

After adding these operators, this should work:

int main() {
    using namespace std::chrono_literals;

    foo(3.5s);
    foo(3.5min);
    foo(3.5h);
    foo(3.5_d);
    foo(3.5_y);
}

Upvotes: -1

Ted Lyngmo
Ted Lyngmo

Reputation: 117148

The proper function template definition should take the period as a template parameter too:

template <class Rep, class Period>
void boo(std::chrono::duration<Rep, Period> duration) {
    
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration)
                     .count()
              << " milliseconds\n";
}

Or in C++20, don't call .count() and let it print the unit automatically:

template <class Rep, class Period>
void boo(std::chrono::duration<Rep, Period> duration) {
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration)
              << '\n';
}

I have no clue about the days literal though. Never seen it, but the rest of them will work.

int main() {
    using namespace std::chrono_literals;

    boo(3s);
    boo(1h);
    boo(100ms);

    boo(3.5s);
    boo(0.5s);
    boo(std::chrono::days(1) * 0.3); // what I got working
}

Demo

Upvotes: 6

Related Questions