Output to two streams

Mar 7, 2010 at 3:25pm
I was wondering if there was a way I could have a stream for logging output to stdout and a file.

The idea would be that I could just do this:
log << "Event occurred\n";
and it would write it to two streams (std::cerr and a file).

I don't see how that could be done, though; so I was thinking I'd have to use a function, like
log("%s\n", "Event occurred");

Mar 7, 2010 at 3:34pm
You can make your own trick class and overload <<
Mar 7, 2010 at 3:41pm
Thanks Bazzy. I'll try something like that.
Last edited on Mar 7, 2010 at 4:15pm
Mar 7, 2010 at 4:04pm
what are the odds of that I was just pondering this same idea not even an hour ago.
Mar 7, 2010 at 4:10pm
I don't know what to use as the type of the return value and the parameter. I thought about using a template; but then won't I have to do something like log <std::string><< "Hello!";?

So what do I replace <type> with here?
1
2
3
4
5
<type> operator<<(<type> out)
{
    std::cerr << out;
    logfile << out;
}


It works with polymorphism but I can't do log << "Hello" << ", world!";; I would have to do log << "Hello, world!"; instead, whereas with std::cerr you can do the former. How can I do this?

@Seraphimsan,
Wow, really? What are you writing? I need a logging function for my emulator so that later on people will be able to tell exactly what caused that damn triple fault.
Last edited on Mar 7, 2010 at 4:31pm
Mar 7, 2010 at 4:43pm
You might have to do some magic to make stream manipulators work.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class dual_stream {
public:
    dual_stream(std::ostream& os1, std::ostream& os2) : os1(os1), os2(os2) {}

    template<class T>
    dual_stream& operator<<(const T& x) {
        os1 << x;
        os2 << x;
        return *this;
    }
private:
    std::ostream& os1;
    std::ostream& os2;
};
Last edited on Mar 7, 2010 at 4:43pm
Mar 7, 2010 at 4:49pm
@R0mai,
Thanks for that, but why do you return a reference to class dual_stream?
Mar 7, 2010 at 4:51pm
So you can chain operator<< calls, just like for std::ostream :

1
2
dual_stream ds(/*two streams*/);
ds << "xyz " << 4 << " " << 3.14 << '\n';
Mar 7, 2010 at 4:53pm
Oh, I see... thank you :)
Mar 7, 2010 at 5:24pm
Nice *takes note of all this* and chrisname I'm writing a video game. But I'm likely going to put that in a header in my includes folder for MinGW...cause I often have use of an error reporting system.
Mar 7, 2010 at 5:40pm
Like I said, I'm using it for my emulator, e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    /* Load the BIOS into (real) memory */
    std::ifstream BIOS;
    BIOS.open(biosfile);
    
    if (BIOS.is_open()) {
        std::string line;

        log << em64::timestamp() << "Found BIOS file \"" << biosfile << "\"\n";
        
        while (!BIOS.eof()) {
            std::getline(std::cin, line);
            buffer += line;
        }
    
        BIOS.close();
    } else {
        log << em64::timestamp() << "Couldn't find/open file \"" << biosfile
            << "\"; check the file exists in the current directory and that you"
            << "have permission to read it.\n";

        return 1;        
    }
    
    log << em64::timestamp() << "Loaded BIOS into memory; running...\n";


This is what the class looks like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    class outlog {
        private:
            std::ofstream logfile;
        public:
            outlog(const char* file)
            {
                logfile.open(file);
        
                if (!(logfile.is_open())) {
                    std::cerr << "[]: Couldn't open file \"" << file << "\" for logging.\n";
                    this->~outlog(); /* Destroy the object */
                }
            }

            ~outlog()
            {
                if (logfile.is_open())
                    logfile.close();
            }

            template <class T>
            outlog& operator<<(const T& out)
            {
                std::cerr << out;
                logfile << out;

                return *this;
            }
    };
Last edited on Mar 7, 2010 at 5:40pm
Mar 7, 2010 at 7:06pm
Boost tee.
http://www.boost.org/doc/libs/1_42_0/libs/iostreams/doc/functions/tee.html

You could write your own if you want, but it is a bit of a pain [to get it right]...
Last edited on Mar 7, 2010 at 7:06pm
Mar 7, 2010 at 7:16pm
Well, the method above works very well for me. I'm not sure of the performance hit of calling operator<< 3 times on everything (ofstream and ostream's operator<< overloads are called too) but R0mai's method is easy to understand and extensible. Thanks for the suggestion, but I think I'm going to stick to the above. If I needed (for whatever reason) more than one logfile, it would be very easy to add one...

Thanks anyway, Duoas.

Edit: Boost isn't standard yet, anyway, is it? A design goal of the program is to have as few unreasonable* dependencies as possible (at the moment, three (SeaBIOS, VGABIOS and SDL)); and I won't add anything to the list unless I have to. As the class in place already does everything I need, I don't see it as being necessary to add Boost to my list of dependencies.

*an "unreasonable dependancy" being one that you couldn't reasonably expect someone wanting to compile your program to have.
Last edited on Mar 7, 2010 at 7:23pm
Mar 7, 2010 at 7:26pm
I would say boost is almost standard. A lot of stuff from boost is getting implemented in C++0x.
Topic archived. No new replies allowed.