Pregunta

Tengo un puntero de función definido por:

typedef void (*EventFunction)(int nEvent);

¿Hay alguna manera 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, tales 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 curry? ¿función que se puede escribir en C ++ que hará este tipo de ajuste de un puntero a una función miembro en un puntero de función normal?

¿Fue útil?

Solución

Recomiendo altamente la excelente biblioteca FastDelegate de Don Clugston. Proporciona todo lo que esperaría de un delegado real y compila algunas instrucciones ASM en la mayoría de los casos. El artículo adjunto también es una buena lectura de los punteros de las funciones de los miembros.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Otros consejos

Puede usar punteros de función para indexar en la vtable de una instancia de objeto determinada. Esto se denomina puntero de función de miembro . Debería cambiar su sintaxis para utilizar ". * & Quot; y el & amp; & amp; :: " operadores:

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

y luego:

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

Aléjese de los punteros de función sin procesar de C ++ y use std: : función en su lugar.

Puede usar boost :: function si está utilizando un compilador antiguo como Visual Studio 2008 que no es compatible con C ++ 11.
boost: function y std :: function son la misma cosa: introdujeron bastante material de impulso en la biblioteca std para C ++ 11.

Nota: es posible que desee leer la documentación de la función de impulso en lugar de la de Microsoft, ya que es más fácil de entender

Puede encontrar Preguntas frecuentes sobre C ++ de Marshall Cline a lo que estás tratando de lograr.

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 anulado en la clase base y su puntero debe apuntar al método de la clase base. Más información sobre punteros a miembros virtuales .

Si está interactuando con una biblioteca C, entonces no puede usar una función miembro de la 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
}

Desafortunadamente, el tipo EventFunction no puede apuntar a una función de B, porque no es el 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 llamas la devolución de llamada con un obhect de B. Pero probablemente quieras poder llamar 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:

  • Convierta a B en una clase base, luego anule una función virtual para cada otra clase que podría llamarse. A luego almacena un puntero a B en lugar de un puntero de función. Mucho más limpio.
  • Si no desea vincular la función a un tipo de clase específico, incluso a una clase base (y no lo culparía), le sugiero que haga la función que se llama función estática: " < code> static void EventFrom A (int nEvent); " ;. Entonces puede llamarlo directamente, sin un objeto de B. Pero probablemente desee que llame 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 personas que no sean B, entonces debe 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 se indicó anteriormente, 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 los sistemas de ventanas

Mencionas que impulsar no es una opción para ti, pero ¿tienes TR1 disponible?

TR1 ofrece objetos de función, enlace y mem_fn basados ??en la biblioteca de impulso, y es posible que ya lo tenga incluido en 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

No está claro qué está tratando de lograr aquí. lo que está claro es que los punteros de función no son el camino.

quizás lo que estás buscando es puntero al método.

Tengo un conjunto de clases para esta cosa exacta que uso en mi marco de trabajo de c ++.

http://code.google.com/p /kgui/source/browse/trunk/kgui.h

La forma en que lo manejo es que cada función de clase que se puede usar como devolución de llamada necesita una función estática que le asigne el tipo de objeto. Tengo un conjunto de macros que lo hacen automáticamente. Realiza una función estática con el mismo nombre, excepto con un " CB_ " prefijo 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);}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top