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

Tutorial

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

Tutorial

How to Read this Tutorial

Этот учебник не предназначен для чтения линейно. Его структура верхнего уровня примерно разделяет различные концепции в библиотеке (например, обработка вызова нескольких слотов, передача значений в слоты и из слотов), и в каждой из этих концепций сначала представлены основные идеи, а затем описаны более сложные применения библиотеки. Каждый из этих разделов отмечен.,ПромежуточныйилиПродвинутый, чтобы помочь читателю.Начинающийразделы включают информацию, которую должны знать все пользователи библиотеки; можно эффективно использовать библиотеку Signals2 после прочтения толькоНачинающийразделы.ПромежуточныйСекции строятся наНачинающийразделы с немного более сложным использованием библиотеки. И, наконец,AdvancedВ разделах подробно описаны очень продвинутые способы использования библиотеки Сигналов2, которые часто требуют основательных рабочих знаний.НачинающийиПромежуточныйтемы; большинству пользователей не нужно читатьПродвинутыйразделы.

Hello, World! (Beginner)

Следующий пример пишет «Привет, мир!» с помощью сигналов и слотов. Во-первых, мы создаем сигнал<sig>, сигнал, который не принимает аргументов и имеет обратное значение пустоты. Далее мы подключаем объект функции<hello>к сигналу с помощью метода<connect>. Наконец, используйте сигнал<sig>как функцию для вызова слотов, которая по очереди вызывает<HelloWorld::operator()>для печати «Привет, Мир!».

struct HelloWorld
{
  void operator()() const
  {
    std::cout << "Hello, World!" << std::endl;
  }
};
  // Signal with no arguments and a void return value
  boost::signals2::signal<void ()> sig;
  // Connect a HelloWorld slot
  HelloWorld hello;
  sig.connect(hello);
  // Call all of the slots
  sig();

Calling Multiple Slots

Connecting Multiple Slots (Beginner)

Вызов одного слота из сигнала не очень интересен, поэтому мы можем сделать программу Hello, World более интересной, разделив работу по печати «Hello, World!» на два совершенно отдельных слота. Первый слот напечатает «Привет» и может выглядеть так:

struct Hello
{
  void operator()() const
  {
    std::cout << "Hello";
  }
};

Второй слот распечатает «Мир!» и новую линию, чтобы завершить программу. Второй слот может выглядеть так:

struct World
{
  void operator()() const
  {
    std::cout << ", World!" << std::endl;
  }
};

Как и в нашем предыдущем примере, мы можем создать сигнал<sig>, который не принимает аргументов и имеет<void>возвращаемое значение. На этот раз мы подключаем оба слота<hello>и<world>к одному и тому же сигналу, и когда мы вызовем сигнал, оба слота будут вызваны.

  boost::signals2::signal<void ()> sig;
  sig.connect(Hello());
  sig.connect(World());
  sig();

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

Hello, World!

Ordering Slot Call Groups (Intermediate)

Слоты могут иметь побочные эффекты, и это может означать, что некоторые слоты придется вызывать раньше других, даже если они не подключены в этом порядке. Начало. Библиотека Signals2 позволяет размещать слоты в группах, которые каким-то образом упорядочены. Для нашей программы «Здравствуй, Мир» мы хотим, чтобы «Здравствуй» было напечатано перед «Миром!», поэтому мы помещаем «Здравствуй» в группу, которая должна быть исполнена перед группой, в которой находится «Мир!». Для этого мы можем предоставить дополнительный параметр в начале вызова<connect>, который определяет группу. Значения группы по умолчанию<int>s и упорядочены целым числом< отношением. Вот как мы строим Hello, World:

  boost::signals2::signal<void ()> sig;
  sig.connect(1, World());  // connect with group 1
  sig.connect(0, Hello());  // connect with group 0

