Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Annex: Alternatives

This section compares the features offered by this library with similar features offered by C++ and by other libraries.

Features

The following table compares local function features.

Local Function Feature

Boost.LocalFunction

C++11 Lambda Function (Not C++03)

Local Functor

Global Functor (Not Local)

Boost.Phoenix

Can be defined locally

Yes.

Yes.

Yes.

No. Therefore this not really an alternative implementation of local functions but it is listed here just for comparison.

Yes.

Can be defined using C++ statement syntax

Yes. Plus eventual compiler errors and debugging retain their usual meaning and format.

Yes. Plus eventual compiler errors and debugging retain their usual meaning and format.

Yes. Plus eventual compiler errors and debugging retain their usual meaning and format.

Yes. Plus eventual compiler errors and debugging retain their usual meaning and format.

No (it uses C++ expression template syntax).

Can be defined within expressions

No. It can be defined only within declarations.

Yes (plus the local function can be unnamed).

No. It can be defined only within declarations.

No. It can be defined only within declarations.

Yes (plus the local function can be unnamed).

Can be passed as template parameter (e.g., to STL algorithms)

Yes. The C++03 standard does not allow to pass local types as template parameters (see [N2657]) but this library implements a "trick" to get around this limitation (see the Implementation section).

Yes.

No on C++03 compilers (but yes on C++11 compilers and some compilers like MSVC 8.0, see [N2657]).

Yes.

Yes.

Access variables in scope

Yes. The variable names are repeated in the function declaration so they can be bound by value, by constant value, by reference, and by constant reference (the object this can also be bound using this_).

Yes. The variable names are repeated in the function declaration (plus there is a short-hand syntax to bind all variables in scope at once) so they can be bound by constant value and by reference (the object this can also be bound). However, variables cannot be bound by constant references (see below).

No. Programmers must manually program functor data members and explicitly specify their types to access variables in scope.

No. Programmers must manually program functor data members and explicitly specify their types to access variables in scope.

Yes. Variables in scope are accessible as usual within expressions (plus boost::phoenix::let can be used to bind variables by constant reference).

Polymorphic in the function parameter type

No (local functions cannot be function templates).

No (C++11 lambdas cannot be function templates).

No (local classes cannot have member function templates).

Yes.

Yes.

C++11 Lambda Function

C++11 lambda functions have most of the features of this library plus some additional feature (see also the example in the Introduction section):

For example, for non-copyable objects (see also noncopyable_cxx11_lambda_error.cpp and noncopyable_local_function.cpp):

C++11 Lambda Function

Boost.LocalFunction

struct n: boost::noncopyable {
    int i;
    n(int _i): i(_i) {}
};


int main(void) {
    n x(-1);

    auto f = [x](void) {    // Error: x is non-copyable, but if
        assert(x.i == -1);  // bind `&x` then `x` is not constant.
    };
    f();

    return 0;
}

struct n: boost::noncopyable {
    int i;
    n(int _i): i(_i) {}
};
BOOST_TYPEOF_REGISTER_TYPE(n) // Register for `bind& x` below.

int main(void) {
    n x(-1);

    void BOOST_LOCAL_FUNCTION(const bind& x) {  // OK: No copy
        assert(x.i == -1);                      // and constant.
    } BOOST_LOCAL_FUNCTION_NAME(f)
    f();

    return 0;
}

Or, for objects with expensive copy operations (see also expensive_copy_cxx11_lambda.cpp and expensive_copy_local_function.cpp):

C++11 Lambda Function

Boost.LocalFunction

struct n {
    int i;
    n(int _i): i(_i) {}
    n(n const& x): i(x.i) { // Some time consuming copy operation.
        for (unsigned i = 0; i < 10000; ++i) std::cout << '.';
    }
};


int main(void) {
    n x(-1);

    auto f = [x]() {        // Problem: Expensive copy, but if bind
        assert(x.i == -1);  // by `&x` then `x` is not constant.
    };
    f();

    return 0;
}

struct n {
    int i;
    n(int _i): i(_i) {}
    n(n const& x): i(x.i) { // Some time consuming copy operation.
        for (unsigned i = 0; i < 10000; ++i) std::cout << '.';
    }
};
BOOST_TYPEOF_REGISTER_TYPE(n) // Register for `bind& x` below.

