Inducing minimal C++ standard version in CMake - c++

CMake has a nice framework for setting and defining an explicit value for the C++ standard, typically:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
However this does not clearly fit my needs, I'd rather states that I need at least c++11. I thought that I could just do instead:
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(p CXX)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(foobar foobar.cxx)
target_compile_features(foobar PRIVATE cxx_nullptr)
where
$ cat foobar.cxx
int main()
{
char * p = nullptr;
}
However again in this case this forces me to use -std=c++11 eventhough by default g++ 6.3.0 default to -std=c++14 (technically -std=gnu++14):
$ c++ -dumpversion
6.3.0
leads to:
$ make VERBOSE=1
[...]
make[2]: Entering directory '/tmp/p'
[ 50%] Building CXX object CMakeFiles/foobar.dir/foobar.cxx.o
/usr/bin/c++ -std=c++11 -o CMakeFiles/foobar.dir/foobar.cxx.o -c /tmp/p/foobar.cxx
[100%] Linking CXX executable foobar
/usr/bin/cmake -E cmake_link_script CMakeFiles/foobar.dir/link.txt --verbose=1
Is there a way to say: "Build this project with at least C++11 Standard" in CMake ?
Typically for a project built using g++ 4.8.5 it would add -std=c++11 but for a project build with g++ 6.3.0 it would leave the default (implicit) -std=c++14
Update: The following is out of the scope for the question, but since I received a lengthy answer from #ComicSansMS, I feel I need to clarify the need for this.
I am working with my Debian Maintainer hat on, and I was convinced a couple of months back that setting an explicit C++ standard version in cmake within a project was the right way to do, hence my proposal:
Mandates explicit -std=c++XY for c++ projects
However there are two things that get mixed here:
Defining a c++ standard version for the interface of the library being built
Defining a c++ standard version for the implementation detail of the library being built.
From a Debian Maintainer perspective setting explicitly a C++ standard version makes it hard to rebuild a portion of the package archive when a library SONAME is being updated. Let's consider the case where GDCM is using the Poppler library. While the implementation details of GDCM are written using C++98, the fact that Poppler library has been build using the default (implicit) standard version of gcc-6 makes it suddenly a compilation failure for GDCM, since an explicit -std=c++98 is being passed.
So while for an implementation prospective, setting an explicit c++ standard version make sense (obviously!), it is a little less clear for an interface prospective. The vast majority of open-source projects do not define multiple c++ ABI (std::string[98] AND std::string[11]) and assume a single version will be used to ship the binary. In this case it makes it important for a c++ package to be build using the default (implicit) version of gcc (at least when uploaded as official Debian package).

You can always test for compilers support of the specific standard flags yourself.
First check for -std=c++14, and if it doesn't exist then check for -std=c++11, and if that doesn't work then error out.
Flags can easily be checked with the CheckCXXCompilerFlag module.
These days you should probably start with -std=c++17 though. You might also want to add checks for the pre-release standard versions like c++1z (for C++17) and c++1y (for C++14).
Start with the highest version, then work your way downward the minimum required version. Stop when it doesn't fail.
For newer versions of CMake you could use target_compile_features to specify features that the target compiler should be able to provide for.
This way, if (for example) your project uses auto type deduction you could tell CMake that the compiler need to suport the cxx_auto_type feature. Then CMake will make sure that the compiler can indeed support C++11 and the auto type deduction.

Is there a way to say: "Build this project with at least C++11 Standard" in CMake ?
No, and this is probably not a reasonable thing to request.
It does not make any sense to request a standards version that is newer than the code you are trying to build, as your code will not make any use of those features anyway.
The other big problem here is that newer standards are not strict supersets of older standards. In recent versions, the C++ standard has been quite keen on deprecating and even removing features that have outlived its usefulness.
What you have is a specific piece of code that expects a specific set of language features to be available. And that is exactly what you should tell the buildsystem. If your code expects the C++11 features to be available, set CMAKE_CXX_STANDARD to 11 and be done with it. It will guarantee that all of the required features are available and safeguard you (within reasonable bounds) against any future deprecations.
Now, there is one scenario where specifying an exact standard is not enough: You might have different implementations in your code and then want to switch between implementations depending on the available compiler capabilities. That is, your code might be C++14 aware and you want it to compile in C++14 mode if available, but still leave the C++11 mode as a fallback.
This is exactly the default behaviour of CMAKE_CXX_STANDARD:
This means that using:
set_property(TARGET tgt PROPERTY CXX_STANDARD 11)
with a compiler
which does not support -std=gnu++11 or an equivalent flag will not
result in an error or warning, but will instead add the -std=gnu++98
flag if supported. This “decay” behavior may be controlled with the
CXX_STANDARD_REQUIRED target property.
So in a nutshell, always specify the latest standard that your code is aware of, but not newer.

