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; } 
4
  • 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. Commented Feb 8, 2013 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 Commented Feb 8, 2013 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]. Commented Feb 8, 2013 at 21:34
  • Another great suggestion, damn i need to get out of the "rust" zone haha Commented Feb 8, 2013 at 21:38

1 Answer 1

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) { // ... } 
Sign up to request clarification or add additional context in comments.

6 Comments

Yeah, i couldn't do it earlier because of the silly time limit :P fear not.
I am having some issues compiling my new code. I have modified my post above with the lastest version of my code and the error, can you have a quick look please? I'm stumped. Also can't seem to get the @sftrabbit to work either :/
@PeterWood I feel like this error is a result of the code that I was provided by him...although I could be wrong.
@MasterGberry I've responded to your edit. It was my mistake.
@MasterGberry Just incase you've seen my edit already, I've edited once again with a much simpler and safer solution.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.