如何从静态成员函数调用指向成员函数的指针?

时间:2014-10-28 09:25:16

标签: c++ pointers member-function-pointers

我需要获得一个由标准函数指针调用的成员函数,所以我试图抽象这样的东西:

class Sample {
public:
    virtual void doSomething(void) = 0;
};


class A : public Sample {
    void doSomething(void);     // details omitted
};

class B : public Sample {
    void doSomething(void);     // details omitted
};


class Executor {
public:
    Executor(Sample *sample)
     : func(&sample->doSomething)
    {
    }

    static void *execute(void *data) {
        Executor *pX = data;

        (pX->*func)();          // error invalid access of func from static function

        (pX->*pX->func)();      // error pointer to member type 'void (Sample::)()'
                                //       incompatible with object type 'Executor'
    }

private:
    void (Sample::*func)(void);
};



int main(void) {
    A   myA;
    B   myB;
    Executor x0(&myA);
    Executor x1(&myB);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);
}

externallyInvoke是一个Linux系统调用,它接受一个函数指针和一个数据指针。 我想将静态成员函数与this-pointer一起用作数据。

...而且我不希望像AB这样的班级拥有静态成员。所以我的想法是创建一个类Sample的接口,它由AB扩展。

我的问题是我不知道如何从Executor::execute函数内部调用指向成员函数的指针。

3 个答案:

答案 0 :(得分:2)

问题是你需要execute内的两个对象 - 一个是Executor的实例,它将提供func,另一个是(一个派生自的类)的实例Sample将调用func。所以你必须将对象存储在Executor内,而不是函数:

class Executor {
public:
    Executor(Sample *sample)
     : obj(sample)
    {
    }

    static void *execute(void *data) {
        Executor *pX = static_cast<Executor*>(data);

        pX->obj->doSomething();
    }

private:
    Sample *obj;
};


int main() { // note that `void main()` is not legal C++
    A   myA;
    B   myB;
    Executor x0(&myA);
    Executor x1(&myB);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);
}

指向成员函数的指针(例如原始void (Sample::*func)())标识类中的函数,但不存储该对象。您仍然需要提供一个来调用该函数。

答案 1 :(得分:0)

您还需要传递一个Sample实例来调用该函数(因为它是指向Sample成员的指针)。有几种方法可以实现实例。您可以将其设为Executor的成员,将std::pair*作为data传递,或者您可以将函数指针和实例组合为仿函数。这是针对后者的基于lamda的方法。 Lamda具有更多功能的优点。除了调用一个类的一个成员之外,还可以做更多的事情。作为奖励,这种方法不会避免可见性规则,尽管这意味着doSomething可能不是私有的(或者它必须通过父指针调用)。

template<class F>
class Executor {
    F f;
public:
    Executor(F f): f(f){}
    static void *execute(void *data) {
        Executor<F> *pX = static_cast<Executor<F>*>(data);
        pX->f();
        return this; // not quite sure what you intend to return, but just to make this a well formed function...
    }
};


int main() {
    A   myA;
    B   myB;
    auto callback0 = [myA]{
        myA.doSomething();
    };
    auto callback1 = [myB]{
        myB.doSomething();
    };
    Executor<decltype(callback0)> x0(callback0);
    Executor<decltype(callback1)> x1(callback1);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);
}

答案 2 :(得分:0)

如果您想与外部系统调用进行交互,您基本上必须自己重新发明std::function。没问题,在Stack Overflow,我们是重塑现有技术的大师。所以......

首先,界面:

struct FunctionStateBase
{
    virtual ~FunctionStateBase() {}
    virtual void Invoke() = 0;
};

extern "C" void InvokeAndDelete(void * data)
{
    auto state = static_cast<FunctionStateBase *>(data);
    state->Invoke();
    delete state;
}

以下是您使用它的方式:

externallyInvoke(&InvokeAndDelete, MakeFunction(&A::doSomething, &myA));

现在我们需要实施MakeFunction

template <typename> struct FunctionState;

template <typename C, typename R>
struct FunctionState<R (C::*)()> : FunctionStateBase
{
    R (C::ptmf_*)();
    C * obj_;

    FunctionState(R (C::ptmf*)(), C * obj) : obj_(obj), ptmf_(ptmf) {}

    virtual void Invoke() { (C->ptmf_)(); }
};

template <typename C, typename R>
FunctionState<R (C::*)()> MakeFunction(R (C::*ptmf)(), C * obj)
{
    return new FunctionState<R (C::*)()>(ptfm, obj);
}

此时我们手动管理函数包装器的生命周期,并注意InvokeAndDelete实际取得函数状态的所有权。在适当的C ++中,我们将整个系统调用调用包装在一个内部封装生命周期管理的类中。

您可以为带参数的成员函数添加更多特化;你只需要在状态中存储一个参数的副本。