Modern C++

This page outlines the changes necessary within the codebase to adopt modern C++.

By 'Modern C++' we mean the ISO C++ Standards published in (and since) 2011. These will be referred to as C++11, C++14, C++17, etc. Older versions of the standard were published in 1998 and updated in 2003, which will be referred to as C++98.

New C++ standards do not break backward compatibility, except in a few well-documented and very limited senses, and it is, therefore, not necessary in general to change source code.

However, certain features may become deprecated in favour of new ones that completely supersede them, and compilers area likely to emit new warnings in certain places. In both cases, Chaste will treat these as errors due to the Werror flag, and some changes are required.

Compiler support

This reference page is a useful guide to which compiler versions support features from modern C++ standards. A brief summary is:

C++11:

  • GCC 4.8
  • Clang 3.3
  • Intel 15
  • MSVC 19 (VS 2015)

C++14:

  • GCC 5.0
  • Clang 3.8
  • Intel 17
  • MSVC 19.1 (VS 2017)

Ubuntu has out-of-the-box compiler support for C++11 as of Ubuntu 14.04, and for C++14 as of Ubuntu 16.04. Additionally, the Toolchain test builds PPA provides the latest GCC versions.

Changes necessary in order to adopt C++11

The following is the minimal set of changes required in order to compile a C++98 version of Chaste with C++11 support.

Build system changes

Add -std=c++11 flag

  • CMake
    • If CMake version < 3.1.3, add the flag globally with, for instance, add_compile_options(-std=c++11)
    • Else, use the CXX_STANDARD property to 11 and the CXX_STANDARD_REQUIRED to ON for each call to add_executable and add_library.
  • Scons
    • Add the flag globally, for instance with extra_flags = extra_flags + ' -std=c++11'

Note that this flag additionally needs to be set in CellMLToSharedLibraryConverter.cpp which explicitly writes out a CMakeLists.txt.

Remove -std=gnu++98 flag

This was added to ChasteCompilerFlags.cmake because, from GCC 6.0, C++14 was enabled by default.

XSD generated code

XSD 3.3 (default package on Ubuntu 14.04) generates code containing std::auto_ptr in ChasteParameters_<version>.hpp. This is deprecated and the compiler emits warnings, which Chaste treats as errors.

The file HeartConfig.hpp has #include "ChasteParameters_3_4.hpp", and HeartConfig.hpp is included in a large proportion of the heart translation units. As this is a known issue, the chosen fix is to turn off deprecated declaration warnings in the entire heart component by adding the following line to heart/CMakeLists.txt:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")

From XSD 4.0 (default package on Ubuntu 16.04), it is possible to generate code without std::auto_ptr, and when Ubuntu 14.04 is removed, this should be addressed.

Codebase changes

Remove std::auto_ptr

std::auto_ptr was deprecated and replaced by std::unique_ptr in C++11.

Unfortunately, due to comparisons being made between std::auto_ptr and boost::shared_ptr within Chaste, it is not possible to simply find-and-replace auto_ptr with unique_ptr.

The easiest change is to replace each instance with boost::shared_ptr.

There is a (slight) overhead to using a shared pointer instead of a unique pointer, but since no instances of auto_ptr were in performance-critical code, there is no problem using boost::shared_ptr.

In the future, all instances of boost::shared_ptr should be replaced with either std::shared_ptr or std::unique_ptr.

Functions returning smart pointers as Booleans

See, for instance, CellCycleModelOdeSolver.hpp:

template<class CELL_CYCLE_MODEL, class ODE_SOLVER>
bool CellCycleModelOdeSolver<CELL_CYCLE_MODEL, ODE_SOLVER>::IsSetUp()
{
    return mpOdeSolver;
}

This explicit conversion from boost::shared_ptr<AbstractIvpOdeSolver> to bool seemed fine in C++98, but the C++11 compilers don't like it. Instead, something like:

template<class CELL_CYCLE_MODEL, class ODE_SOLVER>
bool CellCycleModelOdeSolver<CELL_CYCLE_MODEL, ODE_SOLVER>::IsSetUp()
{
    return static_cast<bool>(mpOdeSolver.get());
}

works well, where .get() accesses the raw pointer.

Boost scoped enums for boost <= 1.56

See this ticket comment for details. In summary, if the boost version being used is <= 1.56, the flag BOOST_NO_CXX11_SCOPED_ENUMS must be set in order to ensure binary compatibility with the boost filesystem library.

There is only one #include <boost/filesystem.hpp> in Chaste: in BoostFilesystem.hpp, and it should be replaced with:

#if BOOST_VERSION <= 105600
#define BOOST_NO_CXX11_SCOPED_ENUMS
#include <boost/filesystem.hpp>
#undef BOOST_NO_CXX11_SCOPED_ENUMS
#else
#include <boost/filesystem.hpp>
#endif

Other Changes for C++11

C++11 introduces a large number of new features, many of which make the language safer, easier to read, or easier to write.

nullptr

In C/C++, NULL is just a macro that gets expanded to 0. This can be bad: take, for instance, two functions with the following overloaded signatures:

f(int);
f(foo*);

If you have a variable of type foo*, but whose value is actually NULL, and you pass it to f, the 'wrong' function may be executed.

To avoid this ambiguity, and to remove an unhelpful macro, C++11 introduced the new keyword nullptr. This should always be used instead of NULL or 0 when checking equality of pointers, or setting a default value.

Possible Compiler Errors

Attempting to use new features

error: ‘auto’ changes meaning in C++11; please remove it [-Werror=c++0x-compat]

or

error: This file requires compiler and library support for the ISO C++ 2011 standard.

etc.

Ensure the C++11 compile flag has been successfully added, and that your compiler is new enough to compile C++11 code.

auto_ptr is deprecated

'auto_ptr<chaste::parameters::v2_2::box_type>' is deprecated [-Werror,-Wdeprecated-declarations]

You shouldn't use std::auto_ptr since C++11. Instead, use std::unique_ptr.

Undefined reference to boost::filesystem

undefined reference to `boost::filesystem::detail::copy_file(boost::filesystem::path const&, boost::filesystem::path const&, boost::filesystem::copy_option, boost::system::error_code*)'

This is the binary incompatibility error with boost filesystem <= 1.56. See Boostscopedenumsforboost1.56.

Returning smart pointers as Booleans

error: cannot convert ‘boost::shared_ptr<AbstractIvpOdeSolver>’ to ‘bool’ in return

This is the attempted implicit conversion between smart pointer and bool: see FunctionsreturningsmartpointersasBooleans

Initalising static const variables

error: 'constexpr' needed for in-class initialization of static data member 'const double TestCryptsAndVillusLiteratePaper::mCryptLength' of non-integral type [-fpermissive]

You just need to replace const with the new keyword constexpr more info on constexpr.