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 |
The new AI programming book by Aussie AI co-founders:
Get your copy from Amazon: Generative AI in C++ |