O''''Reilly Network For Information About''''s Book part 94 doc

6 60 0
O''''Reilly Network For Information About''''s Book part 94 doc

Đang tải... (xem toàn văn)

Thông tin tài liệu

function f_ in a Boolean context. If the function doesn't contain a target, a function or function object, this yields false, which means that we cannot invoke it. This is tested in the implementation of execute. Here's a sample program that uses our new class: 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; // Invoked from some GUI control if (play.enabled()) { play.execute(); } // Invoked from some scripting client 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(); } To create the concrete commands, we use Boost.Bind to create function objects that, when invoked through the function call operator, calls the correct member function of tape_recorder. These function objects are self-contained; they are nullary function objects, meaning that they can be directly invoked without passing any arguments, which is what a boost::function<void()> expects. In other words, the following code snippet creates a function object that invokes the member function play on the instance of tape_recorder that it is configured with. boost::bind(&tape_recorder::play,&tr) Normally, we wouldn't be able to store the returned function object from the call to bind, but because Boost.Function is compatible with any function object, this is possible. boost::function<void()> f(boost::bind(&tape_recorder::play,&tr)); Notice that the class also supports calling record, which takes an argument of type const std::string&, because of the member function set_function. Because the function object must be nullary, we need to bind the context so that record still gets its argument. That, of course, is a job for binders. Thus, before calling record, we create a function object that contains the string to be recorded. std::string s="What a beautiful morning "; record.set_function( boost::bind(&tape_recorder::record,&tr,s)); Executing the function object stored in record passes the string to tape_recorder::record, invoked on the tape_recorder instance tr. With Boost.Function and Boost.Bind, it is possible to achieve the decoupling that makes it possible for the invoking code to know nothing about the code being invoked. It's immensely useful to combine these two libraries in this way. Having shown you the command class, it's time for me to come clean. All you really need, because of the power of Boost.Function, is the following: typedef boost::function<void()> command; Using Boost.Lambda with Boost.Function Just as Boost.Function is compatible with the function objects that are created by Boost.Bind, it also supports Boost.Lambda, which also creates function objects. Any function object that you create with the Lambda library is compatible with the corresponding boost::function. We have covered some ground on binding in the previous section, and the main difference is that it is possible to do even more with Boost.Lambda. We can create these small, unnamed functions at ease, and store them in instances of boost::function for subsequent invocation. We have covered lambda expressions in the previous chapterany of the examples that were provided there produced function objects that could be stored in an instance of function. The combination of function and libraries that create function objects is extremely powerful. Thinking About the Cost There's no such thing as a free lunch, as the saying goes, and this certainly applies to Boost.Function, too. There are some disadvantages of using Boost.Function compared to using function pointers, most notably the increase in size. A function pointer obviously occupies the space of one function pointer (duh!), whereas an instance of boost::function is three times as large. This can be an issue when there is a very large number of callbacks. Function pointers are also slightly more efficient when invoked, because where a function pointer is invoked directly, Boost.Function may require two calls through function pointers. Finally, there may be cases where the backward compatibility with C libraries makes function pointers the only choice. Although these are potential disadvantages of Boost.Function, it is not very often that these are real-world issues. The extra size is still very small, and the overheard of the (potential) extra call through a function pointer usually comes at a very small cost indeed compared to the time it takes to actually perform the computations of the target function. It should be the rare situation that mandates using function instead of Boost.Function. The great advantages and the flexibility that is gained through using the library easily outweigh these costs. Under the Hood As always, it is valuable to understand at least the basics of how a library works. We shall consider the three cases of storing and invoking a function pointer, a pointer to member function, and a function object. These three are all different. To see exactly how Boost.Function works, just look at the source codewe are going to do things a bit differently, in an attempt to clearly show how different versions of such beasts demand slightly different approaches. We will also have a class with different requirements, namely that when calling a member function, a pointer to an instance of the class must be passed to the constructor of function1 (which is the name of our class). function1 supports functions taking exactly one argument. A relaxation of the requirements compared to Boost.Function is that even for member functions, only the type of the result and the argument need to be supplied. This is a direct consequence of the requirement that the constructor must be passed a pointer to an instance of the class for which the member is to be called (the type is automatically deduced). The approach we shall take is to create a parameterized base class that declares a virtual function for the function call operator; then, three classes are derived from this base class for the different forms of function invocations that we are to support. These classes take care of all the work, while another, function1, decides which of these concrete classes to instantiate depending on the arguments to the constructor. Here is the base class for the invokers, invoker_base. template <typename R, typename Arg> class invoker_base { public: virtual R operator()(Arg arg)=0; }; Next, we begin with the definition of function_ptr_invoker, which is a concrete invoker that derives publicly from invoker_base. Its purpose is to invoke free functions. The class also accepts two types, the return type and the argument type, and these are used for the constructor, which takes a function pointer as argument. template <typename R, typename Arg> class function_ptr_invoker : public invoker_base<R,Arg> { R (*func_)(Arg); public: function_ptr_invoker(R (*func)(Arg)):func_(func) {} R operator()(Arg arg) { return (func_)(arg); } }; This class template can be used to call any free function that accepts one argument. The function call operator simply invokes the function stored in func_ with the parameter it is given. Note the (admittedly strange) line that declares a variable to store the function pointer. R (*func_)(Arg); You can make that a little plainer using a typedef. typedef R (*FunctionT)(Arg); FunctionT func_; Next, we need a class template that can handle member function calls. Remember that it should also require a pointer to an instance of the class type when constructed, which is different from the way that Boost.Function works. It does save us some typing, because the compiler, not the programmer, deduces the type of the class. template <typename R, typename Arg, typename T> class member_ptr_invoker : public invoker_base<R,Arg> { 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); } }; This class template is very similar to the version for free function pointers. It differs from the previous one in that the constructor stores a member function pointer and a pointer to an object and the function call operator invokes the member function (func_) on that object (t_). Finally, we need a version that is compatible with function objects. This is actually the easiest implementation of all, at least when defining it our way. By using a single template parameter, we just state that the type T must indeed be a function object, because we intend to invoke it as such. Enough said. template <typename R, typename Arg, typename T> class function_object_invoker : public invoker_base<R,Arg> { T t_; public: function_object_invoker(T t):t_(t) {} R operator()(Arg arg) { return t_(arg); } }; Now that we have these building blocks in place, all that remains is to put the pieces together in our version of boost::function, the function1 class. We want a way to detect which kind of invoker to instantiate. We can then save it in a pointer to invoker_base. The trick here is to provide constructors that are capable of detecting which type of invoker is correct for the supplied arguments. It's just overloading, with a little twist involving parameterizing two of the constructors. template <typename R, typename Arg> class function1 { invoker_base<R,Arg>* invoker_; . bind the context so that record still gets its argument. That, of course, is a job for binders. Thus, before calling record, we create a function object that contains the string to be recorded the class for which the member is to be called (the type is automatically deduced). The approach we shall take is to create a parameterized base class that declares a virtual function for the. for the function call operator; then, three classes are derived from this base class for the different forms of function invocations that we are to support. These classes take care of all the

Ngày đăng: 07/07/2014, 08:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan