tags:

views:

315

answers:

6
+3  Q: 

customize cout

How can I derive a class from cout so that, for example, writing to it

new_cout << "message";

would be equivalent to

cout << __FUNCTION__ << "message" << "end of message" << endl;

A: 

You could also override the operator. It will allow you to call another function or prefix/suffix anything that's going to leave the output buffer with whatever you wish: In your case, you'd have it output a specific string.

Nerdling
-1. Overriding the operator will produce a header and footer for each item formatted: e.g. "new_cout << 1 << 2 << 3;" will produce 3 headers and footers.
j_random_hacker
Yes, my suggestions assumes the person who's writing the override to know where it should be done, for what types, and that his code will actually makes use of it correctly.
Nerdling
A: 

You have to override operator<<(), but you even don't have to subclass std::cout. You may also create a new object or use existing objects like that.

Koraktor
+15  A: 
class Log
{
public:
    Log(const std::string &funcName)
    {
        std::cout << funcName << ": ";
    }

    template <class T>
    Log &operator<<(const T &v)
    {
        std::cout << v;
        return *this;
    }

    ~Log()
    {
        std::cout << " [end of message]" << std::endl;
    }
};

#define MAGIC_LOG Log(__FUNCTION__)

Hence:

MAGIC_LOG << "here's a message";
MAGIC_LOG << "here's one with a number: " << 5;
Daniel Earwicker
+1. Unlike all the answers that just say "override operator<<()", this solution addresses the fact that the header and footer need to be output once per statement, not once per <<.
j_random_hacker
+1 indeed, nice idea. just something about endl: i suspect for a debug message, endl is seldom needed, but if you want to be able to << it into magic log too, put a Log return *this; } .
Johannes Schaub - litb
@Earwicker. Thanks for your nice solution. Please, explain what does it mean to instantiate a class without creating an object variable. I'm new to C++ and have never seen this before.
Jack
@Jack - you can call the constructor of a class so it looks like a function call, and it "returns" a temporary instance of the class, just as you'd have if you called any function that returned a new object of that class. Such "temporaries" always destruct at the next semi-colon.
Daniel Earwicker
@Earwicker - will using a lot of these temporaries degrade logging performance (vs using single global logger) or the time for creating such a small object can be neglected?
Jack
In this case, the class has no data members at all, so it only exists as a "notional" temporary at compile time. Everything will be inlined, so there should be no extra cost compared with writing out the equivalent pattern manually each time.
Daniel Earwicker
+1  A: 

Further from Mykola's response, I have the following implementation in my code. The usage is

         LOG_DEBUG("print 3 " << 3);

prints

         DEBUG (f.cpp, 101): print 3 3

You can modify it to use FUNCTION along/in place of LINE and FILE

/// Implements a simple logging facility. 
class Logger
{
        std::ostringstream os_;
        static Logger* instance_;
        Logger();
public:
        static Logger* getLogger();
        bool isDebugEnabled() const;
        void log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const;
        std::ostringstream& getStream()
        { return os_; }
};

void Logger::log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const
{
        std::cout << logLevelEnumToString(l) << "\t(" << fileName << ": " << lineno << ")\t- " << os.str();
        os.str("");
}

#define LOG_common(level, cptext) do {\
        utility::Logger::getLogger()->getStream() << cptext; \
        utility::Logger::getLogger()->log(utility::level, utility::Logger::getLogger()->getStream(), __FILE__, __LINE__);  \
} while(0); 

enum LogLevelEnum {
        DEBUG_LOG_LEVEL,
        INFO_LOG_LEVEL,
        WARN_LOG_LEVEL,
        ERROR_LOG_LEVEL,
        NOTICE_LOG_LEVEL,
        FATAL_LOG_LEVEL
};

#define LOG_DEBUG(cptext)    LOG_common(DEBUG_LOG_LEVEL, cptext)
#define LOG_INFO(cptext)     LOG_common(INFO_LOG_LEVEL , cptext)
#define LOG_WARN(cptext)     LOG_common(WARN_LOG_LEVEL , cptext)
#define LOG_ERROR(cptext)    LOG_common(ERROR_LOG_LEVEL, cptext)
#define LOG_NOTICE(cptext)   LOG_common(NOTICE_LOG_LEVEL, cptext)
#define LOG_FATAL(cptext)    LOG_common(FATAL_LOG_LEVEL, cptext)

const char* logLevelEnumToString(LogLevelEnum m)
{
        switch(m)
        {
                case DEBUG_LOG_LEVEL:
                        return "DEBUG";
                case INFO_LOG_LEVEL:
                        return "INFO";
                case WARN_LOG_LEVEL:
                        return "WARN";
                case NOTICE_LOG_LEVEL:
                        return "NOTICE";
                case ERROR_LOG_LEVEL:
                        return "ERROR";
                case FATAL_LOG_LEVEL:
                        return "FATAL";
                default:
                        CP_MSG_ASSERT(false, CP_TEXT("invalid value of LogLevelEnum"));
                        return 0;
        }
}
Amit Kumar
+2  A: 
#define debug_print(message) (std::cout << __FUNCTION__ << (message) << std::endl)

This has the advantage that you can disable all debug messages at once when you're done

#define debug_print(message) ()
soulmerge
+1  A: 

For logging purposes I use something like

#define LOG(x) \
  cout << __FUNCTION__ << x << endl

// ...
LOG("My message with number " << number << " and some more");

The problem with your approach is (as Mykola Golybyew explained) that FUNCTION is processed at compile time and would therefore always print the same name with a non-preprocessor solution.

If it's only for adding endl to your messages, you could try something like:

class MyLine {
public:
  bool written;
  std::ostream& stream;
  MyLine(const MyLine& _line) : stream(_line.stream), written(false) { }
  MyLine(std::ostream& _stream) : stream(_stream), written(false) { }
  ~MyLine() { if (!written) stream << "End of Message" << std::endl; }
};

template <class T> MyLine operator<<(MyLine& line, const T& _val) {
  line.stream << _val;
  line.written = true;
  return line;
}

class MyStream {
public:
  std::ostream& parentStream;
  MyStream(std::ostream& _parentStream) : parentStream(_parentStream) { }
  MyLine getLine() { return MyLine(parentStream); }
};

template <class T> MyLine operator<<(MyStream& stream, const T& _val) {
  return (stream.getLine() << _val);
}

int main()
{
      MyStream stream(std::cout);
      stream << "Hello " << 13 << " some more data";
      stream << "This is in the next line " << " 1 ";
    return 0;
}

Note, that it's important not to return references from the operator functions. Since the MyLine should only exist as a temporary (for its destructor triggers the writing of the endl), the first object (returned by the getLine() function in MyStream) would be destructed before the second operator<< is called. Therefore the MyLine object is copied in each operator<< creating a new one. The last object gets destructed without being written to and writed the end of the message in its destructor.

Just try it out in the debugger to understand whats going on...

MartinStettner