Friday, February 11, 2011

How to typedef a pointer to method which returns a pointer the method?

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 almost certainly 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 compiler
    1800 INFORMATION : This sounds like the perfect job for a Y combinator written in the template metaprogramming language
    Chris 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. :("
  • 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 type since the full type is recursive:

    typedef StateMethod (StateMachine::*StateMethod)();
    

    Your best bet is to use typedef void (StateMachine::*StateMethod)(); then do the ugly state = (StateMethod)(this->*state)();

    PS: boost::function requires 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.
  • 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:

    1. It solves the problem as required. Better still, it's type-safe and portable.

    2. Its machinery is transparent: You get natural syntax for the caller/user, and natural syntax for the function's own "return stateA;" statement.

    3. 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::stateA
    

    Compiler 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.

0 comments:

Post a Comment