diff options
| author | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
|---|---|---|
| committer | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
| commit | b3e9e62599532050fc776c5e8f076915b56c2235 (patch) | |
| tree | af252346106a61b18cc6fc6fdbd32e962d096f1c /c23-fallback.h | |
Import official C23 code examples for Modern C (Jens Gustedt, 2024)HEADupstream-importmain
- Add official C source files, Makefile, c23-fallback.h, and LICENSE
- Update README.md with study mirror notice
Diffstat (limited to 'c23-fallback.h')
| -rw-r--r-- | c23-fallback.h | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/c23-fallback.h b/c23-fallback.h new file mode 100644 index 0000000..3331631 --- /dev/null +++ b/c23-fallback.h @@ -0,0 +1,1325 @@ +#ifndef C23_FALLBACK_H +#define C23_FALLBACK_H + +/** @file + ** @brief A header file to augment compatibility of older compilers with C23. + ** + ** This has only been tested with the open source compilers + ** + ** - gcc + ** - clang + ** + ** and the C libraries + ** + ** - glibc + ** - musl + ** + ** The newer the version of these compilers or C libraries you can + ** get, the better. If you use another compiler or C library and have + ** adjustments to make to this file, please share. I will try to + ** include such changes in the future. + ** + ** Commonly supported C23 features that are required are + ** - digit separators such as in `0xAB'CD` + ** - binary integer literals such as `0b1010` or `0B0101` + ** - the new attribute syntax such as `[[deprecated]]` + ** + ** The first two are not easy to circumvent, so probably you should + ** not even try to use C23 enabled code without having support for + ** them. + ** + ** For the attribute syntax there is a feature test + ** __has_c_attribute. It can be used with define (or #ifdef) to test + ** for the syntax itself and with #if and an argument to test for an + ** individual attribute, see below. + ** + ** Another feature test is __has_include, which similarly can be used + ** to test for the preprocessor feature itself and if that is + ** available to test for the availability of a specific header. This + ** can be used to + ** + ** - test for optional headers such as <complex.h>, <threads.h>, or + ** <stdatomic.h>. This now allows to use the corresponding feature + ** test macro (__STDC_NO_COMPLEX__, __STDC_NO_ATOMICS__, + ** __STDC_NO_THREADS__) to test for compiler support of these + ** features, separately from the availability of library + ** interfaces. + ** + ** - test for new headers that come with C23, namely <stdckdint.h> + ** and <stdbit.h>. Even if your C library does not support these, + ** their functionality should mostly be available through + ** type-generic macros that we emulate. Prefer them over function + ** symbols, those may depend on linker symbols that are not yet + ** available. + ** + ** This header here unconditionally includes a bunch of C library + ** headers (see below) and augments them with C23 features if + ** possible. You should not include them yourself, such that we are + ** sure in what we get. This also concerns the new C23 header + ** <stdckdint.h>. + ** + ** Other headers also may be included conditionally, namely + ** <threads.h> and <stdatomic.h>. + ** + ** When emulating the new features, there may be spurious warnings + ** that some attributes are misplaced or ignored. This obviously + ** means that they are not completely taken into account and that the + ** analysis they are expected to provide is not yet fully implemented + ** by your compiler. If you are flooded by these, you might switch + ** some of the diagnostics off by defining the macro + ** `C23_FALLBACK_SILENT` for example by providing the command line + ** argument `-DC23_FALLBACK_SILENT`. + ** + ** But note also, that some errors that are found are actually real + ** errors. These are meant as bad code examples and also as tests if + ** compilers are able to detect these kind of things. If you stumble + ** into these, there should be a code comment close by that explains + ** the particular example. Compilers vary widely in what they are + ** able to detect, so here the more you see, the better. + ** + ** And then, ..., there are the bugs. Currently, the code triggers + ** one particular bug of gcc-13 in `endianness.c`. + ** + ** As of March 2024, gcc-14 and clang-18 work with most of the new + ** C23 features. Current restrictions are + ** + ** - clang-18: `constexpr` is not yet implemented + ** - both: `#embed` is not yet implemented + ** + ** As indicated above, some short comings of your C library + ** implementation can be worked around, but unfortunately not all. As + ** of this writing + ** + ** - glibc: `printf` and `scanf` support only includes the new %b + ** specifier for binary representation, but not the %w length + ** modifiers. + ** + ** - musl: in the current distribution there is no support for C23 + ** features, so in particular `printf` etc will not work with the + ** new formats, yet. + ** + ** We are currently working to have patches included to musl whence + ** the new edition of Modern C and the new standard appear. In the + ** mean time I will try to post a patch set and build recipe such + ** that you may easily build such modified version of musl with + ** complete C23 support on your own. Stay tuned. + **/ + +#include <limits.h> + +/* Needed for musl */ +/* #ifdef __has_include */ +/* # if __has_include(<stdc-predef.h>) */ +/* # include <stdc-predef.h> */ +/* # endif */ +/* #endif */ + + +#if (__STDC_UTF_16__ != 1) || (__STDC_UTF_32__ != 1) +# ifndef C23_FALLBACK_SILENT +# warning "C23 imposes that char16_t and char32_t use UTF-16 and UTF-32 encodings" +# endif +#endif + +/** + ** @brief A preprocessor test for the `__VA_OPT__` feature. + ** + ** Compilers before C23 might not implement `__VA_OPT__`. + ** @return This macro should always evaluate to `0` or `1`. + **/ +#define __has_va_opt C23_HAS_VA_OPT_I(C23) +#define C23_HAS_VA_OPT_I(...) \ + C23_HAS_VA_OPT_IIplus(__VA_OPT__(,) 1, 0,) +#define C23_HAS_VA_OPT_IIplus(_0, _1, ...) _1 + +#if !__has_va_opt +# ifndef C23_FALLBACK_SILENT +# warning "no support of C23 __VA_OPT__ found" +# endif +#endif + +#ifndef BITINT_MAXWIDTH +# ifdef __BITINT_MAXWIDTH__ +# define BITINT_MAXWIDTH __BITINT_MAXWIDTH__ +# else +# ifndef C23_FALLBACK_SILENT +# warning "no support for _BitInt types found" +# endif +# endif +#endif + +/* A simple type-generic conditional. */ +#define GENERIC_IF(COND, A, B) \ + _Generic((char(*)[1+!!(COND)])0, \ + char(*)[2]: (A), \ + default: (B)) + +/* clang has a set of feature tests that are quite comfortable, + + __has_feature, to test if a specific feature is implemented + + __has_extension, to test if a specific feature is implemented + + __is_identifier, to test if a word is an identifier or a keyword. + + In the following we construct similar tests for gcc such that we + may use these features independently from the C standard version + that is supported. */ +# define __tester2(_0, _1, ...) _1 +# define __tester1(...) __tester2(__VA_ARGS__) +#ifndef __has_feature +# define __has_feature(X) __tester1(__has_feature_ ## X 1, 0, ) +#endif +#ifndef __has_extension +# define __has_extension(X) __tester1(__has_extension_ ## X 1, 0, ) +#endif + +#ifndef __is_identifier +# define __is_identifier(X) __tester1(__is_special_ ## X 0, 1, ) +# define __is_special_auto , +# define __is_special_break , +# define __is_special_case , +# define __is_special_char , +# define __is_special_const , +# define __is_special_continue , +# define __is_special_default , +# define __is_special_do , +# define __is_special_double , +# define __is_special_else , +# define __is_special_enum , +# define __is_special_extern , +# define __is_special_float , +# define __is_special_for , +# define __is_special_goto , +# define __is_special_if , +# define __is_special_inline , +# define __is_special_int , +# define __is_special_long , +# define __is_special_register , +# define __is_special_restrict , +# define __is_special_return , +# define __is_special_short , +# define __is_special_signed , +# define __is_special_sizeof , +# define __is_special_static , +# define __is_special_struct , +# define __is_special_switch , +# define __is_special_typedef , +# define __is_special_union , +# define __is_special_unsigned , +# define __is_special_void , +# define __is_special_volatile , +# define __is_special_while , +# define __is_special__Atomic , +# define __is_special__Complex , +# define __is_special__Decimal128 , +# define __is_special__Decimal32 , +# define __is_special__Decimal64 , +# define __is_special__Generic , +# define __is_special__Imaginary , +# define __is_special__Noreturn , +# if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L +# define __is_special_nullptr , +# define __is_special_static_assert , +# define __is_special_thread_local , +# define __is_special_constexpr , +# define __is_special_typeof , +# define __is_special_typeof_unqual , +# endif +# if __GNUC__ > 13 || __STDC_VERSION__ >= 202311L +# define __is_special__BitInt , +# define __is_special_alignas , +# define __is_special_alignof , +# define __is_special_bool , +# define __is_special_false , +# define __is_special_true , +# endif +#endif +#if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L +# define __has_feature_c_fixed_enum , +#endif + +// The feature for fixed underlying types for enums has been so long +// in clang, that they don't even seem to have a feature test for it. +#if !__has_feature(c_fixed_enum) && defined(__clang_major__) && (__clang_major__ < 8) +# ifndef C23_FALLBACK_SILENT +# warning "syntax for fixed underlying integer type of enumerations is not supported" +# endif +#endif + +#if __is_identifier(constexpr) +# ifndef C23_FALLBACK_SILENT +# warning "constexpr keyword is not supported, emulating as static const" +# endif +# define constexpr static const +#endif + +#if __is_identifier(bool) +# ifndef C23_FALLBACK_SILENT +# warning "bool keyword is not supported, emulating as macro" +# endif +# define bool _Bool +#endif +#if __is_identifier(false) +# ifndef C23_FALLBACK_SILENT +# warning "false keyword is not supported, emulating as macro" +# endif +# undef false +# define false ((bool)+0U) +#endif +#if __is_identifier(true) +# ifndef C23_FALLBACK_SILENT +# warning "true keyword is not supported, emulating as macro" +# endif +# undef true +# define true ((bool)+1U) +#endif + +#if __is_identifier(thread_local) +// Maybe this is also provided by the C library implementation, but a +// redefinition makes no harm. +#define thread_local _Thread_local +#endif + +#if __is_identifier(alignof) +#undef alignof +#define alignof _Alignof +#endif + +#if __is_identifier(alignas) +#undef alignas +#define alignas _Alignas +#endif + +#if __is_identifier(static_assert) +#undef static_assert +#define static_assert(...) __static_assert(__VA_ARGS__,) +#define __static_assert(E, ...) _Static_assert(E) +#endif + +#if __is_identifier(typeof) +# if __GNUC__ +# undef typeof +# define typeof __typeof__ +# else +# warning "typeof operator only comes with C23" +# endif +#endif + +#if __STDC_VERSION__ < 202311L +# if __GNUC__ +# if __GNUC__ < 13 +# define auto __auto_type +# endif +# else +# warning "auto type inference only comes with C23" +# endif +#endif + +#ifndef __has_c_attribute +# define __has_c_attribute(X) 0 +#endif + +// C23 has nullptr, use a fallback if that is not available. Here we +// use an enumeration type and constant to do that. The idea is that +// such an enumeration may then be used in _Generic as long the cases +// otherwise only have pointer types. The first choice is to use a +// _BitInt type for that, if that is already available, in an effort +// to not clash with standard integer types. If that is not found, we +// use a standard integer type that has the right width. +// +// If possible, we also use an attribute that forces warnings if any +// other value than a null or 0 is assigned to a variable of type +// nullptr_t. +#if !__is_identifier(nullptr) +// Maybe this is also provided by the C library implementation, but a +// redefinition makes no harm. +typedef typeof(nullptr) nullptr_t; +#elif (__STC_VERSION__ < 202311L) +# ifdef BITINT_MAXWIDTH + +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for nullptr to enumeration constant based on _BitInt type" +# else +# pragma GCC diagnostic ignored "-Wnon-literal-null-conversion" +# endif + +enum +# if __has_c_attribute(clang::enum_extensibility) +[[__clang__::__enum_extensibility__(closed)]] +# endif +nullptr_t : +__typeof__((unsigned _BitInt(sizeof(void*)*CHAR_BIT))0) +{ + nullptr, +}; + +# else + +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for nullptr to enumeration constant based on standard integer type" +# endif + +enum +# if __has_c_attribute(clang::enum_extensibility) +[[__clang__::__enum_extensibility__(closed)]] +# endif +nullptr_t { + nullptr, + __nullptr_max = + GENERIC_IF((sizeof(void*)==sizeof(int)), + -1U, + GENERIC_IF((sizeof(void*)==sizeof(long)), + -1UL, + -1ULL)), +}; + +// Unfortunately such an enumeration type is not yet good enough for +// all compilers. In particular some gcc version has nullptr and +// __nullptr_max as different types. Force an expression that is an +// ICE of value 0 and of the enumeration type. +# define nullptr (1 ? nullptr : __nullptr_max) +# endif +#endif +typedef typeof(nullptr) nullptr_t; + +static_assert(_Generic(nullptr, nullptr_t: true)); +static_assert(sizeof(void*)==sizeof(nullptr)); +static_assert(sizeof(void*)==sizeof(nullptr_t)); + +/**********************************************************************************************/ + +#include <inttypes.h> +#include <setjmp.h> +#include <signal.h> +#include <stdalign.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> + +/**********************************************************************************************/ + +// for call_once +#ifndef ONCE_FLAG_INIT +# ifdef __has_include +# if __has_include(<threads.h>) +# ifndef C23_FALLBACK_SILENT +# warning "including call_once function from <threads.h> instead of <stdlib.h>" +# endif +# include <threads.h> +# endif +# endif +#endif + +// If call_once has not been found we construct a function that is +// based on atomics. +#ifndef ONCE_FLAG_INIT +# ifdef __has_include +# if __has_include(<stdatomic.h>) +# include <stdatomic.h> +# ifndef C23_FALLBACK_SILENT +# warning "emulating call_once function by using atomics" +# endif +enum { once_initial, once_inter, once_final, }; +# define ONCE_FLAG_INIT once_initial + +typedef _Atomic(unsigned) once_flag; + +# if __has_c_attribute(__gnu__::__noinline__) +[[__gnu__::__noinline__]] +# endif +static inline +void call_once(once_flag* flag, void callback(void)) { + if (*flag != once_final) { + unsigned expected = once_initial; + if (atomic_compare_exchange_strong(flag, &expected, once_inter)) { + callback(); + *flag = once_final; + } else { + while (*flag != once_final) { + // + } + } + } else { + // We know already that the implementation does not support threads, + // so the only available fence is this: + atomic_signal_fence(memory_order_seq_cst); + } +} +# endif +# endif +#endif + +/**********************************************************************************************/ + +// We assume that the compiler is already able to handle the new +// attribute syntax such as [[deprecated]]. Nevertheless, maybe +// certain attributes themselves might not yet be implemented. We +// provide fallback macros to corresponding gnu attributes. Here we +// use the "safe" names for these features with double underscores, so +// they don't interfere with application names. + +#if !__has_c_attribute(__unsequenced__) +# ifndef C23_FALLBACK_SILENT +# warning "unsequenced attribute not yet implemented" +# endif +# define __unsequenced__ +# if __has_c_attribute(__gnu__::__const__) +# ifndef C23_FALLBACK_SILENT +# warning "unsequenced attribute could be emulated via gnu attribute by using c23_unsequenced" +# endif +# define c23_unsequenced __attribute__((__const__)) +# endif +#endif + +#if !__has_c_attribute(__reproducible__) +# ifndef C23_FALLBACK_SILENT +# warning "reproducible attribute not yet implemented" +# endif +# define __reproducible__ +# if __has_c_attribute(__gnu__::__pure__) +# ifndef C23_FALLBACK_SILENT +# warning "reproducible attribute could be emulated via gnu attribute by using c23_reproducible" +# endif +# define c23_reproducible __attribute__((__pure__)) +# endif +#endif + +#if !__has_c_attribute(__deprecated__) && __has_c_attribute(__gnu__::__deprecated__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating deprecated attribute via gnu attribute" +# endif +# define __deprecated__ __gnu__::__deprecated__ +#endif + +#if !__has_c_attribute(__nodiscard__) && __has_c_attribute(__gnu__::__warn_unused_result__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating nodiscard attribute via gnu attribute" +# endif +# define __nodiscard__ __gnu__::__warn_unused_result__ +#endif + +#if !__has_c_attribute(__maybe_unused__) && __has_c_attribute(__gnu__::__unused__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating maybe_unused attribute via gnu attribute" +# endif +# define __maybe_unused__ __gnu__::__unused__ +#endif + +#if !__has_c_attribute(noreturn) && __has_c_attribute(__gnu__::__noreturn__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating noreturn attribute via gnu attribute" +# endif +# define noreturn __gnu__::__noreturn__ +#endif + +#if !__has_c_attribute(__fallthrough__) && __has_c_attribute(__gnu__::__fallthrough__) +# warning "emulating fallthrough attribute via gnu attribute" +# define __fallthrough__ __gnu__::__fallthrough__ +#endif + +#if __has_c_attribute(__deprecated__) || defined(__deprecated__) +// Deprecate two interfaces in <time.h> +[[__deprecated__]] char *asctime(const struct tm*); +[[__deprecated__]] char *ctime(const time_t*); +#endif + +// Gnu compilers have a malloc attribute that comes in two different +// forms. Without argument it just certifies that a function returns a +// pointer value that has not been seen before, and thus the pointer +// target will not alias with anything known. The second form receives +// one or two arguments and specifies a function that should be used +// analogous to free. Unfortunately, clang didn't follow that second +// development, yet. But we suppose that there will be at least a +// partial solution in clang-18 final. +#if __has_c_attribute(__gnu__::__malloc__) && !(defined(__clang_major__) && __clang_major__ <= 19) +# define __gnu_free__(...) __gnu__::__malloc__(__VA_ARGS__) +#else +# define __gnu_free__(...) +#endif + +/**********************************************************************************************/ + +/* C23 allows to omit the second argument of va_start */ +#if __STDC_VERSION_STDARG_H__ < 202311L +#undef va_start +#define va_start(...) __va_start1(__VA_ARGS__ , 0, ) +#define __va_start1(...) __va_start2(__VA_ARGS__) +#define __va_start2(v, l, ...) __builtin_va_start(v, l) +#endif + +/**********************************************************************************************/ + +/* C23 has three new tg interfaces in the new <stdkdint.h> header. +They are modeled after similar gcc features. They are meant to do +arithmetic with overflow check by using everything the compiler can +get from instruction flags that already exist on most CPU. +*/ + +#ifdef __has_include +# if __has_include(<stdckdint.h>) +# include <stdckdint.h> +# endif +#endif + +/* If we don't have the header, yet, we may easily emulate it if we +are on a compiler claiming compatibility with gcc. */ +#ifndef ckd_add +# ifdef __GNUC__ +# ifndef C23_FALLBACK_SILENT +# warning "emulating stdckdint.h interfaces via gnu builtins" +# endif +# define ckd_add(R, A, B) __builtin_add_overflow ((A), (B), (R)) +# define ckd_sub(R, A, B) __builtin_sub_overflow ((A), (B), (R)) +# define ckd_mul(R, A, B) __builtin_mul_overflow ((A), (B), (R)) +# else +# warning "checked integer arithmetic as of C23 is not yet implemented" +# endif +#endif + +/**********************************************************************************************/ + +// C23 has the unreachable macro in <stdlib.h>, use a fallback if that +// is not available +#ifndef unreachable +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for unreachable macro" +# endif +# define unreachable() __builtin_unreachable() +#endif + +/**********************************************************************************************/ + +// C23 provides macros for the width of integer types. Provide a +// fallback if they are not available + +#ifndef BOOL_WIDTH +# define BOOL_WIDTH 1 +#endif + +#ifndef CHAR_WIDTH +# define CHAR_WIDTH CHAR_BIT +#endif + +#ifndef UCHAR_WIDTH +# define UCHAR_WIDTH CHAR_BIT +#endif + +#ifndef SCHAR_WIDTH +# define SCHAR_WIDTH CHAR_BIT +#endif + +#ifndef USHRT_WIDTH +# if USHRT_MAX <= 0xFFFFU +# define USHRT_WIDTH 16 +# define SHRT_WIDTH 16 +# else +# if USHRT_MAX <= 0xFFFFFFFFU +# define USHRT_WIDTH 32 +# define SHRT_WIDTH 32 +# else +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 64 +# define SHRT_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 96 +# define SHRT_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 128 +# define SHRT_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// unsigned int has a width of at least 16. +#ifndef UINT_WIDTH +# if UINT_MAX <= 0xFFFFU +# define UINT_WIDTH 16 +# define INT_WIDTH 16 +# else +# if UINT_MAX <= 0xFFFFFFFFU +# define UINT_WIDTH 32 +# define INT_WIDTH 32 +# else +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 64 +# define INT_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 96 +# define INT_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 128 +# define INT_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// size_t has a width of at least 16. +#ifndef SIZE_WIDTH +# if SIZE_MAX <= 0xFFFFU +# define SIZE_WIDTH 16 +# else +# if SIZE_MAX <= 0xFFFFFFFFU +# define SIZE_WIDTH 32 +# else +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// unsigned long has a width of at least 32 +#ifndef ULONG_WIDTH +# if ULONG_MAX <= 0xFFFFFFFFUL +# define ULONG_WIDTH 32 +# define LONG_WIDTH 32 +# else +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 64 +# define LONG_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 96 +# define LONG_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 128 +# define LONG_WIDTH 128 +# endif +# endif +# endif +# endif +#endif + +// unsigned long long has a width of at least 64. +#ifndef ULLONG_WIDTH +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 64 +# define LLONG_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 96 +# define LLONG_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 128 +# define LLONG_WIDTH 128 +# endif +# endif +# endif +#endif + +// In most cases the [u]intmax_t types will just be the long long +// types. +#ifndef UINTMAX_WIDTH +# if UINTMAX_MAX == ULLONG_MAX +# define UINTMAX_WIDTH ULLONG_WIDTH +# define INTMAX_WIDTH LLONG_WIDTH +# endif +#endif + +#ifndef UINT128_MAX +# ifdef __SIZEOF_INT128__ +# if defined(__clang_major__) && (__clang_major__ < 18) +# ifndef C23_FALLBACK_SILENT +# warning "not using compiler intrinsic for int128_t, please upgrade to clang version ≥ 18" +# endif +# else +# define __has_int128 +# endif +# endif +#endif + +#ifndef UINT256_MAX +# ifdef __SIZEOF_INT256__ +# if defined(__clang_major__) && (__clang_major__ < 18) +# ifndef C23_FALLBACK_SILENT +# warning "not using compiler intrinsic for int256_t, please upgrade to clang version ≥ 18" +# endif +# else +# define __has_int256 +# endif +# endif +#endif + +// In C23 uint128_t may exist, even if it is wider than +// uintmax_t. Provide it if the compiler supports it. +#ifndef UINT128_MAX +# ifdef __has_int128 +typedef signed __int128 int128_t; +typedef unsigned __int128 uint128_t; +# define UINT128_WIDTH 128 +# define INT128_WIDTH 128 +# define UINT128_MAX ((uint128_t)-1) +# define INT128_MAX ((int128_t)(((uint128_t)-1)>>1)) +# define INT128_MIN (-INT128_MAX-1) +# if BITINT_MAXWIDTH >= 128 +# define UINT128_C(X) ((uint128_t)+(X ## wbu)) +# define INT128_C(X) ((int128_t)+(X ## wb)) +# endif +# endif +#endif + +// In C23 uint256_t may exist, even if it is wider than +// uintmax_t. Provide it if the compiler supports it. +#ifndef UINT256_MAX +# ifdef __has_int256 +typedef signed __int256 int256_t; +typedef unsigned __int256 uint256_t; +# define UINT256_WIDTH 256 +# define INT256_WIDTH 256 +# define UINT256_MAX ((uint256_t)-1) +# define INT256_MAX ((int256_t)(((uint256_t)-1)>>1)) +# define INT256_MIN (-INT256_MAX-1) +# if BITINT_MAXWIDTH >= 256 +# define UINT256_C(X) ((uint256_t)+(X ## wbu)) +# define INT256_C(X) ((int256_t)+(X ## wb)) +# endif +# endif +#endif + +/**********************************************************************************************/ + +#if __STDC_VERSION_INTTYPES_H__ < 202311L +# ifndef C23_FALLBACK_SILENT +# warning "native C23 library support for %b printf or scanf formats may be missing" +# endif +# ifndef PRIb32 +# if __GLIBC__ > 1 && defined(__PRI64_PREFIX) +# ifndef C23_FALLBACK_SILENT +# warning "no C23 support for %b printf formats found, emulating with glibc features" +# endif +# define PRIB16 "B" +# define PRIB32 "B" +# define PRIB64 __PRI64_PREFIX "B" +# define PRIB8 "B" +# define PRIBFAST16 __PRIPTR_PREFIX "B" +# define PRIBFAST32 __PRIPTR_PREFIX "B" +# define PRIBFAST64 __PRI64_PREFIX "B" +# define PRIBFAST8 "B" +# define PRIBLEAST16 "B" +# define PRIBLEAST32 "B" +# define PRIBLEAST64 __PRI64_PREFIX "B" +# define PRIBLEAST8 "B" +# define PRIBMAX __PRI64_PREFIX "B" +# define PRIBPTR __PRIPTR_PREFIX "B" +# define PRIb16 "b" +# define PRIb32 "b" +# define PRIb64 __PRI64_PREFIX "b" +# define PRIb8 "b" +# define PRIbFAST16 __PRIPTR_PREFIX "b" +# define PRIbFAST32 __PRIPTR_PREFIX "b" +# define PRIbFAST64 __PRI64_PREFIX "b" +# define PRIbFAST8 "b" +# define PRIbLEAST16 "b" +# define PRIbLEAST32 "b" +# define PRIbLEAST64 __PRI64_PREFIX "b" +# define PRIbLEAST8 "b" +# define PRIbMAX __PRI64_PREFIX "b" +# define PRIbPTR __PRIPTR_PREFIX "b" +# define SCNb16 "hb" +# define SCNb32 "b" +# define SCNb64 __PRI64_PREFIX "b" +# define SCNb8 "hhb" +# define SCNbFAST16 __PRIPTR_PREFIX "b" +# define SCNbFAST32 __PRIPTR_PREFIX "b" +# define SCNbFAST64 __PRI64_PREFIX "b" +# define SCNbFAST8 "hhb" +# define SCNbLEAST16 "hb" +# define SCNbLEAST32 "b" +# define SCNbLEAST64 __PRI64_PREFIX "b" +# define SCNbLEAST8 "hhb" +# define SCNbMAX __PRI64_PREFIX "b" +# define SCNbPTR __PRIPTR_PREFIX "b" +# else +# ifndef C23_FALLBACK_SILENT +# warning "no C23 support for %b printf formats found" +# endif +# endif +# endif +#endif + +/**********************************************************************************************/ + +// Complex arithmetic without <complex.h> header +#ifndef __STDC_NO_COMPLEX__ + +// gcc and related compilers implement an "I" or "i" suffix for +// floating point constants since ages. +# ifdef __GNUC__ +# define I (__extension__ 1.0IF) +# define _COMPLEX_I (0.0F + I) + +// Otherwise use the new constexpr construct to produce a named +// constant of complex type. +# else +typedef union _C23_complex _C23_complex; +union _C23_complex { + // These two are guaranteed to have the same representation. + float _C23_v[2]; + _Complex float _C23_c; +}; +// Use static const. Best would be to have constexpr, but using a +// different member than was initialized is not defined for them, +// unfortunately. +static _C23_complex const _C23_I = { { 0.0F, 1.0F, }, }; +# define _COMPLEX_I (_C23_I._C23_c) +# endif + +#define iscomplex(X) (sizeof(1.0F*(X)) == sizeof(1.0F*(X)+_COMPLEX_I)) +#define isimaginary(X) (sizeof(1.0F*(X)+0.0F) == 2*sizeof(1.0F*(X))) + +# define _C23_float_Complex_CASE float _Complex: true, +# define _C23_double_Complex_CASE double _Complex: true, +# define _C23_long_double_Complex_CASE long double _Complex: true, +#else +# define _C23_float_Complex_CASE +# define _C23_double_Complex_CASE +# define _C23_long_double_Complex_CASE +#endif + +/**********************************************************************************************/ + +// Testing for presence of decimal floating types + +#if defined(__DEC32_MAX__) +# define _C23_Decimal32_CASE _Decimal32: true, +#else +# define _C23_Decimal32_CASE +#endif + +#if defined(__DEC64_MAX__) +# define _C23_Decimal64_CASE _Decimal64: true, +#else +# define _C23_Decimal64_CASE +#endif + +#if defined(__DEC128_MAX__) +# define _C23_Decimal128_CASE _Decimal128: true, +#else +# define _C23_Decimal128_CASE +#endif + +#define isdecimalfloating(...) (_Generic((__VA_ARGS__)+0, _C23_Decimal32_CASE _C23_Decimal64_CASE _C23_Decimal128_CASE default: false)) +#ifndef iscomplex +# define iscomplex(...) (_Generic((__VA_ARGS__)+0, _C23_float_Complex_CASE _C23_double_Complex_CASE _C23_long_double_Complex_CASE default: false)) +#endif +#define isstandardrealfloating(...) (_Generic((__VA_ARGS__)+0, float: true, double: true, long double: true, default: false)) +#define isstandardfloating(...) ((bool)(isstandardrealfloating(__VA_ARGS__)||iscomplex(__VA_ARGS__))) +#ifndef isfloating +# define isfloating(...) ((bool)(isstandardfloating(__VA_ARGS__)||isdecimalfloating(__VA_ARGS__))) +#endif + +/**********************************************************************************************/ + +// various tg-conversions and type traits + +#ifndef totype +# define totype(Y, ...) ((typeof(__VA_ARGS__))Y) +#endif +#ifndef tominusone +# define tominusone(...) totype(-1, __VA_ARGS__) +#endif +#ifndef tozero +# define tozero(...) totype(0, __VA_ARGS__) +#endif +#ifndef toone +# define toone(...) totype(1, __VA_ARGS__) +#endif +#ifndef iscompatible +# define iscompatible(X, Y) (_Generic((X), typeof(Y): true, default: false)) +#endif +#ifndef is_potentially_negative +# define is_potentially_negative(...) (tominusone(__VA_ARGS__) < 0) +#endif +#ifndef is_const_target +# define is_const_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void const*: true, void const volatile*: true, default: false)) +#endif +#ifndef is_volatile_target +# define is_volatile_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void volatile*: true, void const volatile*: true, default: false)) +#endif +#ifndef is_const +# define is_const(...) is_const_target(&(typeof(__VA_ARGS__)){ 0 }) +#endif +#ifndef is_volatile +# define is_volatile(...) is_volatile_target(&(typeof(__VA_ARGS__)){ 0 }) +#endif +#ifndef is_null_pointer_constant +struct do_not_use_this_otherwise; +# define is_null_pointer_constant(...) \ + (_Generic((1 ? (struct do_not_use_this_otherwise*)nullptr : (__VA_ARGS__)), \ + struct do_not_use_this_otherwise*: true, \ + default: false)) +#endif +#ifndef is_zero_ice +# define is_zero_ice(...) is_null_pointer_constant((void*)(uintptr_t)(!!(__VA_ARGS__))) +#endif +#ifndef isinteger +# define isinteger(...) is_zero_ice(tozero(__VA_ARGS__)) +#endif +#ifndef issigned +# define issigned(...) ((bool)(is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char))) +#endif +#ifndef isunsigned +# define isunsigned(...) ((bool)(!is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char))) +#endif +#ifndef isice +# define isice(...) is_zero_ice(!((__VA_ARGS__) || 1)) +#endif +#ifndef isvla +# define isvla(...) ((bool)!isice(sizeof(__VA_ARGS__))) +#endif +#ifndef isxwide +# define isxwide(...) \ + ((bool)( \ + isinteger(__VA_ARGS__) \ + &&_Generic((__VA_ARGS__)+0ULL, \ + unsigned long long: false, \ + default: true))) +#endif + +#ifndef is_pointer +struct do_not_use_this_otherwise { char c; }; +# define get_fla(...) \ + GENERIC_IF(isvla(__VA_ARGS__), \ + (struct do_not_use_this_otherwise[1]){ 0 }, \ + (__VA_ARGS__)) +# define is_pointer_nvla(...) \ + (_Generic((typeof(__VA_ARGS__)*)0, \ + typeof(get_fla(*(__VA_ARGS__)))**: true, \ + default: false)) +# define is_pointer_vla(...) \ + (_Generic((typeof(get_fla(*(__VA_ARGS__)))*)0, \ + typeof(struct do_not_use_this_otherwise[1])*: true, \ + default: false)) +# define is_pointer(...) ((bool)(is_pointer_nvla(__VA_ARGS__)||is_pointer_vla(__VA_ARGS__))) +#endif +#ifndef is_array +# define is_array(...) ((bool)!is_pointer(__VA_ARGS__)) +#endif +#ifndef is_fla +# define is_fla(...) ((bool)(is_array(__VA_ARGS__)&&!isvla(__VA_ARGS__))) +#endif + +#ifndef is_void_pointer +# define is_void_pointer(...) \ + _Generic((typeof(*(__VA_ARGS__))const volatile*)nullptr, \ + void const volatile*: true, \ + default: false) +#endif + +/**********************************************************************************************/ + +/* Const preserving functions as of C23. */ + +/* These are not exactly according to the book, because the return + type of the string functions could sometimes be a pointer to a void + type and not a character type.*/ + +#if __STDC_VERSION_STRING_H__ < 202311L + +//QVoid *memchr(QVoid *s, int c, size_t n); +# ifndef memchr +# define memchr(S, C, N) ((typeof(1 ? S : (void*)1))memchr((S), (C), (N))) +# endif + +// QChar *strchr(QChar *s, int c); +# ifndef strchr +# define strchr(S, C) ((typeof(S))strchr((S), (C))) +# endif + +// QChar *strpbrk(QChar *s1, const char *s2); +# ifndef strpbrk +# define strpbrk(S1, S2) ((typeof(S1))strpbrk((S1), (S2))) +# endif + +// QChar *strrchr(QChar *s, int c); +# ifndef strrchr +# define strrchr(S, C) ((typeof(S))strrchr((S), (C))) +# endif + +// QChar *strstr(QChar *s1, const char *s2); +# ifndef strstr +# define strstr(S1, S2) ((typeof(S1))strstr((S1), (S2))) +# endif + +#endif + +#if __STDC_VERSION_WCHAR_H__ < 202311L + +//QWchar *wmemchr(QWchar *s, int c, size_t n); +# ifndef wmemchr +# define wmemchr(S, C, N) ((typeof(S))wmemchr((S), (C), (N))) +# endif + +// QWchar *wcschr(QWchar *s, wchar_t c); +# ifndef wcschr +# define wcschr(S, C) ((typeof(S))wcschr((S), (C))) +# endif + +// QWchar *wcspbrk(QWchar *s1, const wchar_t *s2); +# ifndef wcspbrk +# define wcspbrk(S1, S2) ((typeof(S1))wcspbrk((S1), (S2))) +# endif + +// QWchar *wcsrchr(QWchar *s, wchar_t c); +# ifndef wcsrchr +# define wcsrchr(S, C) ((typeof(S))wcsrchr((S), (C))) +# endif + +// QWchar *wcswcs(QWchar *s1, const wchar_t *s2); +# ifndef wcsstr +# define wcsstr(S1, S2) ((typeof(S1))wcsstr((S1), (S2))) +# endif + +#endif + +#if __STDC_VERSION_STDLIB_H__ < 202311L + +// QVoid *bsearch(const void *key, QVoid *base, size_t nmemb, size_t size, +// int (*compar)(const void *, const void *)); +# ifndef bsearch +# define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR) ((typeof(1 ? (BASE) : (void*)1))bsearch((KEY), (BASE), (NMEMB), (SIZE), (COMPAR))) +# endif + +#endif + +#endif + +/**********************************************************************************************/ + +#ifdef __has_include +# if __has_include(<stdbit.h>) +# include <stdbit.h> +# define __has_feature_c_stdbit_h , +# endif +#endif + +#ifndef __STDC_ENDIAN_LITTLE__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_LITTLE__ __ORDER_LITTLE_ENDIAN__ +# endif +#endif + +#ifndef __STDC_ENDIAN_BIG__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_BIG__ __ORDER_BIG_ENDIAN__ +# endif +#endif + +#ifndef __STDC_ENDIAN_NATIVE__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_NATIVE__ __BYTE_ORDER__ +# else +# ifndef C23_FALLBACK_SILENT +# warning "native endian could not be determined" +# endif +# endif +#endif + +/**********************************************************************************************/ + +/* This adds the 14 type-generic bit interfaces that are added by + C23. The use of the other type-specific interfaces is of less + interest. + +generic_return_type stdc_leading_zeros(generic_value_type value); +generic_return_type stdc_leading_ones(generic_value_type value); +generic_return_type stdc_trailing_zeros(generic_value_type value); +generic_return_type stdc_trailing_ones(generic_value_type value); +generic_return_type stdc_first_leading_zero(generic_value_type value); +generic_return_type stdc_first_leading_one(generic_value_type value); +generic_return_type stdc_first_trailing_zero(generic_value_type value); +generic_return_type stdc_count_zeros(generic_value_type value); +generic_return_type stdc_count_ones(generic_value_type value); +bool stdc_has_single_bit(generic_value_type value); +generic_return_type stdc_bit_width(generic_value_type value); +generic_value_type stdc_bit_floor(generic_value_type value); +generic_value_type stdc_bit_ceil(generic_value_type value); + +*/ + +/* + An auxiliary macro that shifts right by ULLONG_WIDTH, regardless + whether the type has a width smaller than that. For most types this + will just be a zero of the same type. + */ + +#define shift_xright(...) \ + GENERIC_IF(isxwide(__VA_ARGS__), \ + (__VA_ARGS__)>>GENERIC_IF(isxwide(__VA_ARGS__), \ + ULLONG_WIDTH, \ + 0), \ + tozero(__VA_ARGS__)) + +#ifndef stdc_count_ones +# ifdef __GNUC__ + +# define stdc_trailing_zeros(...) \ + ({ \ + auto s_t_z_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_t_z_x), "bit operation needs unsigned type"); \ + (s_t_z_x) \ + ? _Generic((s_t_z_x), \ + bool: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned char: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned short: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned: __builtin_ctz(s_t_z_x), \ + unsigned long: __builtin_ctzl(s_t_z_x), \ + unsigned long long: __builtin_ctzll(s_t_z_x), \ + default: ({ \ + size_t s_t_z_ret = 0; \ + while (s_t_z_x) { \ + unsigned long long s_t_z_z = s_t_z_x; \ + if (s_t_z_z) { \ + s_t_z_ret += __builtin_ctzll(s_t_z_z); \ + s_t_z_x = 0; \ + } else { \ + s_t_z_ret += ULLONG_WIDTH; \ + s_t_z_x = shift_xright(s_t_z_x); \ + } \ + } \ + s_t_z_ret; \ + })) \ + : stdc_count_ones(tominusone(s_t_z_x)); \ + }) + +/* This counts the least significant 1-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_trailing_ones(...) stdc_trailing_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__)) + +# define stdc_leading_zeros(...) \ + ({ \ + auto s_l_o_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_l_o_x), "bit operation needs unsigned type"); \ + (s_l_o_x) \ + ? _Generic((s_l_o_x), \ + unsigned char: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \ + unsigned short: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \ + unsigned: __builtin_clz(s_l_o_x), \ + unsigned long: __builtin_clzl(s_l_o_x), \ + unsigned long long: __builtin_clzll(s_l_o_x), \ + default: \ + ((!isxwide(s_l_o_x)) \ + /* Should only trigger for _BitInt types. */ \ + ? (__builtin_clzll(s_l_o_x) - __builtin_clzll(tominusone(s_l_o_x))) \ + /* determine the long long word with highest 1-bit . */ \ + : ({ \ + size_t s_l_o_w = stdc_count_ones(tominusone(s_l_o_x)) - ULLONG_WIDTH; \ + while ((s_l_o_x+0UL) > ULLONG_MAX) { \ + s_l_o_x = shift_xright(s_l_o_x); \ + s_l_o_w -= ULLONG_WIDTH; \ + } \ + __builtin_clzll(s_l_o_x) + s_l_o_w; \ + }))) \ + /* This should resolve to a compile time constant. */ \ + : stdc_count_ones(tominusone(s_l_o_x)); \ + }) + + +/* This counts the most significant 1-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_leading_ones(...) stdc_leading_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__)) + + +# define stdc_count_ones(...) \ + _Generic((__VA_ARGS__), \ + bool: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned char: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned short: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned long: __builtin_popcountl((unsigned long)(__VA_ARGS__)), \ + unsigned long long: __builtin_popcountll((unsigned long long)(__VA_ARGS__)), \ + default: ({ \ + auto s_c_o_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_c_o_x), "bit operation needs unsigned type"); \ + size_t s_c_o_ret = 0; \ + while (s_c_o_x) { \ + s_c_o_ret += __builtin_popcountll((unsigned long long)s_c_o_x); \ + s_c_o_x = shift_xright(s_c_o_x); \ + } \ + s_c_o_ret; \ + })) + + +/* This counts the 0-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_count_zeros(...) stdc_count_ones((typeof(__VA_ARGS__))~(__VA_ARGS__)) + +# define stdc_has_single_bit(...) \ + ({ \ + auto s_h_s_b_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_h_s_b_x), "bit operation needs unsigned type"); \ + (bool)(s_h_s_b_x && !((s_h_s_b_x != 1) && (s_h_s_b_x & (s_h_s_b_x - 1)))); \ + }) + +# define stdc_bit_width(...) \ + ({ \ + auto s_b_w_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_b_w_x), "bit operation needs unsigned type"); \ + stdc_count_ones(tominusone(s_b_w_x))-stdc_leading_zeros(s_b_w_x); \ + }) + +# define stdc_bit_floor(...) \ + ({ \ + auto s_b_f_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_b_f_x), "bit operation needs unsigned type"); \ + (!s_b_f_x) \ + ? 0 \ + : (toone(s_b_f_x)<<(stdc_bit_width(s_b_f_x)-1)); \ + }) + +# define stdc_bit_ceil(...) \ + (toone(__VA_ARGS__)<<stdc_bit_width(__VA_ARGS__)) + +# endif +#endif |
