0

When you stream variables to an output stream such as cout, type conversion is automatic. What I'm trying to figure out is how to do this via a function call, for example:

inline void DEBUG(ostream& s)   // Don't know if this prototype is appropriate
{
  cout << s;
}


main()
{
  int i = 5;

  DEBUG("The value is: " << i << endl); // This doesn't compile

  DEBUG("The value is: " + i + endl); // Neither does this
}

I found similar questions on here, but they all involve passing the stream object as a parameter, whereas I'm trying to pass the "streamed data" to a function that already has the stream object, so it's the other way round. Is this even possible? I don't want to resort to explicit type conversions. I also found this question, but I really don't want to write a whole logger class if I can avoid it.

At the moment I'm implementing it as a macro, which works, but I'd rather use an inline function if possible.

#define DEBUG(s)    (cout << s)
Community
  • 1
  • 1
Sonic Atom
  • 436
  • 5
  • 15

2 Answers2

1

Well, what (at least some) logging libraries would do is create a temporary proxy object that would act as a stream:

#include <iostream>

struct LoggerProxy {
    LoggerProxy(const char* file, int line)
    {
        std::cout << "File " << file << ", line " << line << ": ";
    }

    template<typename T>
    LoggerProxy& operator<<(T&& t)
    {
        std::cout << t;
        return *this;
    }
};

#define LOG_DEBUG LoggerProxy{__FILE__, __LINE__}

int main()
{
    LOG_DEBUG << "Value is: " << 4;
}

You can do a lot of fancy stuff with this, such as debug level checks, output to different streams or multiple backends (such as simultaneous output to std::cout/cerr and log file) and many more.

Rostislav
  • 3,857
  • 18
  • 30
0

Of course it does not compile. There are many reasons for that.

First, Operator << is not defined for standard streams, and you are trying to do exactly that: stream stream into stream in your DEBUG(). (Pun intended).

Second, operator << is not defined for string literals, and you are trying to invoke it here:

"The value is: " << i

+ is not defined for literals either, by the way.

To achieve the semantic you want to see, you will have to start with the stream. String literal need to be converted to stream first, and than you can apply << to it. This is ONLY way to achieve what you want.

Edit:

Now since I understand the rationale, I can give a better answer. There are many ways how people are trying to segregate different levels of debugging uniformely, and there are several libraries aiming for that (log4cpp, boost.log to name just few). Before you start implementing your own logging, I would definitely suggest looking into those. There is much more to the good logging than just debug levels.

If, for any reason, you want to use your own homebrew, here are the couple of recepies you might explore:

  • Use your own logger class (one of the very rare examples, close to the single one! where Singleton is appropriate). You can than set the logging level in the beggining of your application, and than just call Logger::debug() << ...
  • Enrich above solution with macros. The problem with functions is that, unlike macros, they loose context. So if you want to log file and line number of the logging invocation (and you usually do!), you might want to do LOG_DEBUG << ...; here LOG_DEBUG would expand into something like Logger::debug() << __FILE__ << ":" << __LINE__ << ....
  • Once you've done this, you will see that sometimes you call other functions inside the << chain. At this point you might realize that those functions would be called regardless of your debug level, and might think you do not want to call them when debugging is not enabled (something along the lines LOG_DEBUG << " Object now is " << object.serialize(); So you will want to enrich the LOG_DEBUG macro to not execute anything when debug level does not match.
  • And the saga continues... Ready to use the library?
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • The only reason for the extra indirection is to have one place in code where I can enable / disable "DEBUG" level output, whilst still having other levels of output, such as "INFO" and "WARN". Is there a better way of doing this? Maybe I can typedef a name to point to cout, and then change the typedef to point to some null stream? – Sonic Atom Sep 16 '15 at 13:49
  • No. You can't typedef std::cout. But now I know what you are trying to do, so I will update the answer. – SergeyA Sep 16 '15 at 13:50
  • Thanks for this, it's a lot of information that will no doubt help someone. But since you suggest using a library or logger class + a load of macros, and I'm just using a single macro, I think I'll stick with my 1 macro for now. Maybe in a larger project I'll do differently. Thanks again. – Sonic Atom Sep 16 '15 at 14:20