Boost.Signals 教程

Boost C++ LibrariesHomeLibrariesPeopleFAQMore

Prev Up Home Next

Tutorial

教程

术语表

英文中文注释
block阻塞 
combiner合并器 
compatibility form兼容形式 
connect连接 
connection连接 
disconnect断开 
first-in first-out (FIFO)先进先出(FIFO) 
preferred form首选形式 
scoped域内的作用域内的
signal信号 
Signals library信号库 
slot插槽 

How to Read this Tutorial

如何阅读本教程

This tutorial is not meant to be read linearly. Its top-level structure roughly separates different concepts in the library (e.g., handling calling multiple slots, passing values to and from slots) and in each of these concepts the basic ideas are presented first and then more complex uses of the library are described later. Each of the sections is marked Beginner, Intermediate, or Advanced to help guide the reader. The Beginner sections include information that all library users should know; one can make good use of the Signals library after having read only the Beginner sections. The Intermediate sections build on the Beginner sections with slightly more complex uses of the library. Finally, the Advanced sections detail very advanced uses of the Signals library, that often require a solid working knowledge of the Beginner and Intermediate topics; most users will not need to read the Advanced sections.

本教程不是用来线性阅读的。 其顶层结构大致按库中不同的概念划分,(如处理调用多个插槽、传值到插槽及回传),并且在每一个这些概念中,首先介绍其基本思想,然后说明库较复杂的使用。 每个部分都标注了初级中级,或高级,以帮助指导读者。 初级部分包括库的所有使用者都该了解的信息;只要阅读了初级部分,就可以很好地使用信号库。 中级部分建立在初级 部分之上,是库的稍复杂的使用。 最后,高级部分详述了信号库很高级的应用,这往往需要对初级中级内容扎实的实践经验; 大多数用户无需阅读高级部分

Compatibility Note

对兼容性的注释

Boost.Signals has two syntactical forms: the preferred form and the compatibility form. The preferred form fits more closely with the C++ language and reduces the number of separate template parameters that need to be considered, often improving readability; however, the preferred form is not supported on all platforms due to compiler bugs. The compatible form will work on all compilers supported by Boost.Signals. Consult the table below to determine which syntactic form to use for your compiler. Users of Boost.Function, please note that the preferred syntactic form in Signals is equivalent to that of Function's preferred syntactic form.

Boost.Signals 有两种句法形式:首选形式(preferred form)和兼容形式(compatibility form)。 首选形式能更紧密地适合C++语言, 并减少需要考虑的独立模板参数的个数, 往往能提高可读性; 然而,由于编译器的错误,首选形式并非所有平台都支持。 兼容形式可工作于 Boost.Signals 支持的所有编译器。 参考下表,以确定为你的编译器使用哪种句法形式。 Boost.Function 的用户请注意,Signals 中的首选句法形式等效于 Function 的首选句法形式。

If your compiler does not appear in this list, please try the preferred syntax and report your results to the Boost list so that we can keep this table up-to-date.

如果你的编译器不在这个清单上,请试用首选句法并向Boost列表报告你的结果,以使我们能保持更新该表。

Preferred syntax 首选句法 Portable syntax 兼容句法
  • GNU C++ 2.95.x, 3.0.x, 3.1.x

  • Comeau C++ 4.2.45.2

  • SGI MIPSpro 7.3.0

  • Intel C++ 5.0, 6.0

  • Compaq's cxx 6.2

  • Microsoft Visual C++ 7.1

  • Any compiler supporting the preferred syntax 支持首选句法的任意编译器

  • Microsoft Visual C++ 6.0, 7.0

  • Borland C++ 5.5.1

  • Sun WorkShop 6 update 2 C++ 5.3

  • Metrowerks CodeWarrior 8.1

Hello, World! (Beginner)

Hello, World! (初级)

The following example writes "Hello, World!" using signals and slots. First, we create a signal sig, a signal that takes no arguments and has a void return value. Next, we connect the hello function object to the signal using the connect method. Finally, use the signal sig like a function to call the slots, which in turns invokes HelloWorld::operator() to print "Hello, World!".

下例将使用信号和插槽写出“Hello, World!”。 首先,我们创建信号 sig,该信号无参数并且返回值为空。 接着,我们使用 connect 方法将 hello 函数对象连接到信号。 最后,像函数一样使用信号 sig 来调用插槽, 它将转而调用 HelloWorld::operator() 打印“Hello, World!”。

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

Calling multiple slots

调用多个插槽

Connecting multiple slots (Beginner)
连接多个插槽(初级)

Calling a single slot from a signal isn't very interesting, so we can make the Hello, World program more interesting by splitting the work of printing "Hello, World!" into two completely separate slots. The first slot will print "Hello" and may look like this:

从信号调用单个插槽不是很有意思, 因此我们将打印“Hello, World!”的工作拆分到两个完全独立的插槽, 让 Hello, World 程序更有趣点。 第一个插槽将打印“Hello”,可能看起来像这样:

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

The second slot will print ", World!" and a newline, to complete the program. The second slot may look like this:

第二个插槽将打印“, World!”并回车以完成该程序。 第二个插槽可能看起来像这样:

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

Like in our previous example, we can create a signal sig that takes no arguments and has a void return value. This time, we connect both a hello and a world slot to the same signal, and when we call the signal both slots will be called.

就像我们的上个例子,我们创建信号 sig, 它没有参数并且返回值为 void。 这次,我们将 helloworld 插槽都连接到同一个信号, 当我们调用该信号,两个插槽都将会被调用。

Preferred syntaxPortable syntax
boost::signal<void ()> sig;
 
 sig.connect(Hello());
 sig.connect(World());
 
 sig();
 
boost::signal0<void> sig;
 
 sig.connect(Hello());
 sig.connect(World());
 
 sig();
 

By default, slots are called in first-in first-out (FIFO) order, so the output of this program will be as expected:

默认情况下,插槽以先进先出(FIFO)的次序被调用, 因此该程序的输出应该是:

Hello, World!
 
Ordering slot call groups (Intermediate)
插槽调用组排序(中级)

Slots are free to have side effects, and that can mean that some slots will have to be called before others even if they are not connected in that order. The Boost.Signals library allows slots to be placed into groups that are ordered in some way. For our Hello, World program, we want "Hello" to be printed before ", World!", so we put "Hello" into a group that must be executed before the group that ", World!" is in. To do this, we can supply an extra parameter at the beginning of the connect call that specifies the group. Group values are, by default, ints, and are ordered by the integer < relation. Here's how we construct Hello, World:

插槽可以随意具有副作用,即某些插槽必须在另一些之前调用,即使它们不是按那个次序连接的。 Boost.Signals 库允许插槽被置于按某种方式排序的编组中。 对于我们的 Hello, World 程序,我们要“Hello”在“, World!”之前打印, 所以我们将“Hello”放入一个组,该组将在“, World!”所在组之前执行。 为了做到这一点,我们可以在 connect 调用的头部提供一个额外的参数,以指定该组。 编组的值默认为 int, 并按整型的 < 关系排序。 我们这样构造 Hello, World:

Preferred syntaxPortable syntax
boost::signal<void ()> sig;
 sig.connect(1, World());
 sig.connect(0, Hello());
 sig();
 
boost::signal0<void> sig;
 sig.connect(1, World());
 sig.connect(0, Hello());
 sig();
 

This program will correctly print "Hello, World!", because the Hello object is in group 0, which precedes group 1 where the World object resides. The group parameter is, in fact, optional. We omitted it in the first Hello, World example because it was unnecessary when all of the slots are independent. So what happens if we mix calls to connect that use the group parameter and those that don't? The "unnamed" slots (i.e., those that have been connected without specifying a group name) can be placed at the front or back of the slot list (by passing boost::signals::at_front or boost::signals::at_back as the last parameter to connect, respectively), and defaults to the end of the list. When a group is specified, the final parameter describes where the slot will be placed within the group ordering. If we add a new slot to our example like this:

