Callbacks

Los viejos callbacks de C, ¿quién los recuerda? ¿nadie? ¿punteros a una función? ¿no? Meh, no importa. Imaginemos esta interfaz:

interface Callback {
  bool match(int valor);
}

El método match() devolvería true si el valor es de interés.

En C++ se escribe lo mismo así:

class Callback {
public:
  virtual ~Callback() { }
  virtual bool match(int valor) = 0;
};

Podemos tener un algoritmo que nos dé como resultado el índice del primer ítem que matchea nuestro Callback:

int buscar_primera_coincidencia(const int* array,
                                int elementos,
                                Callback* callback) {
  for (int i=0; i<elementos; ++i) {
    if (callback->match(array[i]))
      return i; // El item i es el primero en cumplir match() == true
  }
  return -1;
}

Así podemos crear miles de implementaciones Callback:

class EsCinco : public Callback {
public:
  bool match(int valor) override {
    return (valor == 5);
  }
};

O también:

class DistintoDeCero : public Callback {
public:
  bool match(int valor) override {
    return (valor != 0);
  }
};

O mucho mejor, hacer una clase genérica:

template<int VALOR>
class EsIgualA : public Callback {
public:
  bool match(int valor) override {
    return (valor == VALOR);
  }
};

Y usarla así:

int main() {
  int array[] = { 4, 8, 15, 16, 23, 42 };

  EsIgualA<16> igual16;
  int resultado = buscar_primera_coincidencia(array, 6, &igual16);

  assert(resultado == 3);
}

Podríamos decir que Callback es algo así como interfaz delegate: Alguien a quien le derivamos parte del trabajo que buscar_primera_coincidencia necesita.

Un ejemplo exactamente igual se da en la función qsort. Sólo que la interfaz Callback en este caso es un puntero a una función:

void qsort(void*  ptr,
           size_t count,
           size_t size,
           int  (*comp)(const void*, const void*));
                 //^ Un verdadero callback: un puntero a una función

Lo que sería igual a:

class Comp {
public:
  virtual ~Comp() { }
  virtual int comp(const void* elementoA, const void* elementoB) = 0;
};

void qsort(void*  ptr,
           size_t count,
           size_t size,
           Comp*  comp);

En un próximo post vamos a ver cómo la STL utiliza conceptos similares a callbacks y delegates en su diseño.

Este es el primer post de una seguidilla que tengo planeada sacar respecto a callbacks, signal programming, signals y slots, patrón observer, cálculo lambda, inversión de control, inyección de dependencias, etc.


 01 June 2015 | cpp programacion