I'm developing an API looking like GLib logging.
My API can also disable these functions when the flag -DDEBUG
isn't passed to GCC.
That means, there are no residues in the binary, all these debug functions including their parameters aren't present in the executable if this flag isn't passed.
It works perfectly in C, because it's macro functions I can easily replace ;
debug.h
#include <stdio.h>
#include <stdlib.h>
#include "term.h"
/**
* @brief Print something in debug mode
* @details No loglevel/context
* @param format
* @param ... Parameters for format
*/
#define printf_debug(fmt, ...)
#define LOG_NONE 0
#define LOG_FATAL 1
#define LOG_ERROR 2
#define LOG_WARNING 3
#define LOG_INFO 4
#define LOG_DEBUG 5
#define LOG_TRACE 6
#if (defined(DEBUG))
#undef printf_debug
#define printf_debug(format, ...) \
fprintf(stderr, " " format "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#endif // (defined(DEBUG))
main.c
#include <debug/debug.h>
int main()
{
printf_debug("Hello world !"); // Show in any debug context
return 0;
}
If I compile without the flag -DDEBUG
, neither my macro function printf_debug()
nor the string "Hello" remains.
I would have like to do the same in C++ with the << operator (such as std::cout
works), but I can't manage << operator like I manage parameters in C.
Here's what I have tried ;
debug.hpp
#include <iostream>
#define LOG_UNDEFINED (-1)
#define LOG_NONE 0
#define LOG_FATAL 1
#define LOG_ERROR 2
#define LOG_WARNING 3
#define LOG_INFO 4
#define LOG_DEBUG 5
#define LOG_TRACE 6
namespace debug
{
#ifdef DEBUG
class debug_cout
: public std::ostream
{
public:
debug_cout(std::streambuf *sbuf)
: std::ios(sbuf), std::ostream(sbuf)
{}
~debug_cout() {}
};
debug_cout cout(int l) {
static int level = l;
return debug_cout(std::cout.rdbuf());
}
#else
class debug_cout
: public std::ostream
{
public:
debug_cout() {}
~debug_cout() {}
};
debug_cout cout(int l) {
return debug_cout();
}
#endif
}
main.cpp
#include <debug/debug.hpp>
int main() {
debug::cout(LOG_ERROR) << "Hello " << 3 << std::endl;
return 0;
}
It compiles correctly this way ;
Using -DDEBUG
flag, it outputs ;
Hello 3
Without -DDEBUG
, it outputs nothing, but there are residues in the executable. I see using objdump
my functions debug::cout()
remains and is called.
I have also tried using GCC optimization flag -O3
.
So my question ; Is it possible to ignore these instructions in C++ ? The ones having << operator.
Note: It's not an issue if I can't use namespace
Note 2: When I talk about residues I mean my function debug::cout()
remains in the executable in C++ and is executed (and it just does nothing).
Is it possible to ignore these instructions in C++
Yes. if constexpr
can eliminate entire branches of code:
#ifdef DEBUG
# define DEBUG_COUT(level) if constexpr (0) {} else real_stream{level}
#else
# define DEBUG_COUT(level) if constexpr (1) {} else null_stream{level}
#endif
And use it like this:
DEBUG_COUT(5) << "Hello, " << 5 << " Worlds\n";
Live example: https://godbolt.org/z/7j6drn9ec
P.S. Different definitions of the same class depending on some macro? That's a recipe for wonderful bugs in case of linking wrong library/object. Don't do that.