From b3e9e62599532050fc776c5e8f076915b56c2235 Mon Sep 17 00:00:00 2001 From: David Faulkner Date: Fri, 7 Aug 2026 23:40:47 -0500 Subject: Import official C23 code examples for Modern C (Jens Gustedt, 2024) - Add official C source files, Makefile, c23-fallback.h, and LICENSE - Update README.md with study mirror notice --- generic.h | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 generic.h (limited to 'generic.h') diff --git a/generic.h b/generic.h new file mode 100644 index 0000000..c7c5174 --- /dev/null +++ b/generic.h @@ -0,0 +1,360 @@ +#ifndef GENERIC_H +#define GENERIC_H 1 + +#include +#include +#include "c23-fallback.h" + +#if !__has_va_opt +# error "no __VA_OPT__ support found, aborting compilation" +// shut off some subsequent error messages +# include "no __VA_OPT__ support found, aborting compilation" +#endif + +/** + ** @brief Type-generic minimum for floating-point values + **/ +#define min(A, B) \ +_Generic((A)+(B), \ + float: minf, \ + long double: minl, \ + default: mind)((A), (B)) + +static inline +double mind(double a, double b) [[__unsequenced__]] { + return a < b ? a : b; +} + +static inline +long double minl(long double a, long double b) [[__unsequenced__]] { + return a < b ? a : b; +} + +static inline +float minf(float a, float b) [[__unsequenced__]] { + return a < b ? a : b; +} + +/** + ** @brief The maximum value for the type of @a X + **/ +#define MAXVAL(X) \ +_Generic((X), \ + bool: (bool)+1, \ + char: (char)+CHAR_MAX, \ + signed char: (signed char)+SCHAR_MAX, \ + unsigned char: (unsigned char)+UCHAR_MAX, \ + signed short: (signed short)+SHRT_MAX, \ + unsigned short: (unsigned short)+USHRT_MAX, \ + signed: INT_MAX, \ + unsigned: UINT_MAX, \ + signed long: LONG_MAX, \ + unsigned long: ULONG_MAX, \ + signed long long: LLONG_MAX, \ + unsigned long long: ULLONG_MAX, \ + float: FLT_MAX, \ + double: DBL_MAX, \ + long double: LDBL_MAX) + +/** + ** @brief The maximum promoted value for @a XT, where @a XT + ** can be an expression or a type name + ** + ** So this is the maximum value when fed to an arithmetic + ** operation such as @c +. + ** + ** @remark Narrow types are promoted, usually to @c signed, + ** or maybe to @c unsigned on rare architectures. + **/ +#define maxof(XT) \ +_Generic(0+(XT)+0, \ + signed: INT_MAX, \ + unsigned: UINT_MAX, \ + signed long: LONG_MAX, \ + unsigned long: ULONG_MAX, \ + signed long long: LLONG_MAX, \ + unsigned long long: ULLONG_MAX, \ + float: FLT_MAX, \ + double: DBL_MAX, \ + long double: LDBL_MAX) + +#define MINVAL(X) \ +_Generic((X), \ + bool: (bool)+0, \ + char: (char)+CHAR_MIN, \ + signed char: (signed char)+SCHAR_MIN, \ + unsigned char: (unsigned char)+0, \ + signed short: (signed short)+SHRT_MIN, \ + unsigned short: (unsigned short)+0, \ + signed: INT_MIN, \ + unsigned: 0U, \ + signed long: LONG_MIN, \ + unsigned long: 0UL, \ + signed long long: LLONG_MIN, \ + unsigned long long: 0ULL, \ + float: -FLT_MAX, \ + double: -DBL_MAX, \ + long double: -LDBL_MAX) + +#define minof(XT) \ +_Generic(0+(XT)+0, \ + signed: INT_MIN, \ + unsigned: 0U, \ + signed long: LONG_MIN, \ + unsigned long: 0UL, \ + signed long long: LLONG_MIN, \ + unsigned long long: 0ULL, \ + float: -FLT_MAX, \ + double: -DBL_MAX, \ + long double: -LDBL_MAX) + +#define strtod(NPTR, ...) \ + STRTOD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOD_I(NPTR) strtod(NPTR, nullptr) +#define STRTOD_II(NPTR, ENDPTR) strtod(NPTR, ENDPTR) +#define strtof(NPTR, ...) \ + STRTOF_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOF_I(NPTR) strtof(NPTR, nullptr) +#define STRTOF_II(NPTR, ENDPTR) strtof(NPTR, ENDPTR) +#define strtold(NPTR, ...) \ + STRTOLD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOLD_I(NPTR) strtold(NPTR, nullptr) +#define STRTOLD_II(NPTR, ENDPTR) strtold(NPTR, ENDPTR) + +/** @brief Output the second argument, if any, or `DEF0` as a default. + ** + ** Error if more than two arguments are given. + **/ +#define DEFAULT1(DEF0, ...) \ + ID_I ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIplus_DEFAULT(_01, ...) ID_I(__VA_ARGS__) + +#define ID_() +#define ID_I(_01) _01 +#define ID_II(_01, _2) _01, _2 +#define ID_III(_01, _2, _3) _01, _2, _3 + +/** @brief Output comma-separated arguments from the third position + ** onward, fill with defaults from the first two. + ** + ** Error if more than four arguments are given. + **/ +#define DEFAULT2(DEF0, DEF1, ...) \ + ID_II ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0, DEF1 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIIplus_DEFAULT(DEF0, DEF1, _01, ...) \ + DEFAULT1(DEF0, _01), DEFAULT1(DEF1, __VA_ARGS__) + +/** @brief Output comma-separated arguments from the fourth position + ** onward, fill with defaults from the first three. + ** + ** Error if more than six arguments are given. + **/ +#define DEFAULT3(DEF0, DEF1, DEF2, ...) \ + ID_III ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0, DEF1, DEF2 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIIIplus_DEFAULT(DEF0, DEF1, DEF2, _01, ...) \ + DEFAULT1(DEF0, _01), DEFAULT2(DEF1, DEF2, __VA_ARGS__) + +#define CALL1(FUNC, DEF0, ...) \ + FUNC(DEFAULT1(DEF0, __VA_ARGS__)) +#define CALL2(FUNC, DEF0, DEF1, ...) \ + FUNC(DEFAULT2(DEF0, DEF1, __VA_ARGS__)) +#define CALL3(FUNC, DEF0, DEF1, DEF2, ...) \ + FUNC(DEFAULT3(DEF0, DEF1, DEF2, __VA_ARGS__)) + +#define strtoul(...) \ + CALL3(strtoul, "0", nullptr, 0, __VA_ARGS__) +#define strtoull(...) \ + CALL3(strtoull, "0", nullptr, 0, __VA_ARGS__) +#define strtol(...) \ + CALL3(strtol, "0", nullptr, 0, __VA_ARGS__) +#define strtoll(...) \ + CALL3(strtoll, "0", nullptr, 0, __VA_ARGS__) + +/** + ** @brief Calls a three-parameter function with default arguments set to + ** specific values + ** + ** @see strtoull10 for an example + **/ +#define strtoul10(NPTR, ...) CALL3((strtoul), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtoull10(NPTR, ...) CALL3((strtoull), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtol10(NPTR, ...) CALL3((strtol), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtoll10(NPTR, ...) CALL3((strtoll), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) + +#define PROMOTE(XT, A) \ + _Generic(0+(XT)+0, \ + signed: (signed)(A), \ + unsigned: (unsigned)(A), \ + signed long: (signed long)(A), \ + unsigned long: (unsigned long)(A), \ + signed long long: (signed long long)(A), \ + unsigned long long: (unsigned long long)(A), \ + float: (float)(A), \ + double: (double)(A), \ + long double: (long double)(A)) + +#define SIGNEDNESS(XT) (PROMOTE(XT, -1) < PROMOTE(XT, 0)) + +inline +signed maxs(signed a, signed b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned maxu(unsigned a, unsigned b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned maxus(unsigned a, signed b) [[__unsequenced__]] { + return b < 0 ? a : maxu(a, b); +} + +inline +unsigned maxsu(signed a, unsigned b) [[__unsequenced__]] { + return a < 0 ? b : maxu(a, b); +} + +inline +signed long maxsl(signed long a, signed long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long maxul(unsigned long a, unsigned long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long maxusl(unsigned long a, signed long b) [[__unsequenced__]] { + return b < 0 ? a : maxul(a, b); +} + +inline +unsigned long maxsul(signed long a, unsigned long b) [[__unsequenced__]] { + return a < 0 ? b : maxul(a, b); +} + +inline +signed long long maxsll(signed long long a, signed long long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long long maxull(unsigned long long a, unsigned long long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long long maxusll(unsigned long long a, signed long long b) [[__unsequenced__]] { + return b < 0 ? a : maxull(a, b); +} + +inline +unsigned long long maxsull(signed long long a, unsigned long long b) [[__unsequenced__]] { + return a < 0 ? b : maxull(a, b); +} + +#define MAX(X, Y) \ +_Generic((X)+(Y), \ + signed: maxs, \ + unsigned: maxu, \ + signed long: maxsl, \ + unsigned long: maxul, \ + signed long long: maxsll, \ + unsigned long long: maxull)((X), (Y)) + +#define MAXUS(X, Y) \ +_Generic((X)+(Y), \ + signed: maxus, \ + unsigned: maxus, \ + signed long: maxusl, \ + unsigned long: maxusl, \ + signed long long: maxusll, \ + unsigned long long: maxusll)((X), (Y)) + +#define MAXSU(X, Y) \ +_Generic((X)+(Y), \ + signed: maxsu, \ + unsigned: maxsu, \ + signed long: maxsul, \ + unsigned long: maxsul, \ + signed long long: maxsull, \ + unsigned long long: maxsull)((X), (Y)) + +#define mix(X, Y) \ +_Generic(&(char[SIGNEDNESS(X)-SIGNEDNESS(Y)+2]){ }, \ + char(*)[1]: MAXUS((X), (Y)), \ + char(*)[2]: MAX((X), (Y)), \ + char(*)[3]: MAXSU((X), (Y))) + +/** + ** @brief Similar to `snprintf` but checks the buffer argument for + ** the size. + ** + ** To be able to do that check, the function has the size argument + ** first and then the buffer, so the buffer can be specified as array + ** parameter that depends on the that size. + **/ +[[maybe_unused, __gnu__::__format__(__printf__, 3, 4)]] +static inline +int snprintf_swapped(size_t n, char s[restrict static n], + char const* restrict form, ...) { + va_list ap; + va_start(ap); + int ret = vsnprintf(s, n, form, ap); + va_end(ap); + return ret; +} + +/** + ** @brief A type-generic interface to `snprintf` that checks arguments. + ** + ** We distinguish two cases. First if the first argument is `nullptr` + ** the second argument is forced to `0` and the function `snprintf` + ** is called. If it is not `nullptr` a function `snprintf_swapped` + ** that just swaps the first two arguments is called. Because + ** that function has the size argument first and then the buffer, the + ** buffer can be specified as array parameter that depends on the + ** that size. + **/ +#define snprintf(S, N, F, ...) \ +_Generic \ + ((S), \ + nullptr_t: \ + (snprintf)(nullptr, GENERIC_IF(isice(N), (N), 0), \ + F __VA_OPT__(,) __VA_ARGS__), \ + default: \ + snprintf_swapped(_Generic((S), \ + nullptr_t: 1, \ + default: (N)), \ + _Generic((S), \ + nullptr_t: \ + (char[1]){ 0 }, \ + default: (S)), \ + (F) __VA_OPT__(,) __VA_ARGS__)) + +#define pow(X, Y) \ + _Generic \ + ( \ + (void (*)(typeof((X)+(Y)+0ULL), typeof((Y)+0ULL)))nullptr, \ + /* Second argument is an integer. */ \ + void (*)(float, unsigned long long): pownf, \ + void (*)(double, unsigned long long): pown, \ + void (*)(unsigned long long, unsigned long long): pown, \ + void (*)(long double, unsigned long long): pownl, \ + /* Second argument is a floating point. */ \ + void (*)(float, float): powf, \ + void (*)(long double, float): powl, \ + void (*)(long double, double): powl, \ + void (*)(long double, long double): powl, \ + /* Second argument is a floating-point, and first */ \ + /* is double or integer. */ \ + default: pow) \ + ((X), (Y)) + + +#endif -- cgit v1.2.3