int main(void) {
    n x(-1);

    void BOOST_LOCAL_FUNCTION(const bind& x) {  // OK: No copy expensive
        assert(x.i == -1);                      // copy but constant.
    } BOOST_LOCAL_FUNCTION_NAME(f)
    f();

    return 0;
}

When constant binding functionality is needed for C++11 lambda functions, the best alternative might be to bind an extra local variable declared constant and initialized to the original variable (for example, see constant blocks implemented with C++11 lambda functions in the Examples section).

Local Functor

The following example compares local functions with C++ local functors (see also add_local_functor.cpp and add.cpp):

Local Functor

Boost.LocalFunction

int main(void) {
    int sum = 0, factor = 10;

    struct local_add { // Unfortunately, boilerplate code to program the class.
        local_add(int& _sum, int _factor): sum(_sum), factor(_factor) {}

        inline void operator()(int num) { // Body uses C++ statement syntax.
            sum += factor * num;
        }

    private: // Unfortunately, cannot bind so repeat variable types.
        int& sum; // Access `sum` by reference.
        const int factor; // Make `factor` constant.
    } add(sum, factor);

    add(1);
    int nums[] = {2, 3};
    // Unfortunately, cannot pass as template parameter to `std::for_each`.
    for(size_t i = 0; i < 2; ++i) add(nums[i]);

    BOOST_TEST(sum == 60);
    return boost::report_errors();
}

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();
}

Global Functor

The following example compares local functions with C++ global functors (see also add_global_functor.cpp and add.cpp):

Global Functor

Boost.LocalFunction

// Unfortunately, cannot be defined locally (so not a real alternative).
struct global_add { // Unfortunately, boilerplate code to program the class.
    global_add(int& _sum, int _factor): sum(_sum), factor(_factor) {}

    inline void operator()(int num) { // Body uses C++ statement syntax.
        sum += factor * num;
    }

private: // Unfortunately, cannot bind so repeat variable types.
    int& sum; // Access `sum` by reference.
    const int factor; // Make `factor` constant.
};

int main(void) {
    int sum = 0, factor = 10;

    global_add add(sum, factor);

    add(1);
    int nums[] = {2, 3};
    std::for_each(nums, nums + 2, add); // Passed as template parameter.

    BOOST_TEST(sum == 60);
    return boost::report_errors();
}

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();
}

However, note that global functors do not allow to define the function locally so they are not a real alternative implementation of local functions.

Boost.Phoenix

The following example compares local functions with Boost.Phoenix (see also add_phoenix.cpp and add.cpp):

Boost.Phoenix

Boost.LocalFunction

int main(void) {
    using boost::phoenix::let;
    using boost::phoenix::local_names::_f;
    using boost::phoenix::cref;
    using boost::phoenix::ref;
    using boost::phoenix::arg_names::_1;

    int sum = 0, factor = 10;
    int nums[] = {1, 2, 3};

    // Passed to template, `factor` by constant, and defined in expression.
    std::for_each(nums, nums + 3, let(_f = cref(factor))[
        // Unfortunately, body cannot use C++ statement syntax.
        ( ref(sum) += _f * _1, _1 ) // Access `sum` by reference.
    ]);

    BOOST_TEST(sum == 60);
    return boost::report_errors();
}

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();
}

The comparison in this section does not include the Boost.Lambda library because that library is obsolete and it was replaced by Boost.Phoenix. The Boost.Phoenix library version 3.0 is used for this comparison.

Performances

The following tables compare run-times, compile-times, and binary sizes for the different alternatives to local functions presented in this section.