При вызове сигнала будет правильно напечатано «Привет, Мир!», поскольку объект<Hello>находится в группе 0, которая предшествует группе 1, где находится объект<World>. Групповой параметр, по сути, необязателен. Мы пропустили его в первом примере Hello, World, потому что это было ненужно, когда все слоты независимы. Так что же произойдет, если мы смешаем вызовы для подключения, которые используют групповой параметр, и те, которые этого не делают? «Неназванные» слоты (то есть те, которые были подключены без указания названия группы) могут быть размещены в передней или задней части списка слотов (путем прохождения<boost::signals2::at_front>или<boost::signals2::at_back>в качестве последнего параметра к<connect>соответственно), и по умолчанию до конца списка. Когда группа определена, конечный<at_front>или<at_back>параметр описывает, где слот будет размещен в групповом порядке. Негруппированные слоты, связанные с<at_front>, всегда предшествуют всем слотам. Негруппированные слоты, связанные с<at_back>, всегда будут иметь успех.

Если мы добавим новый слот к нашему примеру:

struct GoodMorning
{
  void operator()() const
  {
    std::cout << "... and good morning!" << std::endl;
  }
};
  // by default slots are connected at the end of the slot list
  sig.connect(GoodMorning());
  // slots are invoked this order:
  // 1) ungrouped slots connected with boost::signals2::at_front
  // 2) grouped slots according to ordering of their groups
  // 3) ungrouped slots connected with boost::signals2::at_back
  sig();

Мы получим результат, которого хотели:

Hello, World!
... and good morning!

Passing Values to and from Slots

Slot Arguments (Beginner)

Сигналы могут распространять аргументы на каждый из слотов, которые они называют. Например, сигнал, который распространяет события движения мыши, может захотеть пройти по новым координатам мыши и нажать кнопки мыши.

Например, мы создадим сигнал, который передаст два<float>аргумента в его слоты. Затем мы создадим несколько слотов, которые печатают результаты различных арифметических операций на этих значениях.

void print_args(float x, float y)
{
  std::cout << "The arguments are " << x << " and " << y << std::endl;
}
void print_sum(float x, float y)
{
  std::cout << "The sum is " << x + y << std::endl;
}
void print_product(float x, float y)
{
  std::cout << "The product is " << x * y << std::endl;
}
void print_difference(float x, float y)
{
  std::cout << "The difference is " << x - y << std::endl;
}
void print_quotient(float x, float y)
{
  std::cout << "The quotient is " << x / y << std::endl;
}
  boost::signals2::signal<void (float, float)> sig;
  sig.connect(&print_args);
  sig.connect(&print_sum);
  sig.connect(&print_product);
  sig.connect(&print_difference);
  sig.connect(&print_quotient);
  sig(5., 3.);

Эта программа распечатает следующее:

The arguments are 5 and 3
The sum is 8
The product is 15
The difference is 2
The quotient is 1.66667

Таким образом, любые значения, которые даны<sig>, когда она называется функцией, передаются в каждый из слотов. Мы должны заранее объявлять типы этих значений, когда создаем сигнал. Тип<boost::signals2::signal<void (float, float)>>означает, что сигнал имеет<void>возвращаемое значение и принимает два<float>значения. Поэтому любой слот, подключенный к<sig>, должен иметь возможность принимать два<float>значения.

Signal Return Values (Advanced)

Так же, как слоты могут получать аргументы, они также могут возвращать значения. Затем эти значения могут быть возвращены обратно вызывающему сигналу через комбинатор. Комбинатор - это механизм, который может принимать результаты слотов вызова (возможно, нет результатов или сотни; мы не знаем, пока программа не запустится) и объединяет их в один результат, который будет возвращен абоненту. Единый результат часто является простой функцией результатов вызовов слота: результатом последнего вызова слота, максимальным значением, возвращаемым любым слотом, или контейнером всех результатов являются некоторые возможности.

Мы можем немного изменить наш предыдущий пример арифметических операций, чтобы все слоты возвращали результаты вычисления продукта, коэффициента, суммы или разницы. Затем сам сигнал может вернуть значение, основанное на этих результатах, для печати:

float product(float x, float y) { return x * y; }
float quotient(float x, float y) { return x / y; }
float sum(float x, float y) { return x + y; }
float difference(float x, float y) { return x - y; }
boost::signals2::signal<float (float, float)> sig;
  sig.connect(&product);
  sig.connect(&quotient);
  sig.connect(&sum);
  sig.connect(&difference);
  // The default combiner returns a boost::optional containing the return
  // value of the last slot in the slot list, in this case the
  // difference function.
  std::cout << *sig(5, 3) << std::endl;

Этот пример программы выведет<2>. Это связано с тем, что по умолчанию поведение сигнала, который имеет тип возврата<float>, первый аргумент шаблона, приведенный к шаблону класса<boost::signals2::signal>, состоит в том, чтобы вызвать все слоты, а затем вернуть<boost::optional>, содержащий результат, возвращенный последним слотом. Это поведение, по общему признанию, глупо для этого примера, потому что слоты не имеют побочных эффектов, и в результате последний слот подключен.

Более интересным результатом сигнала будет максимум значений, возвращаемых любым слотом. Для этого мы создаем пользовательский комбинатор, который выглядит следующим образом:

// combiner which returns the maximum value returned by all slots
template<typename T>
struct maximum
{
  typedef T result_type;
  template<typename InputIterator>
  T operator()(InputIterator first, InputIterator last) const
  {
    // If there are no slots to call, just return the
    // default-constructed value
    if(first == last ) return T();
    T max_value = *first++;
    while (first != last) {
      if (max_value < *first)
        max_value = *first;
      ++first;
    }
    return max_value;
  }
};

Шаблон класса<maximum>действует как объект функции. Его тип результата определяется параметром шаблона, и это тип, на котором он рассчитывает вычислить максимум на основе (например,<maximum<float>>найдет максимум<float>в последовательности<float>с). Когда объект<maximum>вызывается, ему дается последовательность итератора ввода<[first, last)>, которая включает результаты вызова всех слотов.<maximum>использует эту последовательность входного итератора для вычисления максимального элемента и возвращает это максимальное значение.

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

boost::signals2::signal<float (float x, float y),
              maximum<float> > sig;

Теперь мы можем подключить слоты, которые выполняют арифметические функции и используют сигнал:

  sig.connect(&product);
  sig.connect(&quotient);
  sig.connect(&sum);
  sig.connect(&difference);
  // Outputs the maximum value returned by the connected slots, in this case
  // 15 from the product function.
  std::cout << "maximum: " << sig(5, 3) << std::endl;

Выход этой программы будет<15>, потому что независимо от порядка, в котором соединены слоты, произведение 5 и 3 будет больше коэффициента, суммы или разницы.

В других случаях мы можем вернуть все значения, вычисленные слотами, в одну большую структуру данных. Это легко сделать с помощью другого комбинатора:

// aggregate_values is a combiner which places all the values returned
// from slots into a container
template<typename Container>
struct aggregate_values
{
  typedef Container result_type;
  template<typename InputIterator>
  Container operator()(InputIterator first, InputIterator last) const
  {
    Container values;
    while(first != last) {
      values.push_back(*first);
      ++first;
    }
    return values;
  }
};

Опять же, мы можем создать сигнал с этим новым комбинатором:

boost::signals2::signal<float (float, float),
    aggregate_values<std::vector<float> > > sig;
  sig.connect(&quotient);
  sig.connect(&product);
  sig.connect(&sum);
  sig.connect(&difference);
  std::vector<float> results = sig(5, 3);
  std::cout << "aggregate values: ";
  std::copy(results.begin(), results.end(),
    std::ostream_iterator<float>(std::cout, " "));
  std::cout << "\n";

Выход этой программы будет содержать 15, 8, 1,6667 и 2. Интересно, что первый шаблонный аргумент для класса<signal>,<float>, на самом деле не является ответным типом сигнала. Вместо этого это тип возврата, используемый подключенными слотами, а также<value_type>входных итераторов, передаваемых комбинатору. Комбинатор сам по себе является функциональным объектом, и его<result_type>тип элемента становится возвратным типом сигнала.

Итераторы ввода, переданные комбинатору, преобразуют операции отсчета в вызовы слотов. Таким образом, комбинаторы могут использовать только некоторые слоты до тех пор, пока не будет выполнен определенный критерий. Например, в распределенной вычислительной системе комбинатор может задать каждой удаленной системе, будет ли она обрабатывать запрос. Только одна удаленная система должна обрабатывать конкретный запрос, поэтому после того, как удаленная система принимает работу, мы не хотим просить другие удаленные системы выполнить ту же задачу. Такой комбинатор должен только проверять значение, возвращенное при отмене ссылки на итератор, и возвращать, когда значение приемлемо. Следующий комбинатор возвращает первый указатель non-NULL в структуру данных<FulfilledRequest>, не запрашивая последующие слоты для выполнения запроса:

