Jump to content
  • Advertisement
Sign in to follow this  
Mantear

C++: function pointers and inheritance

This topic is 3669 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Greetings, I'm working on converting a state machine developed in C to C++. I'm trying to preserve as much of the structure of the original design as I can to reduce the chance of breaking anything. This includes the use of function pointers. I have a state base class that performs all of the basic functionality of all the states in the state machine (capturing events, providing hooks to outside of the state machine, changing states, etc). From this class I want to derive the actual states, where each state gets its own derived class. One of the methods provided by the base class is a ChangeState() method, which takes a string to indicate which state to change to. During initialization, each derived class fills in a vector of (member function pointer/string) pairs (the vector is a base class member). When ChangeState() is called, I want the base class to be able to get the member function pointer associated with that string, so it knows what the new state is. When an event comes in, via a ProcessEvent() method provided by the base class, the even is passed to the current state (the 'active' member function). The problem I'm having is the vector of (member function pointer/string) pairs. I can make a vector of (<base class> member function pointer/string) pairs, but I don't think the derived class can place its member functions in that vector, or can it? That's my main question right there. Before I get too many comments about how the state machine should be structure (although I won't ignore any such suggestions, they're more than welcomed), I'm really just curious about the member function pointers going across inheritance hierarchy boundaries. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Member function pointers use what is called contravariance to handle implicit conversions. Basically you can assign a member function pointer of a base class to a member function pointer of a derived class, but not vice versa. Ex:


struct A {
void moo(void) { std::cout << "Moo!"; }
};

struct B : A {
std::string word;

void moo(void) { std::cout << "Moo! " << word ; }
};

int main(int, char **) {
void (B::* fn_ptr)(void) = &B::moo;

B b;
b.word = "Cow!";
(b.*fn_ptr)();

fn_ptr = &A::moo;
(b.*fn_ptr)();
}

Here you can see that both A::moo and B::moo can be called on a B object. However, if you tried calling B::moo on an A object then you would have bad things occur when it tries to access the word member variable.

Share this post


Link to post
Share on other sites
You don't really need inheritance to achieve this. Your transitions between states are very well defined.