Overall, this library has compile-times and generates binary sizes similar to the ones of the other approaches. This library run-times on C++03 compilers were measured to be larger than other approaches when compiler optimization is enabled (using bjam release ...). However, on compilers that allow to pass local types as template parameters (e.g., MSVC 8.0 or GCC 4.5.3 with C++11 features enabled -std=c++0x, see also [N2657] and Boost.Config's BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) this library automatically generates optimized code that runs as fast as the fastest of the other approaches (see the "Boost.LocalFunction" approach below). When this library local function is specified inline (see the "Boost.LocalFunction Inline" approach below and the Advanced Topics section) its run-times are always comparable to both the "Local Functor" and "Global Functor" approaches. However, in these cases the local function cannot be portably passed as template parameter (see [N2657] and Boost.Config's BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) so std::for_each is replaced by a for-loop (on MSVC the for-loop, and not the local function in fact the same applies to local functors, was measured to have worst performances than using std::for_each). Finally, this library run-times are always among the fastest when no compiler optimization is enabled (using bjam debug ...).

[Note] Note

The run-time performances of this library local functions are explained because on C++03 compliant compilers (e.g., GCC 4.5.3 without -std=c++0x) this library needs to use a function pointer in order to portably pass the local function class as a template parameter (see [N2657] and the Implementation section). For all tested compilers, this function pointer prevents the compiler optimization algorithms from inlining the local function calls. Instead, the functors used by other approaches (e.g., Boost.Phoenix) have been observed to allow all tested compilers to inline all the function calls for optimization. This run-time performance cost is not present on compilers that allow to pass local types as template parameters (e.g., MSVC 8.0 or GCC 4.5.3 with C++11 features enabled -std=c++0x, see Boost.Config's BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) because this library does not have to use the extra function pointer to implement the local function call (it directly passes the local class type as template parameter).

This run-time performance cost on C++03 compilers might or might not be an issue depending on the performance requirements of specific applications. For example, an application might already be using a number of indirect function calls (function pointers, virtual functions, etc) for which the overhead added by using the one extra function pointer required by the local function call might not be noticeable within the overall program run-time.

Finally, note that only a very simple local function body with just a single instruction was used for the anaylsis presented here (see the source files below). The authors have not studied how this library and the other approaches will perform with respect to each other when a more complex set of instructions is programmed for the local function body (e.g., if a more complex set of instructions in the local function body were to inhibit some compiler from inlining function objects also other approaches like C++11 lambda functions and Boost.Phoenix could start to show higher run-times even when optimization is enabled).

The following commands were executed from the library example directory to measure compile-time, binary size, and run-time respectively:

> touch <FILE_NAME>.cpp                             # force recompilation
> python chrono.py bjam {release|debug} <FILE_NAME> # compile-time
> size <FILE_NAME>                                  # binary size
> ./<FILE_NAME>                                     # run-time

The local function was called 1e8 times to add together all the elements of a vector and the run-time was measured using Boost.Chrono averaging over 10 executions of the vector summation (see the source files below).

Legend

Approach

Source File

profile_legend_local_function

Boost.LocalFunction

profile_local_function.cpp

profile_legend_local_function_inline

Boost.LocalFunction inline

profile_local_function_inline.cpp

profile_legend_cxx11_lambda

C++11 Lambda Function [a]

profile_cxx11_lambda.cpp

profile_legend_local_functor

Local Functor

profile_local_functor.cpp

profile_legend_global_functor

Global Functor

profile_global_functor.cpp

profile_legend_phoenix

Boost.Phoenix

profile_phoenix.cpp

[a] Measurements available only for C++11 compilers.

GCC 4.5.3 With C++11 Lambda Functions and "Local Classes as Template Parameters" (bjam cxxflags=-std=c++0x ...)

Compiled with bjam release ... for maximum optimization (-O3 -finline-functions) profile_gcc_cxx11_release

Compiled with bjam debug ... for no optimization (-O0 -fno-inline) profile_gcc_cxx11_debug

MSVC 8.0 With "Local Classes as Template Parameters" (Without C++11 Lambda Functions)

Compiled with bjam release ... for maximum optimization (/O2 /Ob2) profile_msvc_release

Compiled with bjam debug ... for no optimization (/Od /Ob0) profile_msvc_debug

GCC 4.3.4 With C++03 Only (Without C++11 Lambda Functions and Without "Local Classes as Template Parameters")

Compiled with bjam release ... for maximum optimization (-O3 -finline-functions) profile_gcc_release

Compiled with bjam debug ... for no optimization (-O0 -fno-inline) profile_gcc_debug


PrevUpHomeNext