как работают boost :: function и boost :: bind

83

Мне не нравится, когда магические коробки разбросаны по всему моему коду ... как именно работают эти два класса, чтобы позволить сопоставить практически любую функцию с объектом функции, даже если функция <> имеет совершенно другой параметр, установленный на тот, который я передаю boost::bind

Он даже работает с различными соглашениями о вызовах (т.е. методы-члены находятся __thiscallв VC, но «обычные» функции обычно __cdeclили __stdcallдля тех, которые должны быть совместимы с C.

Fire Lancer
источник
Dupe: stackoverflow.com/questions/112738/…
Филип Экберг,
1
не совсем - это вопрос о привязке и функции
1800 ИНФОРМАЦИЯ
Да, и поэтому остается вопрос о том, как связать map void MyClass: DoSomething (std :: string str, int number) с boost :: function <void (int)> через bind (& MyClass :: DoSomething, instance, "Hello World ", _1)
Fire Lancer
2
20000 посещений святая корова, это должно быть на главной странице повышения !
unixman83

Ответы:

96

boost::functionпозволяет operator()связать что-либо с правильной подписью в качестве параметра, а результат вашей привязки может быть вызван с параметром int, поэтому он может быть привязан к function<void(int)>.

Вот как это работает (это описание одинаково применимо std::function):

boost::bind(&klass::member, instance, 0, _1) возвращает такой объект

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

где return_typeи intвыводятся из сигнатуры klass::member, а указатель функции и связанный параметр фактически хранятся в объекте, но это не важно

Теперь boost::functionне выполняет никакой проверки типов: он принимает любой объект и любую подпись, которую вы указываете в параметре шаблона, и создает объект, который может быть вызван в соответствии с вашей подписью, и вызывает этот объект. Если это невозможно, это ошибка компиляции.

boost::function на самом деле такой объект:

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

где return_typeи argument_typeизвлекаются из Sig, и fдинамически размещается в куче. Это необходимо, чтобы разрешить привязку к совершенно несвязанным объектам разного размера boost::function.

function_impl это просто абстрактный класс

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

Класс, который выполняет всю работу, является конкретным производным классом boost::function. Есть по одному для каждого типа объекта, которому вы назначаетеboost::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); }
};

Это означает, что в вашем случае назначение функции boost:

  1. создает экземпляр типа function_impl_concrete<void(int), unspecified_type>(конечно, это время компиляции)
  2. создает новый объект этого типа в куче
  3. назначает этот объект члену f функции boost ::

Когда вы вызываете объект функции, он вызывает виртуальную функцию своего объекта реализации, которая направляет вызов вашей исходной функции.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: обратите внимание, что имена в этом объяснении намеренно придуманы. Любое сходство с реальными людьми или персонажами ... вы это знаете. Целью было проиллюстрировать принципы.

jpalecek
источник
так что содержимое struct unspecified_type (то есть код в функции operator ()) в основном сгенерировано из аргументов, прошедших до boost :: bind в каждом конкретном случае, чтобы разрешить любую комбинацию и количество аргументов?
Fire Lancer
1
Нет, есть шаблоны operator (), которые обрабатывают все арности (и разные аргументы шаблона обрабатывают комбинации)
jpalecek
В последнем блоке кода написано: arg0) const=0 { return...... Я никогда такого раньше не видел. Я нашел один нефункционирующий пример на форуме, где последующее сообщение, связанное с C ++ faq, объясняет, что чистая виртуальная функция может иметь тело, но я не могу получить код для компиляции с использованием такого синтаксиса (clang & gcc).
Брайан Ванденберг
Я должен немного уточнить свой вопрос: чистому виртуальному объекту может быть присвоено тело, если оно объявлено с =0;телом, указанным позже (например, void Base::Blah() { /* ... */ }) ... Я конкретно спрашиваю об использованной выше нотации; Полагаю, это просто опечатка.
Брайан Ванденберг
@BrianVandenberg: Я думаю, что это соответствует стандартам, хотя и бессмысленно.
Mooing Duck