我需要获得一个由标准函数指针调用的成员函数,所以我试图抽象这样的东西:
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一起用作数据。
...而且我不希望像A
或B
这样的班级拥有静态成员。所以我的想法是创建一个类Sample
的接口,它由A
和B
扩展。
我的问题是我不知道如何从Executor::execute
函数内部调用指向成员函数的指针。
答案 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 ++中,我们将整个系统调用调用包装在一个内部封装生命周期管理的类中。
您可以为带参数的成员函数添加更多特化;你只需要在状态中存储一个参数的副本。