7
#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.

hiddensunset4
  • 5,825
  • 3
  • 39
  • 61
  • just `template`? – billz Jul 08 '13 at 07:34
  • 1
    @billz: That would be to bind a type, not a particular instance (not sure one can actually bind an instance as a template parameter though). – Matthieu M. Jul 08 '13 at 07:51
  • Here's a working example of using functions and member-function-pointers as template arguments, with a bonus C++98-compatible option: http://stackoverflow.com/questions/17218712/how-to-allow-templated-functor-work-on-both-member-and-non-member-functions/17237671#17237671 – willj Jul 08 '13 at 11:29

3 Answers3

6

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;
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    That would be what I would aim for (albeit with a `private` attribute)... but I wonder whether one can bind a function as a template parameter. It seems no different than a `char const*` in that the instance is a compile-time constant however I cannot seem to find the right syntax if it is indeed possible. – Matthieu M. Jul 08 '13 at 07:55
  • @MatthieuM. Yes you can bind functions as template parameters. Only, you are prone to get template instantiation explosion in that case. It can also work for member functions – sehe Jul 08 '13 at 08:00
  • @MatthieuM. I added an old code sample that uses precisely the approach you seem to be getting at – sehe Jul 08 '13 at 08:09
  • Thanks, this is exactly what I was getting at indeed; the syntax is weird, but consistent with other usages of function pointers. – Matthieu M. Jul 08 '13 at 08:53
3

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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • +1 nice writeup. Note that my edit already linked to a more complete implementation of this, also allowing for pointer-to-member-functions. – sehe Jul 08 '13 at 08:38
  • 1
    @sehe Except, as in the question code, this puts everything into the template argument. Unless encapsulated in a template-id, a function pointer would otherwise be runtime state. – Potatoswatter Jul 08 '13 at 08:49
  • @Potaswatter Hmm? Either you missed it, or the [linked demo](http://ideone.com/120Ezs) confused you on the application. I suggest you focus on the `detail` namespace only, and notice the `template inline static Callback Bind(T* o)` with local struct. The rest is just some plumbing to do the signature extraction and hook it up to some usage scenario someone else once had. – sehe Jul 08 '13 at 10:17
  • @sehe The return type of that function cannot contain the ptmf, and it's passed through the constructor to the member `TFunc Callback<…>::func`. So although it takes a compile-time template argument, it's only used as far as I can see to initialize a runtime object. – Potatoswatter Jul 08 '13 at 10:38
  • Hmm. I don't have time to rereard the sample. it might be different than what I recall. Dammit. It'll have to wait... :( – sehe Jul 08 '13 at 11:49
0

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;
}
hiddensunset4
  • 5,825
  • 3
  • 39
  • 61