I am using an application in c++ that uses a special dprintf function to print information, this is an example:
dprintf(verbose, \"The value is: %d\", i);
I try to avoid using var-arg c-style functions for two main reasons:
I've made a way that works using boost::fusion
, which is given arguments in a type-safe way. It iterates over those arguments, printing out them when a %
is encountered. If too few or too many arguments were given, an exception is thrown.
There is one problem still: Variadic macros are not yet standard in C++. So, i have made two versions. One that work with current C++. You have to invoke it using
dprintf("name: %, value: %\n", ("foo", 42));
Then. The other version, using variadic macros, can be used by defining a preprocessor symbol, which enables you to write
dprintf("name: %, value: %\n", "foo", 42);
Here is the code. The boost.fusion provides more details for this:
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/next.hpp>
#include <stdexcept>
#include <iostream>
template<typename IterS, typename IterSeqE>
void print_vec(IterS b, IterS e, IterSeqE, IterSeqE) {
while(b != e) {
if(*b == '%') {
if(++b != e && *b == '%') {
std::cout << '%';
} else {
throw std::invalid_argument("too many '%'");
}
} else {
std::cout << *b;
}
++b;
}
}
template<typename IterS, typename IterSeqB, typename IterSeqE>
void print_vec(IterS b, IterS e, IterSeqB seqb, IterSeqE seqe) {
while(b != e) {
if(*b == '%') {
if(++b != e && *b == '%') {
std::cout << '%';
} else {
std::cout << *seqb;
return print_vec(b, e, next(seqb), seqe);
}
} else {
std::cout << *b;
}
++b;
}
throw std::invalid_argument("too few '%'");
}
template<typename Seq>
void print_vec(std::string const& msg, Seq const& seq) {
print_vec(msg.begin(), msg.end(), begin(seq), end(seq));
}
#ifdef USE_VARIADIC_MACRO
# ifdef DEBUG
# define dprintf(format, ...) \
print_vec(format, boost::fusion::make_vector(__VA_ARGS__))
# else
# define dprintf(format, ...)
# endif
#else
# ifdef DEBUG
# define dprintf(format, args) \
print_vec(format, boost::fusion::make_vector args)
# else
# define dprintf(format, args)
# endif
#endif
// test, using the compatible version.
int main() {
dprintf("hello %, i'm % years old\n", ("litb", 22));
}
The preprocessor solution will work, but it can be annoying to have to rebuild to change from one to the other. I will often make the decision at run time. I first declare:
static void do_nothing(const char *fmt, ...) { (void)fmt; }
extern void real_dprintf(const char *fmt, ...);
void (*dprintf)(const char *fmt, ...) = do_nothing;
Then in initialization code I have
if (getenv("APPLICATION") && strstr(getenv("APPLICATION"), "dprintf"))
dprintf = real_dprintf;
This way I can quickly change modes by changing the value of an environment variable.
#ifdef DEBUG
#define dprintf(format, ...) real_dprintf(format, __VA_ARGS__)
#else
#define dprintf
#endif
Here real_dprintf() is the "real" function that is invoked, and dprintf() is just a macro wrapping the call.