...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
This section illustrates basic usage of this library.
Local functions are defined using macros from the header file boost/local_function.hpp
. The
macros must be used from within a declarative context (this is a limitation
with respect to C++11
lambda functions which can instead be declared also within expressions):
#include <boost/local_function.hpp> // This library header. ... { // Some declarative context. ... result-type BOOST_LOCAL_FUNCTION(parameters) { body-code } BOOST_LOCAL_FUNCTION_NAME(name) ... }
The code expanded by the macros declares a function object (or functor)
with the local function name specified by BOOST_LOCAL_FUNCTION_NAME
.
[5] The usual C++ scope visibility rules apply to local functions
for which a local function is visible only within the enclosing scope in
which it is declared.
The local function result type is specified just before the BOOST_LOCAL_FUNCTION
macro.
The local function body is specified using the usual C++ statement syntax
in a code block { ...
}
between the BOOST_LOCAL_FUNCTION
and BOOST_LOCAL_FUNCTION_NAME
macros. The body is specified outside any of the macros so eventual compiler
error messages and related line numbers retain their usual meaning and format.
[6]
The local function parameters are passed to the BOOST_LOCAL_FUNCTION
macro as a comma-separated list of tokens (see the No
Variadic Macros section for compilers that do not support variadic
macros):
BOOST_LOCAL_FUNCTION(parameter-type1 parameter-name1
,parameter-type2 parameter-name2, ...
)
The maximum number of parameters that can be passed to a local function is
controlled at compile-time by the configuration macro BOOST_LOCAL_FUNCTION_CONFIG_ARITY_MAX
.
For example, let's program a local function named add
that adds together two integers x
and y
(see also add_params_only.cpp
):
int BOOST_LOCAL_FUNCTION(int x, int y) { // Local function. return x + y; } BOOST_LOCAL_FUNCTION_NAME(add) BOOST_TEST(add(1, 2) == 3); // Local function call.
If the local function has no parameter, it is possible to pass void
to the BOOST_LOCAL_FUNCTION
macro (similarly to the C++ syntax that allows to use result-type
function-name
(void)
to declare
a function with no parameter): [7]
BOOST_LOCAL_FUNCTION(void) // No parameter.
For example, let's program a local function that always returns 10
(see also ten_void.cpp
):
int BOOST_LOCAL_FUNCTION(void) { // No parameter. return 10; } BOOST_LOCAL_FUNCTION_NAME(ten) BOOST_TEST(ten() == 10);
Variables in scope (local variables, enclosing function parameters, data members, etc) can be bound to a local function declaration. Only bound variables, static variables, global variables, functions, and enumerations from the enclosing scope are accessible from within the local function body. The types of bound variables are deduced automatically by this library using Boost.Typeof. [8]
This library introduces the new "keyword" bind
[9] which is used in place of the parameter type to specify the name
of a variable in scope to bind (therefore, bind
cannot be used as a local function parameter type). A variable can be bound
by value:
bind variable-name // Bind by value.
Or by reference prefixing the variable name with &
:
bind& variable-name // Bind by reference.
Furthermore, the "keyword" bind
can be prefixed by const
to
bind the variable by constant value:
const bind variable-name // Bind by constant value.
Or by constant reference:
const bind& variable-name // Bind by constant value.
Note that when const
is used,
it must always precede bind
.
[10]
If a variable is bound by value, then a copy of the variable value is taken at the point of the local function declaration. If a variable is bound by reference instead, the variable will refer to the value it has at the point of the local function call. Furthermore, it is the programmers' responsibility to ensure that variables bound by reference survive the existence scope of the local function otherwise the bound references will be invalid when the local function is called resulting in undefined behaviour (in other words, the usual care in using C++ references must be taken for variables bound by reference).
The type of a bound variable is automatically deduced using Boost.Typeof and it is the exact same type used to declare such a variable in the enclosing scope with the following notes:
bind...
is used instead of const bind...
. However, if a bound variable was not declared constant in the enclosing
scope then it will not be bound as constant unless constant binding is
forced using const bind...
. (Note that binding by constant reference
is not supported by C++11
lambda functions but it is supported by this library.) [11]
bind&
or const bind&
. [12]
When a variable is bound by value (constant or not), its type must be CopyConstructible
(i.e., its must
provide a copy constructor). As with passing parameters to usual C++ functions,
programmers might want to bind variables of complex types by (possibly constant)
reference instead of by value to avoid expensive copy operations when these
variables are bound to a local function.
For example, let's program the local function add
from the example in the Introduction
section. We bind the local variable factor
by constant value (because its value should not be modified by the local
function), the local variable sum
by non-constant reference (because its value needs to be updated with the
summation result), and program the body to perform the summation (see also
add.cpp
):
int main(void) { // Some local scope. int sum = 0, factor = 10; // Variables in scope to bind. void BOOST_LOCAL_FUNCTION(const bind factor, bind& sum, int num) { sum += factor * num; } BOOST_LOCAL_FUNCTION_NAME(add) add(1); // Call the local function. int nums[] = {2, 3}; std::for_each(nums, nums + 2, add); // Pass it to an algorithm. BOOST_TEST(sum == 60); // Assert final summation value. return boost::report_errors(); }
It is also possible to bind the object this
when it is in scope (e.g., from an enclosing non-static member function).
This is done by using the special symbol this_
(instead of this
) as the name
of the variable to bind in the local function declaration and also to access
the object within the local function body. [13]
Warning | |
---|---|
The library will generate a compile-time error if |
The object this
can be bound
by value:
bind this_ // Bind the object `this` by value.
In this case the local function will be able to modify the object when the
enclosing scope is not a constant member and it will not be able to modify
the object when the enclosing scope is a constant member. Otherwise, the
object this
can be bound by
constant value:
const bind this_ // Bind the object `this` by constant value.
In this case the local function will never be able to modify the object (regardless of whether the enclosing scope is a constant member or not).
Note that the object this
can
never be bound by reference because C++ does not allow to obtain a reference
to this
(the library will generate
a compile-time error if programmers try to use bind& this_
or const bind& this_
).
Note that this
is a pointer
so the pointed object is never copied even if this
is bound by value (also it is not possible to directly bind *this
because
*this
is an expression and not a variable name).
For example, let's program a local function add
similar to the one in the example from the Introduction
section but using a member function to illustrate how to bind the object
this
(see also add_this.cpp
):
struct adder { adder() : sum_(0) {} int sum(const std::vector<int>& nums, const int factor = 10) { void BOOST_LOCAL_FUNCTION(const bind factor, bind this_, int num) { this_->sum_ += factor * num; // Use `this_` instead of `this`. } BOOST_LOCAL_FUNCTION_NAME(add) std::for_each(nums.begin(), nums.end(), add); return sum_; } private: int sum_; };
Note that the local function has access to all class members via the bound
object this_
regardless of
their access level (public
,
protected
, or private
). [15] Specifically, in the example above the local function updates
the private data member sum_
.
When local functions are programmed within templates, they need to be declared
using the special macros BOOST_LOCAL_FUNCTION_TPL
and BOOST_LOCAL_FUNCTION_NAME_TPL
:
[16]
#include <boost/local_function.hpp> // This library header. ... { // Some declarative context within a template. ... result-type BOOST_LOCAL_FUNCTION_TPL(parameters) { body-code } BOOST_LOCAL_FUNCTION_NAME_TPL(name) ... }
The BOOST_LOCAL_FUNCTION_TPL
and BOOST_LOCAL_FUNCTION_NAME_TPL
macros have the exact same syntax of the BOOST_LOCAL_FUNCTION
and BOOST_LOCAL_FUNCTION_NAME
macros that we have seen so far.
For example, let's program a local function similar to the one from the
Introduction section
but within a template (see also add_template.cpp
):
template<typename T> T total(const T& x, const T& y, const T& z) { T sum = T(), factor = 10; // Must use the `..._TPL` macros within templates. T BOOST_LOCAL_FUNCTION_TPL(const bind factor, bind& sum, T num) { return sum += factor * num; } BOOST_LOCAL_FUNCTION_NAME_TPL(add) add(x); T nums[2]; nums[0] = y; nums[1] = z; std::for_each(nums, nums + 2, add); return sum; }
[5]
Rationale. The local function name must
be passed to the macro BOOST_LOCAL_FUNCTION_NAME
ending the function definition so this macro can declare a local variable
with the local function name to hold the local function object. Therefore
the local function name cannot be specified within the BOOST_LOCAL_FUNCTION
and it must appear instead after the local function body (even if that
differs from the usual C++ function declaration syntax).
[6] Rationale. If the local function body were instead passed as a macro parameter, it would be expanded on a single line of code (because macros always expand as a single line of code). Therefore, eventual compiler error line numbers would all report the same value and would no longer be useful to pinpoint errors.
[7]
Rationale. The C++03
standard does not allow to pass empty parameters to a macro so the macro
cannot be invoked as BOOST_LOCAL_FUNCTION()
. On C99
compilers with properly implemented empty macro parameter support, it would
be possible to allow BOOST_LOCAL_FUNCTION()
but this is already not the case for
MSVC so this syntax is never allowed to ensure better portability.
[8] Rationale. By binding a variable in scope, the local function declaration is specifying that such a variable should be accessible within the local function body regardless of its type. Semantically, this binding should be seen as an "extension" of the scope of the bound variable from the enclosing scope to the scope of the local function body. Therefore, contrary to the semantic of passing a function parameter, the semantic of binding a variable does not depend on the variable type but just on the variable name: "The variable in scope named x should be accessible within the local function named f". For example, this reduces maintenance because if a bound variable type is changed, the local function declaration does not have to change.
[9]
Obviously, the token bind
is not a keyword of the C++ language. This library parses the token bind
during macro expansion using preprocessor
meta-programming (see the Implementation
section). Therefore, bind
can be considered a new "keyword" only at the preprocessor meta-programming
level within the syntax defined by the macros of this library (thus it
is referred to as a "keyword" only within quotes).
[10]
Rationale. The library macros could have
been implemented to accept both syntaxes const
bind ...
and bind const
...
equivalently. However, handling
both syntaxes would have complicated the macro implementation without adding
any feature so only one syntax const
bind ...
is supported.
[11]
An historical note: Constant binding of variables in scope was the
main use case that originally motivated the authors in developing this
library. The authors needed to locally create a chuck of code to assert
some correctness conditions while these assertions were not supposed
to modify any of the variables they were using (see the Contract++
library). This was achieved by binding by constant reference const bind&
the variables needed by the assertions
and then by programming the local function body to check the assertions.
This way if any of the assertions mistakenly changes a bound variable
(for example confusing the operator ==
with =
), the compiler
correctly generates an error because the bound variable is of const
type within the local function
body (see also constant blocks in the Examples
section).
[12]
Rationale. Variables originally declared
as references are bound by value unless [const] bind&
is used so that references can be bound by both value [const] bind
and reference [const] bind&
(this is the same binding semantic
adopted by Boost.ScopeExit).
However, variables originally declared as constants should never loose
their const
qualifier
(to prevent their modification not just in the enclosing scope but
also in the local scope) thus they are always bound by constant even
if bind[&]
is used instead of const bind[&]
.
[13]
Rationale. The special name this_
was chosen following Boost
practise to postfix with an underscore identifiers that are named
after keywords (the C++ keyword this
in this case). The special symbol this_
is needed because this
is
a reserved C++ keyword so it cannot be used as the name of the internal
parameter that passes the bound object to the local function body. It would
have been possible to use this
(instead of this_
) within
the local function body either at the expenses of copying the bound object
(which would introduce run-time overhead and also the stringent requirement
that the bound object must have a deep copy constructor) or by relying
on an undefined
behaviour of static_cast
(which might not work on all platforms at the cost of portability).
[14]
Rationale. The local function body cannot
be a static member function of the local functor object in order to support
recursion (because the local function name is specified by the BOOST_LOCAL_FUNCTION_NAME
macro only after the body so it must be made available via a functor
data member named after the local function and local classes cannot have
static data members in C++) and nesting (because the argument binding
variable must be declared as a data member so it is visible in a local
function nested within the body member function) -- see the Implementation
section. Therefore, from within the local function body the variable
this
is visible but it refers
to the local functor and not to the bound object.
[15] Rationale. This is possible because of the fix to C++ defect 45 that made inner and local types able to access all outer class members regardless of their access level.
[16]
Rationale. Within templates, this library
needs to use typename
to explicitly
indicate that some expressions evaluate to a type. Because C++03
does not allow to use typename
outside templates, the special ..._TPL
macros are used to indicate that
the enclosing scope is a template so this library can safely use typename
to resolve expression type ambiguities.
C++11 and
other compilers might compile local functions within templates even when
the ..._TPL
macros are not used. However, it is recommended to always use the ..._TPL
macros within templates to maximize portability.