Casting double chrono::seconds into millisecond
Asked Answered
L

3

5

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?

Lyndel answered 31/8, 2023 at 21:12 Comment(4)
Step 1: Read the documentation. Step 2: Figure out what "days" should actually be.Satiety
Don't say "doesn't work". Do say the exact errors you're getting and show the code that generated them.Satiety
Hmm, compile error, i think ? main.cpp:17:9: error: could not convert ‘std::literals::chrono_literals::operator""s(3.5e+0l)’ from ‘duration>’ to ‘duration>’ 17 | boo(3.5s); i mean, im read the documentation, but, its question about tricks, not about "how to use"Lyndel
I don't see days being defined hereCamelliacamelopard
P
6

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

Promontory answered 31/8, 2023 at 21:31 Comment(2)
And yes, thank you, it work, strange, but im trying this before (template with two params), but, maybe im lost something, and it doesnt was work. Good!Lyndel
@Lyndel You're welcome! Great that it worked out!Promontory
W
2

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.

Worldweary answered 1/9, 2023 at 2:15 Comment(0)
F
-1

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);
}
Filemon answered 31/8, 2023 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.