Reza Toghraee
Reza Toghraee

Reputation: 1663

Variadic Template Functions

I want to be able to pass a variadic number of function pointers to a template function, say foo. The example below shows what I have so far however it does not compile when I actually pass more than one template argument:

#include <iostream>
#include <cmath>

using FPtrType = double(*)(double);
constexpr double Identity( double x ) noexcept { return x; }

template <FPtrType Func=Identity>
constexpr double foo( double x ) noexcept( noexcept( Func(x) ) )
{
    return Func(x);
}
template <FPtrType Func, typename... Funcs>
constexpr double foo( double x, Funcs... funcs )
{
    x = Func(x);
    return foo<Funcs...>(x, funcs...);
}

int main()
{
    double x{ 0.5 };
    std::cout << x << '\t' << foo(x) << std::endl;  // OK
    std::cout << x << '\t' << foo<std::sin>(x) << std::endl;  // OK
    std::cout << x << '\t' << foo<std::asin>(x) << std::endl;  // OK
    std::cout << x << '\t' << foo<std::sin, std::asin>(x) << std::endl; // Error!
}

I'm using gcc5 and the compiler spews:

error: no matching function for call to 'foo(double&)' std::cout << x << '\t' << foo<std::sin, std::asin>(x) << std::endl; ^ , followed by another error message:

error: wrong number of template arguments (2, should be at least 0) std::cout << x << '\t' << foo<std::sin, std::asin>(x) << std::endl; ^

Any thoughts?

Upvotes: 3

Views: 567

Answers (2)

Barry
Barry

Reputation: 303017

You could do this simpler without using recursion by just using an expander:

using FPtrType = double(*)(double);

template <FPtrType... Funcs>
constexpr double foo( double x )
{
    using expander = int[];
    expander{0,
        (x = Funcs(x), 0)...
    };
    return x;
}

This trivally works for an empty parameter pack since nothing is ever called, so identity is implied without having to provide your own function for it. Otherwise, this will iteratively call each Func in succession.

You could additionally take them as arguments instead of template non-type parameters using the same approach:

template <typename... Fs>
constexpr double foo(double x, Fs... Funcs)
{
    using expander = int[];
    expander{0,
        (x = Funcs(x), 0)...
    };
    return x;
}

Which would be called thusly:

foo(x, static_cast<FPtrType>(std::sin), static_cast<FPtrType>(std::asin));

The advantage of the latter is now you can pass in more complicated stuff. Like functions that take other types or any other arbitrary callable:

foo(x, [](double d){return d+1;}); // ok!

Upvotes: 5

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48457

You probably meant this:

template <FPtrType Func, FPtrType Func2, FPtrType... Funcs>
constexpr double foo( double x )
{
    x = Func(x);
    return foo<Func2, Funcs...>(x);
}

DEMO

Upvotes: 6

Related Questions