Home

Awesome

Compatibility

Build Status

This library provides an advanced target_compile_features() and write_compiler_detection_header(). The problem with those is that they are controlled via a CMake internal database, which has to be kept manually up-to-date. This version uses check_cxx_source_compiles() instead and is such automatically up-to-date - a compiler supports a feature, if the test code compiles.

Based on the test results, a header file is generated that stores the result and often workaround code, for example a CONSTEXPR macro that is constexpr, if supported, and const otherwise, to use the features anyway.

It also provides the automatic standard deduction from target_compile_features() to activate the right standard.

Example

If you only want the C++ standard activation, simply include() comp_base.cmake and replace target_compile_features() by comp_compile_features().

This is a CMakeLists.txt for a more advanced usage:

# suppose we have a target 'tgt'
include(your/dir/to/comp_base.cmake) # only file you need to download, rest is taken as needed

# we want constexpr, noexcept, std::max_align_t and rtti_support
# instead of cpp11_lang/constexpr and cpp11_lang/noexcept, you could also write cxx_constexpr/cxx_noexcept
comp_target_features(tgt PUBLIC cpp11_lang/constexpr cpp11_lang/noexcept cpp11_lib/max_align_t env/rtti_support)

For convenience we include all generated files in a header named config.hpp. The filenames are made available through macros.

#ifndef CONFIG_HPP
#define CONFIG_HPP

#include COMP_CONSTEXPR_HEADER
#include COMP_NOEXCEPT_HEADER
#include COMP_MAX_ALIGN_T_HEADER
#include COMP_RTTI_SUPPORT_HEADER

#endif

And then we can use it just by including config.hpp in our code:

#include "config.hpp"

// use a workaround macro
COMP_CONSTEXPR int i = 0;

void func() COMP_NOEXCEPT
{
    // use a workaround typedef
    comp::max_align_t foo;

    // or conditional compilation
#if COMP_HAS_RTTI_SUPPORT
    do_sth();
#endif
}

If you don't care about the workarounds, but just want a specific standard, simply call:

comp_target_features(tgt PUBLIC CPP11) # or CPP14 or CPP17

This will only activate C++11/14/17 without doing anything else.

Note: The standard activation is always PRIVATE to allow users of a library to have a different (higher) standard than the library.

Usage

