Publi

Callbacks en C++ con boost

Hemos visto las retrollamadas o callbacks tanto en C como en C++, aunque en este último, era un poco difícil. También, utilizando Glib, con Gtkmm se expresaban los callbacks que se realizan cuando se lleva a cabo una acción.

Ahora le toca el turno a la biblioteca boost para C++, y así ver el método más sencillo. Para probarlo, primero tenemos que tener instalada la biblioteca. En el ejemplo vemos cómo le pasamos una función (A) como argumento a otra función (B), y ésta última ejecutará A. Tras ello vemos el código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <boost/function.hpp>

using namespace std;

typedef boost::function3<int, int, int, char> TFun;

int myfunc(int a, int c, char b)
{
  cout<<"MYFUNC: "<<a<<" CH: "<<b<<endl;
  return 120;
}

void caller(TFun fun)
{
  cout <<"RESULT: "<<fun(4,3 ,'a')<<endl<<"END"<<endl;
}

int main()
{
  caller(myfunc);
}

Es un poco rudimentario, pero podemos ver cómo definimos una función (function3, porque tenemos 3 argumentos; el primer tipo que damos es el del valor de salida, que puede ser void, int, o cualquier otro tipo).
Vemos, cómo myfunc() tiene un código y va a ser ejecutado desde la función caller()

Pero esto es sencillo, queda claro, boost no engorda demasiado el código (para hacer esto), así que ahora pasamos al mundo de las clases y los objetos.

Vamos a crear dos clases (Called, de la cual llamaremos a un método desde otro sitio) y Caller (la que llamará a un método de la primera clase), todo ello utilizando la biblioteca boost para simplificar todo un poco; luego crearemos objetos, uno de cada clase, y desde el objeto caller, llamaremos al método de called (el código está todo seguido y en un mismo archivo, para facilitar copia y pegas, ya es cosa vuestra hacerlo un poco más elegante):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>

using namespace std;

class Called
{
public:
  Called()
  {
    times=0;
  }

  ~Called()
  {
  }

  int ImCalled(int sum)
  {
    times+=sum;
    cout << "I've been called "<<times<<" times."<<endl;
    return times;
  }
private:
  int times;
};

class Caller
{
public:
  Caller(Called &c)
  {
    this->c=&c;
    cback = boost::bind(&Called::ImCalled, &c, _1);
  }

  ~Caller()
  {
  }

  void doCall(int n)
  {
    cout << "Return: "<<cback(n)<<endl;
  }

private:
  Called *c;
   boost::function<int(int)> cback;
};

int main(int argc, char *argv[])
{
  Called called;
  Caller caller(called);

  caller.doCall(1);
  caller.doCall(3);
  caller.doCall(5);
  caller.doCall(7);
  caller.doCall(9);
}

Aquí tenemos que tener en cuenta que en la clase Caller, en la parte privada declaramos cback como una boost::function aunque de forma general podía ser boost::function

Ahora, en el constructor de Caller, cogemos el objeto called y con boost::bind() asociamos cback al método que queremos ejecutar, con el objeto que queremos utilizar para ejecutarlo… como hacíamos con los punteros a miembro.

Sólo necesitamos el método doCall(int n), con el cuál llamamos a cback, como si de una función normal se tratase y hará todo el trabajo.

Algo importante, es la forma en la que hacemos boost::bind(), y es que como primer argumento pasamos el método, luego la clase, y luego vemos un “_1”, esto es que el argumento con el que llamaremos a la función se lo pasaremos luego (cuando se ejecuta cback(n)). Si ejecutamos el programa ahora, veremos que el resultado es simple, y depende de la n que le pasamos desde main(), pero si en el bind(), en lugar de _1, ponemos un número cualquiera, veremos que el resultado dependerá de ese número, por lo que, desde la orden bind() podemos definir algunos de los argumentos con los que después realizaremos la llamada a la función.

Para compilar esto, nada más fácil que:

g++ -o boostcallbacks boostcallbacks.cpp

Si tenemos las bibliotecas instaladas, no debería fallar.

También podría interesarte...

Leave a Reply