 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
ma740988 Guest
|
Posted: Wed Oct 13, 2004 9:34 pm Post subject: Custom streambuf class |
|
|
For starters, I ask that you pardon my ignorace on streams (just
purchased Langer/Kreft) but at issue is the need for a log class.
This log class derived from streambuf should consider a special
enumeration or type for controlling the behavior of the bag 'o
streambufs; to turn on and off logging to file, to control the
verbosity of the log, to turn on date-stamping, etc. So I perused the
web and found a class that provides an excellent start so now:
#include <streambuf>
#include <string>
template <class CharT, class Traits = std::char_traits
class basic_teebuf: public std::basic_streambuf<CharT, Traits>
{
public:
typedef std::basic_streambuf<CharT, Traits> streambuf_type;
typedef Traits traits_type;
typedef CharT char_type;
typedef typename traits_type::int_type int_type;
basic_teebuf(streambuf_type* buf1, streambuf_type* buf2)
:m_buf1(buf1), m_buf2(buf2), m_buffer(new
char_type[BUFFER_SIZE])
{
this->setp(m_buffer, m_buffer + BUFFER_SIZE);
}
~basic_teebuf()
{
this->pubsync();
delete[] m_buffer;
}
protected:
int_type overflow(int_type c = traits_type::eof())
{
//empty the buffer
std::streamsize n =
static_cast<std::streamsize>(this->pptr() - this->pbase());
std::streamsize size1 = m_buf1->sputn(this->pbase(), n);
std::streamsize size2 = m_buf2->sputn(this->pbase(), n);
if (size1 != n || size2 != n)
{
return traits_type::eof();
}
//reset buffer
this->setp(m_buffer, m_buffer + BUFFER_SIZE);
//write the passed character if necessary
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
traits_type::assign(*this->pptr(),
traits_type::to_char_type(c));
this->pbump(1);
}
return traits_type::not_eof(c);
}
int sync()
{
//flush buffer, checking return for eof.
if (traits_type::eq_int_type(overflow(traits_type::eof()),
traits_type::eof()))
{
return -1;
}
//flush held streams.
if (m_buf1->pubsync() == -1
| Quote: | | m_buf2->pubsync() == -1)
{ |
return -1;
}
return 0;
}
private:
streambuf_type* m_buf1;
streambuf_type* m_buf2;
char_type* m_buffer;
static int const BUFFER_SIZE = 4096 / sizeof(char_type);
};
typedef basic_teebuf<char> teebuf;
typedef basic_teebuf<wchar_t> wteebuf;
#include <iostream>
#include <fstream>
int main()
{
{
//write lots of data to check correct buffering:
std::ofstream file("log1.txt");
//create a streambuf that writes to both files.
teebuf tbuf(file.rdbuf(), std::cout.rdbuf());
//replace cout's streambuf with the tbuf
std::streambuf* oldcout = std::cout.rdbuf(&tbuf);
for (int i = 0; i < 1000; ++i)
{
std::cout << "I must not tell lies at school.n";
}
//and importantly before exit, reset cout to its normal buf
//that writes to stdout.
std::cout.rdbuf(oldcout);
}
}
At issue:
1. The constructor of the class supports writing to a file and cout
simultaneously. I'd also like the ability to write to a file OR cout
but have been unable to tailor the class to do so?
2. To use teebuf I've considered 2 alternatives.
a. Consider a class X with an instance of teebuf and akin to:
class X {
public:
X(std::ofstream file)
: file_(file), tbuf(file.rdbuf(), std::cout.rdbuf())
oldcout(std::cout.rdbuf(&tbuf))
{
}
void Log20Hz() { std::cout << idx; } // now use cout
private:
int idx;
std::ofstream file_;
teebuf tbuf;
std::streambuf* oldcout;
};
b. Instead of creating classes with instances of teebuf, the
preferred
approach I suspect would be to register the
class object (ie. the X object) with teebuf. This approach -
seems easier said than done, nonetheless ..
The groups thoughts.
Thanks for your time.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Thu Oct 14, 2004 9:51 pm Post subject: Re: Custom streambuf class |
|
|
ma740988 <ma740988 (AT) pegasus (DOT) cc.ucf.edu> wrote:
| Quote: | For starters, I ask that you pardon my ignorace on streams (just
purchased Langer/Kreft) but at issue is the need for a log class.
This log class derived from streambuf should consider a special
enumeration or type for controlling the behavior of the bag 'o
streambufs; to turn on and off logging to file, to control the
verbosity of the log, to turn on date-stamping, etc. So I perused the
web and found a class that provides an excellent start so now:
|
A custom stream buffer, IMHO, is of no help for your tasks (verbosity and
date stamping). The reason is that the stream buffer is merely a binary
data sink interface, so it has no clue of what kind of data is being
written into it.
More practical solution is to use stream manipulators. It was discussed
here recently, you might like to read it:
http://groups.google.com/groups?selm=Xns956780F6C7D62wrgrpde%40130.133.1.4
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Fri Oct 15, 2004 9:55 pm Post subject: Re: Custom streambuf class |
|
|
"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote
| Quote: | ma740988 <ma740988 (AT) pegasus (DOT) cc.ucf.edu> wrote:
For starters, I ask that you pardon my ignorace on streams (just
purchased Langer/Kreft) but at issue is the need for a log class.
This log class derived from streambuf should consider a special
enumeration or type for controlling the behavior of the bag 'o
streambufs; to turn on and off logging to file, to control the
verbosity of the log, to turn on date-stamping, etc. So I perused
the web and found a class that provides an excellent start so now:
A custom stream buffer, IMHO, is of no help for your tasks (verbosity
and date stamping).
|
So what is the solution. As far as I know, a filtering streambuf has
been the standard solution for date stamping ever since Dietmar Kuehl
and I invented the concept, something like ten years ago. It's even one
of the "classic" examples for an output filtering streambuf at my site.
Depending on what he means to control under the heading verbosity, some
sort of ostream wrapper might be more appropriate for that.
| Quote: | The reason is that the stream buffer is merely a binary data sink
interface, so it has no clue of what kind of data is being written
into it.
|
The streambuf's input isn't binary, but text (formatted characters).
It's trivial to make it insert additional text at the start of each
line; this is the classic way to implement time stamping. I also have
more complicated outputting streambuf's which generate "events" at the
start of line, end of line (before the 'n' is output) and after each
line (after the 'n'); user defined event handlers can be used to do
just about anything at these points.
For outputting binary, about all you have is the standard's guarantee
that 1) if the filebuf is opened in binary mode, and 2) it is imbued
with the locale "C", it will transmit the characters directly to the
system, without any intermediate mapping. On systems where char's can
contain arbitrary binary data (which includes all systems I know of --
but it isn't guaranteed by the standard), you can thus hack binary
output in this way.
(And I know, this has nothing to do with the point you are making. I'm
just knee jerking on the "binary data sink" expression.)
Actually, the thread ended up suggesting ostream wrappers, not
manipulators.
The only issue in the thread was turning logging off or on, according to
the level. The problems of possibly outputting to more than sink, or of
inserting timestamp information, were not addressed. These problems are
best addressed by means of a filtering streambuf.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Fri Oct 15, 2004 9:58 pm Post subject: Re: Custom streambuf class |
|
|
[email]ma740988 (AT) pegasus (DOT) cc.ucf.edu[/email] (ma740988) wrote in message
news:<a5ae824.0410130627.69c64ce8 (AT) posting (DOT) google.com>...
| Quote: | For starters, I ask that you pardon my ignorace on streams (just
purchased Langer/Kreft) but at issue is the need for a log class.
This log class derived from streambuf should consider a special
enumeration or type for controlling the behavior of the bag 'o
streambufs; to turn on and off logging to file, to control the
verbosity of the log, to turn on date-stamping, etc.
|
I think that the verbosity should probably be handled separately; I use
an ostream wrapper class for that. For the rest, the technique you use
is the standard filtering streambuf solution.
| Quote: | So I perused the web and found a class that provides an excellent
start so now:
|
[Code deleted...]
| Quote: | At issue:
1. The constructor of the class supports writing to a file and cout
simultaneously. I'd also like the ability to write to a file OR cout
but have been unable to tailor the class to do so?
|
The obvious solution would be to pass an std::vector< std::streambuf* >
to the constructor, and output in parallele to whatever it contains.
My own solution has generally been along these lines, with the addition
of an ostream wrapper. (You can find a lot of code for this sort of
thing in the Util/IO components at my site: an ostream wrapper, a
generic framework for filtering streambuf's, and a particular
implementation which generates "events" on the streambuf, allowing you
to take control at particular times.) Basically, for each level of
logging, I'll define an std::vector< streambuf* >. The user code will
obtain an ostream wrapper with something like:
log( level ) << ...
where log is a global function, which normally returns a wrapper to an
ostream using the appropriate vector of streambuf*; if the vector is
empty, however, it returns a disactivated wrapper. The advantage is
that you don't even do the formatting if there is no logging output.
The wrapper can also be used to ensure timely flushing, usually
important in logging output.
While I'm at it: the code you posted seems unnecessarily complicated,
mostly because it does buffering. Generally speaking, filtering
streambuf's don't buffer; the buffering is in the final streambufs. In
your case, the following would probably be sufficient:
class LoggingStreambuf : public std::streambuf
{
public:
template< typename FwdIter >
LoggingStreambuf( FwdIter begin, FwdIter end )
: myIsStartOfLine( true )
, myStartOfRecord( true )
, myDestinations( begin, end )
{
}
void endOfRecord()
{
if ( ! myIsStartOfLine ) {
overflow( 'n' ) ;
}
sync() ;
myStartOfRecord = true ;
}
protected:
int overflow( int ch )
{
if ( myIsStartOfLine ) {
if ( myStartOfRecord ) {
// insert timestamp infos, etc. ...
} else {
// indent, whatever ....
}
}
for ( std::vector< streambuf* >::iterator it
= myDestinations.begin() ;
it != myDestinations.end() ;
++ it ) {
(*it)->sputc( ch ) ;
}
myIsStartOfLine = (ch == 'n) ;
return 0 ;
}
int sych()
{
for ( std::vector< streambuf* >::iterator it
= myDestinations.begin() ;
it != myDestinations.end() ;
++ it ) {
(*it)->sync() ;
}
return 0 ;
}
private:
bool myIsStartOfLine ;
bool myStartOfRecord ;
std::vector< streambuf* >
myDestinations ;
} ;
The additional endOfRecord function is used in the destructor of the
wrapper, to ensure that the log record (which may consist of several
lines) is correctly output. But do look at the code for the ostream
wrapper at my site first; you have to watch out for additional copies
when using temporary objects.
Finally, it's fairly usual to wrap the call to the log() function in a
macro, in order to automatically pass it the filename and the line
number, e.g.:
#define LOG(level) log( (level), __FILE__, __LINE__ )
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
ma740988 Guest
|
Posted: Fri Oct 15, 2004 10:47 pm Post subject: Re: Custom streambuf class |
|
|
| Quote: |
A custom stream buffer, IMHO, is of no help for your tasks (verbosity and
date stamping). The reason is that the stream buffer is merely a binary
data sink interface, so it has no clue of what kind of data is being
written into it.
More practical solution is to use stream manipulators. It was discussed
here recently, you might like to read it:
http://groups.google.com/groups?selm=Xns956780F6C7D62wrgrpde%40130.133.1.4
|
I'd perused this post and thought the emphasis placed on debug didn't
prove viable for my application but I suspect I was wrong. I also
got the impression (after perusing google and review posts by Kuehl
among others ) that a stream buff solution would be the ideal
approach.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Sun Oct 17, 2004 1:30 am Post subject: Re: Custom streambuf class |
|
|
<kanze (AT) gabi-soft (DOT) fr> wrote:
| Quote: | While I'm at it: the code you posted seems unnecessarily complicated,
mostly because it does buffering. Generally speaking, filtering
streambuf's don't buffer; the buffering is in the final streambufs.
In your case, the following would probably be sufficient:
|
[...]
Wouldn't it be better to overload also xsputn() and implement overflow()
in terms of it? That way you would avoid the virtual call to overflow()
for each and every character you put into the buffer.
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Sun Oct 17, 2004 1:32 am Post subject: Re: Custom streambuf class |
|
|
<kanze (AT) gabi-soft (DOT) fr> wrote:
| Quote: | "Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote in message
news:<opsfvefjzpti5cme@wkcg6rirwp>...
ma740988 <ma740988 (AT) pegasus (DOT) cc.ucf.edu> wrote:
For starters, I ask that you pardon my ignorace on streams (just
purchased Langer/Kreft) but at issue is the need for a log class.
This log class derived from streambuf should consider a special
enumeration or type for controlling the behavior of the bag 'o
streambufs; to turn on and off logging to file, to control the
verbosity of the log, to turn on date-stamping, etc. So I perused
the web and found a class that provides an excellent start so now:
A custom stream buffer, IMHO, is of no help for your tasks (verbosity
and date stamping).
So what is the solution. As far as I know, a filtering streambuf has
been the standard solution for date stamping ever since Dietmar Kuehl
and I invented the concept, something like ten years ago.
|
I must have missed it, since at that time I had not even finished my
school yet. :)
I would propose a date-stamp manipulator, but I'm not sure now. It might
seem inferior to the filtering stream buffer solution.
| Quote: | It's even one
of the "classic" examples for an output filtering streambuf at my
site. |
Ok, I'll check it out.
[]
| Quote: | The reason is that the stream buffer is merely a binary data sink
interface, so it has no clue of what kind of data is being written
into it.
The streambuf's input isn't binary, but text (formatted characters).
|
I was referring more to that it should be treated as binary. One, for
example, can use ostream::write() to write *real* binary data rather
then human-readable text, and the stream buffer has no way to know that.
| Quote: | It's trivial to make it insert additional text at the start of each
line; this is the classic way to implement time stamping. I also have
more complicated outputting streambuf's which generate "events" at the
start of line, end of line (before the 'n' is output) and after each
line (after the 'n'); user defined event handlers can be used to do
just about anything at these points.
|
So, this implies that one must write text data only into the buffers,
which may sound reasonable since the standard stream are for formatted
*text* output. Or am I missing something?
| Quote: | For outputting binary, about all you have is the standard's guarantee
that 1) if the filebuf is opened in binary mode, and 2) it is imbued
with the locale "C", it will transmit the characters directly to the
system, without any intermediate mapping. On systems where char's can
contain arbitrary binary data (which includes all systems I know of --
but it isn't guaranteed by the standard), you can thus hack binary
output in this way.
(And I know, this has nothing to do with the point you are making.
I'm just knee jerking on the "binary data sink" expression.)
|
Ok :)
| Quote: | More practical solution is to use stream manipulators. It was
discussed here recently, you might like to read it:
http://groups.google.com/groups?selm=Xns956780F6C7D62wrgrpde%40130.13
3.1.4
Actually, the thread ended up suggesting ostream wrappers, not
manipulators.
The only issue in the thread was turning logging off or on, according
to the level.
|
Yes, I should have mentioned it along with "controlling verbosity".
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
ma740988 Guest
|
Posted: Sun Oct 17, 2004 1:42 am Post subject: Re: Custom streambuf class |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] wrote in message
news:<d6652001.0410150115.57e9ae8e (AT) posting (DOT) google.com>...
| Quote: | ma740988 (AT) pegasus (DOT) cc.ucf.edu (ma740988) wrote in message
news:<a5ae824.0410130627.69c64ce8 (AT) posting (DOT) google.com>...
[...] |
| Quote: | At issue:
1. The constructor of the class supports writing to a file and cout
simultaneously. I'd also like the ability to write to a file OR
cout but have been unable to tailor the class to do so?
The obvious solution would be to pass an std::vector< std::streambuf*
to the constructor, and output in parallele to whatever it contains.
My own solution has generally been along these lines, with the
addition of an ostream wrapper. (You can find a lot of code for this
sort of thing in the Util/IO components at my site: an ostream
wrapper, a generic framework for filtering streambuf's, and a
particular implementation which generates "events" on the streambuf,
allowing you to take control at particular times.) Basically, for
each level of logging, I'll define an std::vector< streambuf* >. The
user code will obtain an ostream wrapper with something like:
log( level ) << ...
where log is a global function, which normally returns a wrapper to an
ostream using the appropriate vector of streambuf*; if the vector is
empty, however, it returns a disactivated wrapper. The advantage is
that you don't even do the formatting if there is no logging output.
The wrapper can also be used to ensure timely flushing, usually
important in logging output.
|
I've downloaded your source and I'm off trying to get my 'head around
your approach'. Off hand, do you provide for synchronization issues
if the program to be logged will be multithreaded?
| Quote: | While I'm at it: the code you posted seems unnecessarily complicated,
mostly because it does buffering. Generally speaking, filtering
streambuf's don't buffer; the buffering is in the final streambufs.
In your case, the following would probably be sufficient:
|
[...]
I need to get familar with sput/synch and the myriad of 'stream
functions'.
How would you address question 2 of my initial post. i.e which approach
makes the most sense?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Oct 18, 2004 6:34 pm Post subject: Re: Custom streambuf class |
|
|
"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote
| Quote: | kanze (AT) gabi-soft (DOT) fr> wrote:
"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote in message
news:<opsfvefjzpti5cme@wkcg6rirwp>...
ma740988 <ma740988 (AT) pegasus (DOT) cc.ucf.edu> wrote:
So what is the solution. As far as I know, a filtering streambuf has
been the standard solution for date stamping ever since Dietmar
Kuehl and I invented the concept, something like ten years ago.
I must have missed it, since at that time I had not even finished my
school yet. :)
I would propose a date-stamp manipulator, but I'm not sure now. It
might seem inferior to the filtering stream buffer solution.
|
It depends on what you want. If you want to give the user the
possibility to insert a time stamp where ever he pleases, in any stream
he pleases, then the manipulator is the answer. If you want to
systematically insert a time stamp at the start of each line, in a
particular stream only, and independantly of what the user does, then a
filtering streambuf is the solution.
My impression is that the problem posed here is closer to the second.
| Quote: | []
The reason is that the stream buffer is merely a binary data sink
interface, so it has no clue of what kind of data is being written
into it.
The streambuf's input isn't binary, but text (formatted characters).
I was referring more to that it should be treated as binary. One, for
example, can use ostream::write() to write *real* binary data rather
then human-readable text, and the stream buffer has no way to know
that.
|
I think I know what you were referring to. It's a common misconception
(IMHO). Or maybe (more likely?) a design flaw in the standard library.
But ostream::write() still writes the data as characters, not as binary
data. In particular, it does character translation according to the
imbued locale.
More interestingly, it uses a basic_streambuf<char> for the output.
This means that you are outputting data of type char. On most mahcines,
it doesn't make a difference -- a byte is a byte, regardless of whether
it is a char, an unsigned char or a signed char. Pratically speaking,
any implementation which didn't support it would probably not sell. But
the standard very definitly gives implementations the leeway.
Anyway, the important point in practice is that it isn't sufficient just
to use ostream::write, or some such. You must, absolutely, ensure that
1) the filebuf was opened in binary mode, and 2) it is imbued with
locale "C". I notice that the latter is not always mentioned. Even
though locale "C" is NOT the default.
| Quote: | It's trivial to make it insert additional text at the start of each
line; this is the classic way to implement time stamping. I also
have
more complicated outputting streambuf's which generate "events" at
the start of line, end of line (before the 'n' is output) and after
each line (after the 'n'); user defined event handlers can be used
to do just about anything at these points.
So, this implies that one must write text data only into the buffers,
which may sound reasonable since the standard stream are for formatted
*text* output. Or am I missing something?
|
Well, it's true that my filtering streambuf's are designed for handling
text. I have heard of some, however, that compress or encrypt on the
fly.
Obviously, if you are outputting binary data, and have carefully set up
the filebuf to handle it correctly, you will not want to insert a
filtering streambuf which inserts a timestamp each time you output a
character which was preceded by a 'n'.
But there's nothing new here. Specialized streambuf's are for
specialized use, and nothing in the standard requires that all
streambuf's be as general as filebuf.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Oct 18, 2004 8:14 pm Post subject: Re: Custom streambuf class |
|
|
[email]ma740988 (AT) pegasus (DOT) cc.ucf.edu[/email] (ma740988) wrote in message
news:<a5ae824.0410161358.b656de0 (AT) posting (DOT) google.com>...
| Quote: | kanze (AT) gabi-soft (DOT) fr wrote in message
news:<d6652001.0410150115.57e9ae8e (AT) posting (DOT) google.com>...
[email]ma740988 (AT) pegasus (DOT) cc.ucf.edu[/email] (ma740988) wrote in message
news:<a5ae824.0410130627.69c64ce8 (AT) posting (DOT) google.com>...
[...]
At issue:
1. The constructor of the class supports writing to a file and cout
simultaneously. I'd also like the ability to write to a file OR
cout but have been unable to tailor the class to do so?
The obvious solution would be to pass an std::vector
std::streambuf* > to the constructor, and output in parallele to
whatever it contains.
My own solution has generally been along these lines, with the
addition of an ostream wrapper. (You can find a lot of code for
this sort of thing in the Util/IO components at my site: an ostream
wrapper, a generic framework for filtering streambuf's, and a
particular implementation which generates "events" on the streambuf,
allowing you to take control at particular times.) Basically, for
each level of logging, I'll define an std::vector< streambuf* >.
The user code will obtain an ostream wrapper with something like:
log( level ) << ...
where log is a global function, which normally returns a wrapper to
an ostream using the appropriate vector of streambuf*; if the vector
is empty, however, it returns a disactivated wrapper. The advantage
is that you don't even do the formatting if there is no logging
output.
The wrapper can also be used to ensure timely flushing, usually
important in logging output.
I've downloaded your source and I'm off trying to get my 'head around
your approach'. Off hand, do you provide for synchronization issues
if the program to be logged will be multithreaded?
|
Not directly. This is another thing that would probably best be handled
in the wrapper. The "log" function acquires a lock, and the final
destructor of the wrapper releases it. In my implementation, this could
be done in the constructed and destructed functions of the lifetime
handler. If you're using boost::threads, this probably means that you
need a dynamically allocated scoped lock object. Although... if it is a
data member of the LifetimeHandler, and you construct a new
LifetimeHandler for each call -- which is probably the most typical use,
then it should work correctly. Provided you either replace my reference
counted pointer with boost::shared_ptr.
And of course, regardless of how you handle the locking, the
implementation currently available at my site uses a non-thread-safe
implementation of reference counted pointers. My hope was that I would
be able to move to Boost before I had to address thread safety issues.
It hasn't worked out that way, and I have a thread safe implementation
available, but it isn't at my site yet.
| Quote: | While I'm at it: the code you posted seems unnecessarily
complicated, mostly because it does buffering. Generally speaking,
filtering streambuf's don't buffer; the buffering is in the final
streambufs. In your case, the following would probably be
sufficient:
[...]
I need to get familar with sput/synch and the myriad of 'stream
functions'. How would you address question 2 of my initial post. i.e
which approach makes the most sense?
|
You actually have several problems: 1) controlling verbosity
(i.e. turning logging on or off for a specific level), 2) handling
locking (if you are multithreaded), 3) ventilating the active log to
various destinations, and 4) inserting additional, default text. The
wrapper is very efficient for 1 and 2, especially in the case where
locking is turned off. Filtering streambuf's are better solutions for 3
and 4.
My usual solution uses both -- for each level of locking, I have a set
of active destinations, usually in the form of a
std::vector< streambuf* >, or something similar. The log function
determins from the size() of this vector whether logging is active or
not. If not, it simply returns an OutputStreamWrapper with a null
pointer to ostream, which means that the << operators are no-op, and no
formatting occurs. If there is at least one streambuf, however, the log
function will set up an ostream to the forwarding streambuf, and return
a wrapper with it. In addition, the LifetimeHandler of the wrapper
collaborates with the EventHandler of the streambuf to ensure any
boilerplate text (time stamps, filename and linenumber, etc.).
Typically, of course, I'll actually create the necessary ostreams once,
when configuring the logging, and I'll usually use permantently
allocated LifetimeHandlers and EventHandlers as well. This all works
even in a multithreaded environment, provided that 1) configuration
takes place before threads start or under some other form of absolute
protection, 2) the lock is acquired as soon as it is determined that
there will be some logging output, before starting to set anything up,
in log(), and 3) either the OutputStreamWrapper is modified to manage
the lock itself, taking care to release its ownership of the
LifetimeHandler before releasing the lock, or RefCntPtr has been
modified to be thread safe.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Oct 18, 2004 8:14 pm Post subject: Re: Custom streambuf class |
|
|
"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote
| Quote: | kanze (AT) gabi-soft (DOT) fr> wrote:
While I'm at it: the code you posted seems unnecessarily
complicated, mostly because it does buffering. Generally speaking,
filtering streambuf's don't buffer; the buffering is in the final
streambufs. In your case, the following would probably be
sufficient:
[...]
Wouldn't it be better to overload also xsputn() and implement
overflow() in terms of it? That way you would avoid the virtual call
to overflow() for each and every character you put into the buffer.
|
Depending on the case, you might want to overload xsputn() too. But the
choice to overload it is always purely optimization; in practice, I've
never found it to be worth the bother, at least not when you have to
look at each character anyway. (Similarly, I've never found buffering
to be worth the hassle in such cases either.)
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Tue Oct 19, 2004 5:59 pm Post subject: Re: Custom streambuf class |
|
|
<kanze (AT) gabi-soft (DOT) fr> wrote:
| Quote: | Not directly. This is another thing that would probably best be handled
in the wrapper. The "log" function acquires a lock, and the final
destructor of the wrapper releases it. In my implementation, this could
be done in the constructed and destructed functions of the lifetime
handler. If you're using boost::threads, this probably means that you
need a dynamically allocated scoped lock object. Although... if it is a
data member of the LifetimeHandler, and you construct a new
LifetimeHandler for each call -- which is probably the most typical use,
then it should work correctly. Provided you either replace my reference
counted pointer with boost::shared_ptr.
|
The lock may be as simple as a stream manipulator. Here is an example with
boost::threads:
template<class mutex, mutex* m>
class mutex_lock_manipulator
{
public:
mutex_lock_manipulator()
: l_(*m, false)
{}
private:
template<class T, class U>
friend std::basic_ostream<T, U>&
operator<<(std::basic_ostream
{
m.l_.lock();
return s;
}
mutable typename mutex::scoped_lock l_;
};
boost::mutex cout_mtx;
typedef mutex_lock_manipulator<boost::mutex, &cout_mtx> cout_mtx_lock;
And use it like:
cout << cout_mtx_lock() << "we are in critical section here" << endl;
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Oct 20, 2004 9:35 pm Post subject: Re: Custom streambuf class |
|
|
"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote
| Quote: | kanze (AT) gabi-soft (DOT) fr> wrote:
Not directly. This is another thing that would probably best be
handled in the wrapper. The "log" function acquires a lock, and the
final destructor of the wrapper releases it. In my implementation,
this could be done in the constructed and destructed functions of
the lifetime handler. If you're using boost::threads, this probably
means that you need a dynamically allocated scoped lock object.
Although... if it is a data member of the LifetimeHandler, and you
construct a new LifetimeHandler for each call -- which is probably
the most typical use, then it should work correctly. Provided you
either replace my reference counted pointer with boost::shared_ptr.
The lock may be as simple as a stream manipulator.
|
For the general problem of locking a stream, and supposing that your
compiler is standards compliant with regards to the lifetime of
temporaries (something you cannot count on in portable code), then a
manipulator may be a good solution for managing a lock. (It doesn't
always work -- you may want to output information in a loop without
interruption, for example.)
In this specific case, however, you want the lock to also protect the
wrapper -- managing the use counts of the lifetime handler, for example.
In addition, I've had one or two cases where I've actually wanted an
explicit instance of the handler, something like:
LogStreamWrapper w( log( level ) ) ;
w << "Headern" ;
for ( int i = 0 ; i < count ; ++ i ) {
w << "info " << i << ": " ...
}
The important thing in this design is that the instance of the wrapper
defines a log record (which may be more than one line), and that log
records shouldn't be interrupted.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|