resolucion - punteros a objetos c++
Llamar a un puntero de función C++ en una instancia de objeto específica (10)
Desafortunadamente, el tipo EventFunction no puede apuntar a una función de B porque no es del tipo correcto. Podría hacerlo del tipo correcto, pero probablemente esa no sea realmente la solución que desea:
typedef void (*B::EventFunction)(int nEvent);
... y luego todo funciona una vez que llames a la devolución de llamada con un obécto de B. Pero es probable que quieras poder invocar funciones fuera de B, en otras clases que hacen otras cosas. Ese es el punto de una devolución de llamada. Pero ahora este tipo apunta a algo definitivamente en B. Las soluciones más atractivas son:
- Haga que B sea una clase base y luego anule una función virtual para cada clase que pueda llamarse. A luego almacena un puntero a B en lugar de un puntero a la función. Mucho más limpio.
- Si no quiere vincular la función a un tipo de clase específico, incluso una clase base (y yo no lo culparía), entonces le sugiero que haga la función que recibe el nombre de función estática: "
static void EventFrom A(int nEvent);
". Entonces puede llamarlo directamente, sin un objeto de B. Pero es probable que desee llamar a una instancia específica de B (a menos que B sea un singleton). - Entonces, si desea poder llamar a una instancia específica de B, pero también puede llamar a las que no son B, entonces necesita pasar algo más a su función de devolución de llamada para que la función de devolución de llamada pueda llamar al objeto correcto. Haga que su función sea estática, como arriba, y agregue un parámetro void * que hará un puntero a B.
En la práctica, ve dos soluciones a este problema: sistemas ad hoc donde pasa un vacío * y el evento, y jerarquías con funciones virtuales en una clase base, como sistemas de ventanas
Tengo un puntero a la función definido por:
typedef void (*EventFunction)(int nEvent);
¿Hay alguna forma de manejar esa función con una instancia específica de un objeto C ++?
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne() { handler(1); }
};
class B
{
private:
A a;
public:
B() { a.SetEvent(EventFromA); } // What do I do here?
void EventFromA(int nEvent) { // do stuff }
};
Editar: Orion señaló las opciones que ofrece Boost, como:
boost::function<int (int)> f;
X x;
f = std::bind1st(
std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)
Desafortunadamente, Boost no es una opción para mí. ¿Hay algún tipo de función de "currying" que se pueda escribir en C ++ que haga este tipo de ajuste de un puntero a una función miembro en un puntero de función normal?
Lea sobre punteros a los miembros . Para llamar a un método en la clase derivada, el método debe declararse en la clase base como virtual y ser anulado en la clase base y su puntero debe apuntar al método de la clase base. Más sobre punteros a miembros virtuales .
No está claro qué estás tratando de lograr aquí. lo que está claro es que los indicadores de función no son el camino.
tal vez lo que estás buscando es un indicador del método.
Puede usar punteros de funciones para indexar en el vtable de una instancia de objeto dada. Esto se llama puntero de función miembro . Su sintaxis debería cambiar para usar los operadores ". *" Y "& ::":
class A;
class B;
typedef void (B::*EventFunction)(int nEvent)
y entonces:
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};
class B
{
private:
A a;
public:
B() { a.SetEvent(&B::EventFromA); } // note: "&::"
void EventFromA(int nEvent) { /* do stuff */ }
};
Recomiendo encarecidamente la excelente biblioteca FastDelegate de Don Clugston. Proporciona todas las cosas que esperarías de un delegado real y compila algunas instrucciones de ASM en la mayoría de los casos. El artículo que acompaña es una buena lectura de los indicadores de funciones de miembros también.
Si está interactuando con una biblioteca C, entonces no puede usar una función miembro de clase sin usar algo como boost::bind
. La mayoría de las bibliotecas C que toman una función de devolución de llamada generalmente también le permiten pasar un argumento adicional de su elección (generalmente de tipo void*
), que puede usar para iniciar su clase, así:
class C
{
public:
int Method1(void) { return 3; }
int Method2(void) { return x; }
int x;
};
// This structure will hold a thunk to
struct CCallback
{
C *obj; // Instance to callback on
int (C::*callback)(void); // Class callback method, taking no arguments and returning int
};
int CBootstrapper(CCallback *pThunk)
{
// Call the thunk
return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}
void DoIt(C *obj, int (C::*callback)(void))
{
// foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can''t change it
struct CCallback thunk = {obj, callback};
foobar(&CBootstrapper, &thunk);
}
int main(void)
{
C c;
DoIt(&c, &C::Method1); // Essentially calls foobar() with a callback of C::Method1 on c
DoIt(&c, &C::Method2); // Ditto for C::Method2
}
Tengo un conjunto de clases para esta cosa exacta que utilizo en mi framework c ++.
http://code.google.com/p/kgui/source/browse/trunk/kgui.h
Cómo lo manejo es cada función de clase que se puede usar como una devolución de llamada que necesita una función estática que lo vincule al tipo de objeto. Tengo un conjunto de macros que lo hacen de forma automática. Realiza una función estática con el mismo nombre excepto con un prefijo "CB_" y un primer parámetro adicional que es el puntero del objeto de clase.
Verifique los tipos de clase kGUICallBack y varias versiones de plantilla de los mismos para manejar diferentes combinaciones de parámetros.
#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
Usted menciona que impulsar no es una opción para usted, pero ¿tiene TR1 disponible para usted?
TR1 ofrece objetos de función, vinculación y mem_fn basados en la biblioteca de impulso, y es posible que ya lo tenga incluido con su compilador. Todavía no es estándar, pero al menos dos compiladores que he usado recientemente lo han tenido.
http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx
Huye de los punteros de funciones C ++ sin formato y utiliza std::function
lugar.
Puede usar boost::function
si está utilizando un compilador antiguo como Visual Studio 2008 que no tiene soporte para C ++ 11.
boost:function
y std::function
son la misma cosa: sacaron un poco de impulso en la biblioteca std para C ++ 11.
Nota: es posible que desee leer la documentación de la función boost en lugar de la de Microsoft, ya que es más fácil de entender
Puede encontrar las preguntas frecuentes de C ++ de Marshall Cline útiles para lo que está tratando de lograr.