How to switch with pointer to member functions?

Viewed 222

Well, all I want to do is a "switch" with a function pointer, but with methods pointers. The switch is that if I call the method Run(), it will either redirect to A::RunOn() or A::RunOff() according to Run ptr is pointing to these member functions.

I know it can be done. I did it in plain but I have searched and googled to do the same thing in but no luck.

class A
{
    typedef (void)(A::*RunPtr)(int);
    RunPtr RunMethod;

public:
    RunMethod Run;

    A()
    {
        Run = RunOff;
    }

    void SetOn(bool value)
    {
        if (value)
            Run = RunOn;
        else
            Run = RunOff;
    }

    void RunOn(int)
    {
        // RunOn stuff here
    }

    void RunOff(int)
    {
        // RunOff stuff here
    }
};

So I can call Run() and there will be a switch between the function calls, which I think is more efficient than just doing:

if (on)
    RunOn();
else
    RunOff();

Don't know how to do it!

2 Answers

Your member function pointer typedef is wrong (Despite the other issues in the shown code). You need

typedef void(A::*RunPtr)(int);

Or you can provide the alias for the member function pointer of class A with the help of using keyword as follows:

using RunPtr = void(A::*)(int);
RunPtr RunMethod;

Now in the SetOn you can do member pointer assignment as follows

void SetOn(bool value)
{
    RunMethod = value ? &A::RunOn : &A::RunOff;
}

Now, in order to call the stored member function pointer, you may/ can provide a Run member function as follows:

void  Run(int arg)
{
    std::invoke(RunMethod, this, arg);
    // do something...
}

The call to member function is a bit tricky. However, this can be done using more generic std::invoke from <functional> header (Since ).

Here is the complete example:

#include <iostream>
#include <functional> //  std::invoke

class A
{
    using RunPtr = void(A::*)(int);
    // or with typedef
    // typedef void(A::*RunPtr)(int);
    RunPtr RunMethod;

public:
    void SetOn(bool value)
    {
        RunMethod = value ?  &A::RunOn : &A::RunOff;
    }

    void  Run(int arg)
    {
        std::invoke(RunMethod, this, arg);
        // do something...
    }

    void RunOn(int arg) { std::cout << "RunOn: " << arg << "\n"; }

    void RunOff(int arg) { std::cout << "RunOff: " << arg << "\n";  }
};

int main()
{
    A obj;
    obj.SetOn(true);
    obj.Run(1);       // prints: RunOn: 1  
    obj.SetOn(false);
    obj.Run(0);       // prints: RunOff: 0
}

(See a Demo)

Your code works fine once you fix the syntax mistakes in it, namely:

  1. Class needs to be class.

  2. in RunMethod Run;, RunMethod is not a type, it is a member variable. You meant to use RunPtr Run; instead (and get rid of RunMethod), but that won't work so well for you (see further below for why).

  3. in Run = RunOn; and Run = RunOff;, you need to fully qualify the method name, and prefix it with the & operator, eg Run = &A::RunOn;.

Try the following:

class A {
public:
    typedef void (A::*RunPtr)(int);
    RunPtr Run;
     
    A()
    {
        Run = &A::RunOff;
    }
     
    void SetOn(bool value)
    {
        if (value)
            Run = &A::RunOn;
        else
            Run = &A::RunOff;
    }

    void RunOn(int param)
    {
        //RunOn stuff here
    }

    void RunOff(int param)
    {
        //RunOff stuff here
    }
};

Note, however, that even though you can use Run as a public method pointer like this, the caller will still need to use operator.* or operator->* to actually call it, and that would not look so nice, eg:

A a;
(a.*a.Run)(...);

Online Demo

If you want to be able to call Run() like a.Run(...) then you have to make Run() be a standard method, and have it use a method pointer internally, eg:

class A {
    typedef void (A::*RunPtr)(int);
    RunPtr RunMethod;

public:
    A()
    {
        RunMethod = &A::RunOff;
    }

    void SetOn(bool value)
    {
        if (value)
            RunMethod = &A::RunOn;
        else
            RunMethod = &A::RunOff;
    }

    void RunOn(int param)
    {
        //RunOn stuff here
        cout << "RunOn: " << param << endl;
    }

    void RunOff(int param)
    {
        //RunOff stuff here
        cout << "RunOff: " << param << endl;
    }
    
    void Run(int param)
    {
        (this->*RunMethod)(param);
    }
};
A a;
a.Run(...);

Online Demo

Related