[Boost en español] Ejemplo rápido de aplicación de Bind
Justo ayer, peleándome un rato con freegemas, me surgió la necesidad de iterar por un vector y borrar los elementos que tuvieran cierta propiedad. A primera vista puede parecer sencillo, pero la solución no es trivial:
- Si utilizas un bucle for, al borrar un elemento el tamaño y los índices de los elementos también, por lo que la variable de seguimiento (típicamente
size_t i) ya no vale. - Si utilizas iteradores, al eliminar un elemento aquellos quedan invalidados.
Estuve pensando un poco cómo hacerlo y se me ocurrieron algunas formas poco ortodoxas o que podrían llevar a error. Una de las formas que encontré por internet fue la siguiente, con iteradores y un for algo especial:
for (iterator it = v.begin();
it != v.end();
)
{
if (condition)
it = v.erase(it);
else
++it;
}
Si os fijáis, el
for no tiene tercer parámetro, sino que somos nosotros los que movemos el iterador manualmente. Para evitar la invalidación, utilizamos el valor de retorno de erase.
Pero otra forma de hacerlo, más elegante a mi parecer, era utilizar el algoritmo remove_if y un predicado adecuado. En mi caso, el contenedor tenía objetos de una clase con un método que devolvía un booleano tal que, si es verdadero, el elemento debería borrarse. En un primer intento hice
remove_if(contenedor.begin(), contenedor.end(), boost::bind<bool>(&Clase::Comprobar, _1));
Pero los elementos no se borraban. «Qué raro», pensé. Resulta que el funcionamiento de
remove_if no es el de borrar directamente los elementos, sino que reordena los elementos a borrar al final del contenedor y devuelve un iterador que apunta al primero de esos elementos a borrar. Así, junto al método erase del vector, tenemos:contenedor.erase(remove_if(contenedor.begin(), contenedor.end(), boost::bind<bool>(&Clase::Comprobar, _1)), contenedor.end());
Esto se conoce como el Erase-remove idiom y tiene hasta su propio artículo en la Wikipedia. Muy curioso.

Toda una maniobra ninja, sí señor. Me he enfrentado a este problema en un par de ocasiones y no he encontrado una solución tan buena como la tuya.
En Granny’s Bloodbath la clase que lleva la lógica de juego almacena listas de punteros a Actors (según a la familia de actores a la que pertenecen: enemigos, items o balas). En cada iteración del main loop se comprueba si hay algún Actor cuyo estado sea Actor::ERASE y se borra de la lista. En caso de que dos actores deban ser eliminados a la vez hay problemas.
Simplemente hice que sólo se pudiera eliminar un actor en cada iteración. Cuando el juego va a 30fps no se nota (es muy raro que esto ocurra) pero sigue siendo una solución inifitamente cutre.
¡Saludos!