Simple C++ logging class - ostream reference initialization - c++

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;
}

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.

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;
}

Related

C++ convert int to string inline

My problem seems to be very basic but I could not find a solution for it. I need to write a code which helps debugging by reporting the line and location of exception throwing. The problem is that __LINE__ is an int value and I have problem with its conversion into string in the following code where std::string(line) is used:
#pragma once
#include <stdexcept>
#include <cstring>
class CRuntime_error_line: public std::runtime_error
{
public:
CRuntime_error_line(const char * msg, const char * file,int line)
:runtime_error(std::string(msg)+" #"+":"+std::string(line)){}
};
#define runtime_error_line(msg) CRuntime_error_line(msg,__FILE__,__LINE__)
Seems std::string(line) cannot convert int to string and other solutions suggested online cannot be implemented inline and I don't know how to call a base constructor in second line!
compiler output:
log.h: In constructor ‘CRuntime_error_line::CRuntime_error_line(const
char*, const char*, int)’: log.h:10:124: error: invalid conversion
from ‘int’ to ‘const char*’ [-fpermissive] CRuntime_error_line(const
char * msg, const char * file,int
line):runtime_error(std::string(msg)+" #"+":"+std::string(line)){}
(Using g++ and linux environment)
edit:
the macro is supposed to be called this way:
throw runtime_error_line("Invalid somethihng ...!");
As is suggested by Borgleader std::to_string is your solution. It will also construct a temporary std::string for you, so there's no need to construct a temporary string from msg:
#pragma once
#include <stdexcept>
#include <cstring>
#include <string> // Add this to support std::to_string
class CRuntime_error_line: public std::runtime_error
{
public:
CRuntime_error_line(const char* msg, const char* file, int line)
: runtime_error(msg + " #:"s + std::to_string(line)){} // Use std::to_string here
};
#define runtime_error_line(msg) CRuntime_error_line(msg, __FILE__, __LINE__)
Without C++11 you can still do this it's just not as clean:
#pragma once
#include <stdexcept>
#include <cstring>
#include <sstream> // Use to include std::ostringstream
class CRuntime_error_line: public std::runtime_error
{
public:
CRuntime_error_line(const char* msg, const char* file, int line)
: runtime_error(static_cast<std::ostringstream&>(std::ostringstream() << msg << " #:" << line).str()){} // Use std::ostringstream here
};
#define runtime_error_line(msg) CRuntime_error_line(msg, __FILE__, __LINE__)
int this case may be better :
#define STRING_DEFINE1(x) #x
#define STRING_DEFINE(x) STRING_DEFINE1(x)
...
CRuntime_error_line(msg,__FILE__,STRING_DEFINE(__LINE__))
The simplest thing I can think of would be to write a to_string yourself:
#include <sstream>
std::string to_string(int i)
{
std::ostringstream os;
os << i;
return os.str();
}
Then call it as others have suggested.

const char* usage in constructor

#include "Board.hpp"
#include <iostream>
using namespace std;
Board::Board (const char* filename){
filename = "puz1.txt";
Board::fin (filename);
if(!fin) fatal("Error in opening the file");
}
This is my cpp file...my hpp file is:
#ifndef BOARD_H
#define BOARD_H
#include <iostream>
using namespace std;
#include "tools.hpp"
#include "square.hpp"
class Board {
private:
SqState bd[81];
ifstream fin;
public:
Board(const char* );
ostream& print(ostream& );
};
inline ostream& operator <<(ostream& out, Board& b) { return b.print(out);}
#endif //Board.hpp
I got the below errors while I compile:
Error at line in cpp filename = "puz1.txt".
and error is:
const char* shadows a //parameter.
Error at line in cpp Board::fin (filename);
and error is:
no match call to //(std::basic_ifstream})
How do I fix them?
You can only initialize fin in the contructor initialization list. You also need to #include <fstream>. This would work:
Board::Board (const char* filename): fin(filename)
{
....
}
It is unclear why you are setting filemane to something different to what is passed in the constructor. If you want a default parameter, use
Board::Board (const char* filename="puz1.txt"): fin(filename) {}
About the first error:
filename = "puz1.txt";
You are supposed to pass the filename as an argument, not to assign it there. If you just need to use "puz1.txt" then use than instead of filename.
The second error:
Board::fin (filename);
You can't initialize the ifstream object like that. Simply call open().
fin.open("puz1.txt");
if(fin.is_open()) // you can pass additional flags as the second param
{
}

C++ class termination