Related

How to find default supported C++ standard by g++ compiler in cmake

I saw many threads regarding how to enable certain C++ standard e.g.
add_definitions("-std=c++14")
and
set (CMAKE_CXX_STANDARD 11)
But how to find which standard by default g++ compiler following while compiling code ?
add_definitions("-std=c++14") is not the correct way to enable C++14. The right way either the second one you mentioned: set(CMAKE_CXX_STANDARD 14), or set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14").
add_definitions is for macro definitions, equivalent to #define in your source code.
About your question, it's bad practice to depend on the default version of C++ in your compiler. Even Visual Studio now defines the standard you want to use, which is a new thing in VS. Standards are made to solve this problem by having them specified. So, in your project, you define the target standard, and you define it in your make file. You should not expect your code to work on all C++ standards.
If you're developing an application, then you should not adjust your code based on available compiler version, instead you should specify which compiler standard your code requires:
set (CMAKE_CXX_STANDARD 11)
If you're building a code library that is meant to be used in different contexts, use compiler feature detection and conditional compilation. For example:
write_compiler_detection_header(
FILE "${CMAKE_CURRENT_BINARY_DIR}/compiler_features.h"
PREFIX Foo
COMPILERS GNU
FEATURES
cxx_variadic_templates
)
And then:
#include "compiler_features.h"
#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
# include "with_variadics/interface.h"
#else
# include "no_variadics/interface.h"
#endif
This is especially useful with compilers like MSVC which have many features from C++17 while also lacking some basic features from C++11 (e.g. expression SFINAE).
CMake has built-in detectors for many popular C++ features.
PS. If you're really curious, G++ up to 5.0 uses by default gnu++98 standard, and from 6.0 gnu++14 (GNU dialect of -std=c++14).

How to request C++11 or later on a CMake-target?

(This question is really about the "or later"-part.)
I'm a aware of the other answers which are telling us out to activate C++11 on a target/project in cmake.
My question is really how do we express C++11 or later.
As of CMake 3.8 we have the cxx_std_11-feature which forces C++11 on compilers (-std=c++11) which even support later standards and may even default to C++14 (gcc-7) or even 17 (gcc-8, iiuc).
There is also the CXX_STANDARD-target-property, but this is not transitive and also force the exact standard and not the "or later"-option.
The only way I found until now is to require cxx_range_for (or a similar feature) which makes CMake keep the default C++-standard of the compiler if at least C++11 is supported. This is supported as of CMake 3.1.
What is the correct way to select C++11 or later of a CMake-target?
Unfortunately there's no way to ask for a range of standards in CMake.
Then only way (that I have used in the past) is to use e.g. check_cxx_compiler_flag to check for -std=c++20, -std=c++17, -std=c++14 and -std=c++11 (in that order) and use the highest with e.g. target_compile_options.
If none of the flags listed is supported, then error out.
As #Some programmer dude pointed out, only way to do so is to check for C++17, then C++14 then C++11. Looking into CMake sources itself gives interesting idea how to do it in platform-agnostic way. In here try_compile command is used with corresponding CMAKE_CXX_STANDARD passed, so CMake itself determines which compiler flag should be used. Thus to check for C++14:
try_compile(CMake_CXX14_WORKS
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_LIST_DIR}/cm_cxx14_check.cpp
CMAKE_FLAGS -DCMAKE_CXX_STANDARD=14
OUTPUT_VARIABLE OUTPUT
)
where cm_cxx14_check.cpp is dummy file with main.
This is key idea here, although full logic also checks if CMAKE_CXX_STANDARD wasn't already defined or if CMake knows CMAKE_CXX_STANDARD=17 (CMake >= 3.8). Also, CMake checks for newest standard only for GNU or Clang, but I believe it is because it is not needed in MSVC in non-library code. MSVC has always enabled the newest standard by default.
Unfortunately, here is some also a corner case with GNU 4.8: "The GNU 4.8 standard library's cstdio header is not aware that C++14 honors C11's removal of "gets" from stdio.h and results in an error"

Introduce a new compiler to CMake

