The best way to think of a stored function is a normal function object that is responsible for wrapping another function or function object.. It then makes perfect sense that this stored
Trang 1Usage
To start using Boost.Function, include "boost/function.hpp", or any of the
numbered versions, ranging from "boost/function/function0.hpp" to
"boost/function/function10.hpp" If you know the arity of the functions you want to store in functions, it taxes the compiler less to include the exact headers that are needed When including "boost/function.hpp", the other headers are all included, too
The best way to think of a stored function is a normal function object that is
responsible for wrapping another function (or function object) It then makes
perfect sense that this stored function can be invoked several times, and not
necessarily at the time when the function is created When declaring functions, the most important part of the declaration is the function signature This is where you tell the function about the signature and return type of the functions and/or function objects it will store As we've seen, there are two ways to perform such
declarations Here's a complete program that declares a boost::function that is capable of storing function-like entities that return bool (or a type that is implicitly convertible to bool) and accept two arguments, the first convertible to int, and the second convertible to double
#include <iostream>
#include "boost/function.hpp"
bool some_func(int i,double d) {
return i>d;
}
int main() {
boost::function<bool (int,double)> f;
f=&some_func;
f(10,1.1);
}
When the function f is first created, it doesn't store any function It is empty, which can be tested in a Boolean context or with 0 If you try to invoke a function that doesn't store a function or function object, it throws an exception of the type
bad_function_call To avoid that problem, we assign a pointer to some_func to f using normal assignment syntax That causes f to store the pointer to some_func Finally, we invoke f (using the function call operator) with the arguments 10 (an int) and 1.1 (a double) When invoking a function, one must supply exactly the number of arguments the stored function or function object expects
Trang 2The Basics of Callbacks
Let's look at how we would have implemented a simple callback before we knew about Boost.Function, and then convert the code to make use of function, and examine which advantages that brings forth We will start with a class that
supports a simple form of callbackit can report changes to a value by calling
whoever is interested in the new value The callback will be a traditional C-style callbackthat is, a free function This callback could be used, for example, for a GUI control that needs to inform observers that the user changed its value, without having any special knowledge about the clients listening for that information
#include <iostream>
#include <vector>
#include <algorithm>
#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<function_type> 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<vec_.size();++i) {
(*vec_[i])(value_);
}
}
};
int main() {
notifier n;
n.add_observer(&print_new_value);
Trang 3n.add_observer(&interested_in_the_change);
n.change_value(42);
}
Two functions, print_new_value and interested_in_the_change, have a signature that is compatible with what the notifier class supports The function pointers are stored in a vector, and then invoked in a loop whenever the value changes One syntax for invoking the functions is
(*vec_[i])(value_);
The dereferenced function pointer (which is what is returned from vec_[i]) is passed the value (value_) It's also valid to write the code differently, like this: vec_[i](value_);
This may seem a bit nicer to the eye, but more importantly, it also allows you to replace the function pointer with Boost.Function without syntactic changes for invocation Now, this works fine, but alas, function objects don't work at all with this notifier class Actually, nothing but function pointers work, which is a serious limitation It would work, however, if we were using Boost.Function Rewriting the notifier class is fairly straightforward
class notifier {
typedef boost::function<void(int)> function_type;
std::vector<function_type> vec_;
int value_;
public:
template <typename T> 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<vec_.size();++i) {
vec_[i](value_);
}
}
};
The first thing to do is to change the typedef to refer to boost::function rather than
a function pointer Before, we defined a function pointer; now we are using the
Trang 4generalization, which will soon prove its usefulness Next, we change the signature
of the member function add_observer to be parameterized on the argument type
We could have changed it to accept boost::function instead, but that means that users of our class would need to understand how function works[2] too, rather than just knowing about the requirements for the observer type It should be duly noted that this change of add_observer is not a result of switching to function; the code would continue to work anyway We make the change for generality; now, both function pointers, function objects, and instances of boost::function can be passed
to add_observer, without any changes to existing user code The code for adding elements to the vector is slightly altered, and now creates instances of
boost::function<void(int)> Finally, we change the code that invokes the functions
to the syntax that can be used for functions, function objects, and instances of boost::function.[3] This extended support for different types of function-like
"things" can immediately be put to use with a stateful function object, which
represents something that could not be easily done using functions
[2]
They should know about Boost.Function, but what if they don't? Everything that
we add to an interface will need to be explained to users at some point in time
[3]
Now we know that we should actually have been invoking like this from the beginning
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;
}
};
Trang 5This function object stores the previous value and prints it to std::cout whenever the value changes again Note that the first time it is invoked, it doesn't know about the previous value The function object detects this using a static bool variable in the function, which is initially set to true Because static variables in functions are initialized when the function is first invoked, it is only set to true during the first invocation Although static variables can be used like this to provide state for free functions too, we must understand that it does not scale well, and is hard to do safely in a multithreaded environment So, function objects with state are always to
be preferred over free functions with static variables The notifier class doesn't mind this function object at allit complies with the requirements and is therefore accepted This updated sample program demonstrates how this works
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 important line to examine is where we add an observer that isn't a function pointer, but an instance of the function object knows_the_previous_value Running the program gives the following output:
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
The great advantage here, more than relaxing the requirements on the functions (or rather, the additional support for function objects), is that we can introduce objects with state, which is a very common need The changes that we made to the notifier