The following simplified code introduces a function http_GET()
that is based on a custom async
implementation that takes a callback as parameter. The async
function creates a (here temporary) object entity
which allows a std::future
to be exctracted via get_future()
. This future should fit the return value of the callback function (std::future<response>
).
Now I've got a problem in what I believe is the construction of the entity
object, in that the internal function __invoke_r
can't find the right overload. I'm not really sure why this is. I first thought it was because the return value of the callback (in this case response{}
) couldn't be deduced properly. However gcc actually deduces that right as I see in the errors. What am I missing?
#include <type_traits>
#include <concepts>
#include <memory_resource>
#include <functional>
#include <tuple>
#include <cstdio>
#include <utility>
#include <type_traits>
#include <future>
using allocator_t = std::pmr::polymorphic_allocator<std::byte>;
template <typename R>
struct entity
{
template <typename Cb> requires (std::constructible_from<std::packaged_task<R()>, Cb>)
entity(Cb&& fn, allocator_t allocator = {})
: task_{ std::move(fn) }
{
}
auto get_future() -> std::future<R> {
return std::future<R>{};
}
std::packaged_task<R()> task_;
};
template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_alloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) -> std::future<decltype(fn(std::get<I>(std::move(tuple))...))>
{
return entity<decltype(fn(std::get<I>(std::move(tuple))...))>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
fn(std::forward<decltype(bargs)>(bargs)...);
}, std::get<std::index_sequence<I...>::size()>(std::move(tuple))).get_future();
}
template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_noalloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) -> std::future<decltype(fn(std::get<I>(std::move(tuple))...))>
{
return entity<decltype(fn(std::get<I>(std::move(tuple))...))>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
fn(std::forward<decltype(bargs)>(bargs)...);
}).get_future();
}
template<typename Cb, typename... Args>
auto async2(Cb&& fn, Args&&... args)
{
auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
constexpr std::size_t cnt = sizeof...(Args);
if constexpr (std::same_as<std::remove_cvref_t<std::tuple_element<cnt-1, decltype(tuple)>>, allocator_t>) {
printf("with allocator\n");
return async_impl_alloc(std::forward<Cb>(fn), std::forward<decltype(tuple)>(tuple), std::make_index_sequence<cnt-1>{});
} else {
printf("without allocator\n");
return async_impl_noalloc(std::forward<Cb>(fn), std::forward<decltype(tuple)>(tuple), std::make_index_sequence<cnt>{});
}
}
struct response
{};
auto http_GET(std::pmr::string url)
{
return async2([](const std::pmr::string& url){
return response{};
}, std::move(url));
}
int main()
{
allocator_t allocator;
auto fut = http_GET("http://my.url.com");
}
Error:
/opt/compiler-explorer/gcc-trunk-20230123/include/c++/13.0.1/future:1491:41: error: no matching function for call to '__invoke_r<response>(async_impl_noalloc<http_GET(std::pmr::string)::<lambda(const std::pmr::string&)>, std::__cxx11::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> >&&, 0>(http_GET(std::pmr::string)::<lambda(const std::pmr::string&)>&&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> >&&>, std::index_sequence<0>)::<lambda()>&)'
1491 | return std::__invoke_r<_Res>(_M_impl._M_fn,
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
1492 | std::forward<_Args>(__args)...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Also if someone knows an easier way to deduce return values of the Callbackb fn
that would be helpful as well.