Deriving a new stream buffer class is not commonly necessary, but it can be extraordinarily useful when specialized behavior is needed. For example, consider a log book stream that starts writing at the beginning after reaching a certain size, so that the log file does not grow infinitely. In order to implement this new class, we first need to derive a new stream buffer type. The easiest way to do this is to derive from basic_filebuf, and then reimplement one of the protected virtual functions:
#include <fstream>
template <class charT, class traits = std::char_traits<charT> >
class logbuffer: public std::basic_filebuf<charT, traits>
{
std::streamsize max_size;
std::streamsize cur_size;
public:
typedef charT char_type; //1
typedef traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::off_type off_type;
typedef typename traits_type::pos_type pos_type;
logbuffer(std::streamsize sz) //2
: max_size (sz), cur_size (0) { }
protected:
int_type overflow (int_type c = traits_type::eof ()); //3
};
The overflow() member function is implemented as follows:
template <class charT, class traits>
typename logbuffer<charT, traits>::int_type
logbuffer<charT, traits>::
overflow (typename logbuffer<charT, traits>::int_type c)
{
if (cur_size > max_size)
return traits_type::not_eof (c); //1
std::streamsize len = this->pptr () - this->pbase (); //2
std::streamsize rem = cur_size + len - max_size; //3
std::basic_string<charT, traits> saved;
if (rem > 0) { //4
this->pbump (-rem);
saved.assign (this->pptr (), rem);
if (!traits_type::eq_int_type (c, traits_type::eof ()))
saved += c;
len = max_size - cur_size;
c = traits_type::eof ();
}
const int_type ret = std::basic_filebuf<charT,
traits>::overflow (c); //5
cur_size += len; //5
if (cur_size == max_size) { //6
cur_size = max_size + 1;
this->pubseekoff (0, std::ios_base::beg);
cur_size = 0;
}
if (saved.size ())
this->xsputn (saved.data (), saved.size ()); //7
return ret;
}
In order to use this new logbuf class template, we do not necessarily need a new logstream class template. But it might come in handy in some cases so we provide a definition for one just in case:
template <class charT, class traits = std::char_traits<charT> >
class logstream: public std::basic_iostream<charT, traits>
{
logbuffer<charT, traits> buf; //1
public:
typedef charT char_type; //2
typedef traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::off_type off_type;
typedef typename traits_type::pos_type pos_type;
logstream(std::streamsize, const char*); //3
logbuffer<charT, traits> *rdbuf () const { //4
return (logbuffer<charT, traits>*)&buf;
}
};
Finally, we implement our new log stream class as shown here:
template <class charT, class traits>
logstream<charT, traits>::logstream (std::streamsize sz,
const char* file)
: std::basic_iostream<charT, traits>(&buf), buf (sz)
{
this->init (&buf); //1
if (!buf.open (file, std::ios_base::out)) //2
this->setstate (std::ios_base::failbit);
}
We can use this new log buffer class as follows:
int main ()
{
logstream<char> log (256, "test.log"); //1
char buf [33]; //2
log.rdbuf ()->pubsetbuf (buf, sizeof buf); //2
for (int i = 0; i < 150; i++) { //3
log << '[' << i << ']';
}
}