The only file needed is comp_base.cmake. You can either manually download it, use CMakes file(DOWNLOAD facility, or use git submodules. The branch git-submodule only contains comp_base.cmake and is thus perfect for this purpose. Run git submodule add -b "git-submodule" https://github.com/foonathan/compatibility.git to initialize it and fetch the file. Then you only need to run git submodule update --remote to update it to the newest version.

include() it in your CMakeLists.txt now. First it generates CMake options - COMP_CPP11_FLAG, COMP_CPP14_FLAG and COMP_CPP17_FLAG - storing the calculated compiler flag for a given standard, useful if you want to override it, if it can't find one for your compiler. It also provides the following function:

comp_target_features(<target> <PRIVATE|PUBLIC|INTERFACE> <features...>
                     [NOPREFIX | PREFIX <prefix] [NAMESPACE <namespace>]
                     [CMAKE_PATH <path>] [INCLUDE_PATH <include_path>]
                     [NOFLAGS | CPP11 | CPP14 | CPP17] 
                     [LOG <DEBUG | INFO | QUIET | SILENT | ALL>]
                     [NO_HEADER_MACROS] [SINGLE_HEADER <name>])

Ignoring all the other options, it is like target_compile_features(). It takes a list of features to activate for a certain target. A features is a file in this repository without the .cmake extension, e.g. cpp11_lang for all C++11 language features, or cpp14_lang/deprecated for the C++14 deprecated features.

A feature file with name dir/xxx.cmake belonging to a feature dir/xxx consists of the following, where dir is the category of the feature, xxx the feature name in lowercase, XXX the feature name in upper case <PREFIX> the prefix as given to the function, <prefix> the prefix in lowercase and without a trailing underscore:

To use the generated header files, simply write #include <PREFIX>_XXX_HEADER inside your code, the macro is made available automatically (you could also #incluce the file directly but this is not recommended).

What comp_target_features function actually does is the following:

The behavior can be customized with the other options:

Feature Reference

A feature named dir/xxx is tested in xxx.cmake, defines an override CMake option COMP_HAS_XXX and a macro <PREFIX>HAS_XXX in a file named <prefix>/xxx.hpp (where prefix is <PREFIX> in lowercase without a trailing underscore), filename also made available over the global macro <PREFIX>XXX_HEADER.

There are also alternative names for the CMake target_compile_features() and SD-6 Feature Test Recommondations that are automatically translated. Where appropriate, it will also generate the SD-6 feature macro as specified. This will override the existing value if the new one is greater or the macro COMP_OVERRIDE_SD6 is defined. If a feature is not supported, it will not change or define anything.

For some features, macros are generated that can be used instead (i.e. for noexcept), they have the form <PREFIX>XXX. Those macros often use compiler extensions. If there is none (or a lacking implementation...), an error message will be emmitted. To prevent this, simply define the macro as no-op or as you want prior to including the file.

There are often workaround functions for library features. Those are defined in a namespace and either use the own implementation or the standard library implementation, if it is available.

Prefix and namespace name can be controlled via parameters, see above. A given prefix must always use the same namespace name on each call.

This library currently tests for the following features. The code below assumes no prefix and a namespace name of comp.

*A feature will only be included (by me) if it is not a pure syntactic feature (like auto or lambdas which can be avoided) but if there is either sensible workaround code, e.g. through to compiler extensions or through reimplementing (small!) standard library functionality, or there can be conditional compilation based on the existense, e.g. optional literal definitions or move constructors.

Note that I'm open to PRs of any kind :)*

C++11 language features

These features are all in the subdirectory cpp11_lang.

feature namealternative nameexampleworkaround, if any
alias_templatecxx_alias_templastemplate <typename T> using my_map = std::map<int, T>;no workaround
alignascxx_alignasalignas(int) char cALIGNAS(x), fallback to compiler extension, if available
alignofcxx_alignofalignof(int)ALIGNOF(x), fallback to compiler extension, if available
auto_typecxx_auto_typeauto var = 0no workaround
constexprcxx_constexprconstexpr int foo()CONSTEXPR, fallback to const; CONSTEXPR_FNC, fallback to inline
decltypecxx_decltypedecltype(a)DECLTYPE(x), fallback to typeof extension, if available
default_function_template_argscxx_default_function_template_argstemplate <typename T = int> void foo();no workaround
delete_fnccxx_deleted_functionsvoid foo() = delete;no workaround
enum_classcxx_strong_enumenum class foo {}no workaround
explicit_conversion_opcxx_explicit_conversionexplicit operator bool()no workaround
finalcxx_finalvoid bar() final;FINAL macro, workaround expands to nothing
inline_namespacenoneinline namespace foo {...}no workaround
lambdascxx_lambdas[](){}no workaround
literal_opcxx_user_literalsoperator""no workaround
noexceptcxx_noexceptvoid foo() noexcept;NOEXCEPT, fallback to nothing; NOEXCEPT_IF(x), fallback to nothing; NOEXCEPT_OP(x), fallback to false
noreturnnone[[noreturn]] void foo();NORETURN, fallback to compiler extension, if available
nullptrcxx_nullptrvoid* ptr = nullptr;NULLPTR, fallback to null pointer idiom; also comp::nullptr_t
overridecxx_overridevoid bar() override;OVERRIDE, fallback to nothing
parameter_packcxx_parameter_packtemplate <typename ... T> void f(T... ts);no workaround
range_forcxx_range_forfor (auto var : container)no workaround
right_angle_bracketscxx_right_angle_bracketsstd::vector<std::vector<int>>no workaround
rvalue_refcxx_rvalue_referencesint&& a = 4;no workaround
static_assertcxx_static_assertstatic_assert(std::is_integral<T>::value, "");STATIC_ASSERT(Expr, Msg), fallback to simple undefined struct technique
thread_localcxx_thread_localthread_local int i;THREAD_LOCAL, fallback to __thread extension or similar, if available - does not call constructors or destructors!

Note: In general, it assumes proper C++11 support. The workarounds defined in this library rely on all common C++ features that can not be easily avoided except those listed here with a proper fallback (like noexcept, constexpr, ...).

Get them all by specifying cpp11_lang.

C++11 library features

These features are all in the subdirectory cpp11_lib.

feature nameexampleworkaround, if any
get_new_handlerstd::get_new_handler()comp::get_new_handler(), fallback to old std::set_new_handler(nullptr) technique - not thread safe
get_terminatestd::get_terminate()comp::get_terminate(), same as above
is_triviallystd::is_trivially_XXX<T>comp::is_trivially_XXX, workaround uses std::is_XXX combined with std::is_trivial
max_align_tstd::max_align_tcomp::max_align_t, fallback to ::max_align_t or a struct with a long double and long long
mutexstd::mutex/std::lock_guard/std::unique_lockno workaround
to_stringstd::to_string(54)comp::to_string(), fallback to std::sprintf()

Note: It only checks for minor features where an easy workaround implementation is feasible in the scope of this library.

Get them all by specifying cpp11_lib.

C++14 language features [complete]

These features are all in the subdirectory cpp14_lang.

paperfeature namealternative nameexampleworkaround, if any
N3760deprecatedcxx_attribute_deprecated[[deprecated]] int foo();DEPRECATED and DEPRECATED(Msg), fallback to compiler attribute, if available
N3652general_constexprcxx_relaxed_constexprgeneralized constexprno workaround
N3638return_type_deductionnoneauto return type deduction for normal functionsAUTO_RETURN macro
N3778sized_deallocationnonevoid operator delete(void *ptr, std::size_t size)no workaround
N3651variable_templatecxx_variable_templatestemplate <typename T> T pi;no workaround

Get them all by specifying cpp14_lang.

The following features are not and will never be supported:

paperdescriptionreason
N3323Tweak Certain C++ Contextual Conversionsdifficult to check, avoid relying on behavior
N3472Binary Literalssyntax sugar only
N3648Generalized Lambda Capturelambdas are syntax sugar only
N3649Generic Lambdaslambdas are syntax sugar only
N3653Member initializers and aggregatesdifficult to check, no big user impact
N3664Clarifying Memory Allocationwording change only
N3781Digit seperator for literalssyntax sugar only

C++14 library features [complete]

These features are all in the subdirectory cpp14_lib.

paperfeature nameexampleworkaround, if any
N3668exchangestd::exchange()comp::exchange(), own implementation
N3421generic_operator_functorsstd::greater<>{}comp::greater{} and the rest, no class templates!
N3658integer_sequencestd::index_sequence<4>comp::index_sequence<4> and co, own implementation
N3656make_uniquestd::make_unique()comp::make_unique, own implementation
N3654quotedss >> std::quoted(str)no workaround, use boost
N3659shared_lockstd::shared_lock<std::shared_timed_mutex>no workaround
N3671two_range_algorithmstd::equal(first1, last1, first2, last2)comp::equal()/comp::mismatch()/comp::is_permutation(), own implementation

Get them all by specifying cpp14_lib.

The following features are not and will never be supported:

paperdescriptionreason
N3668Fixing constexpr member functions without constworkaround not possible, just avoid relying on that behavior
N3670Addressing Tuples by Typejust use index version
N3462std::result_of and SFINAEimpossible to check
N3545operator() for std::integral_constantjust "syntax" sugar
N3642UDl's for standard libraryjust "syntax" sugar
N3469Constexpr for std::chronono great workaround possible
N3470Constexpr for std::arrayno great workaround possible
N3471Constexpr for utilitiesno great workaround possible
N3657Heterogeneous lookupoptimization only, workaround for transparent functors supports this extension
N3655Alias templates for traitsjust "syntax" sugar

C++17 language features

These features are all in the subdirectory cpp17_lang.

paperfeature nameexampleworkaround, if any
N4295fold_expressionsreturn (args && ....);no workaround
N3928terse_static_assertstatic_assert(condition);TERSE_STATIC_ASSERT(Cond) macro
N4267utf8_char_literalchar c = u8'A';UTF8_ChAR_LITERAL(Str) macro taking a normal string, appending u8 prefix and converting it to a character

C++17 library features [up-to-date]

These features are all in the subdirectory cpp17_lib.

paperfeature nameexampleworkaround, if any
N4389bool_constantstd::bool_constantcomp::bool_constant
N4280container_accessstd::size(cont)comp::size(cont), likewise for std::empty()/std::data()
N4169invokestd::invoke(f)comp::invoke(f)
N4279map_insertionm.try_emplace(key, value)comp::try_emplace(m, key, value), likewise for insert_or_assign()
N4508shared_mutexstd::shared_mutexno workaround
N4259uncaught_exceptionsstd::uncaught_exceptions()no workaround, note the plural!
N3911void_tstd::void_t<int, char>comp::void_t<int, char>

Get them all by specifying cpp17_lib.

The following features are not and will never be supported:

paperdescriptionreason
N4190Removing deprecated thingsremoval, just don't use it
N4284Contiguous iteratorno actual code change
N4089Conversion for std::unique_ptr<T[]>difficult to check, avoid relying on behavior
N4277TriviallyCopyable std::reference_wrapperdifficult to check, avoid relying on behavior
N4258Cleaning-up noexceptdifficult to check, no big impact on user
N4266Missing SFINAE rule in std::unique_ptrdifficult to check, no big impact on code, avoid relying on behavior
N4387Improving constructor std::pair and std::tupledifficult to check, avoid relying on behavior
N4510Minimal incomplete type support for containersdifficult to check, avoid relying on behavior

Technical specifications

The technical specifications for the C++ standard library. These features are all in the subdirectory ts.

paperfeature namedescriptionworkaround, if any
N3804anystd::experimental::any classnone, use boost or other implementation
N3915applystd::experimental::apply(f, tuple)comp::apply(), own implementation
N4273container_erausrestd::experimental::erase_if(vector, pred)comp::erase_if()/comp::erase(), own implementation
P0013R1logical_operator_traitsstd::experimental::disjunctioncomp::conjunction/comp::disjunction/comp::negation, own implementation
N4391make_arraystd::experimental::make_array()comp::make_array()/comp::to_array(), own implementation
N4076not_fnstd::experimental::not_fn()comp::not_fn(), own implementation
N3793optionalstd::experimental::optionalnone, use boost or other implementation
N3916pmrPolymorphic memory resourceonly comp::memory_resource base class
N3921string_viewstd::experimental::string_viewnone, use other implementation

Get them all by specifying ts.

Environment

Features regarding the general environment. These features are all in the subdirectory env. Note: These checks here aren't that great, it is recommended to set option explicitly.

feature namedescriptionworkaround, if any
exception_supportsupport for exception handlingTHROW(Ex), RETHROW_EX, fallback to std::abort(); TRY, fallback to if (true), CATCH_ALL, fallback to if (false)
hosted_implementationfreestanding vs hostedalias macro HOSTED_IMPLEMENTATION, implementation of std::swap() and std::move()/std::forward() (if rvalue references are supported); those are otherwise not available
rtti_supportsupport for RTTIcomp::polymorhpic_cast, fallback to static_cast
threading_supportsupport for threadingno workaround

Get them all by specifying env.

Common extensions

These features are all in the subdirectory ext.

feature nameexampleworkaround, if any
assume__assume(cond) (from MSVC)ASSUME(x), fallback to nothing
bswap__builtin_bswap(x) (from GCC)``comp::bswap(x)` (for unsigned fixed-sized integers), fallback to manual swap (constexpr)
clz__builtin_clz(x) (from GCC)comp::clz(x) (for unsigned fixed-sized integers), fallback to binary search (constexpr)
counter__COUNTER__ (most compilers)no workaround
ctz__builtin_ctz(x) (from GCC)comp::ctz(x) (for unsigned integers), fallback to binary search (constexpr)
expect__builtin_expect(x, val) (from GCC)EXPECT(x, val) (and LIKELY(cond),UNLIKELY(cond)), fallback to value itself
extension__extension__ (from GCC, marks extensions to silence warnings)EXTENSION, fallback to nothing
fallthrough[[clang::fallthrough]] (from clang)FALLTHROUGH, fallback to nothing
has_include__has_include(header)HAS_INCLUDE(x), fallback to always 0
int128__int128 (from GCC)comp::(u)int128_t, no workaround, just convenience typedefs
popcount__builtin_popcount(x) (from GCC)comp::popcount(x) (for unsigned integers), fallback to bithacks (constexpr)
pretty_function__PRETTY_FUNCTION__ (from GCC)PRETTY_FUNCTION, fallback to __FUNCSIG__ on MSVC
unreachable__builtin_unreachable() (from GCC)UNREACHABLE, fallback to __assume(0) under MSVC, otherwise nothing
unused[[gnu::unused]] (from GCC)UNUSED, fallback to nothing; also MAKE_UNUSED(expr) that uses (void) trick

Get them all by specifying ext.cmake.

Contribution

As you probably noted, there are features missing. I wrote this library in a few hours and concentrated on the most important features for me. If you want to extend it or improve a workaround, please don't hesitate to fork and PR (or just write an issue and let me take care of it, when I have time, if you're lazy).

To write a new feature check, just create a new file in the appropriate subdirectory. Note: Do not include comp_base.cmake!.

Inside the feature file you should only use the following CMake functions in the given order:

  1. comp_api_version(major[.minor[.patch]]) - checks if the API has the required version. major must match exactly, the rest not higher than the API version. If there is a version mismatch, it is an error.

  2. comp_feature(<name> <test_code> <standard> <required...>) - does the feature check. name is the name of the feature without the directory, e.g. constexpr, not cpp11_lang/constexpr. test_code is the code that will be tested to see if the feature is supported. standard is the required C++ standard, this must be one of the COMP_CPPXX_FLAG values or COMP_CPP98_FLAG if no higher standard is needed. required is a list of requirede featuers that need to be supported in order to support this feature. If any of them isn't, this will not be checked.

  3. comp_workaround(<name> <workaround_code> <standard> <required...>) - writes workaround code (optional). name must be the same as in comp_feature(). workaround_code is the workaround code itself. It must use ${COMP_PREFIX} for macros and put anything else into the namespace ${COMP_NAMESPACE] (variable expansion works there, so write it exactly like that). The result of the test is available through ${COMP_PREFIX}HAS_NAME, e.g. #if ${COMP_PREFIX}HAS_CONSTEXPR ... #else ... #endif. standard is like in comp_feature() the standard required for the workaround code (the not-supported case, the supported case gets the standard of comp_feature()). required is a list of required features inside the workaround code. Their headers will be included prior to the workaround making it possible to use other workarounds.

  4. comp_sd6_macro(<name> <sd6_name> <value>) - writes SD 6 macro (optional, since 1.1). name must be the same as in comp_feature(). sd6_name is the SD-6 macro name and value its value.

  5. comp_unit_test(<name> <global_code> <test_code>) - defines a (Catch) unit test for the workaround code (optional). name must be the same as in comp_feature(). global_code will be put into the global namespace right after the including of the appropriate feature header and catch. test_code will be put into a Catch TEST_CASE().

The code for feature checking should be minimal and do not depend on any other advanced features (for example, do not use auto or nullptr) to prevent false failure. The workaround shouldn't use advanced features either, it can use other workarounds though.

The testing code should test the workaround. It will be run by the testing framework for both modes, supported and not supported.

Look at a few other files for example implementations.