Asynchronous operations in Asio all expect a function object argument, the completion handler, which they invoke when the asynchronous operation completes. The signature of the handler depends on the type of operation. For example, a handler posted using
io_service::post()
must have the signature:
void handler();
while an asynchronous wait operation expects:
void handler(error_code ec);
and asynchronous read/write operations want:
void handler(error_code ec, size_t length);
Non-trivial applications will need to pass some context to the completion handler, such as a
this
pointer. One way to do this is to use a function object adapter like
boost::bind
,
std::tr1::bind
or (as of C++0x)
std::bind
.
Unfortunately, for many C++ programmers,
bind
represents a little bit of magic. This is not helped by the impenetrable compiler errors that confront you when you use it incorrectly. And, in my experience, the underlying concept (where some function arguments are bound up-front, while others are delayed until the point of call) can present quite a steep learning curve.
I have put together some diagrams to help explain how bind works. For clarity, I have taken a few liberties with C++ syntax (e.g. omitting the parameter types on the function call operator) and (over-)simplified
bind
‘s implementation. Finally, the examples are limited to those likely to be useful with Asio. Comments and suggestions welcome.
bind
can be used to adapt a user-supplied function expecting one argument into a function object that takes zero arguments. The bound value (
123
in this example) is stored in a function object and is automatically passed to the user-supplied function as required:
[ click images for full size ]
Binding an argument can be used to turn a class member function into a zero-argument function object. As you know, non-static member functions have an implicit
this
parameter. This means that an appropriate pointer needs to be bound into the function object:
Alternatively, the implicit
this
can be made explicit by adapting a member function into a function object taking one argument:
Function objects will often use both bound arguments and arguments supplied at the point of use. This can be done using member functions:
or non-member functions:
Sometimes the function object’s point of use will supply arguments which are not required to call the target function.
bind
will automatically discard these surplus arguments:
The surplus argument(s) need not be the at the end of the function object signature:
Finally,
bind
allows you to the reorder arguments to adapt the target function to the necessary function object signature:
翻译:
在Asio中,所有的异步操作都需要一个函数对象参数,亦称为completion handler,用于在异步操作完成时调用。这个handler的签名取决于异步操作的类型。例如:一个用于
io_service::post()
的handler必须拥有下面的签名:
void handler();
但异步等待操作的handler的签名则如下:
void handler(error_code ec);
而异步读/写操作则需要如下签名的函数:
void handler(error_code ec, size_t length);
事实上,应用程序可能需要传递某种应用环境(context)给这个completion handler,例如
this
指针。用
boost::bind, std::tr1::bind
或者C++0x的
std::bind
等函数对象适配器来达到这个目的,不失为一种办法。
不幸的是,对于许多C++猿类来说,
bind
代表了某种魔法。你在使用它时,如果稍有不慎,编译器就会给你一大堆编译错误,关键是你还不知道你错在哪儿。并且,根据我的经验,它的基本概念(有些函数参数预先被绑定,而有些则是在调用时才被绑定)的学习曲线非常陡峭。
为此,我画了一些图来解释
bind
是如何工作的。为了清晰,我对图中的语法作了一些简化(例如,省略函数调用操作符的参数类型),并且简化了
bind
的实现。最后,the examples are limited to those likely to be useful with Asio(译者注:这句根本不知道是什么意思好吗。。)。欢迎大家给我提建议和留言(感谢xiaoyu建议,这句话大概意思是:下面列举的主要是一些有助于在Asio中使用bind的例子)。
bind
可以用来将用户提供的需要一个参数的函数转换成不需要参数的函数对象。绑定的值(在这个例子中是123)存储在函数对象内并且会被自动传递给用户指定的函数:
参数绑定也可以用于将类成员函数转换成零参数的函数对象。猿类们都知道,非静态成员函数需要一个隐式的
this
参数。这意味着需要绑定一个合适的类实例指针到这个函数对象:
相应地,隐式的
this
指针也可以显式地传递给需要一个参数的函数对象:
函数对象经常同时使用提前绑定的参数和调用时才提供的参数。这个可以用成员函数来实现:
当然也可以使用非成员函数:
有些时候函数对象被调用时会提供多余的参数,而这些参数是目标函数不需要的。
bind
会自动忽略这些多余的参数:
这些多余的参数不需要一定在函数对象签名的最后:
最后,
bind
还允许重新组织函数对象的参数顺序: