diff --git a/maul/files/cmake/Testing.cmake b/maul/files/cmake/Testing.cmake index 144bbe4..68163e3 100644 --- a/maul/files/cmake/Testing.cmake +++ b/maul/files/cmake/Testing.cmake @@ -86,10 +86,12 @@ endfunction() # the family requires. ROOT and NAME default to the project's # source directory and name; cmake/HeaderCheck passes them to check a # library's headers alone. MSVC's cl has no C23 mode (its /std:clatest is -# a draft mode), so the C23 check is skipped there. In C++17 the -# library's _NODISCARD, found in base.h, must not be empty. A macro, -# because enable_language must run at directory scope. Without a C++ -# compiler the C++ checks are skipped. +# a draft mode), so the C23 check is skipped there. In C23 and C++17 the +# library's _NODISCARD, found in base.h, must not be empty: GCC 14 +# reports its C23 as 202000L, so a macro that waits for 202311L would +# leave a dropped status unwarned there. A macro, because enable_language +# must run at directory scope. Without a C++ compiler the C++ checks are +# skipped. macro(maul_add_header_tests) cmake_parse_arguments(MAUL_HT "" "ROOT;NAME" "" ${ARGN}) if(NOT MAUL_HT_ROOT) @@ -131,6 +133,13 @@ macro(maul_add_header_tests) "static_assert(sizeof(MAUL_TEXT(${maul_nodiscard})) > 1,\n" " \"${maul_nodiscard} is empty in C++17\");\n") list(APPEND maul_cxx_sources ${maul_header_dir}/cxx_nodiscard.cpp) + file(WRITE ${maul_header_dir}/c23_nodiscard.c + "#include \"${MAUL_HT_NAME}/base.h\"\n" + "#define MAUL_TEXT(x) MAUL_TEXT_OF(x)\n" + "#define MAUL_TEXT_OF(x) #x\n" + "static_assert(sizeof(MAUL_TEXT(${maul_nodiscard})) > 1,\n" + " \"${maul_nodiscard} is empty in C23\");\n") + list(APPEND maul_c23_sources ${maul_header_dir}/c23_nodiscard.c) endif() if(MSVC) set(maul_header_flags /W4 /WX) diff --git a/maul/files/docs/internals/maul-conventions.md b/maul/files/docs/internals/maul-conventions.md index 80f34b2..1e22be7 100644 --- a/maul/files/docs/internals/maul-conventions.md +++ b/maul/files/docs/internals/maul-conventions.md @@ -36,7 +36,7 @@ The libraries are one product family: a program may link several of them, and a - Library code is C23: checked integer arithmetic, `nullptr`, `constexpr` objects and fixed-width enumerations make it safer and clearer. It uses only what the compiler provides; `` comes from the C library, is missing on some targets, and is not used. Bit operations go through compiler builtins. - Public headers are written in the common subset of C17 and C++17, which is also valid C23, because every consumer's compiler reads them. Attributes only newer dialects accept sit behind library macros (`PP_NODISCARD`). An enumeration whose size matters in a public struct is a `typedef` of a fixed-width integer with named constants. -- MSVC keeps `__cplusplus` at 199711L unless `/Zc:__cplusplus` is given, so the nodiscard macro also reads `_MSVC_LANG`. +- MSVC keeps `__cplusplus` at 199711L unless `/Zc:__cplusplus` is given, so the nodiscard macro also reads `_MSVC_LANG`. In C it takes the attribute in any dialect after C17 whose compiler has it (`__has_c_attribute(nodiscard)`), since GCC 14 reports its C23 as `__STDC_VERSION__` 202000L, not 202311L. The header tests check that the macro is not empty in C23 and C++17. - Minimum compilers are GCC 14 and Clang 19. On Windows the library is built with `clang-cl`, which Visual Studio installs and which produces compatible objects; MSVC's `cl` compiles the headers but not the sources. ## Errors and assertions