Boost.Singals 教程

Posted on 2008-07-04 13:23 RichardHe 阅读(901) 评论(0)  编辑 收藏 引用 所属分类: [转]
来自http://www.cppblog.com/jinq0123/archive/2008/06/30/boostsignalstutorial.html
Boost c++ libraries Home Libraries People FAQ More

PrevUpHomeNext

术语表

英文 中文 注释
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-levelstructure roughly separates different concepts in the library(e.g., handling calling multiple slots, passing values to and fromslots) and in each of these concepts the basic ideas are presentedfirst and then more complex uses of the library are describedlater. Each of the sections is marked Beginner,Intermediate, or Advanced to help guide thereader. The Beginner sections include information that alllibrary users should know; one can make good use of the Signalslibrary after having read only the Beginner sections. TheIntermediate sections build on the Beginnersections with slightly more complex uses of the library. Finally,the Advanced sections detail very advanced uses of theSignals library, that often require a solid working knowledge ofthe Beginner and Intermediate topics; most userswill not need to read the Advanced sections.

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

Compatibility Note

对兼容性的注释

Boost.Signals has two syntactical forms: the preferred form andthe compatibility form. The preferred form fits more closely with theC++ language and reduces the number of separate template parametersthat need to be considered, often improving readability; however, thepreferred form is not supported on all platforms due to compilerbugs. The compatible form will work on all compilers supported byBoost.Signals. Consult the table below to determine which syntacticform to use for your compiler. Users of Boost.Function, please notethat the preferred syntactic form in Signals is equivalent to that ofFunction'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 thepreferred syntax and report your results to the Boost list so thatwe 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 andslots. First, we create a signal sig, a signal thattakes no arguments and has a void return value. Next, we connectthe hello function object to the signal using theconnect method. Finally, use the signalsig like a function to call the slots, which in turnsinvokes 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, sowe can make the Hello, World program more interesting by splittingthe work of printing "Hello, World!" into two completely separateslots. The first slot will print "Hello" and may look likethis:

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

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

The second slot will print ", World!" and a newline, to completethe 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 signalsig that takes no arguments and has avoid return value. This time, we connect both ahello and a world slot to the samesignal, 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 someslots will have to be called before others even if they are not connected in that order. The Boost.Signalslibrary allows slots to be placed into groups that are ordered insome way. For our Hello, World program, we want "Hello" to beprinted before ", World!", so we put "Hello" into a group that mustbe executed before the group that ", World!" is in. To do this, wecan supply an extra parameter at the beginning of theconnect call that specifies the group. Group valuesare, 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 theHello object is in group 0, which precedes group 1 wherethe World object resides. The groupparameter is, in fact, optional. We omitted it in the first Hello,World example because it was unnecessary when all of the slots areindependent. So what happens if we mix calls to connect that use thegroup parameter and those that don't? The "unnamed" slots (i.e., thosethat have been connected without specifying a group name) can beplaced at the front or back of the slot list (by passingboost::signals::at_front or boost::signals::at_backas the last parameter to connect, respectively), and defaults to the end of the list. Whena group is specified, the final parameter describes where the slotwill be placed within the group ordering. If we add a new slotto 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 mightwant to pass along the new mouse coordinates and whether the mousebuttons are pressed.

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

As an example, we'll create a signal that passes twofloat arguments to its slots. Then we'll create a fewslots that print the results of various arithmetic operations onthese 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 iscalled like a function are passed to each of the slots. We have todeclare the types of these values up front when we create thesignal. The type boost::signal<void (float,float)> means that the signal has a voidreturn value and takes two float values. Any slotconnected to sig must therefore be able to take twofloat values.

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

Signal Return Values (Advanced)

信号返回值(高级)

