runUserMethodをテンプレート メソッドにしてみることができます。また、これをベースから派生クラスに安全にキャストするには、dynamic_cast を使用する必要があるため、ベースクラスはポリモーフィックにする必要があります (たとえば、仮想デストラクタを追加することにより)。
class Base {
public:
template <class T>
int runUserMethod( int (T::* pointer_to_method)()) {
T* p = dynamic_cast<T*> (this);
if (p == NULL) throw std::runtime_error("cast error");
// do some stuff before
int retval = (p->*pointer_to_method)();
// do some stuff after
return retval;
}
virtual ~Base() {};
};
関数の使用法は、コード サンプルと同じです。
Base* b = new UserClass;
int r = b->runUserMethod(&User::user_method);
runUserMethod が他の関数シグネチャを受け入れるように、関数の引数と戻り値の型をテンプレート パラメーターにすることもできます。
class Base {
public:
// 0 arguments
template <class T, class R>
int runUserMethod( R (T::* pointer_to_method)()) {
T* p = dynamic_cast<T*> (this);
if (p == NULL) throw std::runtime_error("cast error");
// do some stuff before
R retval = (p->*pointer_to_method)();
// do some stuff after
return retval;
}
// 1 argument
template <class T, class R, class ARG1>
int runUserMethod( R (T::* pointer_to_method)(ARG1), ARG1 arg1) {
T* p = dynamic_cast<T*> (this);
if (p == NULL) throw std::runtime_error("cast error");
// do some stuff before
R retval = (p->*pointer_to_method)(arg1);
// do some stuff after
return retval;
}
// 2 arguments
// [...]
virtual ~Base() {};
};
このようにして、以下のコードも機能します。
class UserClass : public Base {
public:
int user_method1 (void) {return 0;}
float user_method2 (void) {return 0.0f;}
int user_method3 (int x) {return 0;}
};
int main(int argc, char* argv[]) {
Base* b = new UserClass;
int r1 = b->runUserMethod(&UserClass::user_method1);
float r2 = b->runUserMethod(&UserClass::user_method2);
int r3 = b->runUserMethod(&UserClass::user_method3, 2);
}