本库提供了当前标准库所不具备的功能。在那些对商业逻辑的表示层进行解耦的框架中,使用泛型回调是非常自然的、常见的。由于C++标准库不支持保存函数指针和函数对象以供稍后的调用,因此这个工具为标准库提供了非常重要的扩展。而且,本库完全兼容于标准库的绑定器(bind1st 和 bind2nd),就象前面所讨论过的其它绑定器一样,如 Boost.Bind 和 Boost.Lambda.
Function
头文件: "boost/function.hpp"
头文件 "function.hpp" 包含了带有从0到10个参数的函数原型(这是实现所定义的,在当前实现中缺省的上限就是10)。你也可以根据你的需要,只包含相应参数数量的头文件,文件名为 "function/functionN.hpp", 其中N可以从0到10。Boost.Function有两种不同的接口,其中一种的好处在于它的语法接近于函数声明(而且不要求函数的签名包含参数的数量),另一种接口的好处在于可以在多个编译器中工作。选择哪一种接口,至少部分地取决于你使用的编译器。如果可以,就使用我们称为首选语法(preferred syntax)的那种。在本章中,两种格式都会用到。
使用首选语法的声明
一个 function 的声明包括该 function 所兼容的函数或函数对象的签名以及返回类型。结果以及参数的类型以单个参数的方式全部提供给模板。例如,声明一个 function ,它返回 bool 并接受一个类型 int 的参数,如下:
用法
要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从 "boost/function/function0.hpp" 到 "boost/function/function10.hpp". 如果你知道你想保存在 function 中的函数的参数数量,这样做可以让编译器仅包含需要的头文件。如果包含 "boost/function.hpp", 那么就会把其它的头文件也包含进去。
理解被存函数的最佳方法是把它想象为一个普通的函数对象,该函数对象用于封装另一个函数(或函数对象)。这个被存的函数的最大用途是它可以被多次调用,而无须在创建 function 时立即使用。在声明 functions 时,声明中最重要的部分是函数的签名。这部分即是告诉 function 它将保存的函数或函数对象的签名和返回类型。我们已经看到,有两种方法来执行这个声明。这里有一个完整的程序,程序声明了一个 boost::function ,它可以保存返回 bool (或某个可以隐式转换为 bool 的类型)并接受两个参数的类函数实体,第一个参数可以转换为 int, 第二个参数可以转换为 double.
#include
#include "boost/function.hpp"
bool some_func(int i,double d) {
return i>d;
}
int main() {
boost::function f;
f=&some_func;
f(10,1.1);
}
#include
#include
#include
#include "boost/function.hpp"
void print_new_value(int i) {
std::cout <<
"The value has been updated and is now " << i << '\n';
}
void interested_in_the_change(int i) {
std::cout << "Ah, the value has changed.\n";
}
class notifier {
typedef void (*function_type)(int);
std::vector vec_;
int value_;
public:
void add_observer(function_type t) {
vec_.push_back(t);
}
void change_value(int i) {
value_=i;
for (std::size_t i=0;i
class notifier {
typedef boost::function function_type;
std::vector vec_;
int value_;
public:
template void add_observer(T t) {
vec_.push_back(function_type(t));
}
void change_value(int i) {
value_=i;
for (std::size_t i=0;i
class knows_the_previous_value {
int last_value_;
public:
void operator()(int i) {
static bool first_time=true;
if (first_time) {
last_value_=i;
std::cout <<
"This is the first change of value, \
so I don't know the previous one.\n";
first_time=false;
return;
}
std::cout << "Previous value was " << last_value_ << '\n';
last_value_=i;
}
};
int main() {
notifier n;
n.add_observer(&print_new_value);
n.add_observer(&interested_in_the_change);
n.add_observer(knows_the_previous_value());
n.change_value(42);
std::cout << '\n';
n.change_value(30);
}
The value has been updated and is now 42
Ah, the value has changed.
This is the first change of value, so I don't know the previous one.
The value has been updated and is now 30
Ah, the value has changed.
Previous value was 42
class some_class {
public:
void do_stuff(int i) const {
std::cout << "OK. Stuff is done. " << i << '\n';
}
};
boost::function f;
f=&some_class::do_stuff;
some_class s;
f(s,1);
boost::function f;
f=&some_class::do_stuff;
some_class s;
f(&s,3);
#include
#include "boost/function.hpp"
class keeping_state {
int total_;
public:
keeping_state():total_(0) {}
int operator()(int i) {
total_+=i;
return total_;
}
int total() const {
return total_;
}
};
int main() {
keeping_state ks;
boost::function f1;
f1=ks;
boost::function f2;
f2=ks;
std::cout << "The current total is " << f1(10) << '\n';
std::cout << "The current total is " << f2(10) << '\n';
std::cout << "After adding 10 two times, the total is "
<< ks.total() << '\n';
}
The current total is 10
The current total is 10
After adding 10 two times, the total is 0
int main() {
keeping_state ks;
boost::function f1;
f1=boost::ref(ks);
boost::function f2;
f2=boost::ref(ks);
std::cout << "The current total is " << f1(10) << '\n';
std::cout << "The current total is " << f2(10) << '\n';
std::cout << "After adding 10 two times, the total is "
<< ks.total() << '\n';
}
The current total is 10
The current total is 20
After adding 10 two times, the total is 20
class something_else {
public:
void operator()() const {
std::cout << "This works with boost::cref\n";
}
};
something_else s;
boost::function0 f1;
f1=boost::ref(s);
f1();
boost::function0 f2;
f2=boost::cref(s);
f2();
class something_else {
public:
void operator()() {
std::cout <<
"This works only with boost::ref, or copies\n";
}
};
something_else s;
boost::function0 f1;
f1=boost::ref(s); // This still works
f1();
boost::function0 f2;
f2=boost::cref(s); // This doesn't work;
// the function call operator is not const
f2();
int main() {
keeping_state ks;
boost::function1 f1; // 译注:原文为boost::function f1,有误
f1=boost::ref(ks);
boost::function1 f2(f1); // 译注:原文为boost::function f2(f1),有误
boost::function1 f3; // 译注:原文为boost::function f3,有误
f3=f1;
std::cout << "The current total is " << f1(10) << '\n';
std::cout << "The current total is " << f2(10) << '\n';
std::cout << "The current total is " << f3(10) << '\n';
std::cout << "After adding 10 three times, the total is "
<< ks.total() << '\n';
}
class tape_recorder {
public:
void play() {
std::cout << "Since my baby left me...\n";
}
void stop() {
std::cout << "OK, taking a break\n";
}
void forward() {
std::cout << "whizzz\n";
}
void rewind() {
std::cout << "zzzihw\n";
}
void record(const std::string& sound) {
std::cout << "Recorded: " << sound << '\n';
}
};
class command_base {
public:
virtual bool enabled() const=0;
virtual void execute()=0;
virtual ~command_base() {}
};
class play_command : public command_base {
tape_recorder* p_;
public:
play_command(tape_recorder* p):p_(p) {}
bool enabled() const {
return true;
}
void execute() {
p_->play();
}
};
class stop_command : public command_base {
tape_recorder* p_;
public:
stop_command(tape_recorder* p):p_(p) {}
bool enabled() const {
return true;
}
void execute() {
p_->stop();
}
};
int main() {
tape_recorder tr;
// 使用命令模式
command_base* pPlay=new play_command(&tr);
command_base* pStop=new stop_command(&tr);
// 在按下某个按钮时调用
pPlay->execute();
pStop->execute();
delete pPlay;
delete pStop;
}
class tape_recorder_command : public command_base {
void (tape_recorder::*func_)();
tape_recorder* p_;
public:
tape_recorder_command(
tape_recorder* p,
void (tape_recorder::*func)()) : p_(p),func_(func) {}
bool enabled() const {
return true;
}
void execute() {
(p_->*func_)();
}
};
int main() {
tape_recorder tr;
// 使用改进的命令模式
command_base* pPlay=
new tape_recorder_command(&tr,&tape_recorder::play);
command_base* pStop=
new tape_recorder_command(&tr,&tape_recorder::stop);
// 从一个GUI或一个脚本客户端进行调用
pPlay->execute();
pStop->execute();
delete pPlay;
delete pStop;
}
class command {
boost::function f_;
public:
command() {}
command(boost::function f):f_(f) {}
void execute() {
if (f_) {
f_();
}
}
template void set_function(Func f) {
f_=f;
}
bool enabled() const {
return f_;
}
};
template void set_function(Func f) {
f_=f;
}
int main() {
tape_recorder tr;
command play(boost::bind(&tape_recorder::play,&tr));
command stop(boost::bind(&tape_recorder::stop,&tr));
command forward(boost::bind(&tape_recorder::stop,&tr));
command rewind(boost::bind(&tape_recorder::rewind,&tr));
command record;
// 从某些GUI控制中调用...
if (play.enabled()) {
play.execute();
}
// 从某些脚本客户端调用...
stop.execute();
// Some inspired songwriter has passed some lyrics
std::string s="What a beautiful morning...";
record.set_function(
boost::bind(&tape_recorder::record,&tr,s));
record.execute();
}
std::string s="What a beautiful morning...";
record.set_function(
boost::bind(&tape_recorder::record,&tr,s));
虽然 Boost.Function 可能存在这些缺点,但是通常它们都不是什么实际问题。额外增加的大小非常小,而且(可能存在的)额外的函数指针调用所带来的代价与真正执行目标函数所花费的时间相比通常都是非常小的。要求使用函数而不能使用 Boost.Function 的情形非常罕见。使用这个库所带来的巨大优点及灵活性显然超出这些代价。
幕后的细节
至少了解一下这个库如何工作的基础知识是非常值得的。我们来看一下保存并调用一个函数指针、一个成员函数指针和一个函数对象这三种情形。这三种情形是不同的。要真正看到 Boost.Function 如何工作,只有看源代码——不过我们的做法有些不同,我们试着搞清楚这些不同的版本究竟在处理方法上有些什么不同。我们也有一个不同要求的类,即当调用一个成员函数时,必须传递一个实例的指针给 function1 (这是我们的类的名字)的构造函数。function1 支持只有一个参数的函数。与 Boost.Function 相比一个较为宽松的投条件是,即使是对于成员函数,也只需要提供返回类型和参数类型。这个要求的直接结果就是,构造函数必须被传入一个类的实例用于成员函数的调用(类型可以自动推断)。
我们将要采用的方法是,创建一个泛型基类,它声明了一个虚拟的调用操作符函数;然后,从这个基类派生三个类,分别支持三种不同形式的函数调用。这些类负责所有的工作,而另一个类,function1, 依据其构造函数的参数来决定实例化哪一个具体类。以下是调用器的基类,invoker_base.
template class invoker_base {
public:
virtual R operator()(Arg arg)=0;
};
template class function_ptr_invoker
: public invoker_base {
R (*func_)(Arg);
public:
function_ptr_invoker(R (*func)(Arg)):func_(func) {}
R operator()(Arg arg) {
return (func_)(arg);
}
};
template
class member_ptr_invoker :
public invoker_base {
R (T::*func_)(Arg);
T* t_;
public:
member_ptr_invoker(R (T::*func)(Arg),T* t)
:func_(func),t_(t) {}
R operator()(Arg arg) {
return (t_->*func_)(arg);
}
};
template
class function_object_invoker :
public invoker_base {
T t_;
public:
function_object_invoker(T t):t_(t) {}
R operator()(Arg arg) {
return t_(arg);
}
};
template class function1 {
invoker_base* invoker_;
public:
function1(R (*func)(Arg)) :
invoker_(new function_ptr_invoker(func)) {}
template function1(R (T::*func)(Arg),T* p) :
invoker_(new member_ptr_invoker(func,p)) {}
template function1(T t) :
invoker_(new function_object_invoker(t)) {}
R operator()(Arg arg) {
return (*invoker_)(arg);
}
~function1() {
delete invoker_;
}
};
bool some_function(const std::string& s) {
std::cout << s << " This is really neat\n";
return true;
}
class some_class {
public:
bool some_function(const std::string& s) {
std::cout << s << " This is also quite nice\n";
return true;
}
};
class some_function_object {
public:
bool operator()(const std::string& s) {
std::cout << s <<
" This should work, too, in a flexible solution\n";
return true;
}
};
int main() {
function1 f1(&some_function);
f1(std::string("Hello"));
some_class s;
function1
f2(&some_class::some_function,&s);
f2(std::string("Hello"));
function1
f3(boost::bind(&some_class::some_function,&s,_1));
f3(std::string("Hello"));
some_function_object fso;
function1
f4(fso);
f4(std::string("Hello"));
}
Function 总结
在以下情形时使用 Function 库