I\'d like to overload << operator to write the value it takes to a file and cout. I have tried to do it with following code, but couldn\'t succeed it. It just writes t
To implement a full stream interface you should build a stream buffer and a stream:
#include <ostream>
#include <sstream>
#include <streambuf>
#include <vector>
// BasicMultiStreamBuffer
// ============================================================================
template<class Char, class Traits = std::char_traits<Char>, class Allocator = std::allocator<Char> >
class BasicMultiStreamBuffer : public std::basic_stringbuf<Char, Traits, Allocator>
{
// Types
// =====
private:
typedef typename std::basic_stringbuf<Char, Traits> Base;
public:
typedef typename std::basic_streambuf<Char, Traits> buffer_type;
typedef typename buffer_type::char_type char_type;
typedef typename buffer_type::traits_type traits_type;
typedef typename buffer_type::int_type int_type;
typedef typename buffer_type::pos_type pos_type;
typedef typename buffer_type::off_type off_type;
private:
typedef typename std::vector<buffer_type*> container_type;
public:
typedef typename container_type::size_type size_type;
typedef typename container_type::value_type value_type;
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::iterator iterator;
typedef typename container_type::const_iterator const_iterator;
// Construction/Destructiion
// =========================
public:
BasicMultiStreamBuffer()
{}
BasicMultiStreamBuffer(buffer_type* a) {
if(a) {
m_buffers.reserve(1);
m_buffers.push_back(a);
}
}
template <typename Iterator>
BasicMultiStreamBuffer(Iterator first, Iterator last)
: m_buffers(first, last)
{}
~BasicMultiStreamBuffer() {
sync();
}
private:
BasicMultiStreamBuffer(BasicMultiStreamBuffer const&); // No Copy.
BasicMultiStreamBuffer& operator=(BasicMultiStreamBuffer const&); // No Copy.
// Capacity
// ========
public:
bool empty() const { return m_buffers.empty(); }
size_type size() const { return m_buffers.size(); }
// Iterator
// ========
public:
iterator begin() { return m_buffers.begin(); }
const_iterator begin() const { return m_buffers.end(); }
iterator end() { return m_buffers.end(); }
const_iterator end() const { return m_buffers.end(); }
// Modifiers
// =========
public:
void insert(buffer_type* buffer) {
if(buffer) m_buffers.push_back(buffer);
}
void erase(buffer_type* buffer) {
iterator pos = this->begin();
for( ; pos != this->end(); ++pos) {
if(*pos == buffer) {
m_buffers.erase(pos);
break;
}
}
}
// Synchronization
// ===============
protected:
virtual int sync() {
int result = 0;
if( ! m_buffers.empty()) {
char_type* p = this->pbase();
std::streamsize n = this->pptr() - p;
if(n) {
const_iterator pos = m_buffers.begin();
for( ; pos != m_buffers.end(); ++pos) {
std::streamoff offset = 0;
while(offset < n) {
int k = (*pos)->sputn(p + offset, n - offset);
if(0 <= k) offset += k;
else {
result = -1;
break;
}
}
if((*pos)->pubsync() == -1) result = -1;
}
this->setp(this->pbase(), this->epptr());
}
}
if(Base::sync() == -1) result = -1;
return result;
}
private:
container_type m_buffers;
};
typedef BasicMultiStreamBuffer<char> OStreamBuffers;
// BasicMultiStream
// ============================================================================
template<class Char, class Traits = std::char_traits<Char>, class Allocator = std::allocator<Char> >
class BasicMultiStream : public std::basic_ostream<Char, Traits>
{
// Types
// =====
private:
typedef std::basic_ostream<Char, Traits> Base;
public:
typedef BasicMultiStreamBuffer<Char, Traits, Allocator> multi_buffer;
typedef std::basic_ostream<Char, Traits> stream_type;
typedef typename multi_buffer::buffer_type buffer_type;
typedef typename multi_buffer::char_type char_type;
typedef typename multi_buffer::traits_type traits_type;
typedef typename multi_buffer::int_type int_type;
typedef typename multi_buffer::pos_type pos_type;
typedef typename multi_buffer::off_type off_type;
typedef typename multi_buffer::size_type size_type;
typedef typename multi_buffer::value_type value_type;
typedef typename multi_buffer::reference reference;
typedef typename multi_buffer::const_reference const_reference;
typedef typename multi_buffer::iterator iterator;
typedef typename multi_buffer::const_iterator const_iterator;
// Construction
// ============
public:
BasicMultiStream()
: Base(&m_buffer)
{}
BasicMultiStream(stream_type& stream)
: Base(&m_buffer), m_buffer(stream.rdbuf())
{}
template <typename StreamIterator>
BasicMultiStream(StreamIterator& first, StreamIterator& last)
: Base(&m_buffer)
{
while(first != last) insert(*first++);
}
private:
BasicMultiStream(const BasicMultiStream&); // No copy.
const BasicMultiStream& operator = (const BasicMultiStream&); // No copy.
// Capacity
// ========
public:
bool empty() const { return m_buffer.empty(); }
size_type size() const { return m_buffer.size(); }
// Iterator
// ========
public:
iterator begin() { return m_buffer.begin(); }
const_iterator begin() const { return m_buffer.end(); }
iterator end() { return m_buffer.end(); }
const_iterator end() const { return m_buffer.end(); }
// Modifiers
// =========
public:
void insert(stream_type& stream) { m_buffer.insert(stream.rdbuf()); }
void erase(stream_type& stream) { m_buffer.erase(stream.rdbuf()); }
private:
multi_buffer m_buffer;
};
typedef BasicMultiStream<char> MultiStream;
// Test
// =============================================================================
#include <iostream>
int main() {
MultiStream out;
out.insert(std::cout);
out.insert(std::clog);
out << "Hello\n";
}
Here, the output is buffered in a string stream buffer and synchronized to the destination streams.
Create a helper class and overload operators that takes care of streaming to two streams. Use the helper class instead of trying to override the standard library implementations of the overloaded operator<<
functions.
This should work:
#include <iostream>
#include <fstream>
struct MyStreamingHelper
{
MyStreamingHelper(std::ostream& out1,
std::ostream& out2) : out1_(out1), out2_(out2) {}
std::ostream& out1_;
std::ostream& out2_;
};
template <typename T>
MyStreamingHelper& operator<<(MyStreamingHelper& h, T const& t)
{
h.out1_ << t;
h.out2_ << t;
return h;
}
MyStreamingHelper& operator<<(MyStreamingHelper& h, std::ostream&(*f)(std::ostream&))
{
h.out1_ << f;
h.out2_ << f;
return h;
}
int main()
{
std::ofstream fl;
fl.open("test.txt");
MyStreamingHelper h(fl, std::cout);
h << "!!!Hello World!!!" << std::endl;
return 0;
}
The function you've overloaded takes a left hand side of std::ostream&
and a right hand side of const std::string&
. This is how you called the function:
fl << "!!!Hello World!!!" << endl;
The left hand side matches perfectly, but the right hand side doesn't. The string you've passed is not an std::string
but rather a object of type char const*
. The custom overload you made isn't called even though there is a viable conversion from char const*
to const std::string&
because there is in fact a perfect match elsewhere.
The overload that is a perfect match is the overload found in namespace std
which is signed as:
namespace std { // Simplification:
std::ostream& operator<<(std::ostream& os, char const* str);
}
This is a better match because no conversion needs to be made on the matter of the right hand parameter. Another reason this works is because of something called ADL or Argument Dependent Lookup. Normally you would have to explicitly qualify names or functions from other namespaces (like std
) from outside of those namespaces, but when it comes to ADL, if the compiler can find a function which takes a class of user-defined type from that same namespace, calling said function will not require explicit qualification from outside that namespace. Therefore the above is equivalent to:
std::operator<<(f1, "!!!Hello World!!!") << std::endl;
You can see this while using std::getline()
. The following is well-formed even though we do not use using namespace std
or using std::getline
:
getline(std::cin, line);
Because std::cin
is within the same namespace as the function std::getline()
, you do not need to append std::
to the function call.
So for your overload to be called, there has to be a better match. You can force this by creating a std::string
explicitly:
fl << std::string("!!!Hello World!!!") << endl;
Your overload is called and not the one in namespace std
because overloads in the enclosing namespace are considered before outer ones like std
. But not only is this non-intuitive, it will also cause other issues.
Your function needs to return the type std::ostream&
instead of void
and have a return os
statement so that you can chain the << endl
expression.
Inside your function you're performing infinite recursion on the os << str
line. There are a number of ways to solve this issue, the easiest being to do os << str.c_str()
so that the char const*
overload in namespace std
gets called.
Your method isn't the best way to do this so for more complete and better solutions, look toward the other answers and comments in this thread.
The usual way of doing this is to use a filtering streambuf, which forwards to both of the target streambufs. Something like the following should do the trick:
class LoggingStreambuf : public std::streambuf
{
std::streambuf* myPrinciple;
std::ostream* myOwner;
std::filebuf myLogging;
protected:
int overflow( int ch ) override
{
myLogging->sputc( ch );
return myPrinciple->sputc( ch );
}
public:
LoggingStreambuf( std::streambuf* principal, std::string const& logFileName )
: myPrinciple( principal )
, myOwner( nullptr )
, myLogging( logFileName )
{
}
LoggingStreambuf( std::ostream& principal, std::string const& logFileName )
: myPrinciple( principal.rdbuf() )
, myOwner( &principal )
, myLogging( logFileName )
{
myOwner.rdbuf( this );
}
~LoggingStreambuf()
{
if ( myOwner != nullptr ) {
myOwner.rdbuf( myPrinciple );
}
}
};
(This particular code supposes that the file output is a log
file, a secondary output on which errors should be ignored.
Other error handling strategies can also be implemented, and of
course, there's no reason that you couldn't provide two
arbitrary std::streambuf*
, and create a new std::ostream
outputting through an instance of the custom streambuf.)
To use this class, as it is written:
LoggingStreambuf logger( std::cout, "logfile.txt" );
// And output to std::cout as usual...
More generally, of course: anytime you want to do something
special with regards to the data sink or source of a stream, you
implement a new std::streambuf
, since this is the class which
handles sinking and sourcing of data for the streams.
If you are able to use it, you will find that, not unsurprisingly, the boost library has already done most of the hard work for you.
#include <iostream>
#include <fstream>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>
typedef boost::iostreams::tee_device<std::ostream, std::ostream> teedev;
typedef boost::iostreams::stream<teedev, std::char_traits<typename std::ostream::char_type>, std::allocator< typename std::ostream::char_type > > tee_stream;
int main(int argc, char* argv[])
{
std::ofstream of;
of.open( "test.txt" );
teedev td( of, std::cout );
tee_stream ts(td);
ts << "!!!Hello World!!!" << std::endl;
return 0;
}
It just writes the value to the text file because your operator<< method does not get called.
The operator<< should return a reference to the stream (ostream&) because otherwise << str << endl; would not work.
The other problem with your operator<< is that it could create an infinite loop since os << str; has the same signature than fl << "string";