Skip to content

[Boost en español II] Bind y Function

12 julio 2010

Seguimos con la segunda entrega de esta serie de posts sobre Boost en español. En esta ocasión hablaremos de las bibliotecas Bind y Function, que nos facilitan el uso de objetos función, punteros a funciones y cosas parecidas.

Conceptos previos

Un objeto función es una instancia de una clase que tiene definido el operador (). Esto nos permite usar el objeto como si de una función se tratara:

struct Functor{
  void operator()(){
    cout << "Waking up in vegas" << endl;
  }
};

int main(){
  Functor F;
  F();
}

El verdadero potencial de estos objetos radica en que permiten expandir de manera sencilla la mayoría de los algoritmos de la STL de C++, ya que suelen poder recibir objetos función que modifiquen su funcionamiento para adaptarlo a nuestras necesidades.

Por ejemplo, el algoritmo for_each, contenido en la cabecera algorithm, nos permite iterar sobre un contenedor y aplicar una función por cada elemento (for each element). En estos casos, es útil utilizar un objeto función como extensión al algoritmo:

#include <iostream>
#include <boost/array.hpp>
#include <algorithm>

using namespace std;

struct Botella{
    string etiqueta;
    Botella(string e) : etiqueta(e) { }
};

struct Impresor{
    string destino;
    int n;
    Impresor(string d = "ERROR") : destino(d), n(0){ }

    void operator() (Botella & B){
	cout << destino << "(" << n++ << "): " << B.etiqueta << endl;
    }
};

int main(int argc, char *argv[])
{
    boost::array<Botella, 3> vector = { {Botella("Cruzcampo"),
					 Botella("San Miguel"),
					 Botella("Mahou") } };
    Impresor I("DEBUG");
    for_each(vector.begin(), vector.end(), I);
    return 0;
}

En la STL hay un gran número de algoritmos que podemos usar. Para más información, recomiendo la lectura del punto 8, capítulo 7, del libre “Fundamentos de C++”, del Servicio de Publicaciones de la UCA.

La pega que tiene el uso de los objetos función en los algoritmos de la STL es que la sobrecarga del operador() debe tener un solo parámetro del tipo del contenedor, lo cual nos limita bastante.

Boost.Bind

Bind nos da la posibilidad de ligar los parámetros de una función o un objeto función a un valor concreto, o a otra posición. ¿Qué quiere decir esto? Si tenemos, por ejemplo, un objeto función que recibe dos parámetros y nos interesa que uno de ellos sea siempre el mismo, podemos usar bind. Notar que bind siempre devuelve un objeto función cuyo operador () estará ligado a la función original.

#include <iostream>
#include <algorithm>

#include <boost/array.hpp>
#include <boost/bind.hpp>

using namespace std;

struct Elemento{
    int n;
    Elemento(int n = 0) : n(n) { }
};

struct Comparador{
    void operator() (int n, Elemento & E){
	if(E.n > n)
	    cout << "HOYGAN, parece que este elemento (" << E.n 
		 << ") es mayor que " << n << endl;
    }
};

int main(int argc, char *argv[])
{
    boost::array<Elemento, 3> arr = {{Elemento(2),
				      Elemento(4),
				      Elemento(8)}};

    for_each(arr.begin(), arr.end(),
	     boost::bind<void>(Comparador(), 3, _1));
    return 0;
}

Si os fijáis, la sintaxis de bind es sencilla: Recibe un parámetro de plantilla que indica el tipo de dato de vuelto por la función ligada. Normalmente es capaz de deducir qué tipo de datos se devuelve, pero hay casos en los que es necesario indicarlo explícitamente.

Luego, recibe como argumentos la función u objeto función a ligar, y seguidamente los parámetros de ésta. Podemos indicar directamente el valor, o utilizar placeholders, que nos permiten ligar los parámetros de entrada con los de salida. En nuestro ejemplo, _1 que está en el segundo lugar indica que el segundo parámetro de la función será el primero que reciba bind.

La reorganización de parámetros se puede ver mejor con este otro ejemplo:

#include <iostream>
#include <algorithm>

#include <boost/bind.hpp>

using namespace std;

void fun (int A, int B){
    cout << "A: " << A << endl
	 << "B: " << B << endl;
}

int main(int argc, char *argv[])
{
    boost::bind(fun, _2, _1) (1, 2);
    return 0;
}

Boost.Function

En ocasiones nos interesará tener la función ligada como un objeto con el que trabajar, por ejemplo para poder recibirla como argumento de una función. Para esos casos, boost nos proporciona function. Se trata de una clase paramétrica cuyo operador () se sobrecargará para llamar a la función ligada.

class miClase{
public:

    void hacerSuma(int a){
        cout << "Hago una suma: " << a << endl;
    }

    void hacerResta(int a){
        cout << "Hago una resta: " << a << endl;
    }
};

int main(){
 boost::function<void(miClase, int)> operacion;

    if(1 == 0){
        operacion = boost::bind(&miClase::hacerSuma, _1, _2);
    }else{
        operacion = boost::bind(&miClase::hacerResta, _1, _2);
    }
    miClase c;
    operacion(c, 2);
}
2 comentarios leave one →
  1. 12 julio 2010 22:50

    Genial. Muy claro.

  2. 2 octubre 2010 12:26

    ¿Podrías dar un enlace (de Amazon si es posible) sobre el libro al que haces referencia?

    “Fundamentos de C++”, del Servicio de Publicaciones de la UCA

    Gracias de antemano.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: