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

眉间皱痕 提交于 2019-12-10 17:20:17

问题


I need to get a member function called by a standard function pointer, so I tried to abstract things like this:

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 is a Linux system call, which takes a function pointer and a data pointer. I'd like to use a static member function together with a this-pointer as data.

... and I don't want classes like A or B to have static members. So my idea was to create an interface like class Sample, that gets extended by A and B.

My problem is that I don't know how to invoke the pointer to member function from inside the Executor::execute function.


回答1:


The problem is that you need two objects inside execute - one is the instance of Executor which will supply func, and the other is an instance of (a class derived from) Sample on which func will be invoked. So you have to store the object inside Executor, not the function:

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);
}

A pointer to member function (such as your original void (Sample::*func)()) identifies a function within a class, but does not store the object. You'd still need to provide one to call the function.




回答2:


If you want to interact with an external system call, you basically have to reinvent std::function yourself. No problem, here at Stack Overflow we're the masters of reinventing existing technology. So...

First, the interface:

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

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

Here's how you use it:

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

Now we need to implement 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);
}

At this point we're managing the life time of the function wrapper manually, and note that InvokeAndDelete actually takes ownership of the function state. In proper C++, we would wrap the entire system call invocation in a class that would encapsulate the lifetime management internally.

You can add further specializations for member functions that take arguments; you just need to store a copy of the arguments in the state.




回答3:


You'll need to also pass an instance ofSample on which to call the function (since it's a pointer to a member of Sample). There's a few ways to bring the instance along. You could make it a member of Executor, pass a std::pair* as data or you could combine the function pointer and the instance as a functor. Here's a lamda based approach for the latter. Lamda has the advantage of being more versatile. It's possible to do much more than just call one member of one class. As a bonus, this approach does not avoid visibility rules, although that means doSomething may not be private (or it must be called through the parent pointer).

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);
}


来源:https://stackoverflow.com/questions/26604738/how-to-invoke-pointer-to-member-function-from-static-member-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!