views:

639

answers:

3

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);

What is doing is when I define verbose for test purposes then I print the information and when I am working in normal execution I do not define it and I do not see useless information in the screen. My question is how can I do that function or implement the same idea?.

+9  A: 
#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.

João da Silva
I was going to post the same answer, so I save it and upvote yours. This is the only situation in which I use preprocessor macros in C++ code.
Daniel Daranas
+4  A: 

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.

Norman Ramsey
+6  A: 

I try to avoid using var-arg c-style functions for two main reasons:

  • They are not type-safe, can't use operator<<
  • They don't recognize when too few or many arguments were provided

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));
}
Johannes Schaub - litb
I can't speak for other compilers, but for GCC, -Wformat and __attribute__((format)) are your friends. That allows printf-style APIs with all the type-checking goodness of operator<<.
Tom
yeah, i'm aware of those attributes. but it will not allow you to pass own types. for example, you may use a list. and want to overload the operator<< for your list. with printf, you are required to write the same loop all again, and are limited to certain element types that printf understands.
Johannes Schaub - litb
overloading operator<< allows to put printing code on the side of the type you want to print, which i prefer to putting the printing code on the side of my debug code. so, whenever you have something printable, your type-safe dprintf solution already understands it.
Johannes Schaub - litb
it's the very improvement C++ has made over C, that you can separate concerns, and share code between libraries. if a type is printable, it overloads operator<< and everyone can use it. i like that very much.
Johannes Schaub - litb
I agree that it's nice that any object can be printable, but I find that some objects need to be "serializable" with 2-3 different modes. Binary serialization for storage, basic printing, and a debug dump. The ostream<< mechanism assumes that an object has only a single serialization mechanism.
Tom
Tom