How to Deduce Argument List from Function Pointer?
Asked Answered
A

4

10

Given two or more example functions, is it possible to write templated code which would be able to deduce the arguments of a function provided as a template parameter?

This is the motivating example:

void do_something(int value, double amount) {
    std::cout << (value * amount) << std::endl;
}

void do_something_else(std::string const& first, double & second, int third) {
    for(char c : first) 
        if(third / c == 0) 
            second += 13.7;
}

template<void(*Func)(/*???*/)>
struct wrapper {
    using Args = /*???*/;
    void operator()(Args&& ... args) const {
        Func(std::forward<Args>(args)...);
    }
};

int main() {
    wrapper<do_something> obj; //Should be able to deduce Args to be [int, double]
    obj(5, 17.4); //Would call do_something(5, 17.4);
    wrapper<do_something_else> obj2; //Should be able to deduce Args to be [std::string const&, double&, int]
    double value = 5;
    obj2("Hello there!", value, 70); //Would call do_something_else("Hello there!", value, 70);
}

In both uses of /*???*/, I am trying to work out what I could put there that would enable this kind of code.

The following doesn't appear to work, due to Args not being defined before its first use (along with what I have to assume are numerous syntax errors besides), and even if it did, I'm still looking for a version that doesn't require explicit writing of the types themselves:

template<void(*Func)(Args ...), typename ... Args)
struct wrapper {
    void operator()(Args ...args) const {
        Func(std::forward<Args>(args)...);
    }
};

wrapper<do_something, int, double> obj;
Agc answered 2/10, 2017 at 21:4 Comment(7)
you mean std::function ?Suspicion
@Suspicion What I'm looking for specifically is a way to deduce Args from the Function provided. std::function matches function pointers by having those types specified explicitly in the type parameter list.Agc
do you accept C++17? wink winkSuspicion
@Suspicion I'm using MSVC, so my ability to use C++17 is constrained by the quality of Microsoft's compiler implementation, but if an exclusively-C++17 solution exists, I'd be happy to see it.Agc
May I ask what would be the benefits of having such wrappers when you can call do_something or do_something_else directly? Storing pointer to function as template parameter does not seem to be useful. Also if you don't want to write types you can utilize decltype to declare a first template parameter as pointer to method and then just pass pointer value as second parameter.Triphibious
@VTT The use-case is a series of resource wrappers that use function pointers as part of their Destructors. It's too complicated on its own to bother demonstrating here, so I'm just showing the important part.Agc
So you basically want to generate custom deleter template be used with unique_ptr or something similar?Triphibious
S
11

With C++17 we can have auto template non-type parameters which make possible the Wrapper<do_something> w{} syntax 1).

As for deducing Args... you can do that with a specialization.

template <auto* F>
struct Wrapper {};

template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<F>
{
    auto operator()(Args... args) const
    {
        return F(args...);
    }
};
Wrapper<do_something> w{};
w(10, 11.11);

1) Without C++17 it's impossible to have the Wrapper<do_something> w{} nice syntax.

The best you can do is:

template <class F, F* func>
struct Wrapper {};

template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<Ret (Args...), F>
{
    auto operator()(Args... args) const
    {
        return F(args...);
    }
};
Wrapper<declype(do_something), do_something> w{};
Suspicion answered 2/10, 2017 at 21:17 Comment(1)
@Agc C++17 is in the early stages of implementation. clang can compile it without a problem: godbolt.org/g/DyArxaSuspicion
H
5

With C++17, you can do this:

template <auto FUNC, typename = decltype(FUNC)>
struct wrapper;

template <auto FUNC, typename RETURN, typename ...ARGS>
struct wrapper<FUNC, RETURN (*)(ARGS...)> {
    RETURN operator()(ARGS ...args) {
        return FUNC(args...);
    }
};

I've learned this technique from W.F.'s answer

Hex answered 2/10, 2017 at 21:37 Comment(1)
Bolov fixed his solution, and actually I like that solution better now.Hex
T
3

Further improvement of C++17 version: less template parameters and proper noexcept annotation:

template<auto VFnPtr> struct
wrapper;

template<typename TResult, typename... TArgs, TResult ( * VFnPtr)(TArgs...)> struct
wrapper<VFnPtr>
{
    TResult
    operator ()(TArgs... args) const noexcept(noexcept((*VFnPtr)(::std::forward<TArgs>(args)...)))
    {
        return (*VFnPtr)(::std::forward<TArgs>(args)...);
    }
};
Triphibious answered 2/10, 2017 at 23:6 Comment(0)
A
0

With C++11 you can consider a templated make_wrapper helper function. However, with this approach the function pointer is not a template parameter. Instead, the function pointer is "carried" by the non-static data member called f_ in the following example:

#include <iostream>

void do_something(int value, double amount) {
  std::cout << (value * amount) << std::endl;
}

void do_something_else(std::string const& first, double & second, int third) {
  for(char c : first)
    if(third / c == 0)
      second += 13.7;
}

template<class Ret, class... Args>
using function_pointer = Ret(*)(Args...);

template<class Ret, class... Args>
struct wrapper {
  using F = function_pointer<Ret, Args...>;

  F f_;

  explicit constexpr wrapper(F f) noexcept : f_{f} {}

  template<class... PreciseArgs>// not sure if this is required
  Ret operator()(PreciseArgs&&... precise_args) const {
    return f_(std::forward<PreciseArgs>(precise_args)...);
  }
};

template<class Ret, class... Args>
constexpr auto make_wrapper(
  function_pointer<Ret, Args...> f
) -> wrapper<Ret, Args...> {
  return wrapper<Ret, Args...>(f);
}

int main() {
  constexpr auto obj = make_wrapper(do_something);
  obj(5, 17.4);
  constexpr auto obj2 = make_wrapper(do_something_else);
  double value = 5;
  obj2("Hello there!", value, 70);

  return 0;
}
Anibalanica answered 6/10, 2017 at 20:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.