defining a custom output facility

M

Matt Garman

I'd like to create a "custom output facility". In other words, I want
an object whose use is similar to std::cout/std::cerr, but offers more
flexibility. Instead of simply writing the parameter to stdout/stderr,
I'd like it to write to stdout, to a file, and/or call a logging
function.

So my output function might look something like this:

OutputFacility& OutputFacility::put(const std::string& s) {
cout << s; // print to stdout
write_to_logfile(s); // also calling logging function
return *this;
}

Now I can define the << operator to be an alias for put().

My question is: do I have to define put() for every type of input I
expect to print? I.e., I'd like this to work with all primitive types,
int, char, double, float, etc.

Right now I'm thinking that the OutputFacility class could have a
std::eek:stringstream as a private member; every version of
OutputFacility::put() would just in turn call the ostringstream put()
method, THEN call the actual output functions (e.g. write_to_logfile()).

It seems like there should be a simpler method of defining my put
method, i.e. instead of overloading it and defining it multiple times,
if I could just define it once with a "magical" parameter that means
"take anything that can be converted to a string".

Thanks for any advice!
Matt
 
M

Matt Garman

OutputFacility& OutputFacility::put(const std::string& s) {
cout << s; // print to stdout
write_to_logfile(s); // also calling logging function
return *this;
}

My question is: do I have to define put() for every type of input I
expect to print? I.e., I'd like this to work with all primitive types,
int, char, double, float, etc.

It looks like I can get away with the following:

template <class T> OutputFacility& OutputFacility::put(const T& x) {
std::eek:stringstring os;
os << x;
cout << os.str();
write_to_logfile(os.str());
return *this;
}

This seems to get me "close" to what I want to do. It works for
strings, c-style strings (char arrays), and integers. But it doesn't
recognize std::endl and it truncates the fraction part of a float.

Any thoughts?

Thanks again!
Matt
 
J

Jeff Schwab

Matt said:
I'd like to create a "custom output facility". In other words, I want
an object whose use is similar to std::cout/std::cerr, but offers more
flexibility. Instead of simply writing the parameter to stdout/stderr,
I'd like it to write to stdout, to a file, and/or call a logging
function.

So my output function might look something like this:

OutputFacility& OutputFacility::put(const std::string& s) {
cout << s; // print to stdout
write_to_logfile(s); // also calling logging function
return *this;
}

Now I can define the << operator to be an alias for put().

My question is: do I have to define put() for every type of input I
expect to print? I.e., I'd like this to work with all primitive types,
int, char, double, float, etc.

Right now I'm thinking that the OutputFacility class could have a
std::eek:stringstream as a private member; every version of
OutputFacility::put() would just in turn call the ostringstream put()
method, THEN call the actual output functions (e.g. write_to_logfile()).

It seems like there should be a simpler method of defining my put
method, i.e. instead of overloading it and defining it multiple times,
if I could just define it once with a "magical" parameter that means
"take anything that can be converted to a string".

Thanks for any advice!
Matt


template< typename T >
OutputFacility&
operator << ( OutputFacility& out, T const& out )
{
return out.put( lexical_cast< std::string >( s ) );
}

http://www.boost.org/libs/conversion/lexical_cast.htm#lexical_cast
 
A

Andrew Taylor

Matt said:
This seems to get me "close" to what I want to do. It works for
strings, c-style strings (char arrays), and integers. But it doesn't
recognize std::endl and it truncates the fraction part of a float.

std::endl is specific to ostreams. It is not declared as a newline, but
rather a function that writes a newline, then flushes the ostream.
Since your OutputFacility class isn't an ostream, it won't work.

I don't see the float truncation, it works for me. Post a compilable
example.
 
D

David Harmon

I'd like to create a "custom output facility". In other words, I want
an object whose use is similar to std::cout/std::cerr, but offers more
flexibility. Instead of simply writing the parameter to stdout/stderr,
I'd like it to write to stdout, to a file, and/or call a logging
function.

C++ streams are constructed as two layers. The upper layer, derived
from iostream, is concerned with formatting objects to and from a
character representation. The lower level, derived from streambuf, is
concerned with transferring those characters to and from some
receptacle.

It is usually a mistake to try to create a customized stream class by
deriving from the stream classes. What you want instead is to derive
from streambuf, and construct a stream using your custom streambuf.

