Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Design Rationale

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 33. Boost.Signals2

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Design Rationale

User-level Connection Management

Пользователи должны иметь точный контроль над подключением сигналов к слотам и их возможным отключением. Основной подход, принятый Boost. Signals2 возвращает объект signals2::connection, который позволяет подключать/отключать запрос, ручное отключение и автоматическое отключение в режиме разрушения (signals2::scoped_connection). Кроме того, два других интерфейса поддерживаются методом перегрузки ::disconnect:

  • Пассовый слот для отключения : в этой модели интерфейса отключение слота, связанного с sig.connect(typeof(sig)::slot_type(slot_func)) выполняется через sig.disconnect(slot_func). Внутри выполняется линейный поиск с использованием сравнения слотов, и слот, если он найден, удаляется из списка. К сожалению, запрос на подключение заканчивается линейно-временной операцией.

  • Передайте токен для отключения : этот подход идентифицирует слоты с токеном, который легко сопоставим (например, строка), позволяя слотам быть произвольными функциональными объектами. Хотя этот подход по существу эквивалентен подходу соединения, принятому Boost. Сигналы2, возможно, более склонны к ошибкам по нескольким причинам:

    • Связи и отключения должны быть сопряжены, поэтому проблема становится похожей на проблемы, возникающие при сопряжении new и delete для динамического распределения памяти. Хотя ошибки такого рода не будут катастрофическими для реализации сигналов и слотов, их обнаружение, как правило, нетривиально.

    • Если токены не являются уникальными, два слота могут иметь одно и то же имя и быть неразличимыми. В средах, где многие соединения будут выполняться динамически, генерация имен становится дополнительной задачей для пользователя

    Этот тип интерфейса поддерживается в Boost. Сигналы2 через механизм группировки слотов и перегрузка сигнала :: отключите , который принимает аргумент типа Группа сигнала.

Automatic Connection Management

Автоматическое управление соединением в Signals2 зависит от использования boost::shared_ptr для управления временем жизни отслеживаемых объектов. Он отличается от оригинального Boost. Библиотека сигналов, которая вместо этого опиралась на вывод из класса boost::signals::trackable. Библиотека будет уведомлена об уничтожении объекта с помощью разрушителя boost::signals::trackable.

К сожалению, схема boost::signals::trackable не может быть безопасна из-за порядка разрушителя. Деструктор класса, полученного из boost::signals::trackable, всегда будет называться перед деструктором основания boost::signals::trackable class. Однако для обеспечения безопасности потока соединение между сигналом и объектом должно быть отключено до того, как объект запустит свои деструкторы. В противном случае, если объект, разрушаемый в одной нити, соединен с сигналом, одновременно вызывающим в другой нити, сигнал может вызвать частично разрушенный объект.

Мы решаем эту проблему, требуя, чтобы отслеживаемые объекты управлялись с помощью shared_ptr. Слоты сохраняют weak_ptr для каждого объекта, от которого зависит слот. Подключения к слоту отключаются, когда истекает срок действия любого из отслеживаемых weak_ptr. Кроме того, сигналы создают свои собственные временные shared_ptr для всех отслеживаемых объектов слота до вызова слота. Это гарантирует, что ни один из отслеживаемых объектов не будет уничтожен в середине вызова.

Новая схема управления подключением имеет преимущество в том, что она не навязчива. Объекты любого типа можно отслеживать с помощью схемы shared_ptr/weak_ptr. Старая схема boost::signals::trackable требует, чтобы отслеживаемые объекты были получены из базового класса trackable, что не всегда практично при взаимодействии с классами из 3-й партийной библиотеки.

optional_last_value as the Default Combiner

Комбинатор по умолчанию для Boost. Сигналы2 изменились с комбинатора last_value, используемого по умолчанию в исходном Boost. Библиотека сигналов. Это связано с тем, что last_value требует, чтобы по меньшей мере 1 слот был подключен к сигналу при его вызове (за исключением специализации last_value). В многопоточной среде, где вызовы сигналов и соединения слотов и отключения могут происходить одновременно, трудно выполнить это требование. При использовании optional_last_value не требуется подключение слотов при вызове сигнала, поскольку в этом случае комбинатор может просто вернуть пустое boost::optional.

