2011-10-08 12 views
7

Tengo este problema que me molesta. Tengo la clase FSM qué claves asociados a las devoluciones de llamadaFunciones como parámetros de plantilla problema

class FSM 
{ 
public: 

typedef bool (FSM::*InCallback_t)(int); 
typedef std::map< std::string, InCallback_t > Table; 

// Since I would like to allow the user to register both functors and class member functions 
template< typename Callback_t, bool (Callback_t::*CallbackFunct_t)(int) > 
bool callback(int x) 
{ 
    return (Callback_t().*CallbackFunct_t)(x); 
} 

void addCallback(const std::string& iKey, InCallback_t iCallback) 
{ 
    _table.insert(std::make_pair(iKey, iCallback)); 
} 

    [ ... ] 

private: 
    Table _table; 
}; 

y algunas clases de devoluciones de llamada

class CallbackBase 
{ 
public: 

    bool operator()(int x){ return doCall(x); } 

private: 
    virtual bool doCall(int x){ return true; } 
}; 


class Callback: public CallbackBase 
{ 
private: 
    bool doCall(int x) 
    { 
     std::cout << "Callback\n"; 
     return true; 
    } 
}; 

Ahora bien, si en el principal hago:

FSM aFSM; 
// OK 
aFSM.addCallback("one", &FSM::callback< CallbackBase, &CallbackBase::operator() >); 
// KO 
aFSM.addCallback("two", &FSM::callback< Callback, &Callback::operator() >); 

La primera llamada es fina, en el segundo compilador se queja:

Test.cpp: In function ‘int main(int, char**)’: 
Test.cpp:104:77: error: no matching function for call to ‘FSM::addCallback(const char [4], <unresolved overloaded function type>)’ 
Test.cpp:104:77: note: candidate is: 
Test.cpp:24:7: note: void FSM::addCallback(const string&, FSM::InCallback_t) 
Test.cpp:24:7: note: no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘FSM::InCallback_t’ 

Observe también que la siguiente es bien

typedef bool (Callback::*Function_t)(int); 
Function_t aFunction = &Callback::operator(); 
(Callback().*aFunction)(5); 

Alguna idea? Gracias de antemano por su ayuda.

Simone

+2

Parece una falla del compilador. : | – Nawaz

+0

Sí, supongo que también. Es bastante extraño – Simone

+0

También tuve algunos problemas con las funciones de base (u operadores) con un puntero de función miembro de la clase derivada. En MSVC al menos, simplemente no funciona en las plantillas. – Xeo

Respuesta

2

No ha definido devolución de llamada :: operador(). No hay una función secundaria para devolución de llamada, solo la función de CallbackBase que toma una CallbackBase y un int como parámetros. Esta es la razón por la cual el compilador gime sobre "tipo de función sobrecargada sin resolver".

El tipo de la función heredada es bool (CallbackBase :: * operator()) (int). Esta función se puede convertir automáticamente en un bool (Callback :: * operator()) (int) porque siempre puede aplicar una Devolución de llamada a una función que solo acepta CallbackBase. Esta es la razón por la cual los siguientes trabajos: es un elenco automático que ocurre allí.

typedef bool (Callback::*Function_t)(int); 
Function_t aFunction = &Callback::operator(); 

El problema ocurre con el tipo de plantilla deducción:

template< typename Callback_t, bool (Callback_t::*CallbackFunct_t)(int) > 
with: Callback_t = Callback, CallbackFunct_t = bool (CallbackBase::*CallbackFunct_t)(int) 

Esto no funciona ya que los tipos que se dan a través de Callback_t y el tipo requerido por el puntero de función no coinciden cuando instanciando la devolución de llamada función. Puede resolver el problema con un molde explícito del puntero de función a (Callback :: * operator()) (int) antes de que se produzca la deducción de tipo. Si cambia la función de devolución de llamada a lo siguiente, no necesita que los dos tipos sean idénticos y se compila sin el molde.

template< typename Callback_t> 
bool callback(int x) 
{ 
    return Callback_t()(x); 
} 

Lo que no entiendo es por qué agrega la función virtual. ¿No haría lo siguiente lo mismo, sería más simple y legible y aún más rápido (sin llamada de función virtual)? La función doCall debería ser pública.

template< typename Callback_t> 
bool callback(int x) 
{ 
    return Callback_t().doCall(x); 
} 

Otra mejora sería hacer la función de devolución de llamada estática. Sería incluso más simple si las funciones doCall fueran estáticas: esto haría obsoleta la función de devolución de llamada y evitaría crear un temporal para llamar a doCall.

+0

Gracias por su respuesta. Algunos comentarios Acerca del punto si existe Callback :: operator(). Existe porque se hereda de la clase base y, de hecho, las últimas tres líneas de código que he publicado en la pregunta funcionan correctamente. De hecho, me parece más un error de compilación. Sí, es cierto que podría eliminar el punto de función como parámetro de plantilla, pero en ese caso podría manejar solo el objeto de functor, mientras que de hecho me gustaría ser más flexible, permitiendo al usuario especificar también las funciones de miembro en caso de que prefiera tener un único clase implementando todas las devoluciones de llamada como funciones miembro. ... – Simone

+0

.. Acerca de por qué he definido el operador() y el doCall virtual. Este es un patrón llamado método de plantilla. Sí, en el ejemplo anterior es inútil, pero se puede imaginar que en el operador() en el CallbackBase se implementan algunas operaciones previas y posteriores ejecutadas antes y después de la llamada de la función de devolución de llamada implementada en cada clase derivada. Un ejemplo podría tomar estadísticas sobre el tiempo de ejecución de doCall. – Simone

+0

@simone No creo en un error de compilación. Traté de compilar tu código en gcc y en el estudio visual con el mismo resultado. En Visual Studio, el mensaje de error es: no se puede convertir de 'bool (__thiscall CallbackBase :: *) (int)' a 'bool (__thiscall Callback :: * const) (int)'. No hay función Callback :: operator() solo llama a la de la clase base. Lo siguiente no compila en ninguno de los compiladores mencionados: typedef void (Callback :: * func) (int); func f = & Callback :: operator(); –

Cuestiones relacionadas