Home > Net >  C 17 - How to deduct the variadic parameter pack of a lambda passed as a template parameter?
C 17 - How to deduct the variadic parameter pack of a lambda passed as a template parameter?

Time:03-27

I have a class memoize which deducts the variadic argument list of a function pointer passed to it as a template parameter (see code below).

I would like to be able to do the same thing for a lambda, but I don't seem to find the right way to do it. Ideally it should work when uncommenting and completing the last line of the main() function below (or see on https://godbolt.org/z/Y8G47h4GK).

#include <tuple>
#include <iostream>

template<typename... Ts> struct pack { };

template <class> struct pack_helper;

template <class R, class... Args> 
struct pack_helper<R(Args...)> {
    using args = pack<Args...>;
};

template <class R, class... Args> 
struct pack_helper<R(*)(Args...)> {
    using args = pack<Args...>;
};

template <class F, class = typename pack_helper<F>::args> 
class memoize;

template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
    using result_type = std::invoke_result_t<F, Args...>;

    memoize(F &&f) : _f(std::forward<F>(f)) {}

    result_type operator()(Args... args) { return _f(args...); }
private:
    F _f;
};

int test(int x) { return x   1; }

int main() {
    auto test_mem = memoize<decltype(&test)>(&test);
    std::cout << test_mem(2) << '\n';

    auto l = [](int x) -> int { return x   1; };
    // auto l_mem = memoize<?>(?);
}

CodePudding user response:

The following should do the trick. Note that the pack_helper logic has been modified, even for function pointers.

template<typename... Ts> struct pack { };


// helper functions to retrieve arguments for member function pointers

template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...));

template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...) const);

// helper functions: if called with an instance of the first parameter type of memorize, the return type is the one used as the second one

// overload for function
template<class Result, class ...Args>
pack<Args...> pack_helper(Result(Args...));

// overload for function pointer
template<class Result, class ...Args>
pack<Args...> pack_helper(Result (*)(Args...));

// sfinae will prevent this overload from being considered for anything not providing an operator()
// for those types we use the return type of helper function lambda_pack_helper
template<class T>
decltype(lambda_pack_helper(&T::operator())) pack_helper(T);


template <class F, class = decltype(pack_helper(std::declval<F>()))>
class memoize;

template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
    using result_type = std::invoke_result_t<F, Args...>;

    memoize(F&& f) : _f(std::move(f)) {}

    memoize(F const& f) : _f(f) {}

    result_type operator()(Args... args) { return _f(args...); }
private:
    F _f;
};

int test(int x) { return x   1; }

int main() {
    auto test_mem = memoize<decltype(&test)>(&test);
    std::cout << test_mem(2) << '\n';

    auto l = [](int x) -> int { return x   1; };

    auto l_mem = memoize<decltype(l)>(l);
    std::cout << l_mem(3) << '\n';
}

CodePudding user response:

I found another way (see below) but I think fabian's answer above is better!

#include <iostream>

template<typename... Ts> struct pack { };

template <class> struct pack_helper;

template <class R, class... Args> 
struct pack_helper<R(Args...)> {
    using args = pack<Args...>;
};

template <class R, class... Args> 
struct pack_helper<R(*)(Args...)> {
    using args = pack<Args...>;
};

// ---- for lambdas
template <class T> 
struct pack_helper : public pack_helper<decltype(&T::operator())>
{};

template <class LambdaClass, class R, class... Args> 
struct pack_helper<R(LambdaClass::*)(Args...) const> {
    using args = pack<Args...>;
};

template <class F, class =  typename pack_helper<F>::args>
class memoize;

template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
    using result_type = decltype(std::declval<F>()(std::declval<Args>()...));

    memoize(F&& f) : _f(std::forward<F>(f)) {}

    memoize(F const& f) : _f(f) {}

    result_type operator()(Args... args) { return _f(args...); }
private:
    F _f;
};

int test(int x) { return x   1; }

int main() {
    auto test_mem = memoize<decltype(&test)>(&test);
    std::cout << test_mem(2) << '\n';

    auto l = [](int x) -> int { return x   1; };

    auto l_mem = memoize<decltype(l)>(l);
    std::cout << l_mem(3) << '\n';
}
  • Related