C11中的Bind和Func

在写毕业设计的时候,遇到了一个问题。我使用libtins这个库抓包并且解析,在这个库中,有两个很关键的工具,一个是Sniffer,还有一个是StreamFollower。

遇到的问题

Sniffer封装了libpcap的接口,维护了一个pcap的实例,来抓底层的包。而底层的包抓上来都是以太网帧。如果想要解析里面的数据,假设这些以太网帧最终的Payload是HTTP数据报文,那么就要从以太网帧到应用层的HTTP实现一个协议栈来解析。这样太花时间,好在libtins提供了StreamFollower,创建一个这个类的对象,并且不断地将数据报传入这个对象,它就会在数据流中找出TCP连接,并且提供新建连接、客户端发送数据和服务端发送数据的事件处理接口。

问题就出在这个EventHandler上。我用的是Qt,在一个QThread的子类中启动Sniffer,并且希望在成员函数中emit信号来实现跟界面类的互动。可是问题来了,StreamFollower::new_stream_callback()的原型是这样的:

可见这个stream_callback_type其实是一个std::function<void(Stream&)>。

std::function

这std::function是C++11里的东西,维护一个函数指针。可以有几种赋值方法。首先可以把一个函数指针赋给它:

可以把一个Lambda表达式赋值给它:

还可以把一个实现了operator()的对象赋值给它:

这个东西在赋值后,就可以当作是一个函数指针,用法和函数指针完全一样,只要在后面加上括号调用就可以了。它经常被用在回调函数传参的场景,就如同上一节所说的,把new_stream_callback的参数值类型设定为std::function<void(Stream&)>,就可以方便地传参了,想以上述三种方式的任意一种方式传参,都没问题。

std::bind

我在向new_stream_callback传参的时候,就遇到了问题。我想把一个SnifferThread::onNewConnectionCallback(Stream &stream)传进去,结果却出错了。它告诉我,void (SnifferThread::)(Stream &)不能类型转换到void ()(Stream&)。其实问题就出在它是一个类的成员函数上。成员函数和一般的函数是不同的类型,这一点从报错的信息上也可以看出来。如果它们是同一类型,让你传参传进去了,在调用的时候,我怎么知道调用的是哪一个对象的这个函数呢?要解决这个问题,只能在传入函数指针的同时,把它绑定的对象一起传进去。但是给的接口中并没有这么一个让你传入对象的重载,怎么办呢?

其实在研究std::bind的时候,我想到了Python中的装饰器。std::bind也是一个C++11的东西,它可以包装一个函数,用例如下:

这个函数可以传入一个函数,并且指定一些占位符,也可以把现有的值绑定到参数。例子里就把1绑定到了b,并且把调用plusOne时传入的第一个参数绑定到了std::placeholder::_1位置上的参数。通过这个函数,我就可以编写一个wrapper,然后把它传入new_stream_callback里去了:

值得注意的是,placeholder是跟调用时的参数顺序一一对应的:

CC BY-NC-SA 4.0 C11中的Bind和Func by James & Alice is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

发表评论

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据