2008-10-02 8 views
9

Básicamente tengo la clase siguiente:Cómo escribir un puntero al método que devuelve un puntero al método?

class StateMachine { 
... 
StateMethod stateA(); 
StateMethod stateB(); 
... 
}; 

La métodos stateâ() y stateB() debe ser punteros capaces de retorno a stateâ() y stateB(). Cómo escribir el método StateMethod?

+0

Guau, este problema es mucho, mucho más difícil de lo que parece, en mi opinión. Hay todo tipo de formas de resolverlo si estás dispuesto a romper la seguridad total del tipo, pero de lo contrario ... wow. –

Respuesta

14

GotW #57 dice usar una clase proxy con una conversión implícita para este mismo propósito.

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; 
} 

Esta solución tiene tres principales fortalezas:

  1. Resuelve el problema según sea necesario. Mejor aún, es seguro y portátil.

  2. Su maquinaria es transparente: Se obtiene la sintaxis natural para la persona que llama /usuario, y la sintaxis natural para la propia función de "retorno stateâ;" declaración.

  3. Probablemente tiene cero sobrecarga: en los compiladores modernos, la clase de proxy, con su almacenamiento y funciones, debe en línea y optimizar en nada.

+0

No se pueden emitir punteros de función de miembro a los punteros de función normales, pero de lo contrario se enfrían. todavía estás azucarando el elenco :). –

+0

No he realizado ningún desarrollo de C++ durante 5 años, y con mucho gusto me he olvidado de los indicadores de funciones de los miembros. Sí, estoy de acuerdo en que es solo azúcar para el yeso en tu respuesta :) –

+0

Si cambias el typedef, debería estar bien, ¿cierto? typedef StateMethod (StateMachine :: * FuncPtr)(); –

2

Mi filosofía es no utilizar punteros de función de miembro sin formato. Ni siquiera sé realmente cómo hacer lo que quieres usando puntero sin formato typedef, la sintaxis es horrible. Me gusta usar la función boost ::.

Ésta es casi ciertamente equivocada:

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); } 
}; 

Este problema es, sin duda mucho más duro que aparece a simple vista

+0

Heh, vi su respuesta anterior. Este es probablemente el mejor compromiso que existe; Estaba discutiendo esto en el IRC y he llegado a la conclusión de que un tipo de referencia que se refiere a sí mismo es probablemente un problema "demasiado difícil". –

+0

Aunque, en el boost interno :: function0, ¿no tiene que proporcionar un tipo de devolución y tener ese recurse nuevamente? –

+0

Podría ser, no estoy sentado en un compilador –

3

EDIT: njsf me resultó ser malo aquí. Sin embargo, es posible que encuentres el fundido estático más simple de mantener, así que dejaré el resto aquí.

no hay 'correcta' de tipo estático ya que el tipo completo es recursivo:

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

Su mejor opción es utilizar typedef void (StateMachine::*StateMethod)(); luego hacer el feo PS state = (StateMethod)(this->*state)();

: boost::function requiere una explícita tipo de devolución, al menos desde mi lectura de docs: boost::function0<ReturnType>

+0

Sí, ese es un enfoque de" seguridad tipo rotura "como mencioné en los comentarios de la pregunta. Es una lástima, de verdad. Ojalá haya una manera de resolver esto limpiamente. ¿Tal vez C++ 0x proporcionará una forma? –

+0

No, esto es en realidad * imposible * con un sistema de tipo evaluado con impaciencia, no solo con C++. Necesitaría un sistema tipo lazly evaluado. –

8

Utilizando sólo 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); } 

};  
+0

Maldita sea. Me demostró que estaba equivocado, olvidé que los tipos de clase son flojos :) –

0

Nunca puedo recordar la declspec función horrible, C++, por lo que cada vez que tengo que averiguar la sintaxis que describe una función miembro, por ejemplo, sólo Inducir un error de compilador intencional que generalmente muestra la sintaxis correcta para mí.

Así que dado:

class StateMachine { 
    bool stateA(int someArg); 
}; 

Cuál es la sintaxis para typedef de stateâ? Ni idea .. así que vamos a tratar de asignarle algo no relacionado y ver lo que el compilador dice:

char c = StateMachine::stateA 

compilador dice:

error: a value of type "bool (StateMachine::*)(int)" cannot be used to initialize 
     an entity of type "char" 

No es: "bool (StateMachine :: *) (int) "es nuestro typedef.

Cuestiones relacionadas