cómo funcionan boost :: function y boost :: bind

83

No me gusta tener cajas mágicas esparcidas por todo mi código ... ¿cómo funcionan exactamente estas dos clases para permitir que básicamente cualquier función se asigne a un objeto de función, incluso si la función <> tiene un parámetro completamente diferente al que estoy pasando? boost::bind

Incluso funciona con diferentes convenciones de llamada (es decir, los métodos miembro están __thiscallbajo VC, pero las funciones "normales" son generalmente __cdeclo __stdcallpara aquellas que necesitan ser compatibles con C.

Lancer de fuego
fuente
1
no realmente - esta pregunta es sobre vinculación y función
1800 INFORMACIÓN
Sí, y por lo tanto, todavía queda la pregunta de cómo se puede vincular el mapa void MyClass: DoSomething (std :: string str, int number) a boost :: function <void (int)> via bind (& MyClass :: DoSomething, instance, "Hola World ", _1)
Fire Lancer
2
20.000 visitas ¡vaca sagrada, esto debe estar en la primera página de impulso !
unixman83

Respuestas:

96

boost::functionpermite que cualquier cosa con una operator()con la firma correcta se vincule como parámetro, y el resultado de su vinculación se puede llamar con un parámetro int, por lo que se puede vincular a function<void(int)>.

Así es como funciona (esta descripción se aplica por igual std::function):

boost::bind(&klass::member, instance, 0, _1) devuelve un objeto como este

struct unspecified_type
{
  ... some members ...
  return_type operator()(int i) const { return instance->*&klass::member(0, i);
}

donde return_typey intse infieren de la firma de klass::member, y el puntero de la función y el parámetro vinculado se almacenan de hecho en el objeto, pero eso no es importante

Ahora, boost::functionno realiza ninguna verificación de tipo: tomará cualquier objeto y cualquier firma que proporciones en su parámetro de plantilla, y creará un objeto que se pueda llamar de acuerdo con tu firma y llamará al objeto. Si eso es imposible, es un error de compilación.

boost::function es en realidad un objeto como este:

template <class Sig>
class function
{
  function_impl<Sig>* f;
public:
  return_type operator()(argument_type arg0) const { return (*f)(arg0); }
};

donde el return_typey argument_typese extraen de Sig, y fse asigna dinámicamente en el montón. Eso es necesario para permitir que se unan objetos completamente no relacionados con diferentes tamaños boost::function.

function_impl es solo una clase abstracta

template <class Sig>
class function_impl
{
public:
  virtual return_type operator()(argument_type arg0) const=0;
};

La clase que hace todo el trabajo, es una clase concreta derivada de boost::function. Hay uno para cada tipo de objeto que asigneboost::function

template <class Sig, class Object>
class function_impl_concrete : public function_impl<Sig>
{
  Object o
public:
  virtual return_type operator()(argument_type arg0) const=0 { return o(arg0); }
};

Eso significa en su caso, la asignación para impulsar la función:

  1. instancia un tipo function_impl_concrete<void(int), unspecified_type>(eso es tiempo de compilación, por supuesto)
  2. crea un nuevo objeto de ese tipo en el montón
  3. asigna este objeto al miembro f de boost :: function

Cuando llama al objeto de función, llama a la función virtual de su objeto de implementación, que dirigirá la llamada a su función original.

DESCARGO DE RESPONSABILIDAD: Tenga en cuenta que los nombres en esta explicación se inventan deliberadamente. Cualquier parecido con personas o personajes reales ... lo sabes. El propósito era ilustrar los principios.

jpalecek
fuente
Entonces, ¿el contenido de la estructura unspecified_type (es decir, el código en la función operator ()) se genera básicamente a partir de los argumentos pasados ​​para boost :: bind en una base de caso por caso para permitir cualquier combinación y número de argumentos?
Fire Lancer
1
No, hay plantillas de operator () que manejan todas las aridades (y diferentes argumentos de plantilla manejan las combinaciones)
jpalecek
En el último bloque de código se lee: arg0) const=0 { return...... Nunca había visto eso antes. Encontré un ejemplo que no funcionaba en un foro donde un mensaje de seguimiento vinculado a las preguntas frecuentes de C ++ explicando que una función virtual pura podría tener un cuerpo, pero no puedo obtener ningún código para compilar usando una sintaxis como esa (clang & gcc).
Brian Vandenberg
Debo aclarar mi pregunta un poco: a un virtual puro se le puede dar un cuerpo cuando se declara =0;con el cuerpo dado más adelante (por ejemplo, void Base::Blah() { /* ... */ }) ... Estoy preguntando específicamente sobre la notación utilizada anteriormente; Supongo que es solo un error tipográfico.
Brian Vandenberg
@BrianVandenberg: Creo que se ajusta a los estándares, aunque no tiene sentido.
Mooing Duck