hiddensunset4
hiddensunset4

Reputation: 6029

Any function as a template argument

#include <iostream>

void padd(int a, int b) { std::cout << a + b << std::endl; }
void psub(int a, int b) { std::cout << a - b << std::endl; }

template <??? op>
class Foo {
public:
    template<typename... Arguments>
    void execute(Arguments... args) {
        op(args ...);
    }
};

int main() {
    auto f1 = Foo<padd>();
    f1.execute(5, 6); // ideally would print 11

    auto f2 = Foo<psub>();
    f2.execute(5, 6); // ideally would print -1

    return 0;
}

I am trying to figure out how to bind functions (and, if possible, template functions) as template parameters in C++.

As it stands I am not aware if this is possible.

A kicker here is that the function signatures are not guaranteed to be similar.

edit: thanks to @sehe and @Potatoswatter, my current solution is thus: http://ideone.com/0jcbUi. Will write up answer when appropriate.

Upvotes: 7

Views: 246

Answers (3)

hiddensunset4
hiddensunset4

Reputation: 6029

Thanks to Potatoswatter and sehe's help in the C++ Lounge and here, I have formulated the solution to my question.

#include <iostream>
#include <functional>

template <typename func_t, func_t func>
struct Foo {
    template <typename... Arguments>
        void execute(Arguments... args) {
                func(args ...);
        }
};

template <typename T, typename func_t, func_t func>
struct FooMember {
        T member;

        FooMember(T member) : member(member) {}

        template <typename... Arguments>
        void execute(Arguments... args) {
                std::function<void(T&, Arguments ...)> f(func);
                f(this->member, args ...);
        }
};

struct Bar {
        int z;

        Bar(int z) : z(z) {}
        void add(int x, int y) { std::cout << x + y + z << std::endl; }
};

void padd(int x, int y, int z) { std::cout << x + y + z << std::endl; }

int main() {
        auto a = Foo<decltype(&padd), &padd>();
        auto b = FooMember<Bar, decltype(&Bar::add), &Bar::add>(Bar(2));

        a.execute(4, 5, 6); // prints 4+5+6 : 15
        b.execute(4, 5); // prints 4+5+a.z : 4+5+2 : 11

        return 0;
}

Upvotes: 0

Potatoswatter
Potatoswatter

Reputation: 137910

Just making the observation that you don't have or want runtime state. Partly an inference from your comments in the C++ chatroom.

Functions do not have unique types. If you want go generate a unique type that captures which function should be called, use a class template to do so.

template< typename t, t v >
struct constant {
    typedef t type;
    static constexpr t value = v;

    operator t () { return v; }
};

This is essentially the same as std::integral_constant but removes integral from the name to save confusion. (Actually I only tested this using std::integral_constant, if you want to be cleaner it's up to you.)

Now you can give the functions separate types which are stateless, default-constructible functors.

typedef constant< decltype( & padd ), padd > padd_type;
typedef constant< decltype( & psub ), psub > psub_type;

padd_type()( 2, 3 ); // prints 5

Note that stateless lambdas are convertible to function pointers and are compatible with such a system, but you need to specify the function pointer type explicitly. decltype alone won't get you there.

Upvotes: 3

sehe
sehe

Reputation: 393694

I'd suggest letting the compiler worry about resolving function signatures when appropriate. http://ideone.com/ZeLt1E (code included below).

If you need to adapt overload sets or polymorphic interfaces, I would suggest also looking at BOOST_PHOENIX_ADAPT_FUNCTION.

Edit In response to the comments: Here's a demo of how you could use strictly function-pointers and/or pointer-to-member functions directly as function arguments. This is the other extreme approach: http://ideone.com/120Ezs

Full code

#include <iostream>

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

    template<typename... Arguments>
    void execute(Arguments... args) {
        f(args ...);
    }

  private:
    F f;
};

template <typename F>
  Foo<F> make_foo(F&& f = F()) { return {f}; }

void padd(int a, int b) { std::cout << a + b << std::endl; }
void psub(int a, int b) { std::cout << a - b << std::endl; }

int main() {
    auto f = make_foo(padd);
    f.execute(5, 6);

    make_foo(psub).execute(5, 6);

    return 0;
}

Upvotes: 6

Related Questions