proxy connect abort处理方法_Boost c++之事件处理

1.概述

      本文中Boost.Signals2库提供了一个简单的方法在C++中应用这一模式。严格来说,Boost.Function能够将一个以上的事件处理器关联至单个事件。因此,Boost.Signals可以更好地支持事件驱动的开发,当需要进行事件处理时,应作为第一选择。

2.信号signals

      Boost.Signals所实现的模式被命名为'信号至插槽'(signal to slot),它基于以下概念:当对应的信号被发出时,相关联的插槽即被执行。原则上,你可以把单词'信号'和'插槽'分别替换为'事件'和'事件处理器'。不过,由于信号可以在任意给定的时间发出,所以这一概念放弃了'事件'的名字。

       因此,Boost.Signals没有提供任何类似于'事件'的类。相反,它提供了一个名为boost::signal的类,定义与boost/signal.hpp,实际上,这个头文件是唯一一个需要知道的,因为它会包含其它相关的头文件。

eg1:

#include

void func()

{

cout << "hello ,boost c++." << endl;

}

int main()

{

boost::signals2::signal s;

s.connect(func);

s();

return 0;

}

运行结果:

hello ,boost c++

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

        函数func()被通过connect()方法关联至信号s,由于func()符合要求的void()签名,所以该关联成功建立。因此当信号s被触发时,func()将被调用。

         信号是通过调用s来触发的,就像普通的函数那样。这个函数的签名对应于作为模板参数掺入的签名:因为void()不要求任何参数,所以括号内是空的。

        调用s会引发一个触发器,进而执行相应的func()函数-之前用connect()关联了的。
        同一个例子也可以用Boost.function来实现。

eg2:

#include

int main()
{
     boost::function f;
     f = func;
     f();

    return 0;
}

运行结果:

        hello ,boost c++.

       和前一个例子相类似,func()被关联至f,当f被调用时,就会相应执行func()。boost.function仅限于这种情况下适用,而Boost.Signls则提供了多得多的方式,如关联多个函数至单个特定信号。

eg3:

#include

#include

using namespace std;

void func()

{

cout << "hello ,boost c++." << endl;

}

void func2()

{

cout << "hello,world!" << endl;

}

int main()

{

boost::signals2::signal s;

s.connect(func);

s.connect(func2);

s();

return 0;

}

运行结果:

hello ,boost c++.

hello,world!

        boost::signal可以通过反复调用connect()方法来把多个函数赋值给单个特定信号。当该信号被触发时,这些函数被按照之前用connect()进行关联的顺序来执行。

        另外,执行的顺序也可以通过另一个重载版本来明确指定,该重载版本要求以一个int类型的值作为额外的参数。

 eg4:

void func1()

{

std::cout << "Hello" << std::flush;

}

void func2()

{

std::cout << ", world!" << std::endl;

}

int main()

{

boost::signals2::signal s;

s.connect(1, func2);

s.connect(0, func1);

s();

}

运行结果:

Hello,world!

         和之前一个例子一样,func1()在func2()之前执行。要释放某个函数与给定信号的关联,可以使用disconnect()方法。此外,boost::signal还提供了几个方法:

num_slots()返回已关联函数的数量,如果没有函数被关联,则返回0。这这种特定情况下,可以用empty()方法来代替。

disconnect_all_slots()方法所做的实际上正是它的名字所表达的:释放所有已有的关联。

看完了函数如何被关联至信号,以及弄明白了信号被触发时的调用关系后,还有一个问题就是:这个函数的返回值去哪里?

eg5:

#include

#include

int func1()

{

return 1;

}

int func2()

{

return 2;

}

int main()

{

boost::signals2::signal s;

s.connect(func1);

s.connect(func2);

cout << s() << endl;

}

运行结果:

2

func1()和func2()都具有int类型的返回值。s将处理两个返回值,并将它们都写出至标准输出流。那么到底会发生什么呢?

       以上例子实际上会把2写出至标准输出流。两个返回值都会被s正确接收,但除了最后一个值,其他值都会被忽略。缺省情况下,所有关联函数中,实际上只有最后一个返回值被返回。

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

eg6:

#include

#include

#include

int func1()

{

return 1;

}

int func2()

{

return 2;

}

namespace test {

template

struct min_element

{

typedef T result_type;

template

T operator()(InputIterator first, InputIterator last)const

{

return T(first, last);

}

};

}

int main()

{

boost::signals2::signal > > s; //此处使用了test命名空间,否则vs2019会报min_elemet符号不明确

s.connect(func1);

s.connect(func2);

std::vector a = s();

cout << *std::min_element(a.begin(),a.end()) << endl;

}

运行结果:

1

       合成器是一个重载了operator()()操作符的类。这个操作符会被自动调用,传入两个迭代器,指向某个特定信号的所有返回值。以上例子使用了标准c++算法std::min_element()来确定并返回最小的值。

       如果直接调用s(),并用cout打印,不幸的是,我们不可能把象std::min_element()这样的一个算法直接传递给boost::signal作为一个模板参数。boost::signal要求这个合成器定义一个名为result_type的类型,用于说明operator()()操作符返回值的类型。由于在标准c++算法中缺少这个类型,所以在编译时会产生一个相应的错误。

       上述例子把所有的s()的返回值报错在一个vector中,然后再调用min_element算法得到结果。

