4

im have simple function like

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

and next im calling them by

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 im wanna to use this function with:

    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 isnt work. So, question, can im 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 ?

Im trying all described below/above

Illusion
  • 43
  • 3
  • 2
    Step 1: Read [the documentation](https://en.cppreference.com/w/cpp/symbol_index/chrono_literals). Step 2: Figure out what "days" should *actually* be. – tadman Aug 31 '23 at 21:24
  • 3
    Don't say "doesn't work". Do say the *exact* errors you're getting and show the code that generated them. – tadman Aug 31 '23 at 21:24
  • 1
    Careful there, @tadman. If too many people start reading the documentation, job competition will get fierce. – user4581301 Aug 31 '23 at 21:38
  • 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" – Illusion Aug 31 '23 at 21:45

3 Answers3

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

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
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.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
-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);
}
Muhammad Nizami
  • 904
  • 1
  • 5
  • 9