3

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!

Lucas
  • 329
  • 1
  • 8
  • 4
    You are engaging in the sort of micro optimization that often obfuscates code while performing no better than an optimizing compiler can do. – StoryTeller - Unslander Monica Aug 02 '21 at 18:10
  • You may want to look at this: https://isocpp.org/wiki/faq/pointers-to-members – jjramsey Aug 02 '21 at 18:11
  • what you did in plain C should also work in C++ (possibly with minor modifications) – 463035818_is_not_an_ai Aug 02 '21 at 18:30
  • 2
    Why would you think an indirect call would be more efficient than a test followed by a direct call? The `if` case seems easier to optimize because the compiler knows that there are only two ways the code can go. In the indirect call case, the compiler probably can't make any assumptions about what the function might do. – David Schwartz Aug 02 '21 at 18:30

2 Answers2

7

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)

JeJo
  • 30,635
  • 6
  • 49
  • 88
6

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

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770