该程序将正确打印出“Hello, World!”,因为 Hello 对象在组 0,它在 World 对象所在的组 1 之前。 编组参数实际上是可选的。 在第一个 Hello World 例子中我们省略了它, 因为当所有的插槽都独立时,编组是不必要的。 那么,如果我们混合调用使用和不使用编组参数的连接会怎样? “未命名”插槽(即那些连接时未指定组名的插槽)可置于插槽链表的头部或尾部 (通过向 connect 分别传入 boost::signals::at_frontboost::signals::at_back 作为最后的参数), 而默认为链表的结尾。 当指定了编组时,最后的参数描述的是插槽在组内的次序。 如果我们如下向我们的例子添加新的插槽:

struct GoodMorning
 {
   void operator()() const
   {
     std::cout << "... and good morning!" << std::endl;
   }
 };
 
 sig.connect(GoodMorning());
 

... we will get the result we wanted:

……我们将得到我们想要的结果:

Hello, World!
 ... and good morning!
 

Passing values to and from slots

传值到插槽及回传

Slot Arguments (Beginner)
插槽的参数(初级)

Signals can propagate arguments to each of the slots they call. For instance, a signal that propagates mouse motion events might want to pass along the new mouse coordinates and whether the mouse buttons are pressed.

信号可以向它们调用的每个插槽传递参数。 例如,一个传递鼠标移动事件的信号可能要传入新的鼠标坐标以及是否按了鼠标键。

As an example, we'll create a signal that passes two float arguments to its slots. Then we'll create a few slots that print the results of various arithmetic operations on these values.

作为一个例子,我们将创建一个信号,它将传入两个 float 参数到它的插槽。 然后我们将创建几个插槽,它们将打印对这两个参数进行算术运算的各种结果。

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;
 }
 
Preferred syntaxPortable syntax
boost::signal<void (float, float)> sig;
 
 sig.connect(&print_sum);
 sig.connect(&print_product);
 sig.connect(&print_difference);
 sig.connect(&print_quotient);
 
 sig(5, 3);
 
boost::signal2<void, float, float> sig;
 
 sig.connect(&print_sum);
 sig.connect(&print_product);
 sig.connect(&print_difference);
 sig.connect(&print_quotient);
 
 sig(5, 3);
 

This program will print out the following:

该程序将打印输出如下:

The sum is 8
 The product is 15
 The difference is 2
 The quotient is 1.66667
 

So any values that are given to sig when it is called like a function are passed to each of the slots. We have to declare the types of these values up front when we create the signal. The type boost::signal<void (float, float)> means that the signal has a void return value and takes two float values. Any slot connected to sig must therefore be able to take two float values.

当像函数一样调用 sig 时, 输入它的任何值都传给了每一个插槽。 创建信号时,我们必须预先声明这些值的类型。 类型 boost::signal<void (float, float)> 表明信号具有 void 返回值并接受两个 float 值。 因此任何连接到 sig 的插槽都必须能够接受两个 float 值。

Signal Return Values (Advanced)
信号返回值(高级)