struct DistributeRequest {
  typedef FulfilledRequest* result_type;
  template<typename InputIterator>
  result_type operator()(InputIterator first, InputIterator last) const
  {
    while (first != last) {
      if (result_type fulfilled = *first)
        return fulfilled;
      ++first;
    }
    return 0;
  }
};

Connection Management

Disconnecting Slots (Beginner)

Слоты не должны существовать бесконечно после их подключения. Часто слоты используются только для получения нескольких событий, а затем отключаются, и программисту требуется контроль, чтобы решить, когда слот больше не должен быть подключен.

Точка входа для управления соединениями явно является классом<boost::signals2::connection>. Класс<connection>однозначно представляет связь между конкретным сигналом и конкретным слотом. Метод<connected()>проверяет, подключены ли сигнал и слот, и метод<disconnect()>отключает сигнал и слот, если они подключены до того, как он будет вызван. Каждый вызов к способу сигнала<connect()>возвращает объект соединения, который может быть использован для определения, существует ли соединение по-прежнему или для отключения сигнала и слота.

  boost::signals2::connection c = sig.connect(HelloWorld());
  std::cout << "c is connected\n";
  sig(); // Prints "Hello, World!"
  c.disconnect(); // Disconnect the HelloWorld object
  std::cout << "c is disconnected\n";
  sig(); // Does nothing: there are no connected slots

Blocking Slots (Beginner)

Слоты могут быть временно «заблокированы», что означает, что они будут игнорироваться при вызове сигнала, но не будут постоянно отключены. Это обычно используется для предотвращения бесконечной рекурсии в тех случаях, когда в противном случае запуск слота приведет к повторному вызову сигнала, к которому он подключен. Объект<boost::signals2::shared_connection_block>временно блокирует слот. Соединение разблокируется путем уничтожения или вызова<unblock>на все<shared_connection_block>объекты, которые ссылаются на соединение. Вот пример блокировки/разблокировки слотов:

  boost::signals2::connection c = sig.connect(HelloWorld());
  std::cout << "c is not blocked.\n";
  sig(); // Prints "Hello, World!"
  {
    boost::signals2::shared_connection_block block(c); // block the slot
    std::cout << "c is blocked.\n";
    sig(); // No output: the slot is blocked
  } // shared_connection_block going out of scope unblocks the slot
  std::cout << "c is not blocked.\n";
  sig(); // Prints "Hello, World!"}

Scoped Connections (Intermediate)

Класс<boost::signals2::scoped_connection>ссылается на соединение сигнал/слот, которое будет отключено, когда класс<scoped_connection>выходит из области действия. Эта способность полезна, когда соединение должно быть временным.

  {
    boost::signals2::scoped_connection c(sig.connect(ShortLived()));
    sig(); // will call ShortLived function object
  } // scoped_connection goes out of scope and disconnects
  sig(); // ShortLived function object no longer connected to sig

Обратите внимание, что попытки инициализации соединения scoped_ с синтаксисом присваивания будут неудачными из-за его некопируемости. Будет работать либо явный синтаксис инициализации, либо конструкция по умолчанию с последующим назначением из<signals2::connection>:

// doesn't compile due to compiler attempting to copy a temporary scoped_connection object
// boost::signals2::scoped_connection c0 = sig.connect(ShortLived());
// okay
boost::signals2::scoped_connection c1(sig.connect(ShortLived()));
// also okay
boost::signals2::scoped_connection c2;
c2 = sig.connect(ShortLived());

Disconnecting Equivalent Slots (Intermediate)

Можно отсоединить слоты, эквивалентные данному функциональному объекту, используя форму метода<signal::disconnect>, если тип функционального объекта имеет доступного оператора<==>. Например:

