2

I'm fairly new to the concept of function pointer in C++, so I don't know how to write my question properly. Please bear with me.

Basically, what I'm trying to do is to create a Button object whose constructor accepts a function pointer as its parameter. That function pointer points to a function which will change state of the StateMachine.

Here is the sample code (it doesn't work, and irrelevant bits have been stripped out)

Button.h

#include "StateMachine.h"

class Button
{
private:   
    void (*m_onclickAction)();    //a data member
public:
    Button(void (*action)());
};

StateMachine.h (I didn't write it, I just use it with permission. So there should be no problem with the code, and I don't want to modify it)

#include <map>

template<class E, class T>
class StateMachine
{
public:
    typedef void (T::*CallbackOnInitialise)();
    typedef void (T::*CallbackOnExit)();
private:    
    T* m_pOwner;
    E m_currentState;

    // Maps to store function pointers to state functions.
    std::map<E, CallbackOnInitialise> m_statesOnInitialise;
    std::map<E, CallbackOnExit> m_statesOnExit;

public:
    StateMachine(T* pOwner, E emptyState)
    {
        m_currentState = emptyState;
        m_pOwner = pOwner;
    }

    void ChangeState(E statenext)
    {
        //do something to change the state
    }
};

So that in my main Program class, I could be able to do something like this

#include "Button.h"
#include "StateMachine.h"

//Code to instantiate an StateMachine object goes here
Button* aButton = new Button(aStateMachine->ChangeState(NEW_STATE));

The problem is I can't think of a way to correctly pass the NEW_STATE, which is an enum declared in the Program class as the function pointer is expecting no parameter. I have tried tweaking around with it, but no success.

Any suggestion on how should I do it?

3 Answers3

0

You have a couple of problems here. The first is that in your version you call the ChangeState member function when you create the Button instance. The second is that m_onclickAction is a pointer to a function, which is not the same as a pointer to a member function.

For this I suggest you look into std::function and std::bind:

class Button
{
    std::function<void(int)> m_onclickAction;

public:
    Button(std::function<void(int)> action) { ... }
};

Then you can create your button like this:

Button* aButton = new Button(
    std::bind(&StateMachine::ChangeState, aStateMachine, NEW_STATE));
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

For callbacks boost::bind() and boost::function() is a very useful tool. So your Button class may look like this:

class Button
{
public:
    typedef boost::function<void()> Callback;
    Button( Callback clickAction );
private:   
    Callback m_onclickAction;    //a data member
};

Code to pass StateMachine method with parameter then would be:

Button* aButton = new Button( boost::bind( &StateMachine::ChangeState, aStateMachine, NEW_STATE ) );

If you use C++11 you can replace boost::bind with std::bind and boost::function with std::function.

Slava
  • 43,454
  • 1
  • 47
  • 90
0

Tha library has a design bug.

The problem is that a function pointer is just a function pointer in C++ (not a closure) and therefore it doesn't have any context.

If you want to create a button that draws a blue circle you will need a global function that will draw a blue circle taking no parameters.

If you want another button to draw a yellow circle you will need another global function that draws a yellow circle taking no parameters.

More specifically the problem is that the library doesn't have any way to store in the button what is called a "context" to be able to pass your code a color to use to draw the circle.

C++11 has added something that reminds a bit closures (not the real thing because of lifetime issues that are hard to solve in any language without garbage collection) but they are not bare function pointers.

What you want to do is just impossible without changing the Button class unless you use some bad hack as the one you can see here that tries to emulate std::bind in C using bare function pointers.

Community
  • 1
  • 1
6502
  • 112,025
  • 15
  • 165
  • 265