Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Tutorial

Local Functions
Binding Variables
Binding the Object this
Templates

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:

  • If a bound variable was declared constant in the enclosing scope, it will always be bound by constant value or constant reference even if 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]
  • If a bound variable was declared as a reference in the enclosing scope, it will still be bound by value unless it is explicitly bound by reference using 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] Warning

The library will generate a compile-time error if this is mistakenly used instead of this_ to bind the object in the local function declaration. However, mistakenly using this instead of this_ to access the object within the local function body will leads to undefined behaviour and it will not necessarily generate a compile-time error. [14] Programmers are ultimately responsible to make sure that this is never used within a local function.

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.


PrevUpHomeNext