The correct way to declare / define a custom cout-like object

I created my own std::cout object, which writes to both std::cout and the log file.

Currently, I define it as in the header file, but I get unused warning variables.

Header file <MyLib/Log.h>

 static LOut { }; static LOut lo; template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue) { std::string str{toStr(mValue)}; std::cout << str; getLogStream() << str; return mLOut; } 

Using:

 #include <MyLib/Log.h> ... lo << "hello!" << std::endl; 

Should lo be static ? If lo be extern ?

Claims to explain the correct way to declare a cout object and display how the basic standard library implementations do it.


Edit: with the cout -like object, I mean a global variable that is always available after including the corresponding header.

+7
source share
5 answers

std::cout simply declared as follows:

 namespace std { extern ostream cout; } 

This is a regular global variable; you can do the same yourself. Put the extern declaration of your variable in the header; then define the same variable in the source file and bind it to your application:

 // mylog.h extern MyLog mylog; // mylog.cpp MyLog mylog(someparams); 
+6
source

First, I'm not too sure what you mean by a cout -like object? Maybe std::ostream .

In any case, the usual way to do this is to use streambuf filtering. Just write streambuf, which sends it to the log file, in addition to the usual place, and paste it wherever you want:

 class LoggingOutputStreambuf : public std::streambuf { std::streambuf* myDest; std::ofstreambuf myLogFile; std::ostream* myOwner; protected: int overflow( int ch ) { myLogFile.sputc( ch ); // ignores errors... return myDest->sputc( ch ); } public: LoggingOutputStreambuf( std::streambuf* dest, std::string const& logfileName ) : myDest( dest ) , myLogFile( logfileName.c_str(), std::ios_base::out ) , myOwner( nullptr ) { if ( !myLogFile.is_open() ) { // Some error handling... } } LoggingOutputStreambuf( std::ostream& dest, std::string const& logfileName ) : LoggingOutputStreambuf( dest.rdbuf(), logfileName ) { dest.rdbuf( this ); myOwner = &dest; } ~LoggingOutputStreambuf() { if ( myOwner != nullptr ) { myOwner->rdbuf( myDest ); } } }; 

(This is C ++ 11, but it should not be changed for C ++ 03).

To use, you can use something like:

 LoggingOutputStreambuf logger( std::cout ); // ... 

All output from std::cout will be logged until the logger goes out of scope.

In practice, you'll probably use something more complex than filebuf for logging, as you can insert timestamps at the beginning of each line or systematically flush each line at the end. (Filtering streambufs can take care of these issues as well.)

+1
source

std :: cout-like object that writes both files to std :: cout and to the log file

Perhaps boost.iostreams will be enough?

 #include <iostream> #include <fstream> #include <boost/iostreams/stream.hpp> #include <boost/iostreams/tee.hpp> namespace io = boost::iostreams; int main() { typedef io::tee_device<std::ostream, std::ofstream> teedev; typedef io::stream<teedev> LOut; std::ofstream outfile("test.txt"); teedev logtee(std::cout, outfile); LOut mLOut(logtee); mLOut << "hello!" << std::endl; } 
+1
source

Just sending the input value directly to cout did not work for me because I wanted to add headers and information to the log output.

In addition, I had a static Debug class to carry the log stream.

So I managed to do it, I hope it will be useful. I am somehow new to C ++, so feel free to tell me if something is wrong :)

 #include <iostream> #include <sstream> #include <ostream> enum class DebugLevel { INFO, WARNING, ERROR }; class Debug { public: /* other Debug class methods/properties ... */ // out stream object static struct OutStream { std::ostringstream stream; DebugLevel level = DebugLevel::INFO; public: // here you can add parameters to the object, every line log OutStream& operator()(DebugLevel l) { level = l; return *this; } // this overload receive the single values to append via << template<typename T> OutStream& operator<<(T&& value) { stream << value; return *this; } // this overload intercept std::endl, to flush the stream and send all to std::cout OutStream& operator<<(std::ostream& (*os)(std::ostream&)) { // here you can build the real console log line, add colors and infos, or even write out to a log file std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os; stream.str(""); // reset the string stream level = DebugLevel::INFO; // reset the level to info return *this; } } Log; }; Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class int main() { Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl; Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default return 0; } 
+1
source

In one of the projects, I wrote a wrapper for std::cout .

It looks something like this:

 struct out_t { template<typename T> out_t& operator << (T&& x) { std::cout << x; // log << x; return *this; }; }; out_t out; out << 1; 

For a complete search for struct out code in io.h

0
source

Source: https://habr.com/ru/post/949938/


All Articles