Just as slots can receive arguments, they can also return values. These values can then be returned back to the caller of the signal through a combiner. The combiner is a mechanism that can take the results of calling slots (there many be no results or a hundred; we don't know until the program runs) and coalesces them into a single result to be returned to the caller. The single result is often a simple function of the results of the slot calls: the result of the last slot call, the maximum value returned by any slot, or a container of all of the results are some possibilities.

正如插槽可以接收参数,它们也可以返回值。 然后这些值可以通过 合并器(combiner) 返回给信号的调用者。 合并器是这样一种工具,它接收插槽调用的结果(可能没有结果,也可能有100个结果;程序运行时才知道),并且把它们合并成单一的结果返回给调用者。 该单一的结果往往是插槽调用结果的一个简单函数,可能是:最后的插槽调用的结果、所有插槽返回值的最大值,或包含所有结果的容器。

We can modify our previous arithmetic operations example slightly so that the slots all return the results of computing the product, quotient, sum, or difference. Then the signal itself can return a value based on these results to be printed:

我们可以稍微修改前面的算术运算的例子, 使插槽分别返回加减乘除的计算结果。 然后信号本身就可以根据这些结果返回一个值,并打印出来。

Preferred syntaxPortable syntax
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::signal<float (float x, float y)> sig;
 
 sig.connect(&product);
 sig.connect(&quotient);
 sig.connect(&sum);
 sig.connect(&difference);
 
 std::cout << sig(5, 3) << std::endl;
 
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::signal2<float, float, float> sig;
 
 sig.connect(&product);
 sig.connect(&quotient);
 sig.connect(&sum);
 sig.connect(&difference);
 
 std::cout << sig(5, 3) << std::endl;
 

This example program will output 2. This is because the default behavior of a signal that has a return type (float, the first template argument given to the boost::signal class template) is to call all slots and then return the result returned by the last slot called. This behavior is admittedly silly for this example, because slots have no side effects and the result is the last slot connect.

该例程将输出 2。 这是因为具有返回类型(float, 即输入 boost::signal 类模板的第一个模板参数)的信号的默认行为是, 调用所有的插槽,然后返回最后一个被调用插槽的结果。 对本例来说,该行为确实有点傻,因为这些插槽没有副作用并且结果就是最后的插槽连接。

A more interesting signal result would be the maximum of the values returned by any slot. To do this, we create a custom combiner that looks like this:

一个更有意思的信号结果是,求所有插槽返回值的最大值。 为了做到这一点,我们创建了一个自定义合并器,看起来像这样:

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;
   }
 };
 

The maximum class template acts as a function object. Its result type is given by its template parameter, and this is the type it expects to be computing the maximum based on (e.g., maximum<float> would find the maximum float in a sequence of floats). When a maximum object is invoked, it is given an input iterator sequence [first, last) that includes the results of calling all of the slots. maximum uses this input iterator sequence to calculate the maximum element, and returns that maximum value.

maximum 类模板就像一个函数对象。 它的结果类型由其模板参数给出, 并且它正是基于该类型计算最大值 (例如,maximum<float> 将在一系列 float 中查找最大的 float)。 当调用 maximum 对象时, 将给出一个输入迭代器序列 [first, last), 其中包含了所有插槽调用的结果。 maximum 利用该输入迭代器序列来计算最大元素, 并返回那个最大值。

We actually use this new function object type by installing it as a combiner for our signal. The combiner template argument follows the signal's calling signature:

我们要把这个新的函数对象作为合并器安装到我们的信号,才能实际使用它。 合并器模板参数跟在信号的调用签名式之后。

Preferred syntaxPortable syntax
boost::signal<float (float x, float y), 
               maximum<float> > sig;
 
boost::signal2<float, float, float, 
                maximum<float> > sig;
 

Now we can connect slots that perform arithmetic functions and use the signal:

现在我们可以连接执行算术功能的插槽并使用信号了:

sig.connect(&quotient);
 sig.connect(&product);
 sig.connect(&sum);
 sig.connect(&difference);
 
 std::cout << sig(5, 3) << std::endl;
 

The output of this program will be 15, because regardless of the order in which the slots are connected, the product of 5 and 3 will be larger than the quotient, sum, or difference.

该程序的输出为 15, 因为不管插槽的连接次序如何, 5 和 3 的乘积将大于商、和,或差。

In other cases we might want to return all of the values computed by the slots together, in one large data structure. This is easily done with a different combiner:

在其他情况下,我们可能要同时返回插槽计算的所有值, 如保存在一个大型的数据结构中。 这可以用一个不同的合并器来轻松完成:

template<typename Container>
 struct aggregate_values
 {
   typedef Container result_type;
 
   template<typename InputIterator>
   Container operator()(InputIterator first, InputIterator last) const
   {
     return Container(first, last);
   }
 };
 

Again, we can create a signal with this new combiner:

我们再次用这个新的合并器创建信号:

Preferred syntaxPortable syntax
boost::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::copy(results.begin(), results.end(), 
     std::ostream_iterator<float>(cout, " "));
 