See the examples on Dietmar Kuehl's web site
http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/
 
J

Jonathan Turkanis

It is usually a mistake to try to create a customized stream class by
deriving from the stream classes. What you want instead is to derive
from streambuf, and construct a stream using your custom streambuf.

See the examples on Dietmar Kuehl's web site
http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/

I've written a library to make defining new streambufs easy. See
http://groups.yahoo.com/group/boost/files/iostreams_lib.zip (you have
to sign up with boost to access it, I think.)

Jonathan
 
M

Matt Garman

std::endl is specific to ostreams. It is not declared as a newline,
but rather a function that writes a newline, then flushes the ostream.
Since your OutputFacility class isn't an ostream, it won't work.

That makes sense, I forgot that endl is actually a function.

So in the example I posted here, I could create my own endl function to
achieve similar functionality, right?
I don't see the float truncation, it works for me. Post a compilable
example.

I was mistaken; it appears to be a formatting issue. In other words, I
tried it with more floats, and found that it's only printing a certain
number of digits (regardless of the decimal location).

Thank you!
Matt
 
K

Karl Heinz Buchegger

Matt said:
That makes sense, I forgot that endl is actually a function.

So in the example I posted here, I could create my own endl function to
achieve similar functionality, right?

You could.
But you are still doing the whole thing the wrong way.
The right way is to write a new stream buffer class
and make a standard stream use it.
 
M

Matt Garman

But you are still doing the whole thing the wrong way. The right way
is to write a new stream buffer class and make a standard stream use
it.

I guess I don't really understand the underlying principle(s) behind
this method. I looked over the example on Dietmar Kuehl's website (as
suggested in another post), but didn't really "get it". I think I'm
missing some more fundamental concept(s) here :)

Or perhaps I'm looking at it too much from a "vanilla" C perspective. I
mean, if I was doing this in C, I'd probably just have a function (or
possibly even a #define macro) that had a printf() like syntax, but does
the job of printf() plus writes to a logfile, to a window, etc.

I certainly want to do it the right way, though! Does anyone happen to
have any more links that explain this in greater detail?

Thanks again!
Matt
 
K

Karl Heinz Buchegger

Matt said:
I guess I don't really understand the underlying principle(s) behind
this method. I looked over the example on Dietmar Kuehl's website (as
suggested in another post), but didn't really "get it". I think I'm
missing some more fundamental concept(s) here :)

The thing is:

The stream object itself is responsible for proper formatting.
The stream buffer object inside the stream is reponsible for bringing
the already formatted data on its way, it's the transportation layer.

You want to change a string in the behaviour of the transportation
layer, thus your approach should be to write a customized stream buffer
and make the stream use that instead of the one it was born with.
Or perhaps I'm looking at it too much from a "vanilla" C perspective. I
mean, if I was doing this in C, I'd probably just have a function (or
possibly even a #define macro) that had a printf() like syntax, but does
the job of printf() plus writes to a logfile, to a window, etc.

If the system is designed flexible (with C++ it is, with C it is not), then
there would be some sort of hook in printf, which allows you to get a grasp
at the string printf produces internally before printf sends this string to the
device. In this way you don't need to change any printf calls but just
hook into it, and do whatever you want with the printf generated string.

With printf this is not possible, with C++ streams it is. In fact it
is specifically designed to be that way.
I certainly want to do it the right way, though! Does anyone happen to
have any more links that explain this in greater detail?

http://www.google.com
"streambuf custom"

http://cpptips.hyperformix.com/cpptips/tee_strm
http://www.phenix.bnl.gov/~phoncs/oncs/code_documentation/Message/
http://www.codeproject.com/vcpp/stl/zipstream.asp
 
M

Matt Garman

You want to change a string in the behaviour of the transportation
layer, thus your approach should be to write a customized stream
buffer and make the stream use that instead of the one it was born
with.

Okay, I think I'm starting to get it :) Thank you!

Since I want my custom output facility to write to stdout AND a log (or
window or whatever), does that mean that my custom streambuf's
implementation will write to stdout? It seems like I would be
duplicating what the standard library already does. (In other words, is
there a way to say "do what you already do, AND do these other things"?)

Let me re-iterate just to make sure my understanding is correct:

- stream object (e.g. cout) formats data
- stream object passes formatted data to stream buffer
- stream buffer object delivers data to its destination (e.g.
stdout, logfile, window, etc)
there would be some sort of hook in printf, which allows you to get a
grasp at the string printf produces internally before printf sends
this string to the device. In this way you don't need to change any
printf calls but just hook into it, and do whatever you want with the
printf generated string.

That's exactly what sprintf() (and snprintf() on some systems)
does---formats a string and puts it in a buffer. But that opens you up
to all kinds of potential buffer overrun problems. It's a lot of work
to make sure this is done safely. (That's why I'm using c++!)

Thanks again for your help and patience,
Matt
 
K

Karl Heinz Buchegger

Matt said:
Since I want my custom output facility to write to stdout AND a log (or
window or whatever), does that mean that my custom streambuf's
implementation will write to stdout?

typically yes.
But wait: when you tell the stream object to use a different stream
buffer, you get a hold on the stream buffer actually used by the
stream. So you simply could forward all output to the original stream
buffer AND process it on your own.
Let me re-iterate just to make sure my understanding is correct:

- stream object (e.g. cout) formats data
- stream object passes formatted data to stream buffer
- stream buffer object delivers data to its destination (e.g.
stdout, logfile, window, etc)
yep.


That's exactly what sprintf() (and snprintf() on some systems)
does---formats a string and puts it in a buffer. But that opens you up
to all kinds of potential buffer overrun problems. It's a lot of work
to make sure this is done safely. (That's why I'm using c++!)

OK.
So with that analogy an output operation in a stream does something
like that:

void DoOutput( ..... )
char buffer[whatever];
sprintf( buffer, ..... );
puts( buffer );
}

Throughout your program you always use DoOutput() instead of printf().
If you need to retarget the output, you add the retargeting by
replacing the puts() with something else.
In the above, the puts() would be the equivalent to the stream buffer
object (and of course the whole thing wrapped in a class with some
nicer syntax for using and getting rid of the buffer overflow problem,
etc. But that's not the point right now). For retargeting the output
you replace the stream buffer with something else. And in the same
sense as in the DoOutput() solution the caller of this function doesn't
notice anything of the retargeting, the user of a stream object doesn't
notice anything when the stream buffer is replaced. For him everything
stays the same, even custom operator<< or operator>> work as they ever did.
Only the output is retargeted to somwehere else.
 
M

Matt Garman

Okay, thank you for all the verbose feedback! I think I finally
understand the concepts.

So what if I were to take it a step further, and say that my custom
output facility should be able to write to any number of media? In
other words, combine a streambuf with a vector?

I did just that in the code below (tested with g++ under Linux). Is
what I did the "best" way to solve that problem?

Note that I'd actually write a convenience wrapper class for ostream as
well, just to easily facilitate adding and removing streambufs.

Thanks again for all the help!
Matt



#include <iostream>
#include <fstream>
#include <streambuf>
#include <vector>

class streambufvec : public std::streambuf {
public:
typedef std::char_traits<char> traits_type;
typedef traits_type::int_type int_type;
typedef std::vector< std::streambuf* > streambufvec_t;

streambufvec()
{ streambufs.push_back(std::cout.rdbuf()); }

streambufvec(streambufvec_t sbvec)
{ streambufs.insert(streambufs.end(),
sbvec.begin(), sbvec.end()); }

streambufvec(std::streambuf* sb)
{ streambufs.push_back(sb); }

virtual ~streambufvec()
{ streambufs.clear(); }

void push_back(std::streambuf* sb)
{ streambufs.push_back(sb); }

protected:
inline int_type overflow(int_type c)
{
streambufvec_t::iterator ii;
for (ii=streambufs.begin(); ii!=streambufs.end(); ++ii) {
if (traits_type::eof() == (*ii)->sputc(c)) {
return traits_type::eof();
}
}
return c;
}

private:
streambufvec_t streambufs;
};


int main(int argc, char** argv)
{
std::eek:fstream file1("file1.txt");
std::eek:fstream file2("file2.txt");
std::eek:fstream file3("file3.txt");

streambufvec sbvec;
sbvec.push_back(file1.rdbuf());
sbvec.push_back(file2.rdbuf());
sbvec.push_back(file3.rdbuf());

sbvec.push_back(std::cerr.rdbuf());

std::eek:stream of(&sbvec);

of << "Hello, world!" << std::endl;

return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top