diff options
| author | 2025-09-26 12:38:43 -0400 | |
|---|---|---|
| committer | 2025-09-26 12:59:54 -0400 | |
| commit | 023af72f141005860219b3c63c3a63d2c179b2c1 (patch) | |
| tree | 025f0114436af04a8c5bc4c3b90b507cabdbf99c | |
| parent | 98ffb7841e95dfa530bc19fe5946b814d77cdde9 (diff) | |
| download | libalabaster-023af72f141005860219b3c63c3a63d2c179b2c1.tar.gz libalabaster-023af72f141005860219b3c63c3a63d2c179b2c1.tar.bz2 libalabaster-023af72f141005860219b3c63c3a63d2c179b2c1.zip | |
lib: Directly use _assert() functions
Rather than the <assert.h> defined assert() macro that can change based
on include order. As of now the build just checks for known implementation
specific function names and assumes their signature. Obviously that is very fragile
and should be improved in the future. Also add an AL_STATIC_ASSERT() macro.
Signed-off-by: Andrew Opalach <andrew@akon.city>
| -rw-r--r-- | include/al/lib.h | 32 | ||||
| -rw-r--r-- | meson.build | 27 |
2 files changed, 42 insertions, 17 deletions
diff --git a/include/al/lib.h b/include/al/lib.h index 5e65149..f5379e5 100644 --- a/include/al/lib.h +++ b/include/al/lib.h @@ -15,7 +15,10 @@ // https://forum.vcfed.org/index.php?threads/c-item-size-check-at-compile-time.1244920/ #define AL_ASSERT_TYPE_SIZE(type, size) \ - typedef char type##__size_test[(!!(sizeof(type) == size)) * 2 - 1] + typedef char type##__size_assert[(!!(sizeof(type) == size)) * 2 - 1] + +#define AL_STATIC_ASSERT(what, a, cmp, b) \ + typedef char what##__assert[(!!(a cmp b)) * 2 - 1] #define AL_USE_STDLIB //#define AL_PARANOID_REALLOC @@ -43,13 +46,6 @@ #include <wchar.h> #include <stdio.h> #include <stdarg.h> -#if !defined AL_DEBUG && defined AL_RELEASE_ASSERTS -#undef NDEBUG -#include <assert.h> -#define NDEBUG -#else -#include <assert.h> -#endif #include <ctype.h> #ifdef AL_MEMORY_TRACKING @@ -100,12 +96,23 @@ static inline void *al_realloc(void *ptr, size_t n) { return ptr ? realloc(ptr, #define al_snprintf snprintf #define al_vsnprintf vsnprintf #define al_fflush fflush -#if defined AL_DEBUG || defined AL_RELEASE_ASSERTS -#define al_assert assert + +#if (defined AL_DEBUG || defined AL_RELEASE_ASSERTS) && defined AL_ASSERT_FUNCTION +#include <assert.h> +// Directly define al_assert() as <assert.h> would if NDEBUG was not set. +#if defined AL_ASSERT_INCLUDE_FUNC +#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(#expr, __FILE__, __LINE__, __func__)) +#elif defined AL_ASSERT_EXPR_LAST +#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(__FILE__, __LINE__, #expr)) #else -#define al_assert(expr) do { (void)sizeof(expr); } while (0) // NOLINT(bugprone-sizeof-expression) +#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(#expr, __FILE__, __LINE__)) #endif - +// Loosely assert that <assert.h> behaves as we expect and redefines itself on every include. +#undef assert +#else +#define al_assert(expr) ((void)sizeof(expr)) // NOLINT(bugprone-sizeof-expression) +#endif +// Use __VA_ARGS__ to allow for an empty return. #define al_assert_and_return(...) do { \ al_assert(false); \ return __VA_ARGS__; \ @@ -181,7 +188,6 @@ static inline char *al_memdup0(const char *s, size_t n) buf[n] = '\0'; return buf; } - #endif static inline u32 al_next_power_of_two(u32 n) diff --git a/meson.build b/meson.build index 285af45..c948887 100644 --- a/meson.build +++ b/meson.build @@ -24,16 +24,35 @@ have_wide_string_width = compiler.has_function('wcswidth') # For testing: '-Wshadow', '-Wconversion', '-Wc++-compat' alabaster_args = ['-fstrict-aliasing', '-Wstrict-aliasing'] -if get_option('release-asserts') - alabaster_args += ['-DAL_RELEASE_ASSERTS'] -endif - if is_debug alabaster_args += ['-DAL_DEBUG'] +elif get_option('release-asserts') + # It's possible that some external code would rely on NDEBUG for conditionally + # excluding something other than asserts. The trade-off here is to accept that rather + # than possibly miss an assert. + alabaster_args += ['-DAL_RELEASE_ASSERTS'] else alabaster_args += ['-DNDEBUG'] endif +# The definition of the assert() macro is determined by the _last_ place where +# <assert.h> was included, because it redefines itself each time. Making it +# unnecessarily complicated to predict if assert() will be active or a no-op. +# Maybe I just haven't seen why this could make sense but to me it's counterintuitive. +# We define al_assert() to be unchanging and let any dependencies figure it out for themselves. +# Another note, which I hadn't heard before but, this clearly makes relying on NDEBUG +# for anything other than asserts a very bad practice. +# https://sourceware.org/git/?p=glibc.git;a=blob;f=assert/assert.h;h=31892aebcb9115a3b48be4d179eb9e8ab2f8784f;hb=HEAD#l24 +if compiler.has_function('__assert_fail', prefix: '#include <assert.h>') + alabaster_args += ['-DAL_ASSERT_FUNCTION=__assert_fail', '-DAL_ASSERT_INCLUDE_FUNC'] +elif compiler.has_function('_assert', prefix: '#include <assert.h>') + # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/assert-macro-assert-wassert?view=msvc-170 + alabaster_args += ['-DAL_ASSERT_FUNCTION=_assert'] +elif compiler.has_function('__assert', prefix: '#include <assert.h>') + # https://github.com/aosp-mirror/platform_bionic/blob/731631f300090436d7f5df80d50b6275c8c60a93/libc/include/assert.h#L63 + alabaster_args += ['-DAL_ASSERT_FUNCTION=__assert', '-DAL_ASSERT_EXPR_LAST'] +endif + if is_msvc_specifically alabaster_args += ['/arch:AVX'] endif |