1

i'd like to invoke runtime-bound functions of classes, that inherit a binding ability from a common class "Bindable". Is that actually possible?

Here's a stub which surely lacks a lot of template-arguments and namespaces:

#include <iostream>     // std::cout
#include <functional>   // std::bind
#include <map>          // std::map

class Bindable {
public:
    void bindFunction (int x, auto newFn) {
        mFns.insert(std::pair<int, auto>(x,newFn));
    }
    void invokeFunction (int key) {
        mFns.at(key)();
    }

protected:
    std::map<int, function> mFns;
};

class A : Bindable {
    void funAone (void) {
        cout << "called funAone" <<std::endl;
    }
    void funAtwo (void) {
        cout << "called funAtwo" <<std::endl;
    }
};

class B : Bindable {
    void funBone (void) {
        cout << "called funBone" <<std::endl;
    }
    void funBtwo (void) {
        cout << "called funBtwo" <<std::endl;
    }
};

int main() {
    A a;
    B b;

    a.bindFunction(1, &A::funAone);
    a.bindFunction(2, &A::funAtwo);
    b.bindFunction(1, &B::funBone);
    b.bindFunction(2, &B::funBtwo);

    a.invokeFunction(1);
    a.invokeFunction(2);
    b.invokeFunction(1);
    b.invokeFunction(2);
}
DPF
  • 270
  • 1
  • 2
  • 12
  • 1
    by hard-coding the member function's signature and using CRTP [like here](http://coliru.stacked-crooked.com/a/575712d28c328743) ? – Piotr Skotnicki Jan 03 '15 at 14:13
  • Maybe it's worth another question, but: Is there a way, to store functions with different signatures inside the same std::map in "Bindable"? – DPF Jan 03 '15 at 17:31
  • you can find answers [here](http://stackoverflow.com/q/8304582/3953764) and [here](http://stackoverflow.com/q/26158504/3953764) and in many other questions – Piotr Skotnicki Jan 03 '15 at 17:35
  • From the first link I deduced [this.](http://coliru.stacked-crooked.com/a/d8f2d06c8db67ed7) (Just to have it here) – DPF Jan 03 '15 at 18:08

2 Answers2

4

Option #1

Use a CRTP idiom to know what type of pointers to member functions can be stored:

template <typename T>
struct Bindable {   
    void bindFunction (int x, void(T::*newFn)()) {
        mFns.insert(std::make_pair(x,newFn));
    }
    void invokeFunction (int key) {
        (static_cast<T*>(this)->*mFns.at(key))();
    }

protected:
    std::map<int, void(T::*)()> mFns;
};

struct A : Bindable<A> {
    void funAone (void) {
        std::cout << "called funAone" <<std::endl;
    }
    void funAtwo (void) {
        std::cout << "called funAtwo" <<std::endl;
    }
};

DEMO 1

Option #2

Use a type-erasure and make bindFunction a function template:

struct Bindable {    
    template <typename T, typename std::enable_if<std::is_base_of<Bindable, T>{}, int>::type = 0>
    void bindFunction (int x, void(T::*newFn)()) {
        mFns.insert(std::make_pair(x, std::bind(newFn, static_cast<T*>(this))));
    }
    void invokeFunction (int key) {
        mFns.at(key)();
    }
protected:
    std::map<int, std::function<void()>> mFns;
};

struct A : Bindable {
    void funAone (void) {
        std::cout << "called funAone" <<std::endl;
    }
    void funAtwo (void) {
        std::cout << "called funAtwo" <<std::endl;
    }
};

DEMO 2

In both cases you can use the code as follows:

int main() {
    A a;
    B b;

    a.bindFunction(1, &A::funAone);
    a.bindFunction(2, &A::funAtwo);
    b.bindFunction(1, &B::funBone);
    b.bindFunction(2, &B::funBtwo);

    a.invokeFunction(1);
    a.invokeFunction(2);
    b.invokeFunction(1);
    b.invokeFunction(2);
}

Output:

called funAone
called funAtwo
called funBone
called funBtwo
Community
  • 1
  • 1
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
2

Yes, it's possible, using std::bind. Note that auto can't be used as a function or template argument.

#include <iostream>     // std::cout
#include <functional>   // std::bind
#include <map>          // std::map

class Bindable {
public:
    typedef std::function<void()> Function;
    void bindFunction (int x, Function newFn) {
        mFns.insert(std::pair<int, Function>(x,newFn));
    }
    void invokeFunction (int key) {
        mFns.at(key)();
    }

protected:
    std::map<int, Function > mFns;
};

class A : public Bindable {
public:
    void funAone (void) {
        std::cout << "called funAone" <<std::endl;
    }
    void funAtwo (void) {
        std::cout << "called funAtwo" <<std::endl;
    }
};

class B : public Bindable {
public:
    void funBone (void) {
        std::cout << "called funBone" <<std::endl;
    }
    void funBtwo (void) {
        std::cout << "called funBtwo" <<std::endl;
    }
};

int main() {
    A a;
    B b;

    a.bindFunction(1, std::bind(&A::funAone, a)); // more than one way to bind
    a.bindFunction(2, std::bind(&A::funAtwo, &a)); // the object parameter
    b.bindFunction(1, std::bind(&B::funBone, b));
    b.bindFunction(2, std::bind(&B::funBtwo, &b));

    a.invokeFunction(1);
    a.invokeFunction(2);
    b.invokeFunction(1);
    b.invokeFunction(2);
}
arayq2
  • 2,502
  • 17
  • 21