como boost :: function e boost :: bind funcionam

83

Eu não gosto de ter caixas mágicas espalhadas por todo o meu código ... como exatamente essas duas classes funcionam para permitir que basicamente qualquer função seja mapeada para um objeto de função, mesmo se a função <> tiver um parâmetro completamente diferente definido para aquele que estou passando boost::bind

Ele ainda funciona com diferentes convenções de chamada (ou seja, os métodos de membro estão __thiscallsob VC, mas as funções "normais" são geralmente __cdeclou __stdcallpara aqueles que precisam ser compatíveis com C.

Fire Lancer
fonte
1
não realmente - esta pergunta é sobre vinculação e função
1800 INFORMAÇÕES
Sim e, portanto, ainda resta a questão de como vincular o mapa void MyClass: DoSomething (std :: string str, int number) para impulsionar :: function <void (int)> via bind (& MyClass :: DoSomething, instance, "Hello World ", _1)
Fire Lancer
2
20.000 visitas, vaca sagrada, isso precisa estar na primeira página do boost
unixman83

Respostas:

96

boost::functionpermite que qualquer coisa com um operator()com a assinatura correta seja vinculado como parâmetro, e o resultado de sua vinculação pode ser chamado com um parâmetro int, para que possa ser vinculado function<void(int)>.

É assim que funciona (esta descrição aplica-se igualmente a std::function):

boost::bind(&klass::member, instance, 0, _1) retorna um objeto como este

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

em que return_typee intsão inferidos da assinatura de klass::member, e o ponteiro de função e o parâmetro vinculado são de fato armazenados no objeto, mas isso não é importante

Agora, boost::functionnão faz nenhuma verificação de tipo: ele pegará qualquer objeto e qualquer assinatura que você fornecer em seu parâmetro de modelo e criará um objeto que pode ser chamado de acordo com sua assinatura e chamará o objeto. Se isso for impossível, é um erro de compilação.

boost::function é na verdade um objeto como este:

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

de onde return_typee argument_typesão extraídos Sige fsão alocados dinamicamente no heap. Isso é necessário para permitir que objetos completamente não relacionados com tamanhos diferentes sejam vinculados boost::function.

function_impl é apenas uma classe abstrata

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

A classe que faz todo o trabalho é uma classe concreta derivada de boost::function. Existe um para cada tipo de objeto que você atribuiboost::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); }
};

Isso significa, no seu caso, a atribuição para impulsionar a função:

  1. instancia um tipo function_impl_concrete<void(int), unspecified_type>(que é o tempo de compilação, é claro)
  2. cria um novo objeto desse tipo na pilha
  3. atribui este objeto ao membro f de boost :: function

Quando você chama o objeto de função, ele chama a função virtual de seu objeto de implementação, que direcionará a chamada para sua função original.

AVISO LEGAL: Observe que os nomes nesta explicação são deliberadamente inventados. Qualquer semelhança com pessoas ou personagens reais ... você sabe disso. O objetivo era ilustrar os princípios.

jpalecek
fonte
então é o conteúdo de struct unspecified_type (ou seja, o código na função operator ()) basicamente gerado a partir dos argumentos passados ​​para boost :: bind em uma base de caso a caso para permitir qualquer combinação e número de argumentos?
Fire Lancer
1
Não, existem modelos de operator () que lidam com todas as aridades (e diferentes argumentos de modelo lidam com as combinações)
jpalecek
No último bloco de código está escrito: arg0) const=0 { return...... Eu nunca vi isso antes. Eu encontrei um exemplo que não funciona em um fórum onde uma mensagem de acompanhamento vinculada ao faq C ++ explicando que uma função virtual pura poderia ter um corpo, mas não consigo compilar nenhum código usando uma sintaxe como essa (clang & gcc).
Brian Vandenberg
Devo esclarecer minha pergunta um pouco: Um virtual puro pode receber um corpo quando declarado =0;com o corpo dado posteriormente (por exemplo, void Base::Blah() { /* ... */ }) ... Estou perguntando especificamente sobre a notação usada acima; Suponho que seja apenas um erro de digitação.
Brian Vandenberg,
@BrianVandenberg: Acho que está de acordo com os padrões, embora seja inútil.
Mooing Duck de