diff --git a/ArxTypeTraits.h b/ArxTypeTraits.h index a9c49b4..c9864cc 100644 --- a/ArxTypeTraits.h +++ b/ArxTypeTraits.h @@ -3,26 +3,58 @@ #ifndef ARX_TYPE_TRAITS_H #define ARX_TYPE_TRAITS_H -#if defined(ARDUINO_ARCH_AVR)\ - || defined(ARDUINO_ARCH_MEGAAVR)\ - || defined(ARDUINO_ARCH_SAM)\ - || defined(ARDUINO_ARCH_SAMD)\ - || defined(ARDUINO_spresense_ast) - #define ARX_TYPE_TRAITS_DISABLED - #if defined(ARDUINO_ARCH_MEGAAVR)\ - || defined(ARDUINO_ARCH_SAMD)\ - || defined(ARDUINO_spresense_ast) - #define ARX_TYPE_TRAITS_NEW_DISABLED - #endif +#if __cplusplus < 201103L + #error "C++11 must be enabled in the compiler for this library to work, please check your compiler flags" #endif -#include -#ifndef ARX_TYPE_TRAITS_DISABLED - #include - #include - #include +#include "ArxTypeTraits/has_include.h" + +#if !defined(ARX_HAVE_LIBSTDCPLUSPLUS) + #if ARX_SYSTEM_HAS_INCLUDE() + #include + #if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) + // For gcc's libstdc++ and clang's libc++, assume that + // __cplusplus tells us what the standard includes support + #define ARX_HAVE_LIBSTDCPLUSPLUS __cplusplus + #elif defined(__UCLIBCXX_MAJOR__) + // For uclibc++, assume C++98 support only. + #define ARX_HAVE_LIBSTDCPLUSPLUS 199711L + #else + #error "Unknown C++ library found, please report a bug against the ArxTypeTraits library about this." + #endif + #else + // Assume no standard library is available at all (e.g. on AVR) + #define ARX_HAVE_LIBSTDCPLUSPLUS 0 + #endif #endif +// Make sure std namespace exists +namespace std { } + +// Import everything from the std namespace into arx::std, so that +// anything we import rather than define is also available through +// arx::arx_std. +// This includes everything yet to be defined, so we can do this early +// (and must do so, to allow e.g. the C++14 additions in the arx::std +// namespace to reference the C++11 stuff from the system headers. +namespace arx { + namespace arx_std { + using namespace ::std; + } +} + +// Import everything from arx::std back into the normal std namespace. +// This ensures that you can just use `std::foo` everywhere and you get +// the standard library version if it is available, falling back to arx +// versions for things not supplied by the standard library. Only when +// you really need the arx version (e.g. for constexpr numeric_limits +// when also using ArduinoSTL), you need to qualify with arx::arx_std:: +namespace std { + using namespace ::arx::arx_std; +} + +#include "ArxTypeTraits/replace_minmax_macros.h" + #include "ArxTypeTraits/type_traits.h" #endif // ARX_TYPE_TRAITS_H diff --git a/ArxTypeTraits/functional.h b/ArxTypeTraits/functional.h index ed1ff17..06208de 100644 --- a/ArxTypeTraits/functional.h +++ b/ArxTypeTraits/functional.h @@ -3,23 +3,38 @@ #ifndef ARX_TYPE_TRAITS_FUNCTIONAL_H #define ARX_TYPE_TRAITS_FUNCTIONAL_H -#include +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11 -#ifdef ARX_TYPE_TRAITS_NEW_DISABLED - void* operator new (const size_t size, void* ptr) { (void)size; return ptr; } +#include + +#else // Do not have libstdc++11 + +// If we have a header, include it and assume it has placement new +// (for AVR this has always been true, MEGAAVR does not have placement +// new now, but will probably get it before is added). +// This also handles the case where ArduinoSTL is used, which defines an +// inline placement new which would conflict with the below definition. +#if ARX_SYSTEM_HAS_INCLUDE() +#include #else - #include +// When there is no header, there might be a header, but +// not all Arduino platform (versions) define a placement new operator +// in there. +// However, it is hard to detect whether it is or is not defined, but +// the versions that do define it, do not define it inline in the +// header, so we can just define it inline here without conflicts. +// Note that there is no need to include anything to declare the +// non-placement new operators, since those are implicit. +inline void* operator new (const size_t size, void* ptr) noexcept { (void)size; return ptr; } #endif -#ifdef ARX_TYPE_TRAITS_DISABLED - -namespace std { +namespace arx { namespace arx_std { // reference: // stack overflow https://stackoverflow.com/questions/32074410/stdfunction-bind-like-type-erasure-without-standard-c-library template - struct function; + class function; template class function @@ -143,7 +158,8 @@ namespace std { return static_cast(f); } -} // namespace std +} } // namespace arx::std + +#endif // Do not have libstdc++11 -#endif // ARX_TYPE_TRAITS_DISABLED #endif // ARX_TYPE_TRAITS_FUNCTIONAL_H diff --git a/ArxTypeTraits/has_include.h b/ArxTypeTraits/has_include.h new file mode 100644 index 0000000..8f7378d --- /dev/null +++ b/ArxTypeTraits/has_include.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef ARX_TYPE_TRAITS_HAS_INCLUDE_H +#define ARX_TYPE_TRAITS_HAS_INCLUDE_H + + // Check whether __has_include is available, but also check the GCC + // version (__has_include was introduced in gcc 5) to catch + // environments (such as ESP8266) where gcc is old, but some system + // header provides a fake __has_include. We also need to check + // against __clang__ here, since clang pretends to be GCC + // 4.something and would otherwise be detected incorrectly here... + #if !defined(__has_include) || defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) + #if defined(ARDUINO_ARCH_ESP8266) + // ESP8266 does not have a working __has_include, but we + // know it does have a working libstdc++ with all the + // headers we care about, so provide a fake has_include + #define ARX_SYSTEM_HAS_INCLUDE(x) 1 + #else + #error "Compiler does not support __has_include, please report a bug against the ArxTypeTraits library about this." + #endif + #else + #define ARX_SYSTEM_HAS_INCLUDE(x) __has_include(x) + #endif + +#endif // ARX_TYPE_TRAITS_HAS_INCLUDE_H diff --git a/ArxTypeTraits/initializer_list.h b/ArxTypeTraits/initializer_list.h new file mode 100644 index 0000000..b1cd74f --- /dev/null +++ b/ArxTypeTraits/initializer_list.h @@ -0,0 +1,30 @@ +#pragma once + +#ifndef ARX_TYPE_TRAITS_INITIALIZER_H +#define ARX_TYPE_TRAITS_INITIALIZER_H + +// Initializer_list *must* be defined in std, so take extra care to only +// define it when is really not available (e.g. +// ArduinoSTL is C++98 but *does* define ) and not +// already defined (e.g. by ArxContainer). +#if ARX_SYSTEM_HAS_INCLUDE() +#include +#else +namespace std { + template + class initializer_list + { + private: + const T* array; + size_t len; + initializer_list(const T* a, size_t l) : array(a), len(l) {} + public: + initializer_list() : array(nullptr), len(0) {} + size_t size() const { return len; } + const T *begin() const { return array; } + const T *end() const { return array + len; } + }; +} // namespace std +#endif + +#endif // ARX_TYPE_TRAITS_INITIALIZER_LIST_H diff --git a/ArxTypeTraits/replace_minmax_macros.h b/ArxTypeTraits/replace_minmax_macros.h new file mode 100644 index 0000000..9668ccc --- /dev/null +++ b/ArxTypeTraits/replace_minmax_macros.h @@ -0,0 +1,33 @@ +#pragma once + +#ifndef ARX_TYPE_TRAITS_REPLACE_MINMAX_MACROS_H +#define ARX_TYPE_TRAITS_REPLACE_MINMAX_MACROS_H + +// Make sure Arduino.h is actually included, since otherwise it might be +// included later and break *uses* of the min/max methods, rather than +// the declarations of it. +#include + +// These macros are defined by Arduino.h on some platforms, and conflict +// with min/max methods defined or included by ArxTypeTraits, so replace +// them with macros here. +#ifdef max + #undef max + template + constexpr auto max(T1 x, T2 y) + -> decltype(x + y) + { + return (x > y) ? x : y; + } +#endif +#ifdef min + #undef min + template + constexpr auto min(T1 x, T2 y) + -> decltype(x + y) + { + return (x < y) ? x : y; + } +#endif + +#endif // ARX_TYPE_TRAITS_REPLACE_MINMAX_MACROS_H diff --git a/ArxTypeTraits/tuple.h b/ArxTypeTraits/tuple.h index f8d765b..1489533 100644 --- a/ArxTypeTraits/tuple.h +++ b/ArxTypeTraits/tuple.h @@ -3,9 +3,13 @@ #ifndef ARX_TYPE_TRAITS_TUPLE_H #define ARX_TYPE_TRAITS_TUPLE_H -#ifdef ARX_TYPE_TRAITS_DISABLED +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11 -namespace std { +#include + +#else // Do not have libstdc++11 + +namespace arx { namespace arx_std { // https://theolizer.com/cpp-school2/cpp-school2-15/ // https://wandbox.org/permlink/C0BWIzjqg4iO3kKZ @@ -71,7 +75,8 @@ namespace std { return std::tuple::type...>(std::forward::type>(args)...); } -} // namespace std +} } // namespace arx::std + +#endif // Do not have libstdc++11 -#endif // ARX_TYPE_TRAITS_DISABLED #endif // ARX_TYPE_TRAITS_TUPLE_H diff --git a/ArxTypeTraits/type_traits.h b/ArxTypeTraits/type_traits.h index 0b1481f..9c04d09 100644 --- a/ArxTypeTraits/type_traits.h +++ b/ArxTypeTraits/type_traits.h @@ -3,32 +3,38 @@ #ifndef ARX_TYPE_TRAITS_TYPE_TRAITS_H #define ARX_TYPE_TRAITS_TYPE_TRAITS_H -#ifdef ARX_TYPE_TRAITS_DISABLED +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 199711L // Have libstdc++98 -#ifdef max - #undef max - template - constexpr auto max(T1 x, T2 y) - -> decltype(x + y) - { - return (x > y) ? x : y; - } -#endif -#ifdef min - #undef min - template - constexpr auto min(T1 x, T2 y) - -> decltype(x + y) +#include + +#else // Do not have libstdc++98 + +namespace arx { namespace arx_std { + + template + void swap(T& a, T& b) { - return (x < y) ? x : y; + T t = move(a); + a = move(b); + b = move(t); } -#endif +} } // namespace arx::arx_std + +#endif // Do not have libstdc++98 + + +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11 + +#include +#include + +#else // Do not have libstdc++11 -#include #include #include +#include -namespace std { +namespace arx { namespace arx_std { using nullptr_t = decltype(nullptr); @@ -266,11 +272,11 @@ namespace std { template struct is_convertible - : std::conditional < + : conditional < can_apply ::value , true_type - , typename std::conditional < - std::is_arithmetic::value && std::is_arithmetic::value, + , typename conditional < + is_arithmetic::value && is_arithmetic::value, true_type, false_type >::type @@ -287,7 +293,7 @@ namespace std { // specialization for regular functions template struct is_function : true_type {}; - // specialization for variadic functions such as std::printf + // specialization for variadic functions such as printf template struct is_function : true_type {}; // specialization for function types that have cv-qualifiers @@ -377,64 +383,37 @@ namespace std { template using result_of = details::result_of; +} } // namespace arx::arx_std - template - void swap(T& a, T& b) - { - T t = move(a); - a = move(b); - b = move(t); - } - - -#ifndef ARX_TYPE_TRAITS_INITIALIZER_LIST_DEFINED -#define ARX_TYPE_TRAITS_INITIALIZER_LIST_DEFINED - - template - class initializer_list - { - private: - const T* array; - size_t len; - initializer_list(const T* a, size_t l) : array(a), len(l) {} - public: - initializer_list() : array(nullptr), len(0) {} - size_t size() const { return len; } - const T *begin() const { return array; } - const T *end() const { return array + len; } - }; - -#endif // ARX_TYPE_TRAITS_INITIALIZER_LIST_DEFINED - -} // namespace std +#endif // Do not have libstdc++11 +#include "initializer_list.h" #include "tuple.h" #include "functional.h" -#endif // ARX_TYPE_TRAITS_DISABLE_STL - +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201402L // Have libstdc++14 + // Nothing to include here, relevant header files were already included + // for C++11 above. +#else // Do not have libstdc++14 -#if __cplusplus < 201402L // C++11 -#if !defined(OF_VERSION_MAJOR) || !defined(TARGET_WIN32) - -namespace std { +namespace arx { namespace arx_std { template - using enable_if_t = typename std::enable_if::type; + using enable_if_t = typename enable_if::type; template - using decay_t = typename std::decay::type; + using decay_t = typename decay::type; template - using remove_cv_t = typename std::remove_cv::type; + using remove_cv_t = typename remove_cv::type; template - using remove_const_t = typename std::remove_const::type; + using remove_const_t = typename remove_const::type; template - using remove_volatile_t = typename std::remove_volatile::type; + using remove_volatile_t = typename remove_volatile::type; template - using remove_reference_t = typename std::remove_reference::type; + using remove_reference_t = typename remove_reference::type; template - using remove_pointer_t = typename std::remove_pointer::type; + using remove_pointer_t = typename remove_pointer::type; template struct integer_sequence @@ -471,17 +450,17 @@ namespace std { template using index_sequence_for = make_index_sequence; -} // namespace std - -#endif // !defined(OF_VERSION_MAJOR) || !defined(TARGET_WIN32) -#endif // C++11 +} } // namespace arx::arx_std +#endif // Do not have libstdc++14 -#if __cplusplus < 201703L // C++14 -namespace std { +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201703L // Have libstdc++17 + // Nothing to include here, relevant header files were already included + // for C++11 above. +#else // Do not have libstdc++17 -#if !defined(OF_VERSION_MAJOR) || !defined(TARGET_WIN32) +namespace arx { namespace arx_std { template struct Tester { using type = void; }; @@ -489,67 +468,69 @@ namespace std { using void_t = typename Tester::type; template - struct disjunction : std::false_type {}; + struct disjunction : false_type {}; template struct disjunction : Arg::type {}; template - struct disjunction : std::conditional>::type {}; + struct disjunction : conditional>::type {}; template - struct conjunction : std::true_type {}; + struct conjunction : true_type {}; template struct conjunction : Arg::type {}; template - struct conjunction : std::conditional, Arg>::type {}; + struct conjunction : conditional, Arg>::type {}; template - struct negation : std::integral_constant {}; - -#endif // !defined(OF_VERSION_MAJOR) || !defined(TARGET_WIN32) + struct negation : integral_constant {}; // https://qiita.com/_EnumHack/items/92e6e135174f1f781dbb // without decltype(auto) template constexpr auto apply_impl(F&& f, Tuple&& t, index_sequence) - -> decltype(f(std::get(std::forward(t))...)) + -> decltype(f(get(forward(t))...)) { - return f(std::get(std::forward(t))...); + return f(get(forward(t))...); } template constexpr auto apply(F&& f, Tuple&& t) -> decltype(apply_impl( - std::forward(f), - std::forward(t), - make_index_sequence>::value>{} + forward(f), + forward(t), + make_index_sequence>::value>{} )) { return apply_impl( - std::forward(f), - std::forward(t), - make_index_sequence>::value>() + forward(f), + forward(t), + make_index_sequence>::value>() ); } -} // namespace std +} } // namespace arx::arx_std + +#endif // Do not have libstdc++17 -#endif // C++14 +#if ARX_HAVE_LIBSTDCPLUSPLUS > 201703L // Have libstdc++2a + // Nothing to include here, relevant header files were already included + // for C++11 above. +#else // Do not have libstdc++2a -// C++17, C++2a -namespace std { +namespace arx { namespace arx_std { template struct remove_cvref { - typedef std::remove_cv_t> type; + typedef remove_cv_t> type; }; template< class T > using remove_cvref_t = typename remove_cvref::type; -} // namespace std -// C++17, C++2a +} } // namespace arx::arx_std +#endif // Do not have libstdc++2a namespace arx { // others