Code Generation

This section describes how the BDE Build System interacts with code generators.

Overview

The BDE CMake build system generates some code automatically. Currently this is done to simulate variadic templates for code being built with a C++03 compiler (see Expanding Parameter Packs below). The code generation is run automatically as part of the configuration of a project (see User Interaction below). The code generation step generates code into the source tree of the repository, which differs from how many CMake projects are structured. This code is checked into our repositories, both for release labels and our main branch.

We generate the code in the source tree for 2 principal reasons:

  • Debuggability At debug time, the generated code is what has to be visible to the debugger. If it’s a transient artifact, it won’t be available when developers are trying to diagnose issues.

  • Build reproducibility Generating the code in-tree makes sure our releases can be reproduced trivially.

In order to ensure generated code in source control is not out of date, our CI systems perform builds that verify that generated code is up to date with both the latest version of the source code, and the latest version of the code generation tools (see User Interaction below).

User Interaction

After the generation templates are written and the initial code generation is done (e.g., Writing Parameter Packs and Initial simulation tool usage, below), the ongoing use of the code generators is transparent to any user of the bbs_build tool.

Users set up their environment as usual, then run the bbs_build configure step, which sets up the rules for re-generating the target files if necessary:

$ bbs_build configure
    ...
    -- Looking for pthread_create in pthread - found
    -- Found Threads: TRUE
    -- sim_cpp11 generation: /bb/mbiga/mbig1480/bde/groups/bdl/bdlb/bdlb_nullablevalue.cpp -> /bb/mbiga/mbig1480/bde/groups/bdl/bdlb/bdlb_nullablevalue_cpp03.cpp
    -- sim_cpp11 generation: /bb/mbiga/mbig1480/bde/groups/bdl/bdlb/bdlb_nullablevalue.h -> /bb/mbiga/mbig1480/bde/groups/bdl/bdlb/bdlb_nullablevalue_cpp03.h
    -- sim_cpp11 generation: /bb/mbiga/mbig1480/bde/groups/bsl/bslalg/bslalg_arrayprimitives.cpp -> /bb/mbiga/mbig1480/bde/groups/bsl/bslalg/bslalg_arrayprimitives_cpp03.cpp
    -- sim_cpp11 generation: /bb/mbiga/mbig1480/bde/groups/bsl/bslalg/bslalg_arrayprimitives.h -> /bb/mbiga/mbig1480/bde/groups/bsl/bslalg/bslalg_arrayprimitives_cpp03.h
    ...

Afterwards, during the build phase, the generated files are re-generated only if necessary:

$ bbs_build build
    ...
    [99/328] Generating ../../groups/bsl/bslmf/bslmf_functionpointertraits_cpp03.h - sim_cpp11_features.pl updated file
    [100/328] Generating ../../groups/bsl/bslmf/bslmf_nthparameter_cpp03.cpp - sim_cpp11_features.pl did not need to update
    ...

In this example, bslmf_functionpointertraits_cpp03.h needed to be regenerated, but bslmf_nthparameter_cpp03.cpp did not. Unless you are working directly on a component with generated code, the latter is the much more common occurence.

Expanding Parameter Packs

This section describes how BDE simulates parameter packs (a.k.a. variadic templates) on C++03 compilers.

Overview of Parameter Packs

Parameter packs are a feature that was added in C++11, allowing for template expansions with a variable number of parameters. For example:

Unexpanded (source) code

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class VALUE, class ALLOCATOR>
template <class... ARGS>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(ARGS&&... arguments)
{
    emplace(cbegin(), BSLS_COMPILERFEATURES_FORWARD(ARGS, arguments)...);
    return front();
}
#endif

We simulate the variadic expansions in C++03 using bde-tools/BdeBuildSystem/scripts/sim_cpp11_features.pl.

This tool can be applied to any source file (e.g., bslstl_list.h) and generates an _cpp03 file (e.g. bslstl_list_cpp03.h) alongside it, as well as modifying the original file to include the _cpp03 file if necessary.

The generated _cpp03 equivalent of the example above is not as readable, which is why we isolate it off into its own file which can be ignored during code reviews. In this example, only the first 3 expansions are shown; the actual _cpp03 file has 10 expansions covering 300 lines of code just for this one method.

