2012-06-14 16 views
16

Estoy tratando de encontrar una manera de cómo asignar un puntero a funciones con diferente número de argumentos.C++: puntero de función para funciones con número variable de argumentos

Tengo un ciclo while que toma una serie de funciones diferentes como una declaración condicional, así que en lugar de escribir múltiples ciclos while con exactamente el mismo código dentro me gustaría tener uno con un puntero a la función. Todas las funciones son del formato bool f(...). Creo que algo de código mejor ilustrará lo que quiero decir:

int a, b, c, d; 
MyClass* my_class; 

typedef bool (MyClass::*my_fun_t)(); 
my_fun_t my_fun; 

if (condition1) 
    my_fun = &MyClass::function_one(); 
else if (condition2) 
    my_fun = &MyClass::function_two(a, b); 
else if (condition3) 
    my_fun = &MyClass::function_three(a, b, c); 
else if (condition4) 
    my_fun = &MyClass::function_four(a, b, c, d); 

while ((my_class->*my_fun)()) 
{ ... } 

Ahora lo que evidentemente no funciona porque las funciones tienen diferentes firmas. ¿Es posible hacer que funcione de manera similar? ¿Son los functoides algo que debería ver?

+0

¿Cómo sabría cómo aplicar ese puntero de función si no sabe cuántos argumentos se necesitan? – mfontanini

+0

Tenga en cuenta que su sintaxis de asignación a un puntero a función no es válida, está * invocando * las funciones, tomando el resultado, obteniendo un puntero a su almacenamiento temporal y asignando * ese * al puntero de función que declaró previamente. –

+0

Sí, sé que no es una sintaxis válida. Simplemente muestra la idea. – jaho

Respuesta

7

Puede usar std::function<> y std::bind().

#include <functional> 

using std::placeholders::_1; 

typedef std::function<bool(MyClass&)> my_fun_t; 
my_fun_t my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, _1); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, _1, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, _1, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, _1, a, b, c, d); 

while (my_fun(my_class)) { ... } 

Supongamos que utilizará C++ 11. Si no puede usar C++ 11 pero puede usar TR1, reemplace todos los std:: con std::tr1::. También hay un Boost implementation.

+1

Excelente, gracias. No puedo usar C++ 11. ¿Alguna razón para preferir tr1 sobre boost (que estoy usando de todos modos)? – jaho

+1

@Marian TR1 está integrado en la biblioteca estándar, boost no lo es. – kennytm

1

Estoy tratando de encontrar una manera de cómo asignar un puntero a funciones con diferente número de argumentos.

No puede. Los punteros de función son específicos de una firma de función. Esto es completamente lógico: ¿cómo invocarías esa función? (Sí, C permite Invocación de funciones sin especificar en su declaración el número de parámetros de la función tiene, pero esto no funciona en C++, ya que subvierte el sistema de tipos.)

son functoids algo que debo mirar?

En general sí, pero no resuelven este problema.

+0

Hmm, pero solo la 'firma' de una función con argumentos variables sigue siendo única, ¿verdad? La parte de los argumentos de la variable '...' es al menos un simple camuflaje de 'va_list' utilizada dentro de la implementación de las funciones. La invocación de dicha función a través de un puntero de función no debería (no debería) diferir de ninguna manera de invocar dicha función de la manera habitual (con una lista de argumentos variable). –

+0

@ g-makulik Los argumentos variables funcionan, pero esa es una firma específica, no es lo mismo que enlazar con diferentes firmas de funciones. –

+0

Ahh, veo tu punto !. Pero el OP no menciona explícitamente la vinculación (puede ser que leí su código 'pseudo' incorrecto). Al usar definiciones de interfaz (polimorfismo), aún podría ser posible lograr lo que el OP quiere (previsto). –

3

Desea std::function, un objeto de función polimórfica, y std::bind para crear objetos de función vinculando argumentos a los parámetros de otros funtores.

Si no puede usar C++ 11, entonces boost::function y boost::bind son equivalentes, aunque un poco más restrictivos.

std::function<bool()> my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, my_class); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, my_class, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, my_class, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, my_class, a, b, c, d); 

while (my_fun()) 
{ ... } 
+1

Si puede usar C++ 11, también puede usar funciones lambda y extraer las variables del alcance léxico adjunto. Cualquiera que prefieras, supongo. – user268396

+0

@ user268396: Probablemente. Por el momento, estoy atrapado con un compilador que no admite lambdas, por lo que no sé mucho sobre ellos. –

+0

La solución de impulso me ha funcionado. Gracias. – jaho

4

Esto funciona para mí:

#include <iostream> 
#include <cstdarg> 

using namespace std; 

class MyInterface 
{ 
public: 
    virtual bool func(int argc, ...) = 0; 
}; 

class MyImpl : public MyInterface 
{ 
public: 
    virtual bool func(int argc, ...); 
}; 

bool MyImpl::func(int argc, ...) 
{ 
    va_list varargs; 
    va_start(varargs,argc); 
    cout << "Arguments passed:" << endl; 
    for(int i = 0; i < argc; ++i) 
    { 
     // expect double values 
     double val = va_arg(varargs,double); 
     cout << val << endl; 
    } 
    va_end(varargs); 
    return true; 
} 

typedef bool (MyInterface::*MyFunc)(int, ...); 

int main() { 

    MyImpl impl; 
    MyInterface* interface = &impl; 
    MyFunc pfunc = &MyInterface::func; 

    if(!(interface->*pfunc)(2,double(3.14),double(2.72))) 
    { 
     return 1; 
    } 
    return 0; 
} 

Salida:

Arguments passed: 
3.14 
2.72 

Obviamente se puede declarar y utilizar los punteros de función para-miembros() funciona utilizando argumentos variables.

Cuestiones relacionadas