We work with a specific compiler, which is called Cadul. It has its own libraries, targets etc. The problem is that CMake does not support it in contrast to such "standard" compilers as GNU, Intel, Clang etc.
Firstly I thought to use cross compiling but it didn't work, since the host and target platforms are the same.
Then I looked into Modules, where I found the directory named "Compiler" which contains a lot of ".cmake" files specified for each compiler and each enabled language. I tried to substitute the word "GNU" by "Cadul" and hoped to see any changes, such as "The CXX compiler identification is Cadul ...". But it didn't happen.
Then I just removed the whole directory "Modules" from cmake and hoped to see that it doesn't work anymore. Surprisingly it did.
So has anyone ever integrated a new compiler to Cmake? With its own features, etc.
It looks like this has been recommended in the comments, but no one has condensed it to an answer yet.
You can choose a compiler by adding these lines to your CMakeLists.txt (source):
SET(CMAKE_C_COMPILER /path/to/c/compiler)
SET(CMAKE_CXX_COMPILER /path/to/cpp/compiler)
If you need to customize further, using a toolchain file works well. There are some examples in the documentation here.
Yes, I've done this before. But you need a lot more then just setting the compiler path (since CMake would try to identify this compiler and then - since it's unknown to CMake - would throw an error).
An example implementation of a new "compiler" can be found in my answer here:
Generic rule from makefile to cmake
It shows a enable_language(FOO) example that could be replaced with enable_language(Cadul).

Is it possible for gcc/clang to setup library locations for different c++ versions?

I would like to know, if it is possible to install libraries in locations, such that gcc/clang only uses them when the specific c++ language is used.
E.g there are specific c++ versions like: -std=c++98, -std=c++03,-std=c++11, -gnu++98, ...
I'm asking here, because I want to install the library boost 1.56 for old c++03 and for c++11.
Apparanty the object files/library files use a different ABI when either c++11 or c++03 is in use, so I have to install Boost for each language version.
Additional Information:
I am using Linux Mint 17 with the compilers gcc 4.8.2 and clang 3.5.1.
I've already build the boost library for gcc and one for clang.
Now I would like to try some of the new features the C++11 language provides.
Somewhere, I've read, that it is not safe to compile against a library that was compiled with a different c++-version, if it depends heavily on the standard c++ library.
As boost relies heavily on the c++ standard library, I would need for each c++ version another set of boost libraries.
What I would like to have:
It would be convinient, if the compiler uses just the correct boost library, depending on the std compiler option:
std=c++03 => choose boost_atomic_gcc4.8.2_c++03.so
std=c++11 => choose boost_atomic_gcc4.8.2_c++11.so
...
We have this behaviour at least with the c++ runtime, as gcc chooses the correct runtime depending on the std option.
I think the simplest solution may be to do this from your build system. This might work if you use GNU Make:
ifdef ELEVEN
CXXFLAGS += -std=c++11
LDFLAGS += -L/path/to/boost-c++11
else
CXXFLAGS += -std=c++03
LDFLAGS += -L/path/to/boost-c++03
endif

How to properly switch between gcc versions?

I want to play with C++ 2011, so I need the unreleased gcc 4.7. I was able to succesfully get the svn trunk and compile it.
I want to keep the default gcc of my system for safety, so I configured gcc4.7 with a --prefix and installed it in a non-standard location.
Now how should I enable gcc 4.7 over the default gcc of my system ?
I already changed the CC and CXX variables, I updated my PATH to point on the gcc 4.7 bin dir first. When I type gcc --version I get 4.7 OK.
But gcc is more than just an executable. There are many executables in gcc install dir. There are also default includes and std lib c++.
So far, every blog entry / SO question I found on this subject speaks only about the gcc and g++ executables.
Can anyone give me a list of the changes I need to do to the environment to fully use gcc 4.7 ?
update LD_LIBRARY_PATH ? How to give precedence to gcc 4.7 system includes ? Are there other things to consider ?
Thanks in advance.
I would think that THE g++ is pretty much tangled up with things using C++ as the C library is tangled up with the system! Any layout changes in the C++ library classes will cause incompatibilities with other C++ programs or libraries. Thus, I wouldn't replace the system's C++ compiler or, more importantly, its standard C++ library at all (unless, maybe, the compiler vendor makes a strong claim that they retained binary compatibility with the version you are replacing).
To play or even use a different version of g++, using the prefix approach works fine. All the compiler specific tools are implicitly called from within g++ using an appropriate version and tools like ar, ld, ranblib, etc. are not really depending on the compiler version anyway. The important components uses internally are the standard library (both the headers and the library) and the preprocessor. When calling a version of g++ it figures out which of these it really needs.
BTW, when you want to play with C++2011 you can also have a look at clang.
The simplest answer is: nothing; it just works. :)
GCC finds what it needs first relative to itself, second in the "prefix" it was configured with, and finally in the standard places. By this means it's perfectly safe to relocate it wherever you like, as long as you relocate all of it - but beware that the fall back behaviour can hide brokenness if the install is incomplete.
Look at the GCC Configuration docs. I am using program suffixes to distinguish between the different GCC versions. To do that add, e.g., --progam-suffix=-4.7 to your ./configure invocation.