现在的位置: 首页 > 综合 > 正文

Beyond the C++ Standard Library: An Introduction to Boost — Library 12.1 Signals

2013年05月26日 ⁄ 综合 ⁄ 共 3306字 ⁄ 字号 评论关闭

Library 12. Signals

 

Signals 库如何改进你的程序?

  • 函数和函数对象的灵活多点回调

  • 健壮的触发器及事件处理的机制

  • 兼容于函数对象工厂,如 Boost.Bind Boost.Lambda

Boost.Signals 库具体化了信号(signals)和插槽(slots),信号指的是某种可被"抛出"的东西,而插槽是接收该信号的连接者。这是一种著名的设计模式,它还有另外一些名字 Observer, signals/slots, publisher/subscriber, events (event targets),这些名字指的都是同一个东西,指的是一些信息源和某些对这些信息的变化感兴趣的实例之间的一对多关系。这种设计模式的使用有多种情况;最常见的是在GUI代码中,用于使特定动作(例如,用户单击了一个按钮)与其它动作(按钮改变它的外观,执行某个商业逻辑)松散连接。信号与插槽在许多场合都很有用,解耦动作的触发条件(信号)和处理它的代码(一个或多个插槽)。它可用于动态改变处理代码的行为,允许同一信号对应多个处理,或者通过一个信号及插槽的类型间的抽象关联来降低类型依赖性。通过使用 Boost.Signals, 可以创建一些信号来接受任意给定的函数特征的插槽,即插槽接受任意类型的参数。这种方法使得该库非常灵活;它适用于任意范围的信号需求。通过对信号源和处理者的解耦,系统无论在物理和逻辑依赖上都变得更为健壮。它可以让信号类型对插槽类型完全一无所知,反之亦然。这对于更高层次的可复用性是很有必要的,它有助于打破依赖性的循环。因此,一个信号与插槽的库不仅仅关系到面向对象的回调,它也关系到使用它的整个系统的健壮性。

 

Signals 如何适用于标准库?

C++标准库中没有用于回调的工具,而这种工具显然是需要的。Boost.Signals 使用了与标准库相同的态度进行设计,它是标准库工具箱的一个杰出的扩展。

 

Signals

头文件: "boost/signals.hpp"

通过单个头文件包含了整个库。

"boost/signals/signal.hpp"

包含了 signal 的定义。

"boost/signals/slot.hpp"

包含了 slot 类的定义。

"boost/signals/connection.hpp"

包含了类 connectionscoped_connection 的定义。

要使用这个库,可以包含头文件 "boost/signals.hpp",这样可以确保整个库可用,或者可以按照你的需要包含单独的头文件。Boost.Signals 库的核心部分定义在名字空间 boost 中,高级特性则定义在 boost::signals 中。

以下是 signal 的部分内容,其后将对其中最主要的成员函数进行了简要的介绍。如果你需要完整的参考,请见 Signals 的在线文档。

namespace boost {

template<typename Signature,
// Function type R(T1, T2, ..., TN)
typename Combiner = last_value<R>,
typename Group = int,
typename GroupCompare = std::less<Group>,
typename SlotFunction = function<Signature> >
class signal : public signals::trackable,
private noncopyable {
public:
signal(const Combiner&=Combiner(),
const GroupCompare&=GroupCompare());

~signal();

signals::connection connect(const slot_type&);
signals::connection connect(
const Group&,
const slot_type&);

void disconnect(const Group&);

std::size_t num_slots() const;

result_type operator()
(T1, T2, ..., TN);
};
}

类型

我们先来看看 signal 的模板参数。除了第一个参数,其它参数都有相应的缺省值,这有助于理解这些参数的基本意思。第一个模板参数是被调用的函数的签名。在这种 signals 的情况下,signal 本身就是被调用的实体。声明这个签名时,使用与普通函数签名相同的语法[1]。例如,一个返回 double 且接受一个类型 int 的参数的函数签名应该象这样:

[1] 细心的读者可以已经注意到 boost::function 也是这样用的。

signal<double(int)>

Combiner 参数表示一个函数对象,它负责逐个对该 signal 所有已连接的插槽(slot)进行调用。它同时也决定如何将组合这些调用返回的结果。缺省的类型是 last_value, 它只是简单地返回最后一个插槽的调用结果。

Groups 参数是用于组合所有连接到 signal 的插槽的一种类型。通过连接到不同的插槽组,你可以预设调用插槽的顺序,同时也可以断开插槽组。

GroupCompare 参数决定了如何排序 Groups,缺省值为 std::less<Group>, 通常它都是正确的。如果 Groups 使用了定制的类型,就有可能需要其它的排序方法。

最后,SlotFunction 参数表示插槽函数的类型,缺省值为 boost::function. 我想不出有什么理由改变这个缺省值。这个模板参数用于定义插槽的类型,定义的方法是一个公有的 typedef slot<SlotFunction> slot_type.

成员函数

signal(const Combiner&=Combiner(),
const GroupCompare&=GroupCompare());

在构造一个 signal 时,可以传入一个 Combiner,它是一个负责在信号到达时调用相应插槽并对返回值进行处理的对象。

~signal();

析构函数在析构时断开所有已连接的插槽。

signals::connection connect(const slot_type& s);

connect 函数把插槽 s 连接到 signal. 函数指针、函数对象、bind 表达式或者 lambda 表达式都可以用作插槽。connect 返回一个 signals::connection, 它是代表被创建的连接的句柄。通过使用这个句柄,插槽可以从 signal 断开,或者你也可以测试该插槽是否还有连接。

signals::connection connect(const Group& g, const slot_type& s);

这个 connect 的重载版本与前一个作用相似,但是它还把插槽 s 连接到组 g. 把一个插槽连接到一个组意味着当一个 signal 产生时,属于较前面的组的插槽会先被调用(即按组的顺序来调用,signal 模板的 GroupCompare 参数定义了组的顺序),而且属于组的所有插槽会在不属于组的插槽之前被调用(可能只有部分插槽是在组中的)。

void disconnect(const Group& g);

断开所有属于组 g 的已连接插槽。

std::size_t num_slots() const;

返回当前连接到 signal 的插槽数量。要测试插槽是否为空,应该调用函数 empty,而不要调用 num_slots 并测试其返回是否为0,因为 empty 的效率更高。

result_type operator()(T1, T2, ..., TN);

signals 使用调用操作符来调用。当信号产生时,必须传递适当的参数给调用操作符,必须符合 signal 的签名(即声明 signal 类型时的第一个模板参数)。参数的类型必须可以隐式转换为信号所需的类型,只有这样调用才可以成功。

Boost.Signals 中还有其它的类型,我们将在本章剩余部分讨论它们。我们还将讨论 signal 类中有用的 typedefs。

 

抱歉!评论已关闭.