![]() |
Home | Libraries | People | FAQ | More |
This section gives an overview of the key programming techniques used to implement this library.
![]() |
Note |
|---|---|
The code listed here can be used by curious readers and library maintainers as a reference in trying to understand the library source code. There is absolutely no guarantee that the library implementation uses the exact code listed here. |
This library uses a local class to implement the local function object. However,
in C++03
local classes (and therefore the local function objects they implement) cannot
be passed as template parameters (e.g., to the std::for_each
algorithm), this is instead possible in C++11,
MSVC, and some other compilers (see [N2657]).
To work around this limitation, this library investigated the following two
"tricks" (both tricks can be extended to support eventual function
default parameters):
void* pointer (local classes can always be used
for type casting).
operator()
is then called via polymorphism.
For example (see also impl_tparam_tricks.cpp):
#define BOOST_TEST_MODULE TestImplTparamTricks #include <boost/test/unit_test.hpp> #include <vector> #include <algorithm> // Casting functor trick. struct casting_func { explicit casting_func(void* obj, void (*call)(void*, const int&)): obj_(obj), call_(call) {} // Unfortunately, function pointer call is not inlined. inline void operator()(const int& num) { call_(obj_, num); } private: void* obj_; void (*call_)(void*, const int&); }; // Virtual functor trick. struct virtual_func { struct interface { // Unfortunately, virtual function call is not inlined. inline virtual void operator()(const int&) {} }; explicit virtual_func(interface& func): func_(&func) {} inline void operator()(const int& num) { (*func_)(num); } private: interface* func_; }; BOOST_AUTO_TEST_CASE( test_impl_tparam_tricks ) { int sum = 0, factor = 10; // Local class for local function. struct local_add: virtual_func::interface { explicit local_add(int& _sum, const int& _factor): sum_(_sum), factor_(_factor) {} inline void operator()(const int& num) { body(sum_, factor_, num); } inline static void call( void* obj, const int& num) { local_add* self = static_cast<local_add*>(obj); self->body(self->sum_, self->factor_, num); } private: int& sum_; const int& factor_; inline void body(int& sum, const int& factor, const int& num) { sum += factor * num; } } add_local(sum, factor); casting_func add_casting(&add_local, &local_add::call); virtual_func add_virtual(add_local); std::vector<int> v(10); std::fill(v.begin(), v.end(), 1); // std::for_each(v.begin(), v.end(), add_local); // Error but OK on C++11. std::for_each(v.begin(), v.end(), add_casting); // OK. std::for_each(v.begin(), v.end(), add_virtual); // OK. BOOST_CHECK( sum == 200 ); }
The casting functor trick measured slightly better run-time performances than the virtual functor trick so the current implementation of this library uses the casting functor trick (probably because in addition to the indirect function call, the virtual functor trick also requires accessing the virtual function table). However, neither one of tricks has been observed to allow for compiler optimizations that inline the local function calls (because they rely on one indirect function call via either a function pointer or a virtual function respectively). Therefore, on compilers that accept local types as template parameters (MSVC, C++11, etc, see [N2657]), this library automatically generates code that passes the local class type directly as template parameter without using neither one of these two tricks in order to take full advantage of compiler optimizations that inline the local function calls.
This library macros can parse the list of specified parameters and detect if
any of the bound variable names matches the token this_
(to generate special code to bind the object in scope), or if the variable
is bound by const (to generate
special code to bind by constant), etc. The parameter tokens are inspected
using preprocessor metaprogramming and specifically using the macros defined
by the files in the boost/local_function/detail/preprocessor/keyword/
directory. [29]
For example, the following code defines a macro that allows the preprocessor
to detect if a set of space-separated tokens ends with this_
or not (see also impl_pp_keyword.cpp):
#include <boost/local_function/detail/preprocessor/keyword/thisunderscore.hpp> #include <boost/local_function/detail/preprocessor/keyword/const.hpp> #include <boost/local_function/detail/preprocessor/keyword/bind.hpp> #define BOOST_TEST_MODULE TestImplPpKeyword #include <boost/test/unit_test.hpp> // Expand to 1 if space-separated tokens end with `this_`, 0 otherwise. #define IS_THIS_BACK(tokens) \ BOOST_LOCAL_FUNCTION_DETAIL_PP_KEYWORD_IS_THISUNDERSCORE_BACK( \ BOOST_LOCAL_FUNCTION_DETAIL_PP_KEYWORD_BIND_REMOVE_FRONT( \ BOOST_LOCAL_FUNCTION_DETAIL_PP_KEYWORD_CONST_REMOVE_FRONT( \ tokens \ ))) BOOST_AUTO_TEST_CASE( test_impl_pp_keyword ) { BOOST_CHECK( IS_THIS_BACK(const bind this_) == 1 ); BOOST_CHECK( IS_THIS_BACK(const bind& x) == 0 ); }
[29] This technique is at the core of even more complex preprocessor parsing macros like the ones that parse the Contract++ syntax.