What i'm trying to do is:
I want to redirect my error message erither to std::cerr or to a file
depending upon the command-line argument. If there is no log file provided then the program should output the error message on screen.
Here is my approach:
class A {
void SetLogFileStream(T& err_outstream);
};
//main.cpp
A a;
std::string filename;
T1* t1;
if(argc>2) {
filename = argv[1]; //if provided by command line
std::ofstream fp(filename);
t1 = &fp;
}
else {
std::ostream* err_outstream = &std::cerr;
t1 = err_outstream;
}
a.SetLogFileStream(t1);
what should be the argument type of the function SetLogFileStream
or the type T1
so that i can pass a pointer either to file or to std::cerr
No. But the reverse is true. You can pass an std::ofstream to a function expecting an std::ostream&, or an std::ofstream* to a function expecting an std::ostream*. So your function should accept either an std::ostream ref or pointer.
Declare the method as this:
class A {
void SetLogStream(std::ostream& err_outstream);
};
There are several problems with your code. The file stream opened gets out of scope and is destroyed. You need to fix it like this:
std::ofstream f; // <-- this have to remain in scope while you use it for 'a'
A a;
if(args > 2) {
f.open(argv[1]);
a.SetLogStream(f);
} else {
a.SetLogStream(cerr);
}
The type should be std::ostream. It woun't work exactly as you've coded it because your function parameter is a reference and your argument is a pointer. But fix that (either way) and it will work.
Related
I'm trying to read a file and store it in a protected variable. All methods are in the same class.
class A: public B
{
public:
//method declarations
protected:
string d;
};
void A::l(std::string filename)
{
ifstream ifs;
ifs.open(filename);
string d { istreambuf_iterator<char> {ifs}, istreambuf_iterator<char> {} };
ifs.close();
}
void A::f(void)
{
std::cout << d.length() << std::endl;
}
When I try to print the length of the string, it is 0. When I try to print d in f(), nothing is printed. I need d to be a protected variable and I cannot change the methods either. How do I pass the read file string to f method?
You assigned to a local, use the member (this-> is optional here):
this->d.assign(istreambuf_iterator<char> {ifs}, {});
If that doesn't help, you're probably specifying the file name wrong.
Try an absolute path (e.g. /home/user/file.txt or C:\Documents\User\Documents\file.txt) or check the working directory of your program.
You can always check for errors:
if (!ifs) throw std::runtime_error("File could not be opened");
Your problem has nothing to do with the fact that your variable is protected. The problem is that you are defining another variable with the same name. To avoid this problem some people append an underscore to the name of the variables, like 'd_', and other people write 'm_d'. But you don't need to do that if you don't want to.
One way to do what you want to do is the following:
class A
{
public:
void l(std::string filename);
void f();
//method declarations
protected:
string d;
};
void A::l(std::string filename)
{
ifstream ifs{filename};
if(!ifs)
return; // error
std::copy(istreambuf_iterator<char>{ifs},
istreambuf_iterator<char>{},
std::back_inserter(d)); // this is A::d
}
You don't need to use 'this->'. In fact in C++ you never use 'this' (only in sentences like 'return *this').
Also, in C++ you don't write:
void f(void);
but instead, you write
void f();
And also you don't need to close the ifstream. The destructor is going to do it for you.
I have implemented a custom logger where I dump information to the corresponding file:
like this:
Logger::log["log_file"] << "Hi" << "Hi again" << "\n";
The implementation of the operator << is as below. Kindly note the place where the buffer is dumped into the stream when it reaches a limit:
//This is the function signature of std::endl and some other manipulators
typedef CoutType& (*StandardEndLine)(CoutType&);
/// This method defines an operator<< to take in std::endl
BasicLogger& operator<<(StandardEndLine manip);
/// write the log items to buffer
template <typename T>
BasicLogger & operator<< (const T& val)
{
std::stringstream *out = BasicLogger::getOut();
*out << val;
if(out->tellp() > 512000/*500KB*/){
flushLog();
}
return *this;
}
My problem is in the lines that have multiple <<s :
Logger::log["log_file"] << "Hi" << "Hi again" << "\n";
after executing << "Hi", the buffer can get full and flushing to the stream is performed.
This is not desirable for me . I am looking for a solution to ignore that limit(500KB) if a line has not ended yet.
Example scenario:
This problem is highlighted when the application writes buffers filled by different threads to the same file: the last line of the first buffer is half written, when the second buffer from another thread writes another 500kb to the file before I can complete the last line of the first buffer.
I appreciate your thoughts and solutions.
thanks
The traditional solution to grouping multiple << into a single flush is to use a temporary sentry object which is destroyed at the end of the statement.
The destructor would flush (if required), and you can even have it append a newline automatically if that's what you want. As per Andrew Medico's comment, the sentry ctor/dtor should lock/unlock as well, if you need synchronization.
An alternative is to gather everything into a local ostringstream in the sentry, and then you only have to lock/write/flush/unlock a single string from the dtor.
Details:
your Logger::log["log_file"] operator should return a temporary object with the behaviour you chose from the options above
your various << are operator calls taking as the first argument, and returning, a reference to your sentry type
at the ;, the temporary sentry goes out of scope and does any flushing work in its destructor.
Note that if you're just delegating to an existing ostream, you can easily have a single templated Logger::sentry& operator<< (Logger::sentry&, T).
Edit: I thought I'd done this before: the question isn't a duplicate, but the answer is. https://stackoverflow.com/a/19520409/212858
I am looking for a solution to ignore that limit(500KB) if a line has not ended yet.
Why not just check that the last character is a '\n' before flushing the log?
If I knew what was in T, I would give you an example. If its a c++11 string you can just do:
char ch = val.getString().back();
if(out->tellp() > 512000/*500KB*/ && ch == '\n'){
flushLog();
}
Simply flag a carrage return:
bool ended = out.str().find('\n') != string::npos;
if(out->tellp() > 512000/*500KB*/ && ended) {
You can change the implementation of the flushLog method. I assume your implementation writes the contents of the internal stringstream into a file and clears it. You can output the contents of the stringstream only up to the last newline:
void Whatever::flushLog()
{
std::stringstream& out = *BasicLogger::getOut();
std::string stuff = out.str();
size_t pos_of_newline = stuff.rfind('\n');
if (pos_of_newline != std::string::npos)
{
std::string write_it_to_file = stuff.substr(0, pos_of_newline + 1);
... write it to file
std::string leftover = stuff.substr(pos_of_newline + 1);
out.str(leftover);
}
}
The code is only an idea; I didn't check it - maybe there are some bugs. In additon: the str() call duplicates the contents of the stringstream; you might want to use its rdbuf instead (I have no experience with rdbuf, so cannot recommend anything).
Following Useless's suggestion (A suggestion shot by a user called Useless!!!),
I solved my issue using Sentry(temporary) objects:
Logger::log["log_file"] will return the correct instance of BasicLogger
operator<< in BasicLogger will return a copy of a Sentry object.
The subsequent <<s will be taken care of by operator<< in the Sentry object
Sentry will eventually goes out of scope upon hitting ; where its destructor will do the main job:
class BasicLogger {
/// operator overload. write the log items to buffer
template<typename T>
Sentry operator<<(const T& val) {
Sentry t(*this,*getOut());
t << val;//do the first << here
return t;
}
//...
class Sentry {
std::stringstream &out;
BasicLogger &basicLogger;
public:
Sentry(BasicLogger & basicLogger_, std::stringstream &out_) :
out(out_), basicLogger(basicLogger_) {
}
Sentry(const Sentry& t) :
basicLogger(t.basicLogger), out(t.out) {
}
template<typename T>
/// operator overload
Sentry & operator<<(const T& val) {
out << val;
return *this;
}
~Sentry() {
// by some googling this estimated hardcode value promises less cycles to write to a file
if (out.tellp() > 512000/*500KB*/) {
basicLogger.flushLog();
}
}
};
//...
};
class Logger {
//...
public:
static Logger log;
virtual BasicLogger & operator[](const std::string &key);
virtual ~Logger();
};
I am practicing with boost and now I am testing boost shared pointers. I have a Util class which can read files. After I read the file, my "Read" method gives back a boost::shared_ptr which points to the file content. Then I pass this shared pointer to my Parser class, which parses the string line by line. After the parsing is done, then at the end of my Parser class constructor (at '}') I get a "runtime error" which points to a boost header file. More specifically to checked_delete.hpp to the "delete x" line:
template<class T> inline void checked_delete(T * x) {
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x;
}
A simplifyed code looks something like this:
class File {
string _path;
public:
File(string path)
~File()
void Open();
boost::shared_ptr<string> Read();
void Close();
};
class Parse {
public:
Parse(string path) {
File file = File(path);
file.Open();
boost::shared_ptr<string> fileContentPtr = file.Read();
StartParsing(fileContentPtr);
file.Close();
}
~Parse();
StartParsing(boost::shared_ptr<string> fileContentPtr);
};
int main() {
string path = "<some path to my file>";
Parse(path);
}
Anybody can give me a hint, what am I doing wrong? Thanks in advance!
EDIT: My Read() function:
boost::shared_ptr<string> File::Read() {
if(file.is_open()) {
ostringstream stream;
stream << file.rdbuf();
string content = stream.str();
boost::shared_ptr<string> contentPtr(&content);
return contentPtr;
}
else {
throw std::runtime_error("File isn't opened");
}
}
Where "file" variable is a std::fstream file which is used in Open()
The contained object of the boost::shared_ptr must be dynamically allocated,
either explicitly via new or implicitly via boost::make_shared<>. From boost::shared_ptr:
The shared_ptr class template stores a pointer to a dynamically allocated object, typically with a C++ new-expression. The object pointed to is guaranteed to be deleted when the last shared_ptr pointing to it is destroyed or reset.
In this case, the std::string instance is stack allocated and will
be destructed when Read() returns:
string content = stream.str();
boost::shared_ptr<string> contentPtr(&content);
This results in the shared_ptr having a dangling pointer and an attempt
to delete it when the shared_ptr goes out of scope, causing the error.
The program already has undefined behaviour as any code that attempts to access
the shared_ptr after Read() is dereferencing a dangling pointer.
To correct, dynamically allocate the std::string:
return boost::make_shared<std::string>(stream.str());
Or, simpler, just return and store a std::string instance instead:
return stream.str();
as the compiler should be able to use return value optimization.
Note that c++11 introduced smart pointers, std::shared_ptr among them.
Is there any way to keep a stream (to read or write in a file) open from a function to another in C++?
Yes, you can either create the stream outside of the functions and pass it as a parameter to the methods:
void myFunction(ifstream &stream) {...}
Later close the stream when you are done with it: stream.close().
Or create the stream within the first function and return it to the calling method and then pass it to the second function.
Pass it by reference
void myFunction(ifstream &myStream)
Make it global or pass it as an argument but ensure that if you pass it as an argument you past it by reference not by value! If you pass it by value the compiler will NOT complain and weird things start happening.
Since C++11 file stream got move constructor (6). You can use it to pass opened stream between functions. Consider the following code snippet:
#include <iostream>
#include <fstream>
bool open_stream(const std::wstring& filepath, std::ifstream& stream)
{
std::ifstream innerStream;
innerStream.open(filepath.c_str(), std::ios::in | std::ios::binary);
if (innerStream.is_open())
{
stream = std::move(innerStream); // <-- Opened stream state moved to 'stream' variable
return true;
}
return false;
} // <-- innerStream is destructed, but opened stream state is preserved as it was moved to 'stream' variable
Consider the next code to illustrate the usage of open_stream:
int main()
{
std::ifstream outerStream;
std::wcout << L"outerStream is opened: " << outerStream.is_open() << std::endl; // <-- outerStream is opened: 0
if (!open_stream(L"c:\\temp\\test_file.txt", outerStream))
{
return 1;
}
std::wcout << L"outerStream is opened: " << outerStream.is_open() << std::endl; // <-- outerStream is opened: 1
// outerStream is opened and ready for reading here
return 0;
}
I don't know why this is erroring, but I'm just trying to add something "akin" to endl so that I can throw what's in an ostringstream to our debugger. I have the following:
class debug_stream_info
{
public:
debug_stream_info(int errorLine, char *errorFile, int level)
:m_errorLine(errorLine), m_errorFile(errorFile), m_logLevel(level)
{
}
friend std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info);
private:
int m_errorLine;
std::string m_errorFile;
int m_logLevel;
};
std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info)
{
// Write the stream's contents to cpu_debug
// Deleted custom logging function. No errors here though
// Clear the stream for re-use.
os.str("");
os.seekp(0);
return os;
}
int main(int argc, char** argv)
{
std::ostringstream myout;
myout << "hey there" << " and some more " << "Numbers!!: " << 435 << 54.2 << " that's good for numbers" << debug_stream_info(__LINE__, __FILE__, LOG_LEVEL);
return 0;
}
The error I'm getting is: error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'debug_stream_info' (or there is no acceptable conversion) for the line in main. This is on VS2008.
I'm including sstream, iostream, etc, and have the namespaces set up right. I'm getting no other errors. I even tried replacing all occurrances of basic_ostream with just ostringstream and there was no difference (I'll be having a w_char version later, but I wanted the simple case to work first). I made the object on the line above and then passed a fully-constructed object on the line, and the error was exactly the same. I've changed the signature of the second argument to and from const with no change as well.
Any ideas on what I'm doing wrong here?
Edit: since EVERY response seems to want to put it there, I can NOT use std::ostream because I want this to work ONLY for std::ostringstream (and std::basic_ostringstream) and not for any type of output stream. Besides, the function wouldn't compile with ostream anyways, since I'm using the os.str() method, which isn't in ostream, only the sub-classes.
The real problem with your code is that you've overloaded std::ostringstream rather than std::ostream. So your code would work if you write this:
debug_stream_info info(/** blah blah**/);
std::ostringstream oss;
oss << info ; //OK
However this will not work:
oss << 1 << info; //ERROR
This is compilation error because the expression oss<<1 returns an object of type std::ostream& which doesn't have overload which takes debug_stream_info as second argument. That means if you use cast as:
static_cast<std::ostringstream&>(oss << 1) << info; //OK
then that should work again.
So the solution is to overload std::ostream, instead of std::basic_ostringstream.
Also, the second parameter should be const & . This is also a problem with your code.
So write this:
std::ostream& operator<<(std::ostream&, debug_stream_info const &);
//^^^^^^^ note this
The second parameter should be const & so that you could write temporary objects to the stream.
debug_stream_info(__LINE__, __FILE__, LOG_LEVEL); is creating unnamed object which is not returning anything hence error
#include <iostream>
#include <cstdio>
#include <sstream>
using namespace std;
class debug_stream_info
{
public:
debug_stream_info(int errorLine, char *errorFile, int level)
:m_errorLine(errorLine), m_errorFile(errorFile), m_logLevel(level)
{
}
friend std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info);
std::ostringstream& fun(std::ostringstream& os)
{
os<<"Ashish"<<endl;
return os;
}
private:
int m_errorLine;
std::string m_errorFile;
int m_logLevel;
};
std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info)
{
// Write the stream's contents to cpu_debug
// Deleted custom logging function. No errors here though
// Clear the stream for re-use.
// os.str("");
// os.seekp(0);
return os;
}
int main(int argc, char** argv)
{
std::ostringstream myout, test;
myout << "hey there" << " and some more " << "Numbers!!: " << 435 << 54.2 << " that's good for numbers"
<< debug_stream_info(1, "/home/ashish/test", 1).fun(test);
return 0;
}
Nawaz has explained very clearly why you're getting the error. The
usual solution in this case is to define your own stream type, unrelated
to std::istream. Something along the lines of:
class DebugStream
{
std::ostringstring* collector;
public:
template <typename T>
DebugStream& operator<<( T const& value )
{
if ( collector != NULL ) {
*collector << value;
}
return *this;
}
};
There are infinite variations on this; in your case, you could add a
non-template member function for your type; more likely, you'd add a
constructor which took the same arguments:
DebugStream( int lineNumber, std::string const& filename, int logLevel )
: collector( isActive( logLevel ) ? new std::ostringstream : NULL )
{
// Initial insertion of lineNumber, filename, timestamp...
}
You can also add a destructor which atomically flushes the collected
data to a file (or sends an email, or writes it to the system log, or
whatever). (Be very careful about this. You don't want an exception to
escape from the destructor, even if the logging fails.)
Finally, you might want to use a custom streambuf, rather than
stringstream. Say one that keeps the allocated buffer from one
instance to the next. And if you do this, rather than newing the
stream each time, you might pick up an instance from a table, indexed by
the log level (and initialized from a configuration file).