Aussie AI

Debug Wrapper Functions

  • Book Excerpt from "Generative AI in C++"
  • by David Spuler, Ph.D.

Debug Wrapper Functions

Assertions are wonderful but also laborious because you have to add them everywhere. However, one way to avoid assertions to check arguments at every call to a library function is to use a debug wrapper function instead.

It can be helpful during debugging to wrap some standard library function calls with your own versions, so as to add additional parameter validation and self-checking code. These self-tests can be as simple as parameter null tests or as complex as detecting memory stomp overwrites with your own custom code.

Some of the functions which you might consider wrapping include:

  • malloc, calloc, strdup
  • memset
  • memcpy
  • strcpy, etc.

Note that you can wrap the C++ “new” and “delete” operators at the linker level by defining your own versions, but not as macro intercepts. You can also intercept the “new[]” and “delete[]” array allocation versions.

There are different approaches to consider when wrapping system calls, which we examine using memset as an example:

  • Leave “memset” calls in your code (auto-intercepts)
  • Use “memset_wrapper” in your code instead (manual intercepts)

Macro auto-intercepts: You might want to leave your code unchanged using memset. To leave “memset” in your code, but have it automatically call “memset_wrapper” you can use a macro intercept in a header file.

    #undef memset // ensure no prior definition
    #define memset memset_wrapper  // Intercept

Note that you can also use preprocessor macros to add context information to the debug wrapper functions. For example, you could add extra parameters to “memset_wrapper” such as:

    #define memset(x,y,z)  memset_wrapper((x),(y),(z),__FILE__,__LINE__,__func__)

Note that in the above version, the macro parameters must be parenthesized even between commas, because there's a C++ comma operator that could occur in a passed-in expression. Also note that these context macros (e.g. __FILE__) aren't necessary if you have a C++ stacktrace library, such as std::stacktrace, on your platform.

Variadic preprocessor macros: Note also that there is varargs support in C++ #define macros. If you want to track variable-argument functions like sprintf, printf, or fprintf, or other C++ overloaded functions, you can use “...” and “__VA_ARGS__” in preprocessor macros as follows.

    #define sprintf(fmt,...)  sprintf_wrapper((fmt),__FILE__,__LINE__,__func__, __VA_ARGS__ )

Manual Wrapping: Alternatively, you might want to individually change the calls to memset to call memset_wrapper without hiding it behind a macro. If you'd rather have to control whether or not the wrapper is called, then you can use both in the program, wrapped or non-wrapped. Or if you want them all changed, but want the intercept to be less hidden (e.g. later during code maintenance), then you might consider adding a helpful reminder instead:

    #undef memset
    #define memset dont_use_memset_please

This trick will give you a compilation error at every call to memset that hasn't been changed to memset_wrapper.

 

Next:

Up: Table of Contents

Buy: Generative AI in C++: Coding Transformers and LLMs

Generative AI in C++ The new AI programming book by Aussie AI co-founders:
  • AI coding in C++
  • Transformer engine speedups
  • LLM models
  • Phone and desktop AI
  • Code examples
  • Research citations

Get your copy from Amazon: Generative AI in C++