5

I am trying to create a Log class for my project at school. It needs to either be able to write information to the stdout or to a file depending on the parameters it is passed. I was looking into how to do this and I stumbled upon a thread with a similar question here: Obtain a std::ostream either from std::cout or std::ofstream(file)

The only difference between this thread and my own is that I want to do it inside of a class. Looking at the solution though they use std::ostream out(buf) and construct the ostream on the fly with buf. How can i declare this properly in my Log class to be able to construct the "out" object only once i enter my Log constructor?

I took a quick stab at it below but I am not sure if it is correct or if I am on the right track. Appreciate any help, thanks.

EDIT: I want to be able to do out << "Some string" << endl; after i get this Log class working properly.

EDIT2: An error I am now receiving with the new code below error : 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char, _Traits = std::char_traits<char>]' is protected

// log.h
#include <string>
#include <fstream>

#ifndef LOG_H_
#define LOG_H_

class Log 
{
    public:
        enum Mode { STDOUT, FILE };

        // Needed by default
        Log(const char *file = NULL);
        ~Log();

        // Writing methods
        void write(char *);
        void write(std::string);
    private:
        Mode mode;
        std::streambuf *buf;
        std::ofstream of;
        std::ostream out;
};

#endif


// log.cpp
#include "log.h"
#include <iostream>
#include <stdlib.h>
#include <time.h>

Log::Log(const char *file)
{
    if (file != NULL)
    {
        of.open(file);
        buf = of.rdbuf();
        mode = FILE;
    }
    else
    {
        buf = std::cout.rdbuf();
        mode = STDOUT;
    }

    // Attach to out
    out.rdbuf(buf);
}

Log::~Log()
{
    if (mode == FILE)
        of.close();
}

void Log::write(std::string s)
{
    out << s << std::endl;
}

void Log::write(char *s)
{
    out << s << std::endl;
}
Community
  • 1
  • 1
MasterGberry
  • 2,800
  • 6
  • 37
  • 56
  • 1
    I would add `char *file = NULL` or something similar, so that you don't have to pass any arguments in case of the `Log` to stdout. - And give your default arguments in the header file, not the implementation [you need them in any code that calls Log(), which is probably not your "log.cpp" or whatever it may be called. – Mats Petersson Feb 08 '13 at 21:29
  • Good suggestion! Added that in. Oh, ok i didn't remember that default arguments go in the header, not implementation. Thanks, been a while since I have done any serious C++ programming – MasterGberry Feb 08 '13 at 21:31
  • 1
    Other than that, the code looks about right WHat happens when you try it? [Just a thought by the way, do you really need both a "mode" and a "char *file" - why not use "char *file" to indicate whether the output should be a file or stdout - that way, you don't have to have two arguments]. – Mats Petersson Feb 08 '13 at 21:34
  • Another great suggestion, damn i need to get out of the "rust" zone haha – MasterGberry Feb 08 '13 at 21:38

1 Answers1

4

You create tmp with std::ostream tmp(buf); and store the address of it in out with this->out = &tmp;. However, tmp will go out of scope at the end of the constructor and the pointer will no longer be pointing at a valid object.

What you should do instead is make out not a std::ostream* but simply a std::ostream:

std::ostream out;

Then in your constructor, once you've got the buf ready, you can give it to out by doing out.rdbuf(buf);.


Response to edit:

The std::ostream doesn't have a default constructor - it has to take a buffer pointer. My mistake. However, the fix is simple. Use your constructor's member initialization list to pass a null pointer (nullptr in C++11, 0 or NULL in C++03):

Log::Log(const char *file)
  : out(nullptr)
{
  // ...
}
Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324