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

Design Rationale

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 32. Boost.Signals

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

Choice of Slot Definitions

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

  • Слоты происходят из определенного базового класса: Как правило, такая схема требует, чтобы все определяемые пользователем слоты выводились из определенного библиотекой абстрактного класса<Slot>, который определяет виртуальную функцию, вызывающую слот. Адапторы могут использоваться для преобразования такого определения в определение, аналогичное тому, которое используется Boost. Было обнаружено, что использование большого количества небольших классов адаптеров, содержащих виртуальные функции, вызывает неприемлемое увеличение размера исполняемых файлов (типы полиморфных классов требуют больше кода, чем неполиморфные типы).

    Этот подход имеет преимущество простоты реализации и пользовательского интерфейса с объектно-ориентированной точки зрения.

  • Слоты, построенные из набора примитивов: в этой схеме слот может иметь ограниченный набор типов (часто полученных из общего абстрактного базового класса), которые построены из некоторого набора примитивов, определяемых библиотекой, которые часто включают преобразования из указателей свободных функций и указателей функций членов и ограниченного набора возможностей связывания. Такой подход достаточно прост и охватывает большинство распространенных случаев, но он не позволяет обеспечить большую степень гибкости в конструкции слотов. Библиотеки для функциональной композиции объектов стали довольно продвинутыми, и библиотека сигналов и слотов не может включать такие улучшения. Таким образом, рост. Сигналы не включают в себя примитивы связывания аргументов или функциональной композиции объекта, а вместо этого обеспечивают крючок (через механизм<visit_each>), который позволяет существующим библиотекам связывания / композиции предоставлять необходимую информацию для Сигналов.

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

User-level Connection Management

Пользователи должны иметь точный контроль над подключением сигналов к слотам и их возможным отключением. Подход, принятый Boost. Сигналы должны возвращать объект<connection>, который позволяет подключать / отключать запрос, ручное отключение и автоматическое отключение в режиме уничтожения. Некоторые другие возможные интерфейсы включают:

  • Пропуск слота для отключения: в этой модели интерфейса отключение слота, связанного с<sig.connect(slot)>, выполняется через<sig.disconnect(slot)>. Внутри выполняется линейный поиск с использованием сравнения слотов, и слот, если он найден, удаляется из списка. К сожалению, запрос на подключение, как правило, также заканчивается линейно-временными операциями. Эта модель также терпит неудачу по причинам реализации, когда слоты становятся более сложными, чем простые указатели функций, указатели функций членов и ограниченный набор композиций и связующих аргументов: чтобы соответствовать слоту, данному в вызове<disconnect>, с существующим слотом, нам нужно было бы иметь возможность сравнивать произвольные функциональные объекты, что невозможно.

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

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

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

    • Требуется дополнительная параметризация, поскольку тип токена должен определяться пользователем. Дополнительная параметризация обостряет кривую обучения и усложняет простой интерфейс.

    Этот тип интерфейса поддерживается в Boost. Сигналы через механизм группировки слотов. Он дополняет<connection>объектно-ориентированную схему управления соединениями.

Combiner Interface

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

Формулировка Сигналов комбайнов основана на комбинаторе, использующем «тяговый» режим связи, вместо более сложного «толкающего» механизма. С механизмом «вытягивания» состояние комбинатора может храниться на стеке и в счетчике программы, потому что всякий раз, когда требуются новые данные (т.е. вызов следующего слота для извлечения его обратного значения), существует простой интерфейс для извлечения этих данных немедленно и без возвращения из кода комбинатора. Сравните это с механизмом «толчка», где комбинатор должен сохранять все состояние в классе, потому что для каждого сигнала, называемого комбинатором, будут использоваться процедуры комбинатора. Сравните, например, комбинатор, который возвращает максимальный элемент от вызова слотов. Если максимальный элемент когда-либо превышает 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» оплачивается при реализации самой библиотеки Сигналов. Чтобы правильно обрабатывать отключения слотов во время вызовов (например, когда вызывается оператор отсчета), необходимо построить итератор, чтобы пропустить отключенные слоты. Кроме того, итератор должен нести с собой набор аргументов для передачи в каждый слот (хотя достаточно ссылки на структуру, содержащую эти аргументы), и должен кэшировать результат вызова слота, чтобы множественные ссылки не приводили к нескольким вызовам. Это, по-видимому, требует большой степени накладных расходов, хотя, если рассматривать весь процесс вызова слотов, можно увидеть, что накладные расходы почти эквивалентны таковым в модели «толчка», но мы перевернули структуры управления, чтобы сделать комплекс итерации и отсчета (вместо того, чтобы делать комбинаторный комплекс определения состояния).

Connection Interfaces: += operator

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

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

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

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

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

trackable rationale

Класс 80 является основным пользовательским интерфейсом для автоматического управления временем жизни соединения, и его дизайн напрямую влияет на пользователей. Больше всего выделяются две проблемы: странное поведение копирования<trackable>и ограничение, требующее от пользователей исходить из<trackable>для создания типов, которые могут участвовать в автоматическом управлении соединением.

trackable copying behavior

Поведение копирования<trackable>по существу таково, что<trackable>подобъекты никогда не копируются; вместо этого операция копирования является просто безоперационной. Чтобы понять это, мы смотрим на природу соединения сигнал-слот и отмечаем, что соединение основано на сущностях, которые соединяются; когда одна из сущностей разрушается, соединение разрушается. Поэтому, когда субобъект скопирован<trackable>, мы не можем скопировать соединения, потому что соединения не относятся к целевой сущности - они относятся к исходной сущности. Эта причина двойственна той причине, что сигналы не копируются: слоты, подключенные к ним, подключены к этому конкретному сигналу, а не к данным, содержащимся в сигнале.

Why derivation from trackable?

Для правильной работы<trackable>есть два ограничения:

  • <trackable>должно иметь место для хранения, чтобы отслеживать все соединения, сделанные с этим объектом.

  • <trackable>должен быть уведомлен, когда объект разрушается, чтобы он мог отключить свои соединения.

Очевидно, что вывод<trackable>соответствует этим двум руководящим принципам. Мы еще не нашли лучшего решения.

Comparison with other Signal/Slot implementations

libsigc++

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

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

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

.NET delegates

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

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

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

  • Должен называть метод с<this>уже связанным.


PrevUpHomeNext

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




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



:: Главная :: Chapter 32. Boost.Signals ::


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:34:41/0.033321857452393/1