sim_cpp11_features.pl

Purpose

The sim_cpp11_features.pl program converts a file with specially delimited regions of C++11 code and generates a C++03 version of that code that emulates the C++11 features. By default, the C++03 expansions are placed in a separate file with an extra “_cpp03” delimiter before the extension. Currently, this program emulates two constructs:

  • Variadic templates (a.k.a. parameter packs) are emulated by creating multiple copies of the template code, starting with zero template arguments and adding an argument with each repetition (up to 10 arguments by default).

  • Forwarding references in function arguments are emulated by surrounding the argument declaration in a BSLS_COMPILERFEATURES_FORWARD_REF macro invocation and replacing std::forward calls with BSLS_COMPILERFEATURES_FORWARD.

Both emulations are approximate, at best, but experience has found them to be extremely useful.

The program is located in the bde-tools repo as BdeBuildSystem/scripts/sim_cpp11_features.pl and is intended to be invoked by the BBS build system.

Usage

Usage: sim_cpp11_features.pl [ option… ] input-file-name…

Each input file can contain zero or more regions delimited by the simulation marker

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES

and its matching #endif.

A C++11 compiler will see the code within each simulation region verbetim. A C++03 compiler will see an altenative version of the code generated by this tool.

Options

--output= filename.ext

Specifies the name of the output file. By default, the C++11 output overwrites the input file and the C++03 output is written to a file whose name is the same as the input filename except with a “_cpp03” inserted before the filename extension. For example, if the input file is “bxxy_useful.h”, then the C++03 expansion is written to “bxxy_useful_cpp03.h”. If --output is specified, then the C++11 output is written to filename.ext and the C++03 output is written to filename_cpp03.ext. If filename is a single dash (-), then output is written to standard out (--inplace mode only).

--[no-]inplace

The --inplace option causes the C++11 and C++03 code to be intermixed within the same output file, separated by conditional compilation directives. This option, which was the only operating mode prior to June 2020, tends to produce a verbose source file that is difficult to maintain. The default is --no-inplace.

--verify-no-change

Verify that nothing has changed in the main file that would result in a change in generated code (including any generated code within the main file itself). If any output (including the main file) would change, do not write any output and abort with an error.

--clean

When used with --inplace, removes all C++03 emulation code, leaving a C++11-only file that is more convenient to maintain during development. Typically, this option is used once just before adding or modifying code. After a successful build using C++11, this tool is typically run again without --clean to produce release code that can be tested with C++03.

--var-args= max-args

The maximum number of variadic template expansions to generate (default 10). This value is written into the C++11 file as an embedded option (see below) so that future runs do not need to specify this option again.

--test

Runs the tool on a built-in test file, producing a diff of the original and modified file if changes were detected. Usually used with --inplace.

--debug= level

Turns on debugging at the specified level. The higher the level, the more verbose the output.

--trace= subroutine:level

Turns on tracing for the specified subroutine (for debugging).

input-file-name…

One or more input file names. If the input file is a single dash (-), then read from standard input.

Embedded options

A few options can be embedded directly into the input file at the end of an #if directive that introduces a region to be emulated:

// $var-args= n

Globally sets the maximum number of variadic template expansions to n . A value specified using the command-line --var-args option overrides the value of n specified using this embedded option and overwrites n in the generated output.

// $local-var-args= n

Sets the maximum number of variadic template expansions to n only for the current region. The number of expansions returns to the file default after the closing #endif.

Limitations

The C++11 emulation provided by this tool is incomplete at best. It does not produce a semantic interpretation of the input code and is limited to basic pattern matching. Known limitations are:

  • All parameter packs for a given instantiation of a variadic template must be the same length.

  • Perfect-forwarding emulation does not recognize prvalues as rvalues.

  • There is limited support for partial specialization of variadic class templates. In particular, specializing on the empty parameter pack is not currently supported.

Example

The following input file (let’s call it “foo.h”):

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=3

template <class... ARG>
void j(ARG&&... arg) {
    g(std::forward<ARG>(arg)...);
}

#endif

gets rewritten into the same input file (“foo.h”) as:

#include <bsls_compilerfeatures.h>

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// Include version that can be compiled with C++03
// Generated on Mon Nov  2 13:17:20 2020
// Command line: sim_cpp11_features.pl foo.h
# define COMPILING_FOO_H
# include <foo_cpp03.h>
# undef COMPILING_FOO_H
#else

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=3

