← Back to davo.co
aboutsummaryrefslogtreecommitdiffstats
path: root/macro_trace.h
diff options
context:
space:
mode:
authorDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
committerDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
commitb3e9e62599532050fc776c5e8f076915b56c2235 (patch)
treeaf252346106a61b18cc6fc6fdbd32e962d096f1c /macro_trace.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 'macro_trace.h')
-rw-r--r--macro_trace.h302
1 files changed, 302 insertions, 0 deletions
diff --git a/macro_trace.h b/macro_trace.h
new file mode 100644
index 0000000..2125f50
--- /dev/null
+++ b/macro_trace.h
@@ -0,0 +1,302 @@
+#ifndef MACRO_TRACE_H
+#define MACRO_TRACE_H 1
+#include <stdio.h>
+#include "c23-fallback.h"
+
+/**
+ ** @file
+ **
+ ** @brief A sequence of macros for tracing execution
+ **/
+
+#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 A simple version of the macro that just does
+ ** a @c fprintf or nothing
+ **/
+#if NDEBUG
+# define TRACE_PRINT0(F, X) do { /* nothing */ } while (false)
+#else
+# define TRACE_PRINT0(F, X) fprintf(stderr, F, X)
+#endif
+
+/**
+ ** @brief A simple version of the macro that ensures that the @c
+ ** fprintf format is a string literal
+ **
+ ** As an extra, it also adds a newline to the printout, so
+ ** the user doesn't have to specify it each time.
+ **/
+#if NDEBUG
+# define TRACE_PRINT1(F, X) do { /* nothing */ } while (false)
+#else
+# define TRACE_PRINT1(F, X) fprintf(stderr, "" F "\n", X)
+#endif
+
+/**
+ ** @brief A macro that resolves to @c 0 or @c 1 according to @c
+ ** NDEBUG being set
+ **/
+#ifdef NDEBUG
+# define TRACE_ON 0
+#else
+# define TRACE_ON 1
+#endif
+
+/**
+ ** @brief A simple version of the macro that ensures that the @c fprintf call is always evaluated
+ **/
+#define TRACE_PRINT2(F, X) \
+do { if (TRACE_ON) fprintf(stderr, "" F "\n", X); } \
+ while (false)
+
+/**
+ ** @brief Traces a value without having to specify a format
+ **/
+#define TRACE_VALUE0(HEAD, X) TRACE_PRINT2(HEAD " %Lg", (X)+0.0L)
+
+/**
+ ** @brief Traces a pointer without having to specify a format
+ **
+ ** @warning Uses a cast of @a X to @c void*
+ **/
+#define TRACE_PTR0(HEAD, X) TRACE_PRINT2(HEAD " %p", (void*)(X))
+
+/**
+ ** @brief Traces a pointer without specifying a format
+ **/
+#define TRACE_PTR1(HEAD, X) \
+ TRACE_PRINT2(HEAD " %p", ((void*){ (X) }))
+
+/**
+ ** @brief Adds the current line number to the trace
+ **/
+#define TRACE_PRINT3(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%lu: " F "\n", __LINE__+0UL, X); \
+} while (false)
+
+/**
+ ** @brief Adds the name of the current function to the trace
+ **/
+#define TRACE_PRINT4(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:%lu: " F "\n", \
+ __func__, __LINE__+0UL, X); \
+} while (false)
+
+#define STRINGIFY(X) #X
+#define STRGY(X) STRINGIFY(X)
+/** @brief Adds a textual version of the expression that is evaluated
+ **/
+#define TRACE_PRINT5(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ "%s:" STRGY(__LINE__) ":(" #X "): " F "\n", \
+ __func__, X); \
+} while (false)
+
+/**
+ ** @brief Allows multiple arguments to be printed in the
+ ** same trace
+ **/
+#define TRACE_PRINT6(F, ...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \
+ __func__, __VA_ARGS__); \
+} while (false)
+
+/** @brief Only traces with a text message; no values printed
+ **/
+#define TRACE_PRINT7(...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ "%s:" STRGY(__LINE__) ": " __VA_ARGS__ "\n", \
+ __func__); \
+} while (false)
+
+/**
+ ** @brief Extracts the first argument from a list of arguments
+ **/
+#define TRACE_FIRST(...) TRACE_FIRST0(__VA_ARGS__, 0)
+#define TRACE_FIRST0(_0, ...) _0
+
+/**
+ ** @brief Removes the first argument from a list of arguments
+ **
+ ** @remark This is only suitable in our context,
+ ** since this adds an artificial last argument.
+ **/
+#define TRACE_LAST(...) TRACE_LAST0(__VA_ARGS__, 0)
+#define TRACE_LAST0(_0, ...) __VA_ARGS__
+
+/**
+ ** @brief Traces with or without values
+ **
+ ** This implementation uses the C23 feature
+ ** `__VA_OPT__` to add a possibly empty variable
+ ** list at the end of the `fprintf` arguments.
+ **/
+#define TRACE_PRINT8(F, ...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \
+ __func__ __VA_OPT__(,) __VA_ARGS__); \
+} while (false)
+
+/**
+ ** @brief Traces by first giving a textual representation of the
+ ** arguments
+ **/
+#define TRACE_PRINT9(F, ...) \
+TRACE_PRINT8("(" #__VA_ARGS__ ") " F __VA_OPT__(,) __VA_ARGS__)
+
+inline
+char const* trace_skip(char const expr[static 1]){
+ for (char const* p = expr+1; *p; ++p) {
+ // at the end of head we expect the 3 chars ``", ''
+ if (p[0] == '"') return p+3;
+ // watch out for escaped " characters
+ if (p[0] == '\\' && p[1] == '"') ++p;
+ }
+ return 0;
+}
+
+/**
+ ** @brief A function to print a list of values
+ **
+ ** @remark Only call this through the macro ::TRACE_VALUES,
+ ** which will provide the necessary contextual information.
+ **/
+inline
+void trace_values(FILE* s,
+ char const func[static 1],
+ char const line[static 1],
+ char const expr[static 1],
+ char const head[static 1],
+ size_t len, long double const arr[len]) {
+ fprintf(s, "%s:%s:(%s) %s %Lg", func, line,
+ trace_skip(expr), head, arr[0]);
+ for (size_t i = 1; i < len-1; ++i)
+ fprintf(s, ", %Lg", arr[i]);
+ fputc('\n', s);
+}
+
+/**
+ ** @brief Returns the number of arguments in the variable list
+ **
+ ** This version works for lists with up to 31 elements.
+ **
+ ** @remark An empty argument list is taken as one (empty) argument.
+ **/
+#define ALEN(...) ALEN0(__VA_ARGS__, \
+ 0x1E, 0x1F, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, \
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, \
+ 0x0E, 0x0F, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, \
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00)
+
+#define ALEN0(_00, _01, _02, _03, _04, _05, _06, _07, _08, \
+ _09, _0A, _0B, _0C, _0D, _0F, _0E, _10, _11, \
+ _12, _13, _14, _15, _16, _17, _18, _19, _1A, \
+ _1B, _1C, _1D, _1F, _1E, ...) _1E
+
+/**
+ ** @brief Traces a list of arguments without having to specify
+ ** the type of each argument
+ **
+ ** @remark This constructs a temporary array with the arguments
+ ** all converted to @c long double. Thereby implicit conversion
+ ** to that type is always guaranteed.
+ **/
+#define TRACE_VALUES(...) \
+TRACE_VALUES0(ALEN(__VA_ARGS__), \
+ #__VA_ARGS__, \
+ __VA_ARGS__ \
+ )
+
+#define TRACE_VALUES0(NARGS, EXPR, HEAD, ...) \
+do { \
+ if (TRACE_ON) { \
+ if (NARGS > 1) \
+ trace_values(stderr, __func__, STRGY(__LINE__), \
+ "" EXPR "", "" HEAD "", NARGS, \
+ (long double const[NARGS]){ __VA_ARGS__ }); \
+ else \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": %s\n", \
+ __func__, HEAD); \
+ } \
+ } while (false)
+
+/**
+ ** @brief Returns a format that is suitable for @c fprintf
+ **
+ ** @return The argument @a F must be a string literal,
+ ** so the return value will also be one.
+ **
+ **/
+#define TRACE_FORMAT(F, X) \
+_Generic((X)+0LL, \
+ unsigned long long: "" F " %llu\n", \
+ long long: "" F " %lld\n", \
+ float: "" F " %.8f\n", \
+ double: "" F " %.12f\n", \
+ long double: "" F " %.20Lf\n", \
+ default: "" F " %p\n")
+
+/**
+ ** @brief Returns a value that forcibly can be interpreted as
+ ** pointer value
+ **
+ ** That is, any pointer will be returned as such, but other
+ ** arithmetic values will result in a @c nullptr.
+ **/
+#define TRACE_POINTER(X) \
+_Generic((X)+0LL, \
+ unsigned long long: nullptr, \
+ long long: nullptr, \
+ float: nullptr, \
+ double: nullptr, \
+ long double: nullptr, \
+ default: (X))
+
+/**
+ ** @brief Returns a value that is promoted either to a wide
+ ** integer, to a floating point, or to a @c void* if @a X is a
+ ** pointer
+ **/
+#define TRACE_CONVERT(X) \
+_Generic((X)+0LL, \
+ unsigned long long: (X)+0LL, \
+ long long: (X)+0LL, \
+ float: (X)+0LL, \
+ double: (X)+0LL, \
+ long double: (X)+0LL, \
+ default: ((void*){ nullptr } = TRACE_POINTER(X)))
+
+/**
+ ** @brief Traces a value without having to specify a format
+ **
+ ** This variant works correctly with non-void pointers.
+ **
+ ** The formats are tunable by changing the specifiers in
+ ** ::TRACE_FORMAT.
+ **/
+#define TRACE_VALUE1(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ TRACE_FORMAT("%s:" STRGY(__LINE__) ": " F, X), \
+ __func__, TRACE_CONVERT(X)); \
+ } while (false)
+
+#endif