boost::signal2<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::copy(results.begin(), results.end(), 
     std::ostream_iterator<float>(cout, " "));
 

The output of this program will contain 15, 8, 1.6667, and 2. It is interesting here that the first template argument for the signal class, float, is not actually the return type of the signal. Instead, it is the return type used by the connected slots and will also be the value_type of the input iterators passed to the combiner. The combiner itself is a function object and its result_type member type becomes the return type of the signal.

该程序的输出将包含 15、8、1.6667,和 2。 这里有趣的是, signal 类的第一个模板参数, float, 竟然不是信号的返回类型。 相反,该参数是所连接插槽的返回类型, 并且它也是传入合并器的输入迭代器的 value_type。 合并器本身是个函数对象,并且它的 result_type 成员类型将成为信号的返回类型。

The input iterators passed to the combiner transform dereference operations into slot calls. Combiners therefore have the option to invoke only some slots until some particular criterion is met. For instance, in a distributed computing system, the combiner may ask each remote system whether it will handle the request. Only one remote system needs to handle a particular request, so after a remote system accepts the work we do not want to ask any other remote systems to perform the same task. Such a combiner need only check the value returned when dereferencing the iterator, and return when the value is acceptable. The following combiner returns the first non-NULL pointer to a FulfilledRequest data structure, without asking any later slots to fulfill the request:

传给合并器的输入迭代器会将解引用操作转换为插槽调用。 因此合并器可选择仅调用某些符合特定条件的插槽。 例如,在分布计算系统中,合并器可能会询问每个远程系统能否处理请求。 对于一个特定请求,仅需一个远程系统进行处理, 因此当一个远程系统接受该工作后, 我们将不再要求任何其他远程系统来做同一个任务。 这样一个合并器只需检查迭代器解引用的返回值, 并当该值可以接受时就返回。 以下的合并器返回第一个指向 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)
断开插槽(初级)

Slots aren't expected to exist indefinately after they are connected. Often slots are only used to receive a few events and are then disconnected, and the programmer needs control to decide when a slot should no longer be connected.

插槽不要求在它们被连接后无限期地存在。 往往插槽只是用来接收一些事件然后断开, 而程序员需要控制决定何时插槽不应该再继续连接。

The entry point for managing connections explicitly is the boost::signals::connection class. The connection class uniquely represents the connection between a particular signal and a particular slot. The connected() method checks if the signal and slot are still connected, and the disconnect() method disconnects the signal and slot if they are connected before it is called. Each call to the signal's connect() method returns a connection object, which can be used to determine if the connection still exists or to disconnect the signal and slot.

显式管理连接的入口点是 boost::signals::connection 类。 connection 类唯一代表了特定信号与特定插槽之间的连接。 connected() 方法检查信号与插槽是否仍保持连接, 如果信号与插槽是连接着的, disconnect() 方法断开它们的连接。 每次调用信号的 connect() 方法,就返回一个连接对象, 该对象用于确定连接是否仍然存在, 或者用于断开信号和插槽。

boost::signals::connection c = sig.connect(HelloWorld());
 if (c.connected()) {
 // c is still connected to the signal
   sig(); // Prints "Hello, World!"
 }
 
 c.disconnect(); // Disconnect the HelloWorld object
 assert(!c.connected()); c isn't connected any more
 
 sig(); // Does nothing: there are no connected slots
 
Blocking Slots (Beginner)
阻塞插槽(初级)

Slots can be temporarily "blocked", meaning that they will be ignored when the signal is invoked but have not been disconnected. The block member function temporarily blocks a slot, which can be unblocked via unblock. Here is an example of blocking/unblocking slots:

插槽可以被临时“阻塞”,即当信号被调用时,这些插槽将被忽略,但并没有被断开。 block 成员函数临时地阻塞一个插槽, 可通过 unblock 解除阻塞。 这是一个阻塞/开启插槽的例子:

boost::signals::connection c = sig.connect(HelloWorld());
 sig(); // Prints "Hello, World!"
 
 c.block(); // block the slot
 assert(c.blocked());
 sig(); // No output: the slot is blocked
 
 c.unblock(); // unblock the slot
 sig(); // Prints "Hello, World!"
 
Scoped connections (Intermediate)
域内连接(中级)

The boost::signals::scoped_connection class references a signal/slot connection that will be disconnected when the scoped_connection class goes out of scope. This ability is useful when a connection need only be temporary, e.g.,

boost::signals::scoped_connection 类引用了一个信号/插槽的连接,当 scoped_connection 类出作用域时,该连接将会被断开。 当仅需临时连接时,该功能很有用,如:

{
   boost::signals::scoped_connection c = sig.connect(ShortLived());
   sig(); // will call ShortLived function object
 }
 sig(); // ShortLived function object no longer connected to sig
 
Disconnecting equivalent slots (Intermediate)
断开等价的插槽(中级)

One can disconnect slots that are equivalent to a given function object using a form of the disconnect method, so long as the type of the function object has an accessible == operator. For instance:

你可以使用 disconnect 方法断开与给定函数对象等价的多个插槽, 只要该函数对象的类型具有可访问的 == 运算符。例如:

Preferred syntaxPortable syntax
void foo();
 void bar();
 
 signal<void()> sig;
 
 sig.connect(&foo);
 sig.connect(&bar);
 
 // disconnects foo, but not bar
 sig.disconnect(&foo);
 
void foo();
 void bar();
 
 signal0<void> sig;
 
 sig.connect(&foo);
 sig.connect(&bar);
 
 // disconnects foo, but not bar
 sig.disconnect(&foo);
 
Automatic connection management (Intermediate)
自动连接管理(中级)

Boost.Signals can automatically track the lifetime of objects involved in signal/slot connections, including automatic disconnection of slots when objects involved in the slot call are destroyed. For instance, consider a simple news delivery service, where clients connect to a news provider that then sends news to all connected clients as information arrives. The news delivery service may be constructed like this:

Boost.Signals 能自动跟踪信号/插槽连接中所涉及对象的生命期, 包括当插槽调用中涉及的对象销毁时自动断开插槽。 例如,考虑一个简单的新闻发送服务, 其中客户会连接到新闻提供者, 而新闻提供者一有信息到达,就发送新闻到所有连接的客户。 该新闻发送服务可能像这样构造:

Preferred syntaxPortable syntax
class NewsItem { /* ... */ };
 
 boost::signal<void (const NewsItem&)> deliverNews;
 
class NewsItem { /* ... */ };
 
 boost::signal1<void, const NewsItem&> deliverNews;
 

Clients that wish to receive news updates need only connect a function object that can receive news items to the deliverNews signal. For instance, we may have a special message area in our application specifically for news, e.g.,:

希望接收新闻更新的客户只需连接一个函数对象, 该对象可以接收传给 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));
 

However, what if the user closes the news message area, destroying the newsMessageArea object that deliverNews knows about? Most likely, a segmentation fault will occur. However, with Boost.Signals one need only make NewsMessageArea trackable, and the slot involving newsMessageArea will be disconnected when newsMessageArea is destroyed. The NewsMessageArea class is made trackable by deriving publicly from the boost::signals::trackable class, e.g.:

不过,如果用户关闭新闻讯息区, 销毁了 deliverNews 所知的 newsMessageArea 对象, 那会怎么样? 最有可能的是产生段错误。 然而,你只需在使用 Boost.Signals 时,让 NewsMessageArea 可跟踪(trackable), 那么当 newsMessageArea 被销毁时, 调用 newsMessageArea 的插槽就会被断开。 通过公有继承自 boost::signals::trackable 类, NewsMessageArea 类就会变成可跟踪的,例如:

struct NewsMessageArea : public MessageArea, public boost::signals::trackable
 {
   // ...
 };
 

At this time there is a significant limitation to the use of trackable objects in making slot connections: function objects built using Boost.Bind are understood, such that pointers or references to trackable objects passed to boost::bind will be found and tracked.