Combiner Interface

Интерфейс Combiner был выбран для имитации вызова алгоритма в стандартной библиотеке C++. Считается, что, рассматривая результаты вызова слота как просто последовательность значений, доступных с помощью итераторов ввода, интерфейс комбинатора будет наиболее естественным для опытного программиста C++. Конкурирующий дизайн интерфейса обычно требовал, чтобы комбинаторы были построены в соответствии с интерфейсом, который будет настроен для (и ограничен) библиотеки Signals2. Хотя эти интерфейсы, как правило, обеспечивают более простую реализацию сигналов и усилителей; библиотеки слотов, комбинаторы, к сожалению, не могут быть повторно использованы (либо в других сигналах и усилителях; библиотеки слотов или в других общих алгоритмах), и кривая обучения немного покручена, чтобы изучить конкретный интерфейс комбинатора.

Формулировка комбинаторов Signals2 основана на комбинаторе, использующем режим «тяги» связи, вместо более сложного механизма «толкания». С механизмом «вытягивания» состояние комбинатора может храниться на стеке и в счетчике программы, потому что всякий раз, когда требуются новые данные (т.е. вызов следующего слота для извлечения его обратного значения), существует простой интерфейс для извлечения этих данных немедленно и без возвращения из кода комбинатора. Сравните это с механизмом «толчка», где комбинатор должен сохранять все состояние в классе, потому что для каждого сигнала, называемого комбинатором, будут использоваться процедуры комбинатора. Сравните, например, комбинатор, который возвращает максимальный элемент от вызова слотов. Если максимальный элемент когда-либо превышает 100, больше слотов не будет.

Тянуть

Толкать

struct pull_max {
  typedef int result_type;
  template<typename InputIterator>
  result_type operator()(InputIterator first,
                         InputIterator last)
  {
    if (first == last)
      throw std::runtime_error("Empty!");
    int max_value = *first++;
    while(first != last && *first <= 100) {
      if (*first > max_value)
        max_value = *first;
      ++first;
    }
    return max_value;
  }
};
struct push_max {
  typedef int result_type;
  push_max() : max_value(), got_first(false) {}
  // returns false when we want to stop
  bool operator()(int result) {
    if (result > 100)
      return false;
    if (!got_first) {
      got_first = true;
      max_value = result;
      return true;
    }
    if (result > max_value)
      max_value = result;
    return true;
  }
  int get_value() const
  {
    if (!got_first)
      throw std::runtime_error("Empty!");
    return max_value;
  }
private:
  int  max_value;
  bool got_first;
};

В этих примерах следует отметить несколько моментов. Версия «pull» является многоразовым функциональным объектом, который основан на последовательности итератора ввода с целым числом value_type и очень прост в дизайне. Модель «push», с другой стороны, опирается на интерфейс, специфичный для абонента, и обычно не используется повторно. Это также требует дополнительных значений состояния, чтобы определить, например, были ли получены какие-либо элементы. Хотя качество кода и простота использования, как правило, субъективны, модель «вытягивания» явно короче и более многоразовая и часто будет рассматриваться как более простая в написании и понимании, даже вне контекста библиотеки сигналов и усилителей.

Стоимость интерфейса комбинатора «pull» оплачивается при реализации самой библиотеки Signals2. Чтобы правильно обрабатывать отключения слотов во время вызовов (например, когда вызывается оператор отсчета), необходимо построить итератор, чтобы пропустить отключенные слоты. Кроме того, итератор должен нести с собой набор аргументов для передачи в каждый слот (хотя достаточно ссылки на структуру, содержащую эти аргументы), и должен кэшировать результат вызова слота, чтобы множественные ссылки не приводили к нескольким вызовам. Это, по-видимому, требует большой степени накладных расходов, хотя, если рассматривать весь процесс вызова слотов, можно увидеть, что накладные расходы почти эквивалентны таковым в модели «толчка», но мы перевернули структуры управления, чтобы сделать комплекс итерации и отсчета (вместо того, чтобы делать комбинаторный комплекс определения состояния).

