Cómo implementar devoluciones de llamada genéricas en C++
boost boost-bind (3)
Perdone mi ignorancia al hacer esta pregunta básica, pero me he acostumbrado tanto a usar Python, donde este tipo de cosas son triviales, por lo que he olvidado completamente cómo intentaría esto en C ++.
Quiero poder pasar una devolución de llamada a una función que realiza un proceso lento en segundo plano, y llamarlo más tarde cuando se complete el proceso. Esta devolución de llamada puede ser una función gratuita, una función estática o una función miembro. También me gustaría poder inyectar algunos argumentos arbitrarios para el contexto. (es decir, implementando la coroutina de un hombre muy pobre, de alguna manera). Además de eso, esta función siempre tomará una cadena estándar std, que es la salida del proceso. No me importa si la posición de este argumento en la lista de parámetros de devolución de llamada final es fija.
Me da la sensación de que la respuesta implicará boost :: bind and boost :: function pero no puedo resolver las invocaciones precisas que serían necesarias para crear callables arbitrarios (al tiempo que se les da el currículum para que solo tomen una cuerda). Guárdelos en el proceso en segundo plano e invoque el ejecutable correctamente con el parámetro de cadena.
La forma más fácil:
class Callback
{
public:
virtual ~Callback() {}
virtual Callback* clone() const = 0;
// Better to wrap the call (logging, try/catch, etc)
void execute(const std::string& result) { this->executeImpl(result); }
protected:
// Don''t make sense to have them public
Callback() {}
Callback(const Callback&) {}
Callback& operator=(const Callback&) { return *this; }
private:
virtual void executeImpl(const std::string& result) = 0;
};
// Example
class Example: public Callback
{
public:
Example(int a, int b): Callback(), mA(a), mB(b) {}
virtual Example* clone() const { return new Example(*this); }
private:
virtual void executeImpl(const std::string& result) {}
int mA;
int mB;
};
Y luego, puede pasar la clase de devolución de llamada (por puntero / referencia) al proceso. La clase tiene un estado, según se requiera, y se puede copiar si es necesario (si no, suelte el clon).
Parece que quieres usar el patrón Observer .
La devolución de llamada debe almacenarse como boost::function<void, std::string>
. Luego puede usar boost::bind
para "convertir" cualquier otra firma de función a dicho objeto, vinculando los otros parámetros.
Ejemplo
No he intentado compilar esto, pero debería mostrar la idea general de todos modos
void DoLongOperation(boost::function<void, const std::string&> callback)
{
std::string result = DoSomeLengthyStuff();
callback(result);
}
void CompleteRoutine1(const std::string&);
void CompleteRoutine2(int param, const std::string&);
// Calling examples
DoLongOperation(&CompleteRoutine1); // Matches directly
DoLongOperation(boost::bind(&CompleteRoutine2, 7, _1)); // int parameter is bound to constant.
// This one is thanks to David Rodríguez comment below, but reformatted here:
struct S
{
void f( std::string const & );
};
int main()
{
S s;
DoLongOperation( boost::bind( &S::f, &s, _1 ) );
}