4

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
  • 2
    can't access member variable from a static function - what is not clear? Commented Oct 28, 2014 at 9:31

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

shouldn't x0(myA) be x0(&myA)?
@Wimmel Yes of course, I copied that from the OP before it was fixed in the question. Thanks.
@Angew I obviously was looking to reinvent a functor, but your solution is quite cleaner and for so: lovely :) THX
0

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.

Comments

0

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

4 Comments

func is a member, it cannot be used in a static method.
@user657267 ah, gotcha. Fixed the answer.
How does that fix the answer? He wants to call a member function pointer over a static method, it simply isn't possible, making it public changes nothing.
@user657267, damn not my day. I didn't notice that it was not only a member but also a pointer to a member function of another class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.