I have declared a class and instantiated a class in one and expected it to fire
~CLog();
But for some reason, it does not. Does anybody see any obvious errors why this could happen?
I declared the class within a void that ends, so it SHOULD fire, I think.
I do not destroy the class explicitely, but I simply expected it to go out of out scope automatically and terminate.
#pragma once
#include <fstream>
using namespace std;
class CLog
{
public:
CLog(wstring filename);
~CLog();
void WriteString(wstring uString);
private:
wofstream m_stream;
wstring m_sPath;
};
#include "log.h";
#include "strhelper.h"
#include <fstream>
#include <iostream>
#include <string>
#include <locale>
#include <codecvt>
wstring m_sText=L"";
wstring m_sPath=L"";
CLog::CLog(wstring uPath)
{
m_sPath=uPath;
}
void CLog::WriteString(wstring uString)
{
m_sText+=uString;
m_sText+=L"\n";
}
CLog::~CLog()
{
if (FileExists(m_sPath))
{
DeleteFile(m_sPath);
}
//open for appending
m_stream.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t,0x10ffff,std::generate_header>));
m_stream.open(m_sPath,fstream::in | fstream::out | fstream::app);
m_stream << m_sText.c_str();
m_stream.close();
}
I am using Clog like this
void foo() {
wstring sLogPath;
sLogPath=GetSpecialFolderDesktop() + L"\\load.log";
CLog *pLog = new CLog(sLogPath);
pLog->WriteString(L"Something);
}
I am using VC2010.
You are instantiating dynamically the CLog. In that case, you need to delete it explicitly.
If you create it on the stack Clog log(sLogPath), the destructor will be called when the object goes out of scope.

error: ‘logFileObj’ does not name a type

I have a file named global.h whose contents are:
#define DEPTH 10
#define LOGGING //to log the progress of the program.
#ifdef LOGGING
#include <fstream>
#include <string>
extern std::string logFileName;
extern std::ofstream logFileObj;
#endif
Also main.cpp:
#include "global.h"
using namespace std;
#ifdef LOGGING
string logFileName = ".log";
ofstream logFileObj;
logFileObj.open(logFile); //line 13
logFileObj<<"depth: "<<DEPTH<<endl; //line 14
#endif
I am constantly getting the following error in compilation:
src/main.cpp:13:1: error: ‘logFileObj’ does not name a type
src/main.cpp:14:1: error: ‘logFileObj’ does not name a type
Any help appreciated.
C++ does not allow operation outside function. C++ allows you define variable globally but you need to put operations inside functions.
If I read your question correctly, you just need a function and call it when you need to:
#include <fstream>
#include <utility>
#include <string>
template<typename T>
void WriteLog(const std::string& log_file_name, const std::string& prefix, const T& data)
{
std::ofstream log_file_handler(log_file_name.c_str(), std::ios::app); // if you use C++11, you could use string directly
log_file_handler << prefix << data << std::endl;
}
usage:
WriteLog<int>("app.log", "depth:", DEPTH);

Store pointer to istream and ostream in a class C++

game.h
#ifndef GAME_H
#define GAME_H
#include <string>
#include <iostream>
#include "piece.h"
using namespace std;
class Game
{
private:
string white;
string black;
string title;
istream* in;
ostream* out;
public:
Game();
Game(istream&, ostream&);
void display(Colour, short);
};
#endif
game.cpp
#include <iostream>
#include <string>
#include <sstream>
#include "game.h"
#include "board.h"
#include "piece.h"
using namespace std;
Game::Game()
{
//nothing
}
Game::Game(istream& is, ostream& os)
{
in = is;
out = os;
}
void Game::display(Colour colour, short moves)
{
//out << "a";
}
I'm trying to use the istream and ostream in other parts of my class but I can't because g++ won't let me reference is to in. Any ideas?
You simply want a reference variable, not a pointer.
class Game
{
private:
...
istream& in;
ostream& out;
public:
Game(istream&, ostream&);
};
Game::Game(istream& is, ostream& os)
: in( is ),
out( os )
{ }
The existing code compiles because of a couple language quirks:
istream / ostream are convrtible to void* to allow you to check their error status as in
if( in ) { do_something( in ); }
your compiler apparently allowed void* to be converted to ostream* (I believe in error, and you should at least get a warning from this).
You should deference the pointer:
*out << "a";
For more convenient use, to not deference the pointers each time, and for more readability you can use references instead of pointers.
class Game
{
// ...
std::istream& in; // notice explicit namespace std::
std::ostream& out;
// ...
};
Then you can write:
out << "a";
Plus, it is not a good habit to do so:
using namespace std;
This way you are exposing the names of std namespace.
is is a reference not a pointer, therefore if you want to store a pointer you need to use the address operator in = &is;
But please realize that is can cease to exist immediately after the method call, therefore you can easily end up with an invalid pointer. Make sure that you at least document this fact.
If you store pointers, you need to dereference them, like *in or *out << ....