Commits and constructive criticism are welcome
C++ pre-C++98: #define __cplusplus 1 C++98: #define __cplusplus 199711L C++98 + TR1: #define __cplusplus 199711L // ??? C++11: #define __cplusplus 201103L C++14: #define __cplusplus 201402L C++17: #define __cplusplus 201703L
#if (__cplusplus >= 201103L) // C++ 11 #define _STDEX_NATIVE_CPP11_SUPPORT // 11 (nullptr, static_assert) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT // char16_t, char32_t #endif
standart C++98: #define __cplusplus 199711L // C++98 standart C++98 + TR1: #define __cplusplus 200311L // C++03 nonstandart C++11: #define __cplusplus 200411L // C++03 + auto and dectype nonstandart C++11: #define __cplusplus 200511L // C++03 + auto, dectype and constexpr(partly) ... standart C++11: #define __cplusplus 201103L // C++11
#ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif
#ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif
#ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if ((!defined(_MSC_VER) || _MSC_VER < 1600) && !defined(_STDEX_NATIVE_CPP11_SUPPORT)) #define _STDEX_IMPLEMENTS_NULLPTR_SUPPORT #else #define _STDEX_NATIVE_NULLPTR_SUPPORT #endif #if (_MSC_VER >= 1600) #ifndef _STDEX_NATIVE_CPP11_TYPES_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif
#ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if ((!defined(_MSC_VER) || _MSC_VER < 1600) && !defined(_STDEX_NATIVE_CPP11_SUPPORT)) #define _STDEX_IMPLEMENTS_NULLPTR_SUPPORT #else #define _STDEX_NATIVE_NULLPTR_SUPPORT #endif #if (_MSC_VER >= 1600) #ifndef _STDEX_NATIVE_CPP11_TYPES_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if _MSC_VER // Visual C++ fallback #define _STDEX_NATIVE_MICROSOFT_COMPILER_EXTENSIONS_SUPPORT #define _STDEX_CDECL __cdecl #if (__cplusplus >= 199711L) #define _STDEX_NATIVE_CPP_98_SUPPORT #endif #endif // C++ 98 check: #if ((__cplusplus >= 199711L) && ((defined(__INTEL_COMPILER) || defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)))))) #ifndef _STDEX_NATIVE_CPP_98_SUPPORT #define _STDEX_NATIVE_CPP_98_SUPPORT #endif #endif
namespace stdex { namespace detail { template <bool> struct StaticAssertion; template <> struct StaticAssertion<true> { }; // StaticAssertion<true> template<int i> struct StaticAssertionTest { }; // StaticAssertionTest<int> } }
#ifdef _STDEX_NATIVE_CPP11_SUPPORT #define STATIC_ASSERT(expression, message) static_assert((expression), #message) #else // no C++11 support #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 #define STATIC_ASSERT(expression, message)\ struct CONCATENATE(__static_assertion_at_line_, __LINE__)\ {\ stdex::detail::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _WITH__), message);\ };\ typedef stdex::detail::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__) #ifndef _STDEX_NATIVE_NULLPTR_SUPPORT #define static_assert(expression, message) STATIC_ASSERT(expression, ERROR_MESSAGE_STRING) #endif #endif
STATIC_ASSERT(sizeof(void*) == 4, non_x32_platform_is_unsupported);
An important difference between my implementation and the standard one is that there is no overload of this keyword without a message to the user. This is due to the fact that in C ++ it is impossible to define several defines with a different number of arguments but with the same name, and an implementation without a message is much less useful than the chosen option. This feature leads to the fact that in fact STATIC_ASSERT in my implementation is a version added already in C ++ 11.We will understand in order what happened. As a result of checks of __cplusplus versions and non-standard compiler macros, we have information on support of C ++ 11 in sufficient volume (and therefore static_assert ), expressed by the _STDEX_NATIVE_CPP11_SUPPORT define . Therefore, if this macro is defined we can simply use the standard static_assert :
#ifdef _STDEX_NATIVE_CPP11_SUPPORT #define STATIC_ASSERT(expression, message) static_assert((expression), #message)
Note that the second parameter of the STATIC_ASSERT macro is not a string literal at all and therefore, using the preprocessor operator #, we convert the message parameter to a string to be transferred to the standard static_assert .If we don’t have support from the compiler, then we’ll proceed to our implementation. To begin with, we will declare auxiliary macros for “gluing” strings (the preprocessor operator ## is responsible for this).
#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2
I didn’t specifically use just # define CONCATENATE ( arg1 , arg2 ) arg1 ## arg2 in order to be able to pass the result of the same CONCATENATE macro as an argument to arg1 and arg2 .Next, we declare a structure with a beautiful name __static_assertion_at_line_ {line number} (the macro __LINE__ is also defined by the standard and should be expanded into the line number on which it was called), and inside this structure we add a field of our type StaticAssertion named STATIC_ASSERTION_FAILED_AT_LINE_ {line number} _WITH __ { error messages from the caller}.
#define STATIC_ASSERT(expression, message)\ struct CONCATENATE(__static_assertion_at_line_, __LINE__)\ {\ stdex::detail::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _WITH__), message);\ };\ typedef stdex::detail::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__)
GCC:
30: 103: error: field 'STATIC_ASSERTION_FAILED_AT_LINE_36_WITH__non_x32_platform_is_unsupported' has incomplete type 'stdex :: detail :: StaticAssertion <false>'
25:36: note: in definition of macro 'CONCATENATE2'
23:36: note: in expansion of macro 'CONCATENATE1'
30:67: note: in expansion of macro 'CONCATENATE'
24:36: note: in expansion of macro 'CONCATENATE2'
23:36: note: in expansion of macro 'CONCATENATE1'
30:79: note: in expansion of macro 'CONCATENATE'
24:36: note: in expansion of macro 'CONCATENATE2'
23:36: note: in expansion of macro 'CONCATENATE1'
30:91: note: in expansion of macro 'CONCATENATE'
36: 3: note: in expansion of macro 'STATIC_ASSERT'
Borland C ++ Builder:
[C ++ Error] stdex_test.cpp (36): E2450 Undefined structure 'stdex :: detail :: StaticAssertion <0>'
[C ++ Error] stdex_test.cpp (36): E2449 Size of 'STATIC_ASSERTION_FAILED_AT_LINE_36_WITH__non_x32_platform_is_unsupported' is unknown or zero
[C ++ Error] stdex_test.cpp (36): E2450 Undefined structure 'stdex :: detail :: StaticAssertion <0>'
Visual Studio:
Error C2079 'main :: __ static_assertion_at_line_36 :: STATIC_ASSERTION_FAILED_since you work
#ifdef _STDEX_NATIVE_CPP11_SUPPORT #include <cstddef> namespace stdex { namespace detail { template <class T, std::size_t N> constexpr std::size_t _my_countof(T const (&)[N]) noexcept { return N; } } // namespace detail } #define countof(arr) stdex::detail::_my_countof(arr) #else //no C++11 support #ifdef _STDEX_NATIVE_MICROSOFT_COMPILER_EXTENSIONS_SUPPORT // Visual C++ fallback #include <stdlib.h> #define countof(arr) _countof(arr) #elif defined(_STDEX_NATIVE_CPP_98_SUPPORT)// C++ 98 trick #include <cstddef> template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N]; #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x)) #else #define countof(arr) sizeof(arr) / sizeof(arr[0]) #endif
#if !defined(forever) #define forever for(;;) #else #define STRINGIZE_HELPER(x) #x #define STRINGIZE(x) STRINGIZE_HELPER(x) #define WARNING(desc) message(__FILE__ "(" STRINGIZE(__LINE__) ") : warning: " desc) #pragma WARNING("stdex library - macro 'forever' was previously defined by user; ignoring stdex macro definition") #undef STRINGIZE_HELPER #undef STRINGIZE #undef WARNING #endif
unsigned int i = 0; forever { ++i; }
#if defined(assert) #ifndef NDEBUG #include <iostream> #define stdex_assert(condition, message) \ do { \ if (! (condition)) { \ std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \ << " line " << __LINE__ << ": " << message << std::endl; \ std::terminate(); \ } \ } while (false) #else #define stdex_assert(condition, message) ((void)0) #endif #endif
#ifdef _STDEX_NATIVE_CPP11_SUPPORT #define stdex_noexcept noexcept #else #define stdex_noexcept throw() #endif
Source: https://habr.com/ru/post/417099/
All Articles