Question

I've read several similar questions here that have been answered, but I don't grok yet, so please bear that in mind before closing as duplicate :). I want a simple Log object with a Print() method. If Log is constructed with no parameters, logging is to cout. Otherwise, parameters describe a file to which logging is done.

(I suspect part of the problem is understanding the relationship between all the stream classes.)

When compiled, error is:

Log.cpp:11:23: error: invalid initialization of reference of type ‘std::ofstream& {aka std::basic_ofstream<char>&}’ from expression of type ‘std::ostream {aka std::basic_ostream<char>}’

Log.h:

#ifndef LOG_H
#define LOG_H
#include <string>
#include <fstream>

class Log {

public:
    Log();
    Log(const char*, const char*);

    void Print(const char*  msg,...);

private:
    // instance contains a reference to ostream
    std::ofstream&  output_stream;
};

#endif

Log.cpp:

#include "Log.h"
#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <fstream>

// Constructor w/no parms = log to cout
Log::Log() :
    output_stream(cout)
{}

// Constructor w/parms = log to file
Log::Log(const char* dir, const char* file) {
    string output_file_name = string(dir) + "/" + string(file);
    output_stream.open(output_file_name.c_str(), std::ofstream::out);
}

// Print() sends output to the stream (we'll do printf semantics later)
void
Log::Print(const char* msg,...) {
    output_stream << msg << endl;
}
Was it helpful?

Solution

cout is not of type ofstream, so you cannot bind an ofstream reference to it. output_stream should be type ostream& instead, which will allow it to refer to either cout and a file stream, since ofstream is a subclass of ostream.

Also, in the case that the user provides a filename, you still need something for the reference to refer to, you can't just use it as is. I recommend that you store an actual ofstream object, (or a unique_ptr<ofstream>), and make output_stream refer to it. Make sure you declare the ofstream object before the ostream reference in your class definition, otherwise you will have undefined behavior when you try to bind the reference in the initialization list. Or you can make it a pointer, instead of a reference, and assign it in the body of the constructor.

OTHER TIPS

I suggest shuffling filebufs or other streambufs.

#include <string>
#include <ostream>
#include <fstream>

class Log {

public:
    Log();
    Log(const char*, const char*);

    void Print(const char*  msg,...);

private:
    // instance contains a reference to ostream
    std::ostream  output_stream;
    std::ofstream _file;
};

And the cpp:

#include <iostream>
#include <string>
#include <fstream>

// Constructor w/no parms = log to cout
Log::Log()
    : output_stream(std::cout.rdbuf())
{}

// Constructor w/parms = log to file
Log::Log(const char* dir, const char* file) 
    : output_stream(nullptr)
{
    std::string output_file_name = std::string(dir) + "/" + std::string(file);
    _file.open(output_file_name.c_str(), std::ofstream::out);
    output_stream.rdbuf(_file.rdbuf());
}

// Print() sends output to the stream (we'll do printf semantics later)
void Log::Print(const char* msg,...) {
    output_stream << msg << std::endl;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top