Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Deduce Argument List from Function Pointer?

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;
like image 984
Xirema Avatar asked Oct 02 '17 21:10

Xirema


People also ask

Can a template parameter be deduced from a default argument?

Type template parameter cannot be deduced from the type of a function default argument: Deduction of template template parameter can use the type used in the template specialization used in the function call: Besides function calls and operator expressions, template argument deduction is used in the following situations:

How does the compiler deduce a function template argument?

The compiler can deduce a function template argument from a pointer to function or pointer to member function argument given several overloaded function names. However, none of the overloaded functions may be function templates, nor can more than one overloaded function match the required type.

Can We declare a pointer to a function?

Here ptrInteger is a pointer to integer. If you understand this, then logically we should not have any problem in declaring a pointer to a function.

How do I pass a function pointer as a parameter?

Let's see a simple example of how we can pass the function pointer as a parameter. We have defined two functions named 'display ()' and print_numbers (). Inside the main () method, we have declared a function pointer named as (*p), and we call the display () function in which we pass the print_numbers () function.


3 Answers

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{};
like image 111
bolov Avatar answered Oct 17 '22 15:10

bolov


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

like image 5
geza Avatar answered Oct 17 '22 16:10

geza


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)...);
    }
};
like image 3
user7860670 Avatar answered Oct 17 '22 16:10

user7860670