Connection Interfaces: += operator

Boost.Signals2 поддерживает синтаксис соединения с формой sig.connect(slot), но предложен более строгий синтаксис sig += slot (и использовался другими сигналами & реализациями слотов). Существует несколько причин, по которым этот синтаксис был отклонен:

  • Необязательно: синтаксис соединения, предоставляемый Boost. Сигналы2 не менее мощны, чем сигналы, подаваемые оператором +=. Экономия при наборе текста (connect() против +=) по существу незначительна. Кроме того, можно утверждать, что вызов connect() более читаем, чем перегрузка +=.

  • Двусмысленный тип возврата : существует двусмысленность относительно значения возврата операции +=: должна ли она быть ссылкой на сам сигнал, чтобы включить sig += slot1 += slot2, или должна ли она возвращать signals2::connection для вновь созданного соединения сигнал/слот?

  • Переход к операторам -=, +: при добавлении оператора соединения += кажется естественным наличие оператора отключения -=. Однако это создает проблемы, когда библиотека позволяет произвольным функциональным объектам неявно становиться слотами, потому что слоты больше не сопоставимы.

    Вторым очевидным дополнением, когда у вас есть оператор +=, будет добавление оператора + , который поддерживает добавление нескольких слотов с последующим назначением сигнала. Однако для этого потребуется реализовать + так, чтобы он мог принимать любые два функциональных объекта, что технически невозможно.

Signals2 Mutex Classes

Библиотека Boost.Signals2 предоставляет 2 класса mutex: boost::signals2::mutex и boost::signals2::dummy_mutex. Мотивация для предоставления boost::signals2::mutex заключается просто в том, что класс boost::mutex предоставлен Boost. В настоящее время библиотека Thread требует ссылки на libboost_thread. Класс boost::signals2::mutex позволяет Signals2 оставаться библиотекой только для заголовков. При желании вы можете использовать boost::mutex, указав его в качестве шаблона Mutex для ваших сигналов.

Класс boost::signals2::dummy_mutex позволяет чувствительным к производительности однопоточным приложениям минимизировать накладные расходы, избегая ненужной блокировки mutex.

Comparison with other Signal/Slot implementations

libsigc++

libsigc++ представляет собой C++-сигналы и усилитель; библиотека слотов, которая первоначально начиналась как часть инициативы по обертыванию интерфейсов C в библиотеки GTK на C++ и выросла в отдельную библиотеку, поддерживаемую Карлом Нельсоном. Существует много сходств между libsigc++ и Boost.Signals2. Сигналы находились под сильным влиянием Карла Нельсона и libsigc++. Беглый осмотр каждой библиотеки позволит найти похожий синтаксис для построения сигналов и использования соединений. Существуют некоторые различия в дизайне, которые разделяют эти библиотеки:

  • Определения слотов: слоты в libsigc++ создаются с использованием набора примитивов, определенных библиотекой. Эти примитивы допускают связывание объектов (как части библиотеки), явную адаптацию от аргумента и типов возврата сигнала к аргументу и типам возврата слота (libsigc++ по умолчанию более строг в отношении типов, чем Boost.Signals2).

  • Интерфейс комбинатора/маршаллера: эквивалент Boost. Комбинаторами Signals2 в libsigc++ являются маршалы. Маршаллеры похожи на интерфейс «push», описанный в Combiner Interface, и там дается правильная трактовка темы.

.NET delegates

Microsoft ввела . NET Framework и связанный с ним набор языков и языковых расширений, одним из которых является делегат. Делегаты похожи на сигналы и слоты, но они более ограничены, чем большинство сигналов C++ и слотов в том, что они:

  • Требуется точное соответствие типа между делегатом и тем, что он называет.

  • Возврат только результата последней цели без возможности настройки.

  • Можно назвать метод с , что уже связано.

Последние изменения: 12 июня 2007 года в 14:01:23 -0400


PrevUpHomeNext

Статья Design Rationale раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 33. Boost.Signals2 может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 33. Boost.Signals2 ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:35:46/0.010494947433472/0