Hace tiempo, hice una serie de posts sobre callbacks:
Hay algunos posts más, pero se salen del tema (y seguro que salen sugeridos más abajo). El caso es que dejé un poco el tema de lado y me gustaría retomarlo con los cambios de la especificación C++11 (vale, tenemos C++14 lista, pero la versión de 2011 es una de las que más cambios introdujeron (y que también valen para C++14). Aunque se ha escrito mucho sobre el tema, pero desde aquí quiero dar a conocer mi humilde visión.
Serán una serie de posts ya que es un tema muy amplio y me gusta poner gran cantidad de ejemplos.
Una variable que contiene una función
El ejemplo más sencillo, crear una variable que contenga una función, es decir, declaramos una variable y le asignamos como valor una función, así llamamos a la función que tenga asignada la variable en cada momento (podrá cambiar, podremos darle la opción al usuario para que tenga un valor u otro, o nos permitirá abstraer un poco más nuestro código):
Si usas g++, deberás hacer lo siguiente:
g++ -o simple simple.cpp -std=c++11
El programa, como vemos, asigna a la variable fun (de tipo auto, ya el compilador se encarga de poner el tipo que corresponda, lo cual nos ayuda mucho, que los tipos en C o C++ pueden ser muy complicados de sacar o largos de escribir, en este caso sería un tipo void (fun*)(), cuando tengamos argumentos, ya será otro cantar. Aunque, para hacerlo al más puro estilo C++11, deberíamos declararla como:
(como tenemos using namespace std no volveremos a decir que está ahí, pero es bueno saberlo) y hacer, arriba del todo, un
Pero vamos, si vamos a hacer una inicialización inmediata como en el ejemplo, podemos utilizar auto y listo, es más, el archivo generado puede variar, usar function aunque nos ayuda bastante, se queda todo mucho más limpio y después podremos hacer más cosas, no deja de incluir algo de complejidad a nuestro programa.
Luego, como la función goodbye() y la función hello() son iguales, las dos son funciones que no devuelven nada y no tienen argumentos, a fun, le podemos asignar tanto hello, como goodbye, y llamando a fun se ejecutará la función pertinente.
De la misma manera, también funcionaría si hacemos llamadas a funciones estáticas dentro de una clase o un struct:
En este caso, he metido un argumento en cada método y un valor de salida, para que veamos que no hay diferencia en cuanto a la forma de usarlo, bueno, cuando llamamos a fun, tenemos que pasarle el argumento y éste nos dará un valor de salida, pero bueno, con auto, no tenemos que preocuparnos.
La forma de declarar la función en C (pero que no vamos a hacerlo, es sólo por curiosidad) sería:
Y, en C++11, utilizando functional lo haríamos de esta forma:
Debemos hacerlo así cuando no podamos utilizar auto, es decir, cuando no vayamos a inicializar la variable con ninguna función (y por tanto, el compilador no tenga forma de saber qué tipo queremos darle).
Usando funciones anónimas o lambdas
Podemos incluir la función en línea, es decir, dentro de la propia llamada a la función, es posible implementar una función que no podremos llamar de ninguna manera si no es con la variable a la que la asignamos o con la función a la que llamamos. (Ver ejemplo). Esto es muy útil cuando se trate, por ejemplo de funciones de búsqueda que nos piden una función para comparar o cuando a través de una función producimos una salida (por pantalla o por cualquier otro medio), o cuando debemos pasar a una función la forma con la que queremos tratar los datos:
La salida de esto será:
El cuadrado de 1 es 1
El cuadrado de 2 es 4
El cuadrado de 3 es 9
El cuadrado de 4 es 16
El cuadrado de 5 es 25
El cuadrado de 6 es 36
El cuadrado de 7 es 49
El cuadrado de 8 es 64
El cuadrado de 9 es 81
En este caso, la función itera hace un bucle empezando en desde y terminando en hasta-1 y en cada iteración ejecutará la función llamada, dicha función, se la hemos pasado en main() a través de una función anónima. Como vemos empezará en [](tipo valor,...) con tantos argumentos como tengamos. La salida, en este caso, la averiguará el compilador automáticamente, aunque si queremos imponer un tipo de salida, lo podremos hacer así:
Otro ejemplo, utilizando la biblioteca algorithm (std), ordenando los valores de un vector, utilizando para ordenar una función anónima (para que podamos elegir el criterio de ordenación que queramos):
Las funciones anónimas tienen más cosas que trataremos más adelante, junto con muchos más ejemplos sobre callbacks en C++11 y posibilidades para nuestros programas. Por el momento, lo dejamos aquí hasta la próxima entrega (el día 9 de noviembre de 2015), en el que trataremos el tema de las llamadas a métodos de una clase. Esta vez, no serán métodos estáticos, sino que estarán asociados a un objeto.
Foto: Aaron (Flickr-cc)