ポリモーフィック関数ラッパーのほとんど重要でないオーバーヘッドを本当に避けたい場合の1つのオプションは、これらの関数を静的void*
にし、関数がメンバーであるクラスの適切なインスタンスを指す「ユーザーデータ」パラメーターを受け取るようにすることです。の。次に、静的関数内で、適切なタイプにキャストバックします。
#include <iostream>
struct A{
typedef void (*callback_type)(void*, int);
callback_type callback;
void* user_data;
void set_callback(callback_type cb, void* ud){
callback = cb; user_data = ud;
}
void invoke(){ callback(user_data, 42); }
};
struct B{
static void cb_foo(void* vself, int data){
B* self = static_cast<B*>(vself);
self->foo(data);
}
void foo(int data){ std::cout << data * 2 << "\n"; }
};
struct C{
static void cb_bar(void* vself, int data){
C* self = static_cast<C*>(vself);
self->bar(data);
}
void bar(int data){ std::cout << data / 2 << "\n"; }
};
int main(){
A a;
B b;
a.set_callback(&B::cb_foo, &b);
a.invoke();
C c;
a.set_callback(&C::cb_bar, &c);
a.invoke();
}
Ideoneの実例。
std::function
ただし、上記はコールバックとして受け入れることができるものが大幅に制限されているため、個人的には使用することをお勧めします。std::function
はポリモーフィック関数ラッパーです。つまり、通常の関数ポインター、メンバー関数ポインター、さらには関数(関数オブジェクト)を受け取り、それらすべてを同じ方法で呼び出すことができます。std::bind
パラメータを関数にバインドできる、と一緒に、メンバー関数への簡単なコールバックを作成できます。Boostもそれらを提供します(Boost.Function、Boost.Bind)。
#include <iostream>
#include <functional> // C++11
//#include <boost/function.hpp>
//#include <boost/bind.hpp>
struct A{
std::function<void(int)> callback;
void invoke(){ callback(42); }
};
struct B{
void foo(int data){ std::cout << data * 2 << "\n"; }
};
struct C{
void bar(int data){ std::cout << data / 2 << "\n"; }
};
int main(){
using namespace std::placeholders; // namespace for argument placeholders for std::bind
// not needed for Boost.Bind
A a;
B b;
a.callback = std::bind(&B::foo, &b, _1);
a.invoke();
C c;
a.callback = std::bind(&C::bar, &c, _1);
a.invoke();
};
Ideoneの実例。
基本的std::bind
に、最初のバージョンで手動で実行しなければならなかったことを自動的に実行し、オブジェクトポインターを保存して、その上でメンバー関数を呼び出します。void*
ただし、これはポインターを介して行われるのではなく、std::bind
オブジェクトポインターごとに異なるバインダータイプを返します。std::function
それはあなたがそれを渡すものを気にしないので、あなたが必要とする理由です。