void foo() { std::cout << "foo"; }
void bar() { std::cout << "bar\n"; }
boost::signals2::signal<void ()> sig;
  sig.connect(&foo);
  sig.connect(&bar);
  sig();
  // disconnects foo, but not bar
  sig.disconnect(&foo);
  sig();

Automatic Connection Management (Intermediate)

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

class NewsItem { /* ... */ };
typedef boost::signals2::signal<void (const NewsItem&)> signal_type;
signal_type deliverNews;

Клиенты, желающие получать обновления новостей, должны подключать к сигналу<deliverNews>только функциональный объект, который может принимать новости. Например, у нас может быть специальная область сообщений в нашем приложении специально для новостей, например:

struct NewsMessageArea : public MessageArea
{
public:
  // ...
  void displayNews(const NewsItem& news) const
  {
    messageText = news.text();
    update();
  }
};
// ...
NewsMessageArea *newsMessageArea = new NewsMessageArea(/* ... */);
// ...
deliverNews.connect(boost::bind(&NewsMessageArea::displayNews,
  newsMessageArea, _1));

Но что, если пользователь закроет область новостных сообщений, уничтожив объект<newsMessageArea>, о котором<deliverNews>знает? Скорее всего, произойдет сбой сегментации. Тем не менее, с Boost. Сигналы2 можно отслеживать любой объект, который управляется общим_ptr, используя<slot::track>. Слот автоматически отключается, когда истекает срок действия любого из отслеживаемых объектов. Кроме того, рост. Signals2 гарантирует, что ни один отслеживаемый объект не истекает, пока слот, с которым он связан, находится в середине исполнения. Он делает это, создавая временные общие копии отслеживаемых объектов слота перед его выполнением. Для отслеживания<NewsMessageArea>мы используем файл shared_ptr для управления временем его существования и передаем файл shared_ptr в слот с помощью метода<slot::track>перед его подключением, например:

// ...
boost::shared_ptr<NewsMessageArea> newsMessageArea(new NewsMessageArea(/* ... */));
// ...
deliverNews.connect(signal_type::slot_type(&NewsMessageArea::displayNews,
  newsMessageArea.get(), _1).track(newsMessageArea));

Обратите внимание, что в приведенном выше примере нет явного вызова для связывания (). Если конструктор<signals2::slot>передается более чем одному аргументу, он автоматически передает все аргументы<bind>и использует возвращаемый объект функции.

Также обратите внимание, что мы передаем обычный указатель в качестве второго аргумента конструктору слота, используя<newsMessageArea.get()>вместо передачи самого<shared_ptr>. Если бы мы прошли сам<newsMessageArea>, копия<shared_ptr>была бы связана с функцией слота, предотвращая истечение<shared_ptr>. Однако использование<slot::track>подразумевает, что мы хотим разрешить исчезновение отслеживаемого объекта и автоматически отключить соединение, когда это произойдет.

<shared_ptr>классы, отличные от<boost::shared_ptr>(например,<std::shared_ptr>), также могут отслеживаться для целей управления соединением. Они поддерживаются методом<slot::track_foreign>.

Postconstructors and Predestructors (Advanced)

Одним из ограничений использования<shared_ptr>для отслеживания является то, что объект не может настроить отслеживание себя в своем конструкторе. Однако можно настроить отслеживание в постконструкторе, который называется после того, как объект был создан и передан<shared_ptr>. Начало. Библиотека Signals2 обеспечивает поддержку пост-конструкторов и пре-деструкторов через заводскую функцию<deconstruct()>.

В большинстве случаев самый простой и надежный способ настроить постконструкторы для класса — это определить связанную<adl_postconstruct>функцию, которую можно найти<deconstruct()>, сделать конструкторов класса частными и предоставить<deconstruct>доступ к частным конструкторам, объявив<deconstruct_access>другом. Это гарантирует, что объекты класса могут быть созданы только посредством функции<deconstruct()>, и их связанная функция<adl_postconstruct()>всегда будет называться.

Разделпримеровсодержит несколько примеров определения классов с помощью постконструкторов и предеструкторов и создания объектов этих классов с использованием<deconstruct()>.

