How to Compose Output Streams, So Output Goes Multiple Places at Once

How can I compose output streams, so output goes multiple places at once?

You mention having not found anything in Boost.IOStreams. Did you consider tee_device?

Same string to multiple streams

The "proper" way to do something like this is to have a stream buffer writing to multiple destinations and use this stream buffer via a std::ostream. This way the code looks as if it writing just once but the characters are sent multiple times. Searching for "teebuf Dietmar" will find a few variations on the same theme.

To also comment on your question: Which one of the three alternatives is the fastest depends on the exact expressions you have:

  1. needs to evaluate the involved expressions and perform the conversions three times. Depending on what you actually do this may still be fairly fast.
  2. actually creates and destroys multiple streams and does multiple allocations for std::string. I'd expect this to be slowest.
  3. still creates a stream (which should actually be a std::ostringstream) and allocates some memory. Out of your options I'd expect it to be fastest.

Using a teebuf is probably fastest, at least, when it does some buffering but uses only fixed suze arrays, both for the buffer and the array of stream buffer pointers. Note, that you'll need to override sync() to deal with the buffer in a timely manner, though.

To determine the actual performance you'll need to measure!

Output to two streams simultaneously

There's no built-in function that does exactly what you want; so consider writing one. An interesting aspect of printf is that it's a variadic function; fortunately, the printf family is designed to make it easy to do this sort of thing. Something like:

#include <stdio.h>
#include <stdarg.h>

int stdout_fprintf(FILE * restrict stream, const char * restrict format, ...) {
va_list ap;
va_start(ap, format);
vprintf(format, ap);
int result = vfprintf(stream, format, ap);
va_end(ap);
return result;
}

How to parametrize an output stream?

In your case you may use ternary operator:

std::ostream& ostr = (condition ?
std::cout :
(ofstr.open("file.txt"), ofstr)); // Comma operator also used
// To allow fstream initialization.

How do I output console to multiple streams at once in C#?

You could simply read that stream log it and print it out.

It depends a little on your code if you assign the output stream to the inputstream of the outfile this could be a little harder if you read the content to a buffer that should be a little easier.


About your update I would suggest that you exchange all Console with a custom logging function e.g. a instance of MyLogger (code below) . Which writes your output to the console and to your log file.

class MyLogger {
private FileStream appLogStream;

public MyLogger() {
appLogStream = new FileStream(logFile, FileMode.Append, FileAccess.Write,
FileShare.Read);
appLogStream.WriteLine("Started at " + DateTime.Now);
}

public Write(string msg) {
Console.Write(msg);
appLogStream.Write(msg);
}

public WriteLine(string msg) {
Console.WriteLine(msg);
appLogStream.WriteLine(msg);
}
}

Doubling a stream

You could create the type of your myErr class like this:

class stream_fork
{
std::ostream& _a;
std::ostream& _b;

public:
stream_fork( std::ostream& a, std::ostream& b)
: _a( a ),
_b( b )
{
}

template<typename T>
stream_fork& operator<<( const T& obj )
{
_a << obj;
_b << obj;
return *this;
}

// This lets std::endl work, too!
stream_fork& operator<<( std::ostream& manip_func( std::ostream& ) )
{
_a << manip_func;
_b << manip_func;
return *this;
}
};

Usage:

stream_fork myErr( std::cerr, errorString );
myErr << "Error Message" << std::endl;

How to write data to two java.io.OutputStream objects at once?

Try the Apache Commons TeeOutputStream.

Compose two Transform streams in Node.js

To compose streams, you pipe() the first to the second, but you also have to modify pipe() and unpipe() so they apply to the second stream. This way additional calls to pipe() on the composed stream will use the output of the second stream instead of the first.

function compose(s1, s2) {
s1.pipe(s2);
s1.pipe = function (dest) {
return s2.pipe(dest);
}
s1.unpipe = function (dest) {
return s2.unpipe(dest);
}
return s1;
}

var composed = compose(test(), test());
gulp.src('source/*')
.pipe( composed ) // src -> s1 -> s2
.pipe( ... ); // s2 -> ...

Write simultaneousely to two streams

User laalto is correct, but on Linux, the function you are looking for is called fopencookie. Correcting laalto's example for Linux results in:

int my_writefn(void *cookie, const char *data, int n) {
FILE **files = (FILE **)cookie;
fwrite(data, n, 1, files[0]);
return fwrite(data, n, 1, files[1]);
}

int noop(void) { return 0; }
cookie_io_functions_t my_fns = {
(void*) noop,
(void*) my_writefn,
(void*) noop,
(void*) noop
};

FILE *files[2] = ...;

FILE *f = fopencookie((void *)files, "w", my_fns);

// ... use f as you like ...

When you write to f, the system will execute your my_writefn function passing it the data that was passed to fwrite. To make things easier, you may also want to change the buffering for your file stream to be line oriented:

setvbuf(f, NULL, _IOLBF, 0);

That will buffer up the data passed to fwrite until a newline is output or any data is read from any stream attached to the processes (e.g. stdin). NOTE: you must call sevbuf after fopencookie but before any data is written to the stream.

I use line buffering because I usually use fopencookie to redirect stderr to syslog, or over a network socket, and processing line oriented data is easier and more efficient.



Related Topics



Leave a reply



Submit