I think I'd do things a bit differently. I've probably made this just a bit more elaborate than necessary -- I'm afraid I may have gotten a little carried away with trying to put new C++11 features to good use. Anyway, on with the code:
#include <streambuf>
#include <fstream>
#include <vector>
#include <iostream>
#include <initializer_list>
namespace multi {
class buf: public std::streambuf {
std::vector<std::streambuf *> buffers;
public:
typedef std::char_traits<char> traits_type;
typedef traits_type::int_type int_type;
buf(std::vector<std::ofstream> &buf) {
for (std::ofstream &os : buf)
buffers.push_back(os.rdbuf());
}
void attach(std::streambuf *b) { buffers.push_back(b); }
int_type overflow(int_type c) {
bool eof = false;
for (std::streambuf *buf : buffers)
eof |= (buf -> sputc(c) == traits_type::eof());
return eof ? traits_type::eof() : c;
}
};
class stream : public std::ostream {
std::vector<std::ofstream> streams;
buf outputs;
public:
stream(std::initializer_list<std::string> names)
: streams(names.begin(), names.end()),
outputs(streams),
std::ostream(&outputs)
{ }
void attach(std::ostream &b) {
outputs.attach(b.rdbuf());
}
};
}
int main() {
multi::stream icl({"c:\\out1.txt", "c:\\out2.txt"});
icl.attach(std::cout);
icl << "Blah blah blah" << std::endl;
}
As you can see, this already accepts manipulators (which should work with any manipulator, not just std::endl
). If you want to write to multiple files (things that could/can be opened as fstreams) you can specify as many of those names in the constructor as you like (within the limits imposed by your system, of course). For things like std::cout
and std::cerr
for which you don't necessarily have a file name, you can use attach
as you originally intended.
I suppose I should add that I'm not entirely happy with this as-is. It'd take some fairly serious rewriting to do it, but after some thought, I think the "right" way would probably be for multi::stream
's ctor to be a variadic template instead, so you'd be able to do something like: multi::stream icl("c:\\out1.txt", std::cout);
, and it would sort out how to use each parameter based on its type. I may update this answer to include that capability some time soon.
As far as the second question goes, I've written another answer that covers the basic idea, but is probably a bit overly elaborate, so the part you care about may kind of get lost in the shuffle, so to speak -- it has quite a bit of logic to deal with line lengths that you don't really care about (but does produce each output line with a specified prefix, like you want).
std::vector<>
is somewhat both) of references? That restriction being, you can't? You may want to consider that while working on your design. – Janeejaneenstreambuf
that dispatches the output. – Wontedstd::unique_ptr<std::ostream>
as the container contents, then create the appropriate stream types on the fly based on what is being registered. If the streams are externally "owned" I would use bare pointers and be VERY public about how the registered streams cannot be destroyed while still registered with the manager. – Janeejaneen