template <class... ARG>
void j(ARG&&... arg) {
    g(std::forward<ARG>(arg)...);
}

#endif

#endif // End C++11 code

and a new “_cpp03” header file is created with the C++03 equivalent expansions (“foo_cpp03.h”):

// foo_cpp03.h                                                        -*-C++-*-

// Automatically generated file.  **DO NOT EDIT**

#ifndef INCLUDED_FOO_CPP03
#define INCLUDED_FOO_CPP03

//@PURPOSE: Provide C++03 implementation for foo.h
//
//@CLASSES: See foo.h for list of classes
//
//@SEE_ALSO: foo
//
//@DESCRIPTION:  This component is the C++03 translation of a C++11 component,
// generated by the 'sim_cpp11_features.pl' program.  If the original header
// contains any specially delimited regions of C++11 code, then this
// generated file contains the C++03 equivalent, i.e., with variadic templates
// expanded and rvalue-references replaced by 'bslmf::MovableRef' objects.
// The header code in this file is designed to be '#include'd into the
// original header when compiling with a C++03 compiler.  If there are no
// specially delimited regions of C++11 code, then this header contains no
// code and is not '#include'd in the original header.
//
// Generated on Mon Nov  2 13:17:20 2020
// Command line: sim_cpp11_features.pl foo.h

#ifdef COMPILING_FOO_H

#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl foo.h
#ifndef FOO_VARIADIC_LIMIT
#define FOO_VARIADIC_LIMIT 3
#endif
#ifndef FOO_VARIADIC_LIMIT_A
#define FOO_VARIADIC_LIMIT_A FOO_VARIADIC_LIMIT
#endif

#if FOO_VARIADIC_LIMIT_A >= 0
    void j() {
    g();
    }
#endif  // FOO_VARIADIC_LIMIT_A >= 0

#if FOO_VARIADIC_LIMIT_A >= 1
    template <class ARG_1>
    void j(BSLS_COMPILERFEATURES_FORWARD_REF(ARG_1) arg_1) {
    g(BSLS_COMPILERFEATURES_FORWARD(ARG_1, arg_1));
    }
#endif  // FOO_VARIADIC_LIMIT_A >= 1

#if FOO_VARIADIC_LIMIT_A >= 2
    template <class ARG_1,
          class ARG_2>
    void j(BSLS_COMPILERFEATURES_FORWARD_REF(ARG_1) arg_1,
       BSLS_COMPILERFEATURES_FORWARD_REF(ARG_2) arg_2) {
    g(BSLS_COMPILERFEATURES_FORWARD(ARG_1, arg_1),
      BSLS_COMPILERFEATURES_FORWARD(ARG_2, arg_2));
    }
#endif  // FOO_VARIADIC_LIMIT_A >= 2

#if FOO_VARIADIC_LIMIT_A >= 3
    template <class ARG_1,
          class ARG_2,
          class ARG_3>
    void j(BSLS_COMPILERFEATURES_FORWARD_REF(ARG_1) arg_1,
       BSLS_COMPILERFEATURES_FORWARD_REF(ARG_2) arg_2,
       BSLS_COMPILERFEATURES_FORWARD_REF(ARG_3) arg_3) {
    g(BSLS_COMPILERFEATURES_FORWARD(ARG_1, arg_1),
      BSLS_COMPILERFEATURES_FORWARD(ARG_2, arg_2),
      BSLS_COMPILERFEATURES_FORWARD(ARG_3, arg_3));
    }
#endif  // FOO_VARIADIC_LIMIT_A >= 3

#else
// The generated code below is a workaround for the absence of perfect
// forwarding in some compilers.

    template <class... ARG>
    void j(BSLS_COMPILERFEATURES_FORWARD_REF(ARG)... arg) {
    g(BSLS_COMPILERFEATURES_FORWARD(ARG, arg)...);
    }

// }}} END GENERATED CODE
#endif

#else // if ! defined(DEFINED_FOO_H)
# error Not valid except when included from foo.h
#endif // ! defined(COMPILING_FOO_H)

#endif // ! defined(INCLUDED_FOO_CPP03)

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------