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?
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);
}