Basically I have the following class:
class StateMachine {
...
StateMethod stateA();
StateMethod stateB();
...
};
The methods stateA() and stateB() should be able return pointers to stateA() and stateB(). How to typedef the StateMethod?
-
My philosophy is don't use raw member function pointers. I don't even really know how to do what you want using raw pointer typedef's the syntax is so horrible. I like using boost::function.
This is
almostcertainly wrong:class X { public: typedef const boost::function0<Method> Method; // some kind of mutually recursive state machine Method stateA() { return boost::bind(&X::stateB, this); } Method stateB() { return boost::bind(&X::stateA, this); } };This problem is definitely a lot harder than first meets the eye
Chris Jester-Young : Heh, I saw your earlier answer. This is probably the best compromise there is; I was discussing this on IRC and have come to conclude that a typedef referring to itself is probably a "too hard" problem.Chris Jester-Young : Though, in the inner boost::function0, don't you have to provide a return type, and have that recurse again?1800 INFORMATION : Could be, I'm not sitting at a compiler1800 INFORMATION : This sounds like the perfect job for a Y combinator written in the template metaprogramming languageChris Jester-Young : Doesn't it just! Argh, static typing can be a pain sometimes, if you ask me....Jacob : @1800 INFORMATION: yeah, my first thought was "well, I'll just write a Y Combinator up on typedefs... oh boy. :("From 1800 INFORMATION -
EDIT: njsf proved me wrong here. You might find static casting simpler to maintain, however, so I will leave the rest here.
There is no 'correct' static typesince the full type is recursive:typedef StateMethod (StateMachine::*StateMethod)();Your best bet is to use
typedef void (StateMachine::*StateMethod)();then do the uglystate = (StateMethod)(this->*state)();PS:
boost::functionrequires an explicit return type, at least from my reading of the docs:boost::function0<ReturnType>Chris Jester-Young : Yeah, that's a "break type-safety" approach as I mentioned in the question comments. It's too bad, really. I do wish there's a way to solve this cleanly. Maybe C++0x will provide a way?Simon Buchan : No, this is actually *impossible* with an eagerly evaluated type system - not just C++'s. You would need a lazly evaluated type system.From Simon Buchan -
GotW #57 says to use a proxy class with an implicit conversion for this very purpose.
struct StateMethod; typedef StateMethod (StateMachine:: *FuncPtr)(); struct StateMethod { StateMethod( FuncPtr pp ) : p( pp ) { } operator FuncPtr() { return p; } FuncPtr p; }; class StateMachine { StateMethod stateA(); StateMethod stateB(); }; int main() { StateMachine *fsm = new StateMachine(); FuncPtr a = fsm->stateA(); // natural usage syntax return 0; } StateMethod StateMachine::stateA { return stateA; // natural return syntax } StateMethod StateMachine::stateB { return stateB; }This solution has three main strengths:
It solves the problem as required. Better still, it's type-safe and portable.
Its machinery is transparent: You get natural syntax for the caller/user, and natural syntax for the function's own "return stateA;" statement.
It probably has zero overhead: On modern compilers, the proxy class, with its storage and functions, should inline and optimize away to nothing.
Simon Buchan : Can't cast Member function pointers to regular function pointers, but otherwise cool. I should point out that you are still just sugaring the cast :).Jacob : I haven't done any C++ development for 5 years, and have gladly forgotten about member function pointers. Yes, I agree that it is just sugar for the cast in your answer :)1800 INFORMATION : If you change the typedef it should be okay though right? typedef StateMethod (StateMachine:: *FuncPtr)();Simon Buchan : Yes, though njsf's is cooler, and smaller :)Jacob : Ah, that's the syntax I was reaching for. Thank you!From Jacob -
Using just typedef:
class StateMachine { public: class StateMethod; typedef StateMethod (StateMachine::*statemethod)(); class StateMethod { statemethod method; StateMachine& obj; public: StateMethod(statemethod method_, StateMachine *obj_) : method(method_), obj(*obj_) {} StateMethod operator()() { return (obj.*(method))(); } }; StateMethod stateA() { return StateMethod(&StateMachine::stateA, this); } StateMethod stateB() { return StateMethod(&StateMachine::stateB, this); } };Simon Buchan : Damn. Proved me wrong, forgot class types are lazy :)From njsf -
I can never remember the horrible C++ function declspec, so whenever I have to find out the syntax that describes a member function, for example, I just induce an intentional compiler error which usually displays the correct syntax for me.
So given:
class StateMachine { bool stateA(int someArg); };What's the syntax for stateA's typedef? No idea.. so let's try to assign to it something unrelated and see what the compiler says:
char c = StateMachine::stateACompiler says:
error: a value of type "bool (StateMachine::*)(int)" cannot be used to initialize an entity of type "char"There it is: "bool (StateMachine::*)(int)" is our typedef.
From Assaf Lavie
0 comments:
Post a Comment