Just as slots can receive arguments, they can also returnvalues. These values can then be returned back to the caller of thesignal through a combiner. The combiner is a mechanismthat can take the results of calling slots (there many be noresults or a hundred; we don't know until the program runs) andcoalesces them into a single result to be returned to the caller.The single result is often a simple function of the results of theslot calls: the result of the last slot call, the maximum valuereturned by any slot, or a container of all of the results are somepossibilities.

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

We can modify our previous arithmetic operations exampleslightly so that the slots all return the results of computing theproduct, quotient, sum, or difference. Then the signal itself canreturn 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 thedefault behavior of a signal that has a return type(float, the first template argument given to theboost::signal class template) is to call all slots andthen return the result returned by the last slot called. Thisbehavior is admittedly silly for this example, because slots haveno side effects and the result is the last slot connect.

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

A more interesting signal result would be the maximum of thevalues returned by any slot. To do this, we create a customcombiner 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 functionobject. Its result type is given by its template parameter, andthis is the type it expects to be computing the maximum based on(e.g., maximum<float> would find the maximumfloat in a sequence of floats). When amaximum object is invoked, it is given an inputiterator sequence [first, last) that includes theresults of calling all of the slots. maximum uses thisinput iterator sequence to calculate the maximum element, andreturns that maximum value.

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

We actually use this new function object type by installing itas a combiner for our signal. The combiner template argumentfollows 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 anduse 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, becauseregardless of the order in which the slots are connected, the productof 5 and 3 will be larger than the quotient, sum, ordifference.

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

In other cases we might want to return all of the valuescomputed by the slots together, in one large data structure. Thisis 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. Itis interesting here thatthe 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 willalso be the value_type of the input iterators passedto the combiner. The combiner itself is a function object and itsresult_type member type becomes the return type of thesignal.

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

The input iterators passed to the combiner transform dereferenceoperations into slot calls. Combiners therefore have the option toinvoke only some slots until some particular criterion is met. Forinstance, in a distributed computing system, the combiner may askeach remote system whether it will handle the request. Only oneremote system needs to handle a particular request, so after aremote system accepts the work we do not want to ask any otherremote systems to perform the same task. Such a combiner need onlycheck the value returned when dereferencing the iterator, andreturn when the value is acceptable. The following combiner returnsthe first non-NULL pointer to a FulfilledRequest datastructure, without asking any later slots to fulfill therequest:

传给合并器的输入迭代器会将解引用操作转换为插槽调用。因此合并器可选择仅调用某些符合特定条件的插槽。例如,在分布计算系统中,合并器可能会询问 每个远程系统能否处理请求。对于一个特定请求,仅需一个远程系统进行处理,因此当一个远程系统接受该工作后,我们将不再要求任何其他远程系统来做同一个任 务。这样一个合并器只需检查迭代器解引用的返回值,并当该值可以接受时就返回。以下的合并器返回第一个指向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 areconnected. Often slots are only used to receive a few events andare then disconnected, and the programmer needs control to decidewhen a slot should no longer be connected.

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

The entry point for managing connections explicitly is theboost::signals::connection class. Theconnection class uniquely represents the connectionbetween a particular signal and a particular slot. Theconnected() method checks if the signal and slot arestill connected, and the disconnect() methoddisconnects the signal and slot if they are connected before it iscalled. Each call to the signal's connect() methodreturns a connection object, which can be used to determine if theconnection 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 beignored when the signal is invoked but have not been disconnected. Theblock member functiontemporarily blocks a slot, which can be unblocked viaunblock. Here is an example ofblocking/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 classreferences a signal/slot connection that will be disconnected whenthe scoped_connection class goes out of scope. Thisability 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 functionobject using a form of thedisconnect method, so long asthe 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 objectsinvolved in signal/slot connections, including automaticdisconnection of slots when objects involved in the slot call aredestroyed. For instance, consider a simple news delivery service,where clients connect to a news provider that then sends news toall connected clients as information arrives. The news deliveryservice 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 afunction object that can receive news items to thedeliverNews signal. For instance, we may have aspecial 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 thatdeliverNews knows about? Most likely, a segmentationfault will occur. However, with Boost.Signals one need only makeNewsMessageArea trackable, and the slotinvolving newsMessageArea will be disconnected whennewsMessageArea is destroyed. TheNewsMessageArea class is made trackable by derivingpublicly 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 oftrackable objects in making slot connections: functionobjects built using Boost.Bind are understood, such that pointersor references to trackable objects passed toboost::bind will be found and tracked.

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

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

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

When can disconnections occur? (Intermediate)

何时断开?(中级)

Signal/slot disconnections occur when any of these conditionsoccur:

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

  • The connection is explicitly disconnected via the connection'sdisconnect method directly, or indirectly via thesignal's disconnect method orscoped_connection's destructor.

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

  • A trackable object bound to the slot isdestroyed.

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

  • The signal is destroyed.

    销毁信号。

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

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

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

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

Passing slots (Intermediate)

传递插槽(中级)

Slots in the Boost.Signals library are created from arbitraryfunction objects, and therefore have no fixed type. However, it iscommonplace to require that slots be passed through interfaces thatcannot be templates. Slots can be passed via theslot_type for each particular signal type and anyfunction object compatible with the signature of the signal can bepassed 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 equivalentto the connect method of the onClicksignal, but the details of the doOnClick method can behidden 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


PrevUpHomeNext


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


posts - 94, comments - 138, trackbacks - 0, articles - 94

Copyright © RichardHe