Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write one template class instead of several ones?

There are several template classes that work with function pointers taking one, two, etc. arguments.

template <typename A1, int(*func)(A1)>
class Something1
{
public:
    static void f()
    {
        A1 a1;
        // ...
        int r = func(a1);
        // ...
    }
};

template <typename A1, typename A2, int(*func)(A1, A2)>
class Something2
{
public:
    static void f()
    {
        A1 a1;
        A2 a2;
        // ...
        int r = func(a1, a2);
        // ...
    }
};

How do I write one template class that can work with pointers to functions that take any number of arguments? Is it possible?

like image 380
Dyzzet Avatar asked Dec 02 '25 10:12

Dyzzet


1 Answers

You cannot define a class template that has only one template parameter and accepts a function pointer the arguments of which must be deduced.
To do that, you have to use something like this:

template<typename F, F *func>
struct S;

template<typename R, typename... A, R(*f)(A...)>
struct S<R(A...), f> {};

And a pretty ugly notation like this one:

S<decltype(f), &f> s;

A possible alternative is by using the function pointer as an argument to the constructor:

#include<type_traits>
#include<utility>

template<typename>
struct S;

template<typename R, typename... A>
struct S<R(A...)> {
    S(R(*f)(A...)): f{f} {}
    R operator()(A... args) { return f(args...); }

private:
    R(*f)(A...);
};

void g() {}
int h(int) { return 0; }

int main() {
    S<void(void)> s1{&g};
    S<int(int)> s2{&h};
    s1();
    s2(42);
}

If you want to go a bit further and have a more flexible solution, you can work around it also by using lambdas and a factory method:

#include<type_traits>
#include<utility>

template<typename F>
struct S: F { S(F &&f): F{std::forward<F>(f)} {} };

template<typename F>
constexpr auto create(F &&f)
-> decltype(S<typename std::decay<F>::type>{std::forward<F>(f)}) {
    return S<typename std::decay<F>::type>{std::forward<F>(f)};
}

void g() {}
int h(int) { return 0; }

int main() {
    auto s1 = create([](){ g(); });
    auto s2 = create([](int i){ return h(i); });
    s1();
    s2(42);
}

As a side note, if you can use C++14, syntax of the last snippet becomes even nicer:

#include<type_traits>
#include<utility>

template<typename F>
struct S: F { S(F &&f): F{std::forward<F>(f)} {} };

template<typename F>
constexpr auto create(F &&f) {
    return S<std::decay_t<F>>{std::forward<F>(f)};
}

void g() {}
int h(int) { return 0; }

int main() {
    auto s1 = create([](){ g(); });
    auto s2 = create([](auto&&... args){ return h(std::forward<decltype(args)>(args)...); });
    s1();
    s2(42);
}
like image 147
skypjack Avatar answered Dec 05 '25 00:12

skypjack



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!