Boost学习3-事件处理

事件处理是指一个事件的发生引发了一系列的相关活动,这些活动可以动态的添加和删除。比如我们点一个按钮,可以触发很多的操作,我们可以注册一些活动给这个事件,也可以注销一些活动。观察者模式是解决事件处理一个很给力的机制,我们将要触发的活动作为Observer注册给事件,当事件发生的时候所有Observer会收到消息进行不同的操作。利用Boost::Signal库可以方便的实现观察者模式。

boost::signal是事件类,可以为事件注册多个事件处理器(也可以被称为插槽),这里的事件处理器是值符合signal要求的函数或者函数对象,看一个例子:

#include <boost/signal.hpp>
#include <iostream>

void funcHandler()
{
std::cout << "function" << std::endl;
}

class ObjectHandler
{
public:
void operator()() const
{
std::cout << "function object" << std::endl;
}
};


int main(int argc, char *argv[])
{
boost::signal<void ()> s;
s.connect(funcHandler); //1
s.connect(ObjectHandler()); //2
s();
return 0;
}


boost::signal编译的时候要将signal库包含进来,编译的命令是:

g++ -o out testsignal.cc -lboost_signals

可以看到,事件处理器可以是普通函数或者函数对象。boost::signal 实际上被实现为一个模板函数,具有被用作为事件处理器的函数的签名,该签名也是它的模板参数。 在这个例子中,只有签名为 void () 的函数可以被成功关联至信号 s。

我们首先声明了一个signal,他所需要的处理器形式(插槽形式)是返回参数void、返回值为void的函数,然后调用signal的connect成员函数来注册事件处理器(插槽),当调用s()的时候这两个插槽立即被调用。

通过反复调用connect可以为事件注册多个事件处理器,当事件被触发的时候,这些事件处理器按照之前用connect注册的顺序执行。

可以利用分组的形式来显示指定插槽的执行顺序。signal 有一个模板参数,名为 Group,其缺省类型为int,还可以是String等. Groups 缺省以 std::less<Group> 为排序标准,对于 int类型来说就是 operator< 。

把插槽指定到一个组的方法是,传递一个 Group 给 signal::connect. 一个已连接插槽不能改变其所属的组;要改变一个插槽所属的组,必须先断开它的连接,然后重新把它连接到 signal 上并同时指定新组。下面看一个例子:
#include <boost/signal.hpp>
#include <iostream>

void func1()
{
std::cout << "Hello, " << std::endl;
}

void func2()
{
std::cout << "World! :" << std::endl;
}

int main()
{
boost::signal<void ()> s;
s.connect(2, func1);
s.connect(1, func2);
s();
std::cout << s.num_slots() << std::endl;

return 0;
}


在调用connect时候指定了分组号,当触发s()时候会先执行func2。

分组号的类型也可以使用其他类型,比如std::string。

#include <boost/signal.hpp>
#include <iostream>
#include <string>

class slot
{
public:
slot(const std::string &ss) : s(ss) {}

void operator()() const
{
std::cout << s << std::endl;
}
private:
std::string s;
};

int main(int argc, char *argv[])
{
boost::signal<void (), boost::last_value<void>, std::string> sig;

slot s1("First");
slot s2("No group");
slot s3("Second");

sig.connect(s2);
sig.connect("Last group", s3);
sig.connect("First group", s1);

sig();

return 0;
}


编译运行,可得到结果:
First
Second
No group

首先我们定义一个插槽类,它在执行时输出一个 std::string 到 std::cout。

然后,我们声明 signal. boost::signal定义signal的模板参数中,第一个参数是插槽类型,第二个参数是Combiner类型,Combiner类型与返回值有关,我们在后面会讲到,第三个参数是Group类型。因为 Groups 参数是在 Combiner 类型之后的,所以我们必须同时指定 Combiner (我们只是按缺省值boost::last_value<void>来声明)。我们把 Groups 类型设为 std::string 。

在连接到插槽 s1, s2, 和 s3 时,所创建的组是以字母顺序排序的(因为这是 std::less<std::string> 的行为),因此 "First group" 先于 "Last group".

这个例子也说明了没有分组的插槽会在分组插槽之后被执行。

要释放某个函数与给定信号的关联,可以用 disconnect() 方法。
#include <boost/signal.hpp> 
#include <iostream>

void func1()
{
std::cout << "Hello" << std::endl;
}

void func2()
{
std::cout << ", world!" << std::endl;
}

int main()
{
boost::signal<void ()> s;
s.connect(func1);
s.connect(func2);
s.disconnect(func2);
s();
}