struct State {
State * ChangeState(const std::string & s) {
actOnState(s);

Transitions::iterator i = transitions.find(s);
if (i == transitions.end() {
// not found, we just broke our FSM
}
return i->second;
}

bool isEndState();

virtual void actOnState(s) = 0;

typedef std::map<std::string, State *> Transitions;
Transitions transitions;
};


Now, whether you add polymorphism or not is irrelevant. ChangeState behaves exactly the same in every case. It searches the map and returns next state. You may need some other function to perform the polymorphic behavior, but as far as ChangeState is concerned, you do not.

So instead you provide actOnState() which is called when the state is entered (or left, or something else), but you keep ChangeState non-virtual.

The driver then does something like this:
State * s = startState;
do {
s = s->ChangeState(readInput());
} while (!s->isEndState());



The main reason you need to mess with function pointers in C is because you lack what "C with classes" provides.

Share this post


Link to post
Share on other sites
@ SiCrane: Makes sense. Thanks for the explaination.

Now, what if I made the base class templated, taking as the template parameter the typedef-ed member function pointer of the derived class? Something like this:
template <typename T> struct A {
T b_fn_ptr;
};


struct B;
typedef void (B::*fn_ptr)(void);

struct B : A<fn_ptr> {
void moo(void) { std::cout << "Moo Cow!"; }
};



int main(int, char **) {
B b;
A* a = &b;

a->b_fn_ptr = &B::moo;

??? // not sure what the calling convention would be when trying to call 'B::moo' using the 'b_fn_ptr' found in 'a'.
}



Or will this crash and burn horribly.


EDIT: @ Antheus: I'm reading over what you wrote now.

Share this post


Link to post
Share on other sites
Ok, so I started to get something working, but I can't figure out the function calling syntax. Here's what I have so far.
// StateMachineState.h
template <typename T>
class StateMachineState
{
public:
StateMachineState()
: m_CurrentState(std::make_pair("NULL", reinterpret_cast<T>(NULL))) { }
~StateMachineState()
{ // Delete any sub-states.
for (SubStates::iterator iter = m_SubStates.begin();
iter != m_SubStates.end();
++iter)
{ delete iter->second; }
}

void ProcessEvent(SINCGARS_EVENT Event)
{
assert(m_CurrentState.second != reinterpret_cast<T>(NULL));

( (this).*(m_CurrentState.second) )(Event); // <--- This will not compile, what should the syntax be???
}

protected:
void ChangeState(const std::string& new_state)
{ bool valid_state = false;
for (States::iterator iter = m_States.begin();
iter != m_States.end();
++iter)
{ if ((*iter).first == new_state)
{ m_CurrentState = *iter;
valid_state = true;
break;
}
}
assert(valid_state);
}

typedef std::vector< std::pair<std::string, StateMachineState*> > SubStates; // Each state machine can have sub-state machines as well as it's own internal states. Not a part of the issue here.
typedef std::pair<std::string, T> State;
typedef std::vector<State> States;
SubStates m_SubStates;
States m_States;
State m_CurrentState;
};


// State_Main.h, a class that derives from StateMachineState
#include "StateMachineState.h"

class State_Main;
typedef void (State_Main::*StateMainFunction)(int sm_event);

class State_Main : public StateMachineState<StateMainFunction>
{
public:
State_Main()
{
m_States.push_back(std::make_pair("Initial", &State_Main::MainState_Initial));
m_States.push_back(std::make_pair("State1", &State_Main::MainState_State2));
m_States.push_back(std::make_pair("State2", &State_Main::MainState_State1));

ChangeState("Initial"); // This works great and is handled by the base class.

ProcessEvent(2); // See the base class for the problem here.

}
~State_Main();

private:
void MainState_Initial(int sm_event);
void MainState_State1(int sm_event);
void MainState_State2(int sm_event);
};



Everything works up until I try to call the function that I have a pointer to. I can't get the syntax correct and always get compile time errors. Specifically,
\statemachinestate.h(57) : error C2296: '.*' : illegal, left operand has type 'StateMachineState<T> *const '

Any thoughts?

Share this post


Link to post
Share on other sites
Alright, I think that is what I wanted. However, I'm now getting the error I was sort of expecting:
error C2440: 'newline' : cannot convert from 'StateMachineState<T> *const ' to 'State_Main *const '

I'm trying to use the 'this' pointer from the base class to call a member function of a derived class, which is a no-no. However, I'm guaranteed that the actual objects will always be of a derived class (there will be pure virtuals in the base class), so using the 'this' pointer should somehow be valid. I'm just not sure how to do that. Any suggestions?

Share this post


Link to post
Share on other sites
If it's something you can guarantee, then you can just cast either the object pointer or the function pointer so that the two match.

Share this post


Link to post
Share on other sites
@ SiCrane: My call ended up looking like this:
((this)->*(static_cast<SMStateFunction>(m_CurrentState.second)))(sm_event);

where I had to put
typedef void (StateMachineState::*SMStateFunction)(int sm_event);
within the StateMachineState class definition. I ran the code (under VS2008) and it seems to work just fine. I decided to cast the member function pointer and not the 'this' pointer because I frankly have no idea how I would cast the 'this' pointer that way. I'm a bit surprised about a couple things, though.

First, I'm essentially calling a function that the caller should know nothing about (calling a derived class member function using a base class member function pointer on a base class pointer). As long as there is never a base class instantiation (which there won't be in my design; it'll have a couple pure virtual member functions), it should be ok, but it still feels dangerous.

Second, I didn't get a warning about the static_cast from a derived class member function pointer to a base class member function pointer. I had expected to need to perform a reinterpret_cast at least. Is a static_cast correct to use here?

Thanks!

Share this post


Link to post
Share on other sites
For every implicit conversion, a static_cast can be used to reverse that implicit conversion. Since contravariance allows a base member function pointer to be implicitly converted to a derived member function pointer, a static_cast can be used to transform the derived member function pointer to a base member function pointer. (At least in the case of single inheritance, with multiple inheritance its possible to get an ambiguous conversion which would cause things to go wonky.)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!