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

问题描述:

我需要通过一个标准的函数指针称为成员函数,所以我试图抽象的东西是这样的:如何从静态成员函数调用指向成员函数的指针?

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函数内部调用指向成员函数的指针。

+2

无法从静态函数访问成员变量 - 什么不清楚? – 2014-10-28 09:31:59

的问题是,你需要内部execute两个对象 - 一个是Executor的实例,它会提供func,另一个是(从派生的类)Samplefunc将被调用的一个实例。所以,你必须存储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)())标识类中的一个功能,但不存储对象。您仍然需要提供一个来调用该函数。

+0

不应该'x0(myA)'是'x0(&myA)'? – wimh 2014-10-28 10:08:29

+0

@Wimmel当然,在问题中解决问题之前,我从OP复制了该问题。谢谢。 – Angew 2014-10-28 10:13:27

+0

@Angew我显然希望重塑一个仿函数,但是你的解决方案比较干净,可爱:) THX – django013 2014-10-30 07:20:56

您还需要传递一个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); 
} 
+0

'func'是一个成员,它不能在'static'方法中使用。 – user657267 2014-10-28 09:32:54

+0

@ user657267啊,gotcha。修复了答案。 – user2079303 2014-10-28 09:35:35

+0

这怎么解决答案?他想通过一个'static'方法调用成员函数指针,这根本不可能,使得“public”不会改变。 – user657267 2014-10-28 09:37:35

如果您想要与外部系统调用进行交互,您必须自己重新创建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++中,我们会将整个系统调用调用包装在一个将封装生命周期管理的内部类中。

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