num_slots() 返回已关联函数的数量。如果没有函数被关联,则 num_slots() 返回0。 在这种特定情况下,可以用 empty() 方法来替代。 disconnect_all_slots() 方法所做的实际上正是它的名字所表达的:释放所有已有的关联。


下面看一下插槽的返回值,看下面例子:
#include <boost/signal.hpp> 
#include <iostream>

int func1()
{
return 1;
}

int func2()
{
return 2;
}

int main()
{
boost::signal<int ()> s;
s.connect(func1);
s.connect(func2);
std::cout << s() << std::endl;
}


boost::signal第二个参数是表示combiner类型,指定事件触发时调用s()的返回值,默认是boost::last_value<void>,只有最后一个返回值被返回。

可以定制一个信号,令每个返回值都被相应地处理。 为此,要把一个称为合成器(combiner)的东西作为第二个参数传递给 boost::signal。
#include <boost/signal.hpp>
#include <iostream>

int func1()
{
return 1;
}

int func2()
{
return 2;
}

template <typename T>
struct min_element
{
typedef T result_type;

template <typename InputIterator>
T operator()(InputIterator first, InputIterator last) const
{
if (first == last)
return T();

T min = *first++;
while (first != last) {
if( min > *first ) {
min = *first;
}
++first;
}
return min;
}
};

int main(int argc, char *argv[])
{
boost::signal<int (), min_element<int> > s;

s.connect(1, func1);
s.connect(2, func2);

std::cout << s() << std::endl;
return 0;
}


函数可以通过由 boost::signal 所提供的 connect() 和 disconnect() 方法的帮助来进行管理。 由于 connect() 会返回一个类型为 boost::signals::connection 的值,它们可以通过其它方法来管理。
#include <boost/signal.hpp> 
#include <iostream>

void func()
{
std::cout << "Hello, world!" << std::endl;
}

int main()
{
boost::signal<void ()> s;
boost::signals::connection c = s.connect(func);
s();
c.disconnect();
}


除了 disconnect() 方法之外,boost::signals::connection 还提供了其它方法,如 block() 和 unblock()。被block的插槽在unblock之前不会被调用。如:
#include <boost/signal.hpp>
#include <iostream>
#include <string>

class slot
{
public:
slot(const std::string &ss) : s(ss) {}

void operator()() const
{
std::cout << s << std::endl;
}
private:
std::string s;
};

int main(int argc, char *argv[])
{
boost::signal<void (), boost::last_value<void>, std::string> sig;

slot s1("First");
slot s2("No group");
slot s3("Second");

boost::signals::connection c = sig.connect(s2);
sig.connect("Last group", s3);
sig.connect("First group", s1);

c.block();
sig();

std::cout << std::endl;

c.unblock();
sig();

return 0;
}


输出为:
First
Second

First
Second
No group

可以看到,s2在第一次事件被触发时没有被调用。

除了 boost::signals::connection 以外,还有一个名为 boost::signals::scoped_connection 的类,它会在析构时自动释放连接。
#include <boost/signal.hpp> 
#include <iostream>

void func()
{
std::cout << "Hello, world!" << std::endl;
}

int main()
{
boost::signal<void ()> s;
{
boost::signals::scoped_connection c = s.connect(func);
}
s();
}


虽然 boost::signals::scoped_connection 的确令自动释放连接更为容易,但是该类型的对象仍需要管理。 如果在其它情形下连接也可以被自动释放,而且不需要管理这些对象的话,就更好了。
我们看下面的例子:
#include <boost/signal.hpp> 
#include <boost/bind.hpp>
#include <iostream>
#include <memory>

class world
{
public:
void hello() const
{
std::cout << "Hello, world!" << std::endl;
}
};

int main()
{
boost::signal<void ()> s;
{
std::auto_ptr<world> w(new world());
s.connect(boost::bind(&world::hello, w.get()));
}
std::cout << s.num_slots() << std::endl;
s();
}


在 s() 被实际调用的时候,该指针所引向的对象已不再存在。

可以如下修改这个程序,使得一旦对象 w 被销毁,连接就会自动释放:
#include <boost/signal.hpp> 
#include <boost/bind.hpp>
#include <iostream>
#include <memory>

class world :
public boost::signals::trackable
{
public:
void hello() const
{
std::cout << "Hello, world!" << std::endl;
}
};

int main()
{
boost::signal<void ()> s;
{
std::auto_ptr<world> w(new world());
s.connect(boost::bind(&world::hello, w.get()));
}
std::cout << s.num_slots() << std::endl;
s();
}


仅需的修改是让 world 类继承自 boost::signals::trackable。 当使用对象的指针而不是对象的副本来关联函数至信号时,boost::signals::trackable 可以显著简化连接的管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值