Search code examples
c++templateslambdaspecialization

Template specialization based on return type of passed lambda - C++


I am trying to make a template specialization based on return type of lambda function which it gets as a parameter. The goal is to have a function to which I can pass a lambda (or another function if possible) let the function measure run time of that lambda and return the lambda result. Problem comes when lambda does not have return value (void).

For now I am using two functions one for void return type and second for all other return types.

template <typename F, typename ... Args>
static void measured_run(double& time, F f, Args&&...args)
{
    auto start = boost::posix_time::microsec_clock::local_time();
    f(std::forward<Args>(args)...);
    auto end = boost::posix_time::microsec_clock::local_time();
    time += (end - start).total_milliseconds();
}


template <typename F, typename ... Args>
static auto measured_run_ret_val(double & time, F f, Args&&...args) -> decltype(f(std::forward<Args>(args)...))
{
    auto start = boost::posix_time::microsec_clock::local_time();        
    auto result = f(std::forward<Args>(args)...);    
    auto end = boost::posix_time::microsec_clock::local_time();
    time += (end - start).total_milliseconds();        
    return result;
}

I would like to be able to use it like this:

double time;
measured_run_ret_val(time, []() {return false; });
measured_run_ret_val(time, []() {printf("void\n"); });

But I get the error:

<source>: In instantiation of 'decltype (f((forward<Args>)(measured_run_ret_val::args)...)) measured_run_ret_val(double&, F, Args&& ...) [with F = main()::<lambda()>; Args = {}; decltype (f((forward<Args>)(measured_run_ret_val::args)...)) = void]':
<source>:27:25:   required from here
<source>:17:10: error: 'void result' has incomplete type
   17 |     auto result = f(std::forward<Args>(args)...);
      |          ^~~~~~
<source>:20:12: error: return-statement with a value, in function returning 'void' [-fpermissive]
   20 |     return result;
      |            ^~~~~~

Right now I am using c++14, but if there would be a solution in 17/20 I would be curious about it as well.

Thank you for any suggestions.


Solution

  • Using C++17 you can use constexpr if to change the logic in the function depending on if the function returns void or not. That could look like:

    template <typename F, typename ... Args>
    static auto measured_run(double & time, F f, Args&&...args)
    {
        using ret_t = decltype(f(std::forward<Args>(args)...));
        if constexpr (std::is_same_v<ret_t, void>)
        {
            auto start = boost::posix_time::microsec_clock::local_time();
            f(std::forward<Args>(args)...);
            auto end = boost::posix_time::microsec_clock::local_time();
            time += (end - start).total_milliseconds();
            return;
        }   
        else      
        {
            auto start = boost::posix_time::microsec_clock::local_time();        
            auto result = f(std::forward<Args>(args)...);    
            auto end = boost::posix_time::microsec_clock::local_time();
            time += (end - start).total_milliseconds();        
            return result;
        }
    }