By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,635 Members | 2,085 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,635 IT Pros & Developers. It's quick & easy.

defining a custom output facility

P: n/a
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::ostringstream 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

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #1
Share this Question
Share on Google+
12 Replies


P: n/a
On Tue, 13 Jan 2004 20:11:51 GMT, Matt Garman <fa**@not-real.bogus> wrote:
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::ostringstring 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

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #2

P: n/a
Matt Garman wrote:
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::ostringstream 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...m#lexical_cast

Jul 22 '05 #3

P: n/a
Matt Garman wrote:
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.

--
Andrew
Jul 22 '05 #4

P: n/a
On Tue, 13 Jan 2004 20:11:51 GMT in comp.lang.c++, Matt Garman
<fa**@not-real.bogus> was alleged to have written:
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.d.../c++/iostream/


Jul 22 '05 #5

P: n/a

"David Harmon" <so****@netcom.com> wrote in message
news:40**************@news.west.earthlink.net...
On Tue, 13 Jan 2004 20:11:51 GMT in comp.lang.c++, Matt Garman
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.d.../c++/iostream/


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

Jonathan
Jul 22 '05 #6

P: n/a
On Tue, 13 Jan 2004 20:44:47 GMT, Andrew Taylor <at*****@its.to> wrote:
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

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #7

P: n/a
Matt Garman wrote:

On Tue, 13 Jan 2004 20:44:47 GMT, Andrew Taylor <at*****@its.to> wrote:
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?


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.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #8

P: n/a
On Thu, 15 Jan 2004 18:56:43 +0100, Karl Heinz Buchegger
<kb******@gascad.at> wrote:
So in the example I posted here, I could create my own endl function
to achieve similar functionality, right?


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

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #9

P: n/a
Matt Garman wrote:

On Thu, 15 Jan 2004 18:56:43 +0100, Karl Heinz Buchegger
<kb******@gascad.at> wrote:
So in the example I posted here, I could create my own endl function
to achieve similar functionality, right?
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 :)


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/on...ation/Message/
http://www.codeproject.com/vcpp/stl/zipstream.asp

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #10

P: n/a
On Fri, 16 Jan 2004 13:02:45 +0100, Karl Heinz Buchegger
<kb******@gascad.at> wrote:
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

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #11

P: n/a
Matt Garman wrote:

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.
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++!)


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.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #12

P: n/a
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::ofstream file1("file1.txt");
std::ofstream file2("file2.txt");
std::ofstream 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::ostream of(&sbvec);

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

return 0;
}

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Jul 22 '05 #13

This discussion thread is closed

Replies have been disabled for this discussion.