jak działają boost :: function i boost :: bind

83

Nie podoba mi się, gdy magiczne pola są rozrzucone po całym kodzie ... jak dokładnie te dwie klasy działają, aby w zasadzie umożliwić mapowanie dowolnej funkcji na obiekt funkcji, nawet jeśli funkcja <> ma zupełnie inny parametr ustawiony niż ten, do którego przechodzę boost::bind

Działa nawet z różnymi konwencjami wywoływania (tj. Metody składowe są __thiscallobjęte VC, ale „normalne” funkcje są generalnie __cdecllub __stdcalldla tych, które muszą być zgodne z C.

Ognisty Lancer
źródło
1
nie bardzo - to pytanie dotyczy wiązania i funkcji
1800 INFORMACJE
Tak, więc wciąż pozostaje pytanie, jak można powiązać map void MyClass: DoSomething (std :: string str, int number) z boost :: function <void (int)> via bind (& MyClass :: DoSomething, instance, "Hello World ”, _1)
Fire Lancer
2
20 000 odwiedzin Święta krowa, to musi być na pierwszej stronie boostu !
unixman83

Odpowiedzi:

96

boost::functionpozwala, aby wszystko, co ma operator()właściwą sygnaturę, zostało powiązane jako parametr, a wynik powiązania można wywołać z parametrem int, więc można go powiązać function<void(int)>.

Oto jak to działa (ten opis dotyczy również std::function):

boost::bind(&klass::member, instance, 0, _1) zwraca obiekt taki jak ten

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

gdzie return_typei intsą wywnioskowane z podpisu klass::member, a wskaźnik funkcji i powiązany parametr są w rzeczywistości przechowywane w obiekcie, ale to nie jest ważne

Teraz boost::functionnie wykonuje żadnego sprawdzenia typu: weźmie dowolny obiekt i każdy podpis, który podasz w parametrze szablonu i utworzy obiekt, który można wywołać zgodnie z Twoim podpisem i wywoła obiekt. Jeśli to niemożliwe, jest to błąd kompilacji.

boost::function to właściwie taki obiekt:

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

gdzie return_typei argument_typesą wyodrębniane z Sigi fjest dynamicznie przydzielane na stercie. Jest to konieczne, aby umożliwić powiązanie całkowicie niepowiązanych obiektów o różnych rozmiarach boost::function.

function_impl to tylko klasa abstrakcyjna

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

Klasa, która wykonuje całą pracę, jest konkretną klasą pochodną boost::function. Istnieje po jednym dla każdego typu obiektu, do którego przypisujeszboost::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); }
};

Oznacza to w twoim przypadku przypisanie do funkcji boost:

  1. tworzy instancję typu function_impl_concrete<void(int), unspecified_type>(to oczywiście czas kompilacji)
  2. tworzy nowy obiekt tego typu na stercie
  3. przypisuje ten obiekt elementowi f funkcji boost :: function

Kiedy wywołujesz obiekt funkcji, wywołuje on funkcję wirtualną swojego obiektu implementacji, która skieruje wywołanie do twojej oryginalnej funkcji.

ZRZECZENIE SIĘ: Należy pamiętać, że nazwy w tym wyjaśnieniu są celowo wymyślone. Jakiekolwiek podobieństwo do prawdziwych osób lub postaci ... wiesz o tym. Celem było zilustrowanie zasad.

jpalecek
źródło
tak więc, czy zawartość struct unspecified_type (tj. kod w funkcji operator ()) jest generowana w zasadzie z argumentów z przeszłości, aby boost :: bind na podstawie wielkości liter, aby umożliwić dowolną kombinację i liczbę argumentów?
Fire Lancer
1
Nie, istnieją szablony operatora (), które obsługują wszystkie arcje (i różne argumenty szablonów obsługują kombinacje)
jpalecek
W ostatnim bloku kodu brzmi: arg0) const=0 { return...... nigdy wcześniej tego nie widziałem. Znalazłem jeden niedziałający przykład na forum, gdzie wiadomość uzupełniająca związana z C ++ faq wyjaśniająca, że ​​czysta funkcja wirtualna może mieć ciało, ale nie mogę uzyskać żadnego kodu do kompilacji przy użyciu takiej składni (clang i gcc).
Brian Vandenberg
Powinienem nieco wyjaśnić moje pytanie: Czysty wirtualny może otrzymać ciało, gdy zostanie zadeklarowany z =0;ciałem podanym później (np. void Base::Blah() { /* ... */ }) ... Pytam konkretnie o zastosowaną powyżej notację; Domyślam się, że to tylko literówka.
Brian Vandenberg
@BrianVandenberg: Myślę, że jest to zgodne ze standardami, chociaż całkiem bezcelowe.
Mooing Duck,