c++std-function

How to make c++ compiler distinguish between std::function<void()> and std::function<asio::awaitable<void>()> parameters?


In my another question, I learned that std::function<asio::awaitable<void>()> can be automatically converted to std::function< void()>. This leads to a problem like the following:

I need to write a function with two overloads that accept two callbacks: std::function<asio::awaitable<void>()> or std::function<void()>. Based on the previous question, it's clear that this won't work.

But if you change it to only accept std::function<void()> type callbacks. While passing in std::function<asio::awaitable<void>()> type callbacks would be equally legal, the true return value of the callback would not be available inside the function.

Of course it could be written as 2 functions with different names. But the problem still remains, the version of std::function<void()> will lead to very easy misuse. Because even if you accidentally pass std::function<asio::awaitable<void>, the compiler will not report an error.

Any ideas on how to solve the above problem?


Solution

  • Use a concept on the return type of the callable you pass. Such as returns_awaitable_void in this demonstration program:

    namespace asio
    {
        template<typename T> struct awaitable {};
    }
    
    #include <concepts>
    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    concept returns_awaitable_void =
        std::is_same_v<std::invoke_result_t<T>, asio::awaitable<void>>;
    
    void f(returns_awaitable_void auto&& g)
    {
        std::clog << "in f(returns_awaitable_void&&)\n";
        g();
    }
    
    template<std::invocable Callable>
    void f(Callable&& g)
        requires (not returns_awaitable_void<Callable>)
    {
        std::clog << "in f(Callable&&)\n";
        g();
    }
    
    void foo() {}
    
    asio::awaitable<void> bar() { return {}; }
    
    
    int main()
    {
        f(foo);
        f(bar);
    }