What are good ways to implement the following debugging features? I would like to define an easy to use function specifier similar to inline that tells the compiler when to completely ignore a function based on enabled preprocessor flags. Ideally, I want to declare functions like this:
DEBUG_FUNCTION_VERBOSE void saveMesh(const std::string &fileName, const Mesh &mesh, ...);
DEBUG_FUNCTION_INFO void logInfo(const std::string &message);
DEBUG_FUNCTION_CHECK void checkVertexAttributes(const Mesh &mesh);
DEBUG_FUNCTION_ERROR void logError(const std::string &messageOfDoom);
There is an ordering from less important and slow to critical and faster. The idea is that I set a preprocessor flag which determines which of these special debug functions are enabled. E.g., there might be a flag like DEBUG_LEVEL_CHECK which enables the latter two types of debug functions or another flag DEBUG_LEVEL_VERBOSE which enables all of them. If a debug function type is disabled I essentially want the compiler to ignore all calls to that function, including the evaluation of its arguments (like assert in release mode).
One motivation for these features is speeding up debugging. I want to call such debug functions in my code and leave them there. For controlling what is output and checked, I rather want to use a preprocessor flag instead of inserting and removing calls to such functions manually.
Another motivation is to avoid clumsy preprocessor guards as much as possible when declaring and calling those functions. Instead of calling a function like this:
#ifdef DEBUG_LEVEL_CHECK
checkVertexAttributes(mesh);
#endif // DEBUG_LEVEL_CHECK
I simply want to call the function normally:
checkVertexAttributes(mesh);
Ideally, the implementation of this debugging scheme should have these properties: First, there should be only one point in code per function where I have to declare its debug level (might be similar to adding a specifier like the inline keyword). Second, I want to define the debug level logic only once since that is always the same. Third, these debug functions have an arbitrary context (belong to some class, some namespace or ...) and I might want to add such functions later. That means which functions are debug functions should be independent of the implementation of the debug level logic itself. I don't want to bloat that logic with listing all the used debug functions at this place. Fourth, if a function is disabled, not only its execution itself but also the evaluation of its arguments should be skipped (like for assert). Lastly, I want to conveniently make calls of debug only functions dependent on that logic but in a transparent way as depicted above.
I saw this post: What exactly happens to empty inline functions? But that rather looks like it will lead to redundant code in my case and I am not sure how you would apply that to overloaded functions that share the same name.
You can make the compiler swallow any function calls with a variadic macro.
void test(int, int) {}
void test(int) {}
#define test(...)
int main()
{
test(78, 12);
test(12);
test(swallows literally anything);
}
As for the levels, you can put some #ifdef
s together and make a simple working system for it.
#ifdef DEBUG_FUNCTION_VERBOSE
#define DEBUG_FUNCTION_INFO
#else
#define saveMesh(...)
#endif
#ifdef DEBUG_FUNCTION_INFO
#define DEBUG_FUNCTION_CHECK
#else
#define logInfo(...)
#endif
#ifdef DEBUG_FUNCTION_CHECK
#define DEBUG_FUNCTION_ERROR
#else
#define checkVertexAttributes(...)
#endif
#ifndef DEBUG_FUNCTION_ERROR
#define logError(...)
#endif