Boost.Bind provides argument binding for free functions, class member functions, and class variables.. This is a perfect fit for Boost.Function, where we often need this type of binding
Trang 1This is equivalent to using boost::ref and assigning the function object ks to each instance of function
The power that is wielded with the addition of state to callbacks is tremendous, and
is one of the great advantages to using Boost.Function rather than function
pointers
Using Boost.Bind with Boost.Function
Things become even more interesting when we combine Boost.Function with a library that supports argument binding Boost.Bind provides argument binding for free functions, class member functions, and class variables This is a perfect fit for Boost.Function, where we often need this type of binding because the classes that
we are working with are not themselves function objects So, we transform them into function objects using Boost.Bind, and then we can store them for later
invocation with Boost.Function When separating graphical user interfaces (GUIs) from details on how to handle actions (events) from the user, callbacks of some sort are almost always used If this callback mechanism is based on function
pointers, it is hard to avoid severe limitations of the types that can be used with the callback, which in turn increases the risk of adding coupling between the
presentation and the business logic We can avoid this altogether by using
Boost.Function, and when combined with a library that supports argument binding,
we can supply the context to invocations of functions with ease This is one of the most common uses of this libraryto separate knowledge of the business logic from the presentation layer
This example includes a state-of-the-art tape recorder, which is defined thusly
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";
Trang 2}
void rewind() {
std::cout << "zzzihw\n";
}
void record(const std::string& sound) {
std::cout << "Recorded: " << sound << '\n';
}
};
This tape recorder could be controlled from a GUI, or perhaps from a scripting client, or from any other source, which means that we don't want to couple the invocation of the functions to their implementation A common way to create that separation is through special objects that are only responsible for executing a
command, allowing the client to remain ignorant of how the command is executed This is known as the Command pattern, and is very useful in its own right One of the problems with the typical implementation of this pattern is the need to create separate classes to execute each command The following snippet demonstrates how this might look:
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) {}
Trang 3bool enabled() const {
return true;
}
void execute() {
p_->stop();
}
};
This is not a very attractive solution, because it leads to code bloat in the form of numerous simple command classes, all with the sole responsibility of invoking a single member function on an object Sometimes, this can prove necessary,
because the commands may need to implement business logic as well as execute a function, but it is often just a result of limitations in the tools that we are using These command classes can be used like this:
int main() {
tape_recorder tr;
// Using the command pattern
command_base* pPlay=new play_command(&tr);
command_base* pStop=new stop_command(&tr);
// Invoked when pressing a button
pPlay->execute();
pStop->execute();
delete pPlay;
delete pStop;
}
Now, rather than creating an additional number of concrete command classes, we could generalize a bit if we take advantage of the fact that the commands we are implementing are all calling a member function with a void return, taking no argument (ignoring for the moment the function record, which does take an
argument) Rather than creating a family of concrete commands, we could store a pointer to the correct member function in the class This is a huge step in the right direction,[6] and it works like this:
[6]
Albeit slightly less efficient
class tape_recorder_command : public command_base {
void (tape_recorder::*func_)();
tape_recorder* p_;
Trang 4public:
tape_recorder_command(
tape_recorder* p,
void (tape_recorder::*func)()) : p_(p),func_(func) {}
bool enabled() const {
return true;
}
void execute() {
(p_->*func_)();
}
};
This implementation of the commands is much nicer, because it relieves us from having to create separate classes that do basically the same thing The difference here is that we are storing a pointer to a tape_recorder member function in func_, which is provided in the constructor The execution of the command is not something you should show all your friends, because the pointer-to-member
operators do have a habit of confusing people However, this can be considered a low-level implementation detail, so that's fine With this class, we have performed
a generalization that proves useful in that we don't have to implement separate command classes anymore
int main() {
tape_recorder tr;
// Using the improved command
command_base* pPlay=
new tape_recorder_command(&tr,&tape_recorder::play);
command_base* pStop=
new tape_recorder_command(&tr,&tape_recorder::stop);
// Invoked from a GUI, or a scripting client
pPlay->execute();
pStop->execute();
delete pPlay;
delete pStop;
}
You may not realize it, but we're actually starting to implement a simplified
version of boost::function, which already does what we want Rather than reinvent the wheel, let's focus on the task at hand: separating the invocation and the
Trang 5implementation Here is a new implementation of the command class, which is a lot easier to write, maintain, and understand
class command {
boost::function<void()> f_;
public:
command() {}
command(boost::function<void()> f):f_(f) {}
void execute() {
if (f_) {
f_();
}
}
template <typename Func> void set_function(Func f) {
f_=f;
}
bool enabled() const {
return f_;
}
};
By using Boost.Function in the implementation, we can immediately benefit from the flexibility of being compatible both with functions and function
objectsincluding function objects produced by binders The command class stores the function in a boost::function that returns void and doesn't take any arguments To make the class more flexible, we provide a way to change the
function object at runtime, using a parameterized member function,
set_function
template <typename Func> void set_function(Func f) {
f_=f;
}
By using parameterization, any function, function object, or binder is compatible with our command class We could also have opted to have a boost:: function as argument, and achieve the same effect using the converting constructor of
function This command class is very general, and we can use it with our
tape_recorder class or just about anything else An additional advantage over the previous approach when using a base class and concrete derived classes (which in
Trang 6turn makes us use pointers for polymorphic behavior) is that it becomes easier to manage lifetime issueswe don't have to delete the command objects anymore, as they can be passed and saved by value We test whether the command is enabled
or not by using the