How to Deduce Argument List from Function Pointer?

烂漫一生 提交于 2019-12-23 08:49:46

问题


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;

回答1:


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{};



回答2:


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




回答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)...);
    }
};



回答4:


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


来源:https://stackoverflow.com/questions/46533698/how-to-deduce-argument-list-from-function-pointer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!