Self-testing C programs on startup 2023-12-11

Before C and C++ standardized attributes for functions, pretty much any industrial-strength compiler would implement some sort of mechanism. GCC (and, to offer compatibility, Clang) are no exceptions.

One of them is the constructor attribute, which registers a function to be executed during startup, before crt0 yields control to main(). (This requires libc support and not all of them supports this mechanism, but it's not hard to fiddle around and implement something that's a little bit more portable – even if this particular attribute isn't.)

By using this attribute, I'd like to present a trick I've been using in Lwan to define self-tests that are executed during startup. This often has things like calls to assert() and other things that only make sense in a debug build, so the SELF_TEST macro, defined below, is defined conditionally on the presence of the NDEBUG macro.

#if defined(NDEBUG)
#define SELF_TEST(name) \
  __attribute__((unused)) \
  static void self_test_##name(void)
#else
#define SELF_TEST(name) \
  __attribute__((constructor)) \
  static void self_test_##name(void)
#endif

With this macro defined, it's possible to then define functions that will be executed during startup when building for debugging purposes, and have it stripped (since it uses the unused attribute) on any other build.

For instance, a test function can be defined like so:

SELF_TEST(parse_boolean)
{
     /* parse_boolean(string, default_value) returns `default_value'
      * if parsing `string' fails. */
     assert(parse_boolean("true", false) == true);
     assert(parse_boolean("false", true) == false);
}

One can verify with tools such as nm that the functions are stripped when the code is compiled with NDEBUG:

$ # assert() enabled:
$ gcc -Wall -Wextra -O3  self-test.c ; nm -a | grep self_test
$ # assert() disabled:
$ gcc -DNDEBUG -Wall -Wextra -O3  self-test.c ; nm -a | grep self_test
0000000000001040 t self_test_parse_boolean

Hope you find this trick useful!

🖂 Send me an email about this blog post
If you liked this post, consider getting me a coffee!