Although it's a short function/macro, there are a number of problems here:
__FILE__
and __LINE__
are expanded in the function definition, rather than at the call site. We need an interface that passes those in, so that the macro is
#define debug(args...) if (DEBUG) _debug(__FILE__, __LINE__, args);
That's a non-standard variadic macro expansion. The standard way is to write the parameter as ...
and expand it using __VA_ARGS__
.
Identifiers in global scope beginning with underscore are reserved for the implementation. Use a namespace instead (e.g. debugging::debug
).
Debug information should go to std::clog
, not std::cout
.
The macro doesn't play nicely in if
/else
statements - use the do
...while(0)
idiom to make it statement-like.
Conventional style is to use all-caps for macros (to indicate their dangers) and nothing else. We have this exactly backwards here, with debug
and DEBUG
.
Improved version
#include <iostream>
namespace debugging
{
#ifdef ENABLE_DEBUG
constexpr bool debug = true;
#else
constexpr bool debug = false;
#endif
template <typename... Args>
void print(const char* file, int line, Args... args) {
(std::clog << "[" << file << ":" << line << "] "
<< ... << args) << std::endl;
}
}
#define DEBUG(...) \
do { \
if (debugging::debug) \
debugging::print(__FILE__, __LINE__, __VA_ARGS__); \
} while (0)
And a quick test (that demonstrates that the arguments are evaluated only when we're debugging):
int main()
{
DEBUG("Started main");
int status = 1;
DEBUG("Leaving main, status=", status=0);
return status;
}
Addendum
We don't need the debug
constant - just change the definition of the macro according to whether or not we're debugging:
#ifndef ENABLE_DEBUG
#define DEBUG(...) ((void)0)
#else
#include <iostream>
namespace debugging
{
template<typename... Args>
void print(const char* file, int line, Args... args) {
(std::clog << "[" << file << ":" << line << "] "
<< ... << args) << std::endl;
}
}
#define DEBUG(...) debugging::print(__FILE__, __LINE__, __VA_ARGS__)
#endif