1

Baseメソッドを持つ基本クラスがありrunUserMethod(void * pointer_to_method)ます。

class Base {
    int runUserMethod(METHTYPE pointer_to_method) {
        // do some stuff before
        int retval = pointer_to_method();
        // do some stuff after
        return retval;
    }
};

runUserMethodここで、 のサブクラスによって実装された任意のメソッドを実行できるように実装したいと思いますBaseint <methodname> (void)

たとえば、ユーザーが次のUserClassようなクラスを作成するとします。

class UserClass : public Base {
    int user_method (void);
};

次のことができるようにするにはどうすればMETHTYPEよいですか?

Base   * b = new UserClass();
int retval = b->runUserMethod(&UserClass::user_method);

また、それは機能しますか?これを行うより良い方法はありますか?基本的に、私がやろうとしているのは、ユーザーに対して多かれ少なかれ透過的な方法で、サブクラスのメソッド呼び出しへの呼び出しをインターセプトすることです。

4

3 に答える 3

2

std::functionこれは、 と を使用して実行できますstd::bind

struct Base
{
    typedef std::function<int()> METHTYPE;

    int runUserMethod(METHTYPE pointer_to_method)
    {
        return pointer_to_method();
    }
};

UserObject b;
auto func_object = std::bind(&UserObject::user_method, std::ref(b));

int retval = b.runUserMethod(func_object);
于 2013-02-25T16:53:28.557 に答える
1

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);
}
于 2013-02-25T19:30:17.223 に答える
0

例をより実際に実行可能な状態に変更すると、次の問題が発生します。

int retval       = (this->*methptr)();

醜い構文は別として、問題は、Base *型の「this」ポインタをメソッド呼び出しに渡す必要があることですが、派生クラスのメソッドにポインタを渡すことを想定し
ているため、runUserMethodに派生クラスを認識させる必要があります(おそらくテンプレートによって) )または問題のリスクがあるベースメソッドへのポインタへの派生メソッドへの型キャストポインタ:

class Derived1 : public Base ...
class Derived2 : public Base ...
Derived2 d2;
d2.runUserMethod( static_cast<METHTYPE>( &Derived1::func ) ); // problem Derived2 * will be passed to Deriived1 method and there is no easy way to detect that

したがって、std::bindを使用する方が安全です。そして、あなたはBaseから継承する必要はありません(あなたの場合、それが良いかどうかはわかりません)

于 2013-02-25T17:10:55.033 に答える