不幸的是,我们不可能把象 std::min_element() 这样的一个算法直接传给 boost::signal 作为一个模板参数。 boost::signal 要求这个合成器定义一个名为 result_type 的类型,用于说明 operator()() 操作符返回值的类型。由于在标准 C++ 算法中缺少这个类型,所以在编译时会产生一个相应的错误。
除了对返回值进行分析以外,合成器也可以保存它们。
#include <boost/signal.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
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
{
return T(first, last);
}
};
int main()
{
boost::signal<int (), min_element<std::vector<int> > > s;
s.connect(func1);
s.connect(func2);
std::vector<int> v = s();
std::cout << *std::min_element(v.begin(), v.end()) << std::endl;
}
这个例子把所有返回值保存在一个 vector 中,再由 s() 返回。
三、连接 Connections
函数可以通过由 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();
}
boost::signal的disconnect()方法需要传入一个函数指针,而直接调用 boost::signals::connection 对象上的 disconnect() 方法则略去该参数。
除了 disconnect() 方法之外,boost::signals::connection 还提供了其它方法,如 block() 和 unblock()。
#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);
c.block();
s();
c.unblock();
s();
}
以上程序只会执行一次 func()。 虽然信号 s 被触发了两次,但是在第一次触发时 func() 不会被调用,因为连接 c 实际上已经被 block() 调用所阻塞。 由于在第二次触发之前调用了 unblock(),所以之后 func() 被正确地执行。
除了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();
}
因为连接对象 c 在信号触发之前被销毁,所以 func() 不会被调用。
boost::signals::scoped_connection 实际上是派生自 boost::signals::connection 的,所以它提供了相同的方法。它们之间的区别仅在于,在析构 boost::signals::scoped_connection 时,连接会自动释放。
虽然 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();
}
以上程序使用 Boost.Bind 将一个对象的方法关联至一个信号。 在信号触发之前,这个对象就被销毁了,这会产生问题。 我们不传递实际的对象 w,而只传递一个指针给 boost::bind()。 在 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();
}
如果现在再执行,num_slots()会返回0以确保不会试图调用已销毁对象之上的方法。仅需的修改是让world类继承自boost::signals::trackable。当使用对象的指针而不是对象的副本来关联函数至信号时,boost::signals::trackable可以显著简化连接的管理。
四、练习
编写一个程序,定义一个名为 button 的类,表示GUI中的一个可点击按钮。 为该类加入两个方法 add_handler() 和 remove_handler(),它们均要求一个函数名作为参数。 如果 click() 方法被调用,已登记的函数将被按顺序执行。
如下测试你的代码,创建一个 button 类的实例,从事件处理器内部向标准输出流写出一个信息。 调用 click() 函数模拟用鼠标点击该按钮。
五、解答
实在搞不懂这个Boost库了,我试了1_33、1_42、1_47三个版本,提示三种奇怪的头文件错误,开始时怀疑是我的老编译器VC6.0的问题,后来在linux下换用开源的Eclipse,居然也一样报错,无语,继续寻找问题,下面的代码是自己写的,为验证,待此问题解决后我会验证并修改错误。
#include <boost/bind.hpp>
#include <boost/signal.hpp>
#include <memory>
#include <iostream>
/*使用该方法可以再结束后自动释放连接并销毁对象,最简洁*/
class button:public boost::signals::trackable
{
public:
button(){}
void add_handler()
{
std::cout<<"Handle is added!!"<<std::endl;
}
void remove_handler()
{
std::cout<<"Handle is removed!!"<<std::endl;
}
};
int main(int argc,char *argv[])
{
boost::signal<void ()> click;
std::auto_ptr<button> but(new button());
click.connect(boost::bind(&button::add_handler,but.get()));
click.connect(boost::bind(&button::remove_handler,but.get()));
click();
return 0;
}