3.连接Connections

   可以借助提供的connect()和disconnect()成员功能来管理功能boost::signals2::signal。因为connect()返回类型为Object的对象Boost::signals2::connection,所以也可以以不同的方式管理关联。

eg7:

#include

#include

int main()

{

boost::signals2::signal s;

boost::signals2::connection c = s.connect(

[] {

std::cout << "Hello, world!" << std::endl;

}

);

s();

c.disconnect();

}

运行结果:

Hello,world!

       上述例子中,boost :: signals2 :: signal的disconnect()成员函数需要传递函数指针。然而通过在boost ::signals2 :: connection对象上调用disconnect()可以避免这种情况。

       boost::signals2::shared_connection_block使用时可以短时间内阻止功能而不从信号中删除关联。

eg8:

     #include

int main()

{

boost::signals2::signal s;

boost::signals2::connection c = s.connect([] { std::cout <

s();

boost::signals2::shared_connection_block b{c};

s();

b.unblock();

s();

}

运行结果:

Hello,world!

Hello,world!

   其实执行了两次lambda函数。信号s触发了3次,但是由于boost::signals::shared_connection_block创建了一个对象类型来阻止调用,因此第二次不调用lambda函数。一旦对象超出范围,该块将被自动删除。也可以通过调用明确删除一个块unblock()。因为它是最后一个触发器之前调用的,所以再次执行了对lambda函数的最终调用。

      另外unblock(),boost::signals2::shared_connection_block提供了成员函数block()和blocking()。前者用于在调用之后阻止连接unblock(),而后者则可以检查当前是否阻止了连接。

       请注意,boost::signals2::shared_connection_block带有单词“ shared ”是有原因的:boost::signals2::shared_connection_block可以使用同一连接初始化多个类型的对象。

eg9:

using namespace boost::signals2;

int main()

{

signal s;

connection c = s.connect([] { std::cout << "Hello,world!\n"; });

shared_connection_block b1{ c, false };

{

shared_connection_block b2{ c };

std::cout << std::boolalpha << b1.blocking() << '\n';

s();

}

s();

}

运行结果:

false

        Hello, world!

       两次访问s,但是仅第二次调用lambda函数。程序Hello, world!仅一次写入标准输出流。因为false作为第二个参数传递给构造函数,所以类型的第一个对象boost::signals2::shared_connection_block不会阻塞与信号s的连接。因此,调用blocking()对象b1返回false。

   但是,在第一次访问s时不会执行lambda函数,因为访问仅在boost::signals2::shared_connection_block实例化第二个类型的对象之后发生。通过不向构造函数传递第二个参数,该连接将被对象阻止。当第二次访问时,执行lambda函数,因为一旦块被自动移除b2去的范围进行。

一旦其成员函数与信号相关联的对象被破坏,Boost.Signals2就可以释放连接。

除了boost::signals2::connection这个外,还有一个名为boost::signals::scoped_connection的类,它会在析构时候自动释放连接。

eg10:

void func()

{

cout << "func id called." << endl;

}

int main()

{

boost::signals2::signal s;

{

boost::signals2::scoped_connection c =s.connect(func);        

}

s();

cout<

}

运行结果:

Love China!

因为连接对象c在信号触发之前被销毁,所以func()不会被调用。

boost::signals::scoped_connect实际上是派生自 boost::signals::connection 的,所以它提供了相同的方法。它们之间的区别仅在于,在析构 boost::signals::scoped_connection 时,连接会自动释放。

虽然 boost::signals::scoped_connection 的确令自动释放连接更为容易,但是该类型的对象仍需要管理。如果在其它情形下连接也可以被自动释放,而且不需要管理这些对象的话,就更好了。

eg11:

#include
#include
#include
#include

 class test

 {

 public:

         voidfun()const

         {

                 std::cout<< "test" << std::endl;

         }

 private:

 };

 int main()

 {

         boost::signals2::signal s;

         {

                 std::auto_ptrw(new test());        

                 s.connect(boost::bind(&test::fun,w.get()));

         }

         cout<< s.num_slots() << endl;

         s();

 }

运行结果:

1

test

         以上程序使用 Boost.Bind 将一个对象的方法关联至一个信号。在信号触发之前,这个对象就被销毁了,这会产生问题,存在一定的风险。我们不传递实际的对象 w,而只传递一个指针给 boost::bind()。在 s() 被实际调用的时候,该指针所引向的对象已不再存在。

      可以如下修改这个程序,使得一旦对象 w 被销毁,连接就会自动释放。仅需的修改是让 test 类继 boost::signals2::trackable。

如果现在再执行,num_slots() 会返回 0 以确保不会试图调用已销毁对象之上的方法。  当使用对象的指针而不是对象的副本来关联函数至信号时,boost::signals2::trackable 可以显著简化连接的管理。

当继承了 boost::signals2::trackable后运行结果为:

0

从而提高了程序的安全性。

总结:

db29f4d0798b0dcbcc41cc8d3ed8020d.png

参考:http://zh.highscore.de/cpp/boost/eventhandling.html

             https://theboostcpplibraries.com/boost.signals2-connections

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值