0

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?

Demo

#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.

glades
  • 3,778
  • 1
  • 12
  • 34
  • 1
    Is this the shortest reproducible example? :( – HolyBlackCat Jan 23 '23 at 17:58
  • @HolyBlackCat Yes unfortunately. But I already found the bug... The return value of the inner lambda which is forwarded as callback to std::packaged_task constructor of course needs to have the return value of the outer callback, otherwise they don't match :) I had to read my own question again to understand what was going on – glades Jan 23 '23 at 18:10
  • 1
    @glades Sounds like a perfect opportunity to answer your own question to give others with similar problems some help. :) – Ted Lyngmo Jan 23 '23 at 18:14
  • @TedLyngmo Honetly, it looks like an obscure typo to me, not very useful to others. – HolyBlackCat Jan 23 '23 at 18:16
  • @HolyBlackCat Yeah, that's the other option. Close as a typo :) – Ted Lyngmo Jan 23 '23 at 18:17
  • @HolyBlackCat Close as typo pls. There are simpler questions with the same problem. – glades Jan 23 '23 at 18:20

0 Answers0