Expanded (generated) code

#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl bslstl_list.h
#ifndef BSLSTL_LIST_VARIADIC_LIMIT
#define BSLSTL_LIST_VARIADIC_LIMIT 10
#endif
#ifndef BSLSTL_LIST_VARIADIC_LIMIT_E
#define BSLSTL_LIST_VARIADIC_LIMIT_E BSLSTL_LIST_VARIADIC_LIMIT
#endif
#if BSLSTL_LIST_VARIADIC_LIMIT_E >= 0
template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(
                          )
{
    emplace(cbegin());
    return front();
}
#endif  // BSLSTL_LIST_VARIADIC_LIMIT_E >= 0

#if BSLSTL_LIST_VARIADIC_LIMIT_E >= 1
template <class VALUE, class ALLOCATOR>
template <class ARGS_01>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_01) arguments_01)
{
    emplace(cbegin(), BSLS_COMPILERFEATURES_FORWARD(ARGS_01, arguments_01));
    return front();
}
#endif  // BSLSTL_LIST_VARIADIC_LIMIT_E >= 1

#if BSLSTL_LIST_VARIADIC_LIMIT_E >= 2
template <class VALUE, class ALLOCATOR>
template <class ARGS_01,
          class ARGS_02>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_01) arguments_01,
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_02) arguments_02)
{
    emplace(cbegin(), BSLS_COMPILERFEATURES_FORWARD(ARGS_01, arguments_01),
                      BSLS_COMPILERFEATURES_FORWARD(ARGS_02, arguments_02));
    return front();
}
#endif  // BSLSTL_LIST_VARIADIC_LIMIT_E >= 2

#if BSLSTL_LIST_VARIADIC_LIMIT_E >= 3
template <class VALUE, class ALLOCATOR>
template <class ARGS_01,
          class ARGS_02,
          class ARGS_03>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_01) arguments_01,
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_02) arguments_02,
                       BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_03) arguments_03)
{
    emplace(cbegin(), BSLS_COMPILERFEATURES_FORWARD(ARGS_01, arguments_01),
                      BSLS_COMPILERFEATURES_FORWARD(ARGS_02, arguments_02),
                      BSLS_COMPILERFEATURES_FORWARD(ARGS_03, arguments_03));
    return front();
}
#endif  // BSLSTL_LIST_VARIADIC_LIMIT_E >= 3

As you can see, manually maintaining such expanded code is a nightmare.

Writing parameter packs

Parameter pack templates are written as normal C++11 code in the header, source file, and test driver of the component. Each template member is surrounded by a specific #if block:

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
//...
#endif

Also, anywhere that bsl::forward would be used, BSLS_COMPILERFEATURES_FORWARD is used instead (see the unexpanded example above (Unexpanded (source) code)).

Initial simulation tool usage

We store generated code in our source tree. See Overview for the rationale.

Once a variadic template is added (to a header, source, or test driver file) for the first time, the developer adds a <component>_cpp03 sub-component to <package>.mem file for the package. The build systems determines on which componets to run the variadic simulation expansion by looking for subordinate components with _cpp03 extensions.

.../bde-tools/BdeBuildSystem/scripts/sim_cpp11_features.pl bsl_list.h
.../bde-tools/BdeBuildSystem/scripts/sim_cpp11_features.pl bsl_list.cpp
.../bde-tools/BdeBuildSystem/scripts/sim_cpp11_features.pl bsl_list.t.cpp
echo bslstl_list_cpp03 >> package/bslstl.mem
sort -o package/bslstl.mem package/bslstl.mem
git add bsl_list_cpp03.{h,cpp,t.cpp} package/bslstl.mem
git commit -m'Adding cpp03 files'

Ongoing synchronization of the _cpp03 files

The bbs_build tool automatically generates rules to re-run sim_cpp11_features.pl if the source files have changed.

A different option is passed to bbs_build by the nightly and feature branch test builds which causes the build to fail if the source and _cpp03 files are out of sync, allowing us to make sure that the state of committed code is in sync.

bbs_build build --cpp11-verify-no-change