Search code examples
c++c++11templatesboostc++-chrono

How to properly take a boost::optional<std::chrono::duration> as a function parameter?


I need to have a function accept a boost::optional<std::chrono::duration>. I would like to be able to pass it, for example, either std::chrono::milliseconds, std::chrono::seconds, or boost::none.

I have a couple of solutions which do not exactly achieve what I want.

Attempt 1:

template <class PeriodT>
void foo(boost::optional<std::chrono::duration<int64_t, PeriodT>> duration);

Problems with attempt 1:

I cannot simply pass an std::chrono::duration or even boost::none. To pass boost::none I have to do the following:

boost::optional<std::chrono::seconds> input = boost::none;
foo(input);

I cannot call

foo(boost::none); 

or

std::chrono::seconds input(10);
foo(input);

Attempt 2:

void foo(boost::optional<std::chrono::milliseconds> duration);

Problems with attempt 2:

Automatic conversion won't happen.

foo(std::chrono::seconds(10));

Will not compile.

foo(std::chrono::milliseconds(10));

Will compile, and so will

foo(boost::none);

Is there any way I can have a function cleanly accept a boost::optional
of any rep/period?

The function in my actual use case will need to accept multiple of these optional durations, so default values won't work for me.


Solution

  • #include "boost/optional.hpp"
    #include <chrono>
    #include <iostream>
    
    void foo(boost::optional<std::chrono::milliseconds> duration)
    {
        if (duration)
            std::cout << duration->count() << "ms\n";
        else
            std::cout << "none\n";
    }
    
    void foo(std::chrono::milliseconds duration)
    {
        foo(boost::optional<std::chrono::milliseconds>{duration});
    }
    
    int
    main()
    {
        using namespace std::chrono;
        foo(10ms);
        foo(10s);
        foo(boost::none);
    }
    

    If you want to accept any chrono::duration<Rep, Period>, you need to add one more overload and template everything. Also you need to decide what Rep and Period you want to default to for boost::none:

    #include "boost/optional.hpp"
    #include "date/date.h"
    #include <chrono>
    #include <iostream>
    
    template <class Rep, class Period>
    void foo(boost::optional<std::chrono::duration<Rep, Period>> duration)
    {
        using date::operator<<;
        if (duration)
            std::cout << *duration << "\n";
        else
            std::cout << "none\n";
    }
    
    template <class Rep, class Period>
    void foo(std::chrono::duration<Rep, Period> duration)
    {
        foo(boost::optional<std::chrono::duration<Rep, Period>>{duration});
    }
    
    void foo(boost::none_t)
    {
        foo(boost::optional<std::chrono::seconds>{});
    }
    
    int
    main()
    {
        using namespace std::chrono;
        foo(10ms);
        foo(10s);
        foo(boost::none);
    }
    

    Above I've also used Howard's date lib for easier printing of the arbitrary durations:

    10ms
    10s
    none