目前,用 trackable 对象制作插槽连接有个重要的限制: 它理解使用 Boost.Bind 构建的函数对象,如传入 boost::bindtrackable 对象指针或引用将会被发现和跟踪。

Warning: User-defined function objects and function objects from other libraries (e.g., Boost.Function or Boost.Lambda) do not implement the required interfaces for trackable object detection, and will silently ignore any bound trackable objects. Future versions of the Boost libraries will address this limitation.

警告: 用户自定义函数对象和来自其他库的函数对象 (例如来自 Boost.Function 或 Boost.Lambda), 没有实现所要求的 trackable 对象检测接口, 将会默默地忽略任何绑定的可跟踪对象。 Boost库的未来版本将会解除该限制。

When can disconnections occur? (Intermediate)
何时断开?(中级)

Signal/slot disconnections occur when any of these conditions occur:

以下任一条件发生时,信号/插槽将断开:

  • The connection is explicitly disconnected via the connection's disconnect method directly, or indirectly via the signal's disconnect method or scoped_connection's destructor.

    连接显式断开: 直接通过连接的 disconnect 方法, 或间接地通过信号的 disconnect 方法或 scoped_connection 的析构函数。

  • A trackable object bound to the slot is destroyed.

    销毁绑定于插槽的 trackable 对象。

  • The signal is destroyed.

    销毁信号。

These events can occur at any time without disrupting a signal's calling sequence. If a signal/slot connection is disconnected at any time during a signal's calling sequence, the calling sequence will still continue but will not invoke the disconnected slot. Additionally, a signal may be destroyed while it is in a calling sequence, and which case it will complete its slot call sequence but may not be accessed directly.

这些事件可以发生于任何时间,而不会破坏信号的调用序列。 如果信号/插槽的连接在信号调用序列的任意时刻被断开, 调用序列仍将继续,只是不会调用被断开的插槽。 此外,信号可以在调用序列中间被销毁,这时,它将完成其插槽调用序列, 只是不可以被直接访问。

Signals may be invoked recursively (e.g., a signal A calls a slot B that invokes signal A...). The disconnection behavior does not change in the recursive case, except that the slot calling sequence includes slot calls for all nested invocations of the signal.

信号可以被递归调用(例如,信号A调用插槽B,而插槽B又调用信号A……)。 递归情况下,断开的行为不会改变, 只是插槽调用序列包括所有嵌套的信号调用。

Passing slots (Intermediate)
传递插槽(中级)

Slots in the Boost.Signals library are created from arbitrary function objects, and therefore have no fixed type. However, it is commonplace to require that slots be passed through interfaces that cannot be templates. Slots can be passed via the slot_type for each particular signal type and any function object compatible with the signature of the signal can be passed to a slot_type parameter. For instance:

Boost.Signals 库中的插槽可以从任意的函数对象创建,因此没有固定的类型。 不过通常要求通过不可模板化的接口传递插槽。 对于每个特定的信号类型, 都可以通过 slot_type 传递插槽, 而与信号的签名式兼容的任意函数对象, 都可以传给 slot_type 参数。 例如:

Preferred syntaxPortable syntax
class Button 
 {
   typedef boost::signal<void (int x, int y)> OnClick;
 
 public:
   void doOnClick(const OnClick::slot_type& slot);
 
 private:
   OnClick onClick;
 };
 
 void Button::doOnClick(
       const OnClick::slot_type& slot
     )
 {
   onClick.connect(slot);
 }
 
 void printCoordinates(long x, long y)
 {
   std::cout << "(" << x << ", " << y << ")/n";
 }
 
 void f(Button& button)
 {
   button.doOnClick(&printCoordinates);
 }
 
class Button 
 {
   typedef boost::signal2<void,int,int> OnClick;
 
 public:
   void doOnClick(const OnClick::slot_type& slot);
 
 private:
   OnClick onClick;
 };
 
 void Button::doOnClick(
       const OnClick::slot_type& slot
     )
 {
   onClick.connect(slot);
 }
 
 void printCoordinates(long x, long y)
 {
   std::cout << "(" << x << ", " << y << ")/n";
 }
 
 void f(Button& button)
 {
   button.doOnClick(&printCoordinates);
 }
 

