--- /dev/null
+/*
+ * trace.h
+ *
+ * Function-like macros to issue tracing information on expressions.
+ *
+ * TRACE(e) - default formatting
+ * TRACEX(e) - print unsigned and floating point values in hexadecimal
+ * TRACES(e) - treat character pointers like strings
+ * TRACEXS(e) - all of the above
+ *
+ * An expression `e´ passed as argument to one of the trace macros is
+ * delegated by a C11 _Generic selector to one of the variadic inline
+ * helper functions, based on its type. The macros evaluate their
+ * argument only once and resolve to the value of the expression.
+ *
+ * Expressions having a type not compatible with any of the explicit
+ * associations are reported as not recognized.
+ *
+ * Should build cleanly using
+ * cc [-std=c11 [-Wpedantic]] -Wall -Wextra -DTRACE_ON [...]
+ * with cc being one of: glibc-gcc, musl-gcc, glibc-clang, musl-clang.
+ *
+ * CAVEAT:
+ * In C11 mode any expression of array type decays to (and thus takes
+ * on the type of) a pointer to its first element; non-trivial pointers
+ * are listed as expressions of unrecognized type.
+ * When compiled without explicitly requesting C standard conformance
+ * using either gcc or clang, an additional heuristic will interpret
+ * any otherwise unrecognized suitably sized object as pointer.
+ * However, as a trade-off, any attempt to pass in an expression of
+ * array type will result in a compile time error.
+ *
+ */
+
+#ifndef TRACE_H_
+#define TRACE_H_
+
+#ifdef TRACE_ON
+
+#ifdef __cplusplus
+ #error "C++: Now there's your problem!"
+#endif
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ < 201112L)
+ #error "At least C11 support is required!"
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/* Attempt to infer integer promotion target types from implementation. */
+#if (INT_MAX) == (INT32_MAX)
+ #define I_PROM_ int32_t
+ #define U_PROM_ uint32_t
+#elif (INT_MAX) == (INT64_MAX)
+ #define I_PROM_ int64_t
+ #define U_PROM_ uint64_t
+#else
+ #error "Integer type promotion heuristic failed. Take five!"
+#endif
+
+/* Adjust this to complement the libc printf %p conversion format! */
+#if defined(__QNXNTO__)
+ #define TRACE_PPFX_ "0x"
+#else
+ #define TRACE_PPFX_ ""
+#endif
+
+/* Helper macros. */
+#define TRACE_PRF_(...) ((void)fprintf(stderr, __VA_ARGS__))
+#define TRACE_LOC_(fi_, fn_, ln_) TRACE_PRF_("%s:%s@%d ", fi_, fn_, ln_)
+
+/* Generate helper functions for arithmetic types: */
+#define TRACE_AGEN_(t_, p_, s_, c_, f_) \
+ static inline t_ trace_##s_##_(const char *fi, const char *fn, \
+ int ln, const char *n, ...){ \
+ t_ v; va_list a; va_start(a, n); v = va_arg(a, p_); va_end(a); \
+ if (fi && fn) { \
+ TRACE_LOC_(fi, fn, ln); \
+ } \
+ TRACE_PRF_("%s=<%s> "f_"\n", n, c_, v); \
+ return v; \
+ }
+
+TRACE_AGEN_(char, I_PROM_, ch, "char", "'%c'")
+TRACE_AGEN_(int8_t, I_PROM_, i8, "i8", "%"PRId8)
+TRACE_AGEN_(uint8_t, U_PROM_, u8, "u8", "%"PRIu8)
+TRACE_AGEN_(uint8_t, U_PROM_, x8, "u8", "0x%02"PRIx8)
+TRACE_AGEN_(int16_t, I_PROM_, i16, "i16", "%"PRId16)
+TRACE_AGEN_(uint16_t, U_PROM_, u16, "u16", "%"PRIu16)
+TRACE_AGEN_(uint16_t, U_PROM_, x16, "u16", "0x%04"PRIx16)
+TRACE_AGEN_(int32_t, I_PROM_, i32, "i32", "%"PRId32)
+TRACE_AGEN_(uint32_t, U_PROM_, u32, "u32", "%"PRIu32)
+TRACE_AGEN_(uint32_t, U_PROM_, x32, "u32", "0x%08"PRIx32)
+TRACE_AGEN_(int64_t, int64_t, i64, "i64", "%"PRId64)
+TRACE_AGEN_(uint64_t, uint64_t, u64, "u64", "%"PRIu64)
+TRACE_AGEN_(uint64_t, uint64_t, x64, "u64", "0x%016"PRIx64)
+TRACE_AGEN_(long long, long long, ll, "ll", "%lld")
+TRACE_AGEN_(unsigned long long, unsigned long long, ull, "ull", "%llu")
+TRACE_AGEN_(unsigned long long, unsigned long long, xll, "ull", "0x%016llx")
+TRACE_AGEN_(float, double, f, "float", "%f")
+TRACE_AGEN_(float, double, xf, "float", "%a")
+TRACE_AGEN_(double, double, d, "double", "%f")
+TRACE_AGEN_(double, double, xd, "double", "%a")
+TRACE_AGEN_(long double, long double, ld, "ldouble", "%Lf")
+TRACE_AGEN_(long double, long double, xld, "ldouble", "%La")
+
+#undef TRACE_AGEN_
+#undef I_PROM_
+#undef U_PROM_
+
+/* Generate helper functions for pointer types: */
+#define TRACE_PGEN_(t_, s_, c_) \
+ static inline t_ trace_p##s_##_(const char *fi, const char *fn, \
+ int ln, const char *n, ... ){ \
+ t_ v; va_list a; va_start(a, n); v = va_arg(a, t_); va_end(a); \
+ TRACE_LOC_(fi, fn, ln); \
+ TRACE_PRF_("%s=<%s*> "TRACE_PPFX_"%p", n, c_, (void *)v); \
+ if (v) { \
+ TRACE_PRF_(", *"); trace_##s_##_(NULL, NULL, 0, n, *v); \
+ } else { \
+ TRACE_PRF_("\n"); \
+ } \
+ return v; \
+ }
+
+TRACE_PGEN_(char *, ch, "char")
+TRACE_PGEN_(int8_t *, i8, "i8")
+TRACE_PGEN_(uint8_t *, u8, "u8")
+TRACE_PGEN_(uint8_t *, x8, "u8")
+TRACE_PGEN_(int16_t *, i16, "i16")
+TRACE_PGEN_(uint16_t *, u16, "u16")
+TRACE_PGEN_(uint16_t *, x16, "u16")
+TRACE_PGEN_(int32_t *, i32, "i32")
+TRACE_PGEN_(uint32_t *, u32, "u32")
+TRACE_PGEN_(uint32_t *, x32, "u32")
+TRACE_PGEN_(int64_t *, i64, "i64")
+TRACE_PGEN_(uint64_t *, u64, "u64")
+TRACE_PGEN_(uint64_t *, x64, "u64")
+TRACE_PGEN_(long long *, ll, "ll")
+TRACE_PGEN_(unsigned long long *, ull, "ull")
+TRACE_PGEN_(unsigned long long *, xll, "ull")
+TRACE_PGEN_(float *, f, "float")
+TRACE_PGEN_(float *, xf, "float")
+TRACE_PGEN_(double *, d, "double")
+TRACE_PGEN_(double *, xd, "double")
+TRACE_PGEN_(long double *, ld, "ldouble")
+TRACE_PGEN_(long double *, xld, "ldouble")
+
+#undef TRACE_PGEN_
+
+/* Void pointers and strings require special handling: */
+static inline void *trace_pvd_(const char *fi, const char *fn, int ln,
+ const char *n, ...){
+ void *v; va_list a; va_start(a, n); v = va_arg(a, void *); va_end(a);
+ TRACE_LOC_(fi, fn, ln);
+ TRACE_PRF_("%s=<void*> "TRACE_PPFX_"%p\n", n, v);
+ return v;
+}
+
+static inline void *trace_pstr_(const char *fi, const char *fn, int ln,
+ const char *n, ...){
+ char *v; va_list a; va_start(a, n); v = va_arg(a, char *); va_end(a);
+ TRACE_LOC_(fi, fn, ln);
+ TRACE_PRF_("%s=<char*> "TRACE_PPFX_"%p, %s=<string> \"%s\"\n",
+ n, (void *)v, n, v);
+ return v;
+}
+
+/* Handle the default case depending on current compile mode. */
+#if !defined(__STRICT_ANSI__) && (defined(__GNUC__) || defined(__clang__))
+ /* Use non-standard compiler extensions to guess arbitrary pointer
+ * types. Chokes on arrays! */
+ #define TRACE_DFLT(fi_, fn_, ln_, x_) \
+ ({ \
+ const char *typ, *fmt; typeof(x_) y_ = x_; \
+ if (sizeof(y_) == sizeof(void *)) { \
+ typ = "?*?"; fmt = "%s=<%s> "TRACE_PPFX_"%p\n"; \
+ } else { \
+ typ = "???"; fmt = "%s=<%s>\n"; \
+ } \
+ TRACE_LOC_(fi_, fn_, ln_); TRACE_PRF_(fmt, #x_, typ, y_); \
+ y_; \
+ })
+#else
+ /* Use C standard conforming catch-all. */
+ #define TRACE_DFLT(fi_, fn_, ln_, x_) \
+ (TRACE_LOC_(fi_, fn_, ln_), TRACE_PRF_("%s=<???"">\n", #x_), x_)
+ #undef TRACE_PPFX_
+#endif
+
+/*
+ * Issue a tracing message about an expression, formatted as
+ * file:func@line: expr=<type> value
+ * for arithmetic types, or
+ * file:func@line: expr=<type*> address, *exp=<type> value
+ * for supported pointer types, or
+ * file:func@line: expr=<???>
+ * for expressions of unrecognized type.
+ *
+ * In non-standard conforming mode anything that looks even
+ * remotely like some arbitrary pointer is printed as:
+ * file:func@line: expr=<?*?> address
+ *
+ * Parameters:
+ * x_ the expression to be traced
+ *
+ * Returns:
+ * The value of x_.
+ */
+#define TRACE(x_) (_Generic( (x_), \
+ /* arithmetic types */ \
+ char: trace_ch_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int8_t: trace_i8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint8_t: trace_u8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int16_t: trace_i16_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint16_t: trace_u16_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int32_t: trace_i32_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint32_t: trace_u32_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int64_t: trace_i64_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint64_t: trace_u64_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ long long: trace_ll_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ unsigned long long: \
+ trace_ull_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ float: trace_f_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ double: trace_d_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ long double: trace_ld_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ /* pointer types */ \
+ void *: trace_pvd_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ char *: trace_pch_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int8_t *: trace_pi8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint8_t *: trace_pu8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ int16_t *: trace_pi16_(__FILE__, __func__, __LINE__, #x_, x_), \
+ uint16_t *: trace_pu16_(__FILE__, __func__, __LINE__, #x_, x_), \
+ int32_t *: trace_pi32_(__FILE__, __func__, __LINE__, #x_, x_), \
+ uint32_t *: trace_pu32_(__FILE__, __func__, __LINE__, #x_, x_), \
+ int64_t *: trace_pi64_(__FILE__, __func__, __LINE__, #x_, x_), \
+ uint64_t *: trace_pu64_(__FILE__, __func__, __LINE__, #x_, x_), \
+ long long *: trace_pll_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ unsigned long long *: \
+ trace_pull_(__FILE__, __func__, __LINE__, #x_, x_), \
+ float *: trace_pf_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ double *: trace_pd_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ long double *: trace_pld_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ /* catch-all clause */ \
+ default: TRACE_DFLT (__FILE__, __func__, __LINE__, x_) \
+))
+
+/*
+ * Same as TRACE(), except unsigned and floating point values are
+ * printed in hexadecimal.
+ */
+#define TRACEX(x_) (_Generic( (x_), \
+ /* arithmetic types */ \
+ uint8_t: trace_x8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint16_t: trace_x16_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint32_t: trace_x32_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint64_t: trace_x64_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ unsigned long long: \
+ trace_xll_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ float: trace_xf_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ double: trace_xd_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ long double: trace_xld_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ /* pointer types */ \
+ uint8_t *: trace_px8_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ uint16_t *: trace_px16_(__FILE__, __func__, __LINE__, #x_, x_), \
+ uint32_t *: trace_px32_(__FILE__, __func__, __LINE__, #x_, x_), \
+ uint64_t *: trace_px64_(__FILE__, __func__, __LINE__, #x_, x_), \
+ unsigned long long *: \
+ trace_pxll_(__FILE__, __func__, __LINE__, #x_, x_), \
+ float *: trace_pxf_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ double *: trace_pxd_ (__FILE__, __func__, __LINE__, #x_, x_), \
+ long double *: trace_pxld_(__FILE__, __func__, __LINE__, #x_, x_), \
+ /* catch-all clause */ \
+ default: (TRACE(x_)) \
+))
+
+/*
+ * Same as TRACE(), except char pointers are printed as strings.
+ */
+#define TRACES(x_) (_Generic( (x_), \
+ char *: trace_pstr_(__FILE__, __func__, __LINE__, #x_, x_), \
+ default: (TRACE(x_)) \
+))
+
+/*
+ * Same as TRACEX(), except char pointers are printed as strings.
+ */
+#define TRACEXS(x_) (_Generic( (x_), \
+ char *: trace_pstr_(__FILE__, __func__, __LINE__, #x_, x_), \
+ default: (TRACEX(x_)) \
+))
+
+#else
+ #define TRACE(x_) (x_)
+ #define TRACEX(x_) (x_)
+ #define TRACES(x_) (x_)
+ #define TRACEXS(x_) (x_)
+#endif //def TRACE_ON
+
+#endif //ndef TRACE_H_