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

How to invoke pointer to member function from static member function?

本文关键字:成员 函数 指针 静态成员 函数调用      更新时间:2023-10-16

我需要获取一个由标准函数指针调用的成员函数,所以我尝试抽象如下:

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 指针一起使用作为数据。

。而且我不希望像AB这样的类有静态成员。所以我的想法是创建一个像类Sample这样的接口,它通过AB进行扩展。

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

问题是execute里面需要两个对象 - 一个是将提供funcExecutor实例,另一个是将调用func的(派生自(Sample的实例。所以你必须将对象存储在 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)()(标识类中的函数,但不存储对象。您仍然需要提供一个来调用该函数。

如果你想与外部系统调用进行交互,你基本上必须自己重新发明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++中,我们将整个系统调用调用包装在一个类中,该类将在内部封装生命周期管理。

您可以为接受参数的成员函数添加进一步的专用化;您只需要在状态中存储参数的副本。

您还需要传递一个Sample实例来调用该函数(因为它是指向 Sample 成员的指针(。有几种方法可以引入实例。你可以让它成为 Executor 的成员,将std::pair*作为data传递,或者你可以将函数指针和实例组合为函子。这是针对后者的基于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);
}