The doOnClick method is now functionally equivalent to the connect method of the onClick signal, but the details of the doOnClick method can be hidden in an implementation detail file.

doOnClick 方法现在功能上等效于 onClick 信号的 connect 方法, 但是 doOnClick 方法的细节可以隐藏于细节实现文件中。

Example: Document-View

例子:文档-视图

Signals can be used to implement flexible Document-View architectures. The document will contain a signal to which each of the views can connect. The following Document class defines a simple text document that supports mulitple views. Note that it stores a single signal to which all of the views will be connected.

信号可用于实现灵活的文档-视图(Document-View)架构。 文档包含一个信号,而每个视图连接该信号。 下面的 Document 类定义了一个简单的支持多视图的文本文档。 注意它保存了一个单一的信号,所有视图都连接到该信号。

class Document
 {
 public:
     typedef boost::signal<void (bool)>  signal_t;
     typedef boost::signals::connection  connection_t;
 
 public:
     Document()
     {}
 
     connection_t connect(signal_t::slot_function_type subscriber)
     {
         return m_sig.connect(subscriber);
     }
 
     void disconnect(connection_t subscriber)
     {
         subscriber.disconnect();
     }
 
     void append(const char* s)
     {
         m_text += s;
         m_sig(true);
     }
 
     const std::string& getText() const
     {
         return m_text;
     }
 
 private:
     signal_t    m_sig;
     std::string m_text;
 };
 

Next, we can define a View base class from which views can derive. This isn't strictly required, but it keeps the Document-View logic separate from the logic itself. Note that the constructor just connects the view to the document and the destructor disconnects the view.

接下来,我们可以定义视图的基类 View。 这并非严格需要,但它能让文档-视图的逻辑与逻辑本身分离。 注意构造函数仅仅连接视图到文档,而析构函数断开视图。

class View
 {
 public:
     View(Document& m)
         : m_document(m)
     {
         m_connection = m_document.connect(boost::bind(&View::refresh, this, _1));
     }
 
     virtual ~View()
     {
         m_document.disconnect(m_connection);
     }
 
     virtual void refresh(bool bExtended) const = 0;
 
 protected:
     Document&               m_document;
 
 private:
     Document::connection_t  m_connection;
 };
   

Finally, we can begin to define views. The following TextView class provides a simple view of the document text.

最后,我们可以开始定义视图。 下面的 TextView 类提供了文档文本的一个简单视图。

class TextView : public View
 {
 public:
     TextView(Document& doc)
         : View(doc)
     {}
 
     virtual void refresh(bool bExtended) const
     {
         std::cout << "TextView: " << m_document.getText() << std::endl;
     }
 };
 

Alternatively, we can provide a view of the document translated into hex values using the HexView view:

此外,我们可以提供文档翻译成16进制后的视图, 如 HexView 视图:

class HexView : public View
 {
 public:
     HexView(Document& doc)
         : View(doc)
     {}
 
     virtual void refresh(bool bExtended) 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;
     }
 };
 

To tie the example together, here is a simple main function that sets up two views and then modifies the document:

将例子合起来,下面一个简单的 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;
 }
 

The complete example source, contributed by Keith MacDonald, is available in libs/signals/example/doc_view.cpp.

例子完整的源代码在 libs/signals/example/doc_view.cpp, 由 Keith MacDonald 提供。

Linking against the Signals library

链接信号库

Part of the Boost.Signals library is compiled into a binary library that must be linked into your application to use Signals. Please refer to the Getting Started guide. You will need to link against the boost_signals library.

Boost.Signals 库要编译成二进制库, 为使用信号库,应用程序必须链接该二进制库。 请参考 Getting Started 指导。 你将需要链接 boost_signals 库。

Last revised: January 29, 2007 at 15:05:29 -0500


Prev Up Home Next
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值