Имейте в виду, что поддержка постконструктора / предразрушителя в Boost. Сигналы2 никоим образом не важны для использования библиотеки. Использование<deconstruct>является чисто факультативным. Одним из вариантов является определение статических заводских функций для ваших классов. Заводская функция может создать объект, передать право собственности на объект<shared_ptr>, настроить отслеживание объекта, а затем вернуть<shared_ptr>.

When Can Disconnections Occur? (Intermediate)

Отключение сигнала/слота происходит при любом из этих условий:

  • Соединение явно отсоединяется методом<disconnect>соединения непосредственно или косвенно методом<disconnect>сигнала или<scoped_connection>деструктора

    .
  • Объект, отслеживаемый слотом, разрушается.

  • Сигнал уничтожен.

Эти события могут произойти в любое время без нарушения последовательности вызова сигнала. Если соединение сигнал/слот отключено в любое время во время последовательности вызова сигнала, последовательность вызова все еще будет продолжаться, но не вызовет отключенный слот. Кроме того, сигнал может быть уничтожен, пока он находится в последовательности вызовов, и в каком случае он завершит свою последовательность вызовов слота, но к ней нельзя получить прямой доступ.

Сигналы могут вызываться рекурсивно (например, сигнал A вызывает слот B, который вызывает сигнал A...). Поведение отключения не изменяется в рекурсивном случае, за исключением того, что последовательность вызова слота включает вызовы слота для всех вложенных вызовов сигнала.

Обратите внимание, что даже после того, как соединение отключено, связанный с ним слот все еще может находиться в процессе выполнения. Другими словами, отключение не блокирует ожидание завершения выполнения связанного с соединением слота. Эта ситуация может возникнуть в многопоточной среде, если отключение происходит одновременно с вызовом сигнала, или в однопоточной среде, если слот отключается.

Passing Slots (Intermediate)

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

// a pretend GUI button
class Button
{
  typedef boost::signals2::signal<void (int x, int y)> OnClick;
public:
  typedef OnClick::slot_type OnClickSlotType;
  // forward slots through Button interface to its private signal
  boost::signals2::connection doOnClick(const OnClickSlotType & slot);
  // simulate user clicking on GUI button at coordinates 52, 38
  void simulateClick();
private:
  OnClick onClick;
};
boost::signals2::connection Button::doOnClick(const OnClickSlotType & slot)
{
  return onClick.connect(slot);
}
void Button::simulateClick()
{
  onClick(52, 38);
}
void printCoordinates(long x, long y)
{
  std::cout << "(" << x << ", " << y << ")\n";
}
  Button button;
  button.doOnClick(&printCoordinates);
  button.simulateClick();

Способ<doOnClick>теперь функционально эквивалентен<connect>способу<onClick>сигнала, но детали метода<doOnClick>могут быть скрыты в файле деталей реализации.

Example: Document-View

Сигналы могут использоваться для реализации гибких архитектур Document-View. Документ будет содержать сигнал, к которому может подключиться каждая из точек зрения. Следующий класс<Document>определяет простой текстовый документ, который поддерживает нечеткие представления. Обратите внимание, что он хранит один сигнал, к которому будут подключены все виды.

class Document
{
public:
    typedef boost::signals2::signal<void ()>  signal_t;
public:
    Document()
    {}
    /* Connect a slot to the signal which will be emitted whenever
      text is appended to the document. */
    boost::signals2::connection connect(const signal_t::slot_type &subscriber)
    {
        return m_sig.connect(subscriber);
    }
    void append(const char* s)
    {
        m_text += s;
        m_sig();
    }
    const std::string& getText() const
    {
        return m_text;
    }
private:
    signal_t    m_sig;
    std::string m_text;
};

Далее можно начать определять взгляды. Следующий<TextView>класс обеспечивает простое представление текста документа.

class TextView
{
public:
    TextView(Document& doc): m_document(doc)
    {
        m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
    }
    ~TextView()
    {
        m_connection.disconnect();
    }
    void refresh() const
    {
        std::cout << "TextView: " << m_document.getText() << std::endl;
    }
private:
    Document&               m_document;
    boost::signals2::connection  m_connection;
};

В качестве альтернативы мы можем предоставить представление документа, переведенного в шестнадцатеричные значения, используя<HexView>вид:

class HexView
{
public:
    HexView(Document& doc): m_document(doc)
    {
        m_connection = m_document.connect(boost::bind(&HexView::refresh, this));
    }
    ~HexView()
    {
        m_connection.disconnect();
    }
    void refresh() const
    {
        const std::string&  s = m_document.getText();
        std::cout << "HexView:";
        for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
            std::cout << ' ' << std::hex << static_cast<int>(*it);
        std::cout << std::endl;
    }
private:
    Document&               m_document;
    boost::signals2::connection  m_connection;
};

Чтобы связать пример вместе, вот простая функция<main>, которая устанавливает два представления, а затем изменяет документ:

int main(int argc, char* argv[])
{
    Document    doc;
    TextView    v1(doc);
    HexView     v2(doc);
    doc.append(argc == 2 ? argv[1] : "Hello world!");
    return 0;
}

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

Giving a Slot Access to its Connection (Advanced)

Вы можете столкнуться с ситуациями, когда вы хотите отключить или заблокировать соединение слота внутри самого слота. Например, предположим, что у вас есть группа асинхронных задач, каждая из которых испускает сигнал, когда он завершается. Вы хотите подключить слот ко всем задачам, чтобы получить их результаты по мере их завершения. Как только заданная задача завершена и слот запущен, слот больше не должен быть подключен к завершенной задаче. Поэтому вы можете пожелать очистить старые соединения, отключив слот, когда он работает.

Чтобы слот отключал (или блокировал) свое вызывающее соединение, он должен иметь доступ к объекту<signals2::connection>, который ссылается на вызывающее соединение сигнал-слот. Сложность заключается в том, что объект<connection>возвращается методом<signal::connect>и поэтому недоступен до тех пор, пока слот не будет уже подключен к сигналу. Это может быть особенно проблематично в многопоточной среде, где сигнал может одновременно вызываться другой нитью, пока слот подключен.

Следовательно, классы сигналов обеспечивают<signal::connect_extended>методы, которые позволяют подключать к сигналу слоты, которые принимают дополнительный аргумент. Дополнительным аргументом является<signals2::connection>объект, который относится к соединению сигнал-слот, в настоящее время вызывающему слот.<signal::connect_extended>использует слоты типа, заданного<signal::extended_slot_type>typedef.

Раздел примеров включает в себя программуextended_slot, которая демонстрирует синтаксис для использования<signal::connect_extended>.

Changing the Mutex Type of a Signal (Advanced).

В большинстве случаев тип по умолчанию<boost::signals2::mutex>для<signals2::signal><Mutex>шаблонного параметра типа должен быть хорошим. Если вы хотите использовать альтернативный тип mutex, он должен быть конструктивным по умолчанию и соответствовать концепции<Lockable>, определенной Boost. Библиотека ниток. То есть он должен иметь<lock()>и<unlock()>методы (концепция<Lockable>также включает<try_lock()>метод, но эта библиотека не требует пробной блокировки).

Библиотека Boost.Signals2 предоставляет один альтернативный класс mutex для использования с<signal>:<boost::signals2::dummy_mutex>. Это поддельный mutex для использования в однопоточных программах, где блокировка реального mutex была бы бесполезной накладной. Другие типы mutex, которые вы можете использовать с<signal>, включают<boost::mutex>или<std::mutex>из C++11.

Изменение параметра типа шаблона сигнала<Mutex>может быть утомительным из-за большого количества параметров шаблона, которые ему предшествуют. Метафункция<signal_type>особенно полезна в этом случае, поскольку она позволяет использовать параметры типа шаблона для класса<signals2::signal>. Например, чтобы объявить сигнал, который принимает<int>в качестве аргумента и использует<boost::signals2::dummy_mutex>для его<Mutex>типов, вы можете написать:

namespace bs2 = boost::signals2;
using namespace bs2::keywords;
bs2::signal_type<void (int), mutex_type<bs2::dummy_mutex> >::type sig;

Linking against the Signals2 library

В отличие от оригинального Boost. Библиотека сигналов, Буст. В настоящее время Signals2 используется только для заголовков.

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


PrevUpHomeNext

Статья Tutorial раздела 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:33:49/0.015573978424072/0