| | |
overloading Operator << to accept endl
Please support our C advertiser: Programming Forums - DaniWeb Sister Site
![]() |
Thanks for all your help - I'll try it out and let you know.
By the way, can you explain this syntax? I've only seen it used for when you are calling the constructor of a parent class from child class... I know that you are setting the class variable _out with the out that was passed in, and setting the _first variable = true, but not sure why it needs to be done with this way rather than the doing it inside the {} . (I did notice that it complains about _out not being initialized if you try it any other way...)
Log(ostream& out = cout) : _out(out), _first(true) {};
By the way, can you explain this syntax? I've only seen it used for when you are calling the constructor of a parent class from child class... I know that you are setting the class variable _out with the out that was passed in, and setting the _first variable = true, but not sure why it needs to be done with this way rather than the doing it inside the {} . (I did notice that it complains about _out not being initialized if you try it any other way...)
Log(ostream& out = cout) : _out(out), _first(true) {};
Narue,
I rewrote the class as follows:
----------------------------------
I have two questions
1) How do I detect a problem writing to the file after it's open? !out.good() does not seem to catch me deleting the file while the program is running
2) How do I get the file name from out? (In the case where I'm using a file rather than the screen) for use in the exception call?
Maybe I should just have a FileLog class that descends from NewLog? (will be renamed to Log when it's finished..)
I rewrote the class as follows:
C Syntax (Toggle Plain Text)
#include "MainInclude.h" #ifndef __NEW_LOG__ #define __NEW_LOG__ class NewLog { private: ostream& out; bool is_file; bool includeDate; public: NewLog(ostream& myout = cout) : out(myout) { includeDate = false; } NewLog( ofstream &o) : out(o) { includeDate = false; if ( out.fail() ) { throw DHException ("Could not open output file for writing..."); } } void setIncludeDate( bool inclDate ) { includeDate = inclDate; } ostream& operator<<(const string s) { if ( !out.good() ) { throw DHException ("Could not open output file for writing..."); } if ( includeDate ) { out<<Date::getFormattedDate()<<"->"; } return out<< s; } }; #endif
I have two questions
1) How do I detect a problem writing to the file after it's open? !out.good() does not seem to catch me deleting the file while the program is running
2) How do I get the file name from out? (In the case where I'm using a file rather than the screen) for use in the exception call?
Maybe I should just have a FileLog class that descends from NewLog? (will be renamed to Log when it's finished..)
>not sure why it needs to be done with this way rather than the doing it inside the {}
Some members cannot be assigned to, and references are among these. Since _out is a reference, by the time you get inside the constructor body, you can no longer initialize it. You should favor an initializer list rather than assignment in the body because it always works even for those troublesome types, and is never less efficient than assignment in the constructor body.
>!out.good() does not seem to catch me deleting the file while the program is running
The current design of your Log suggests that out is always available and valid between the constructor and the destructor. If you're going to assume that the stream is a file stream (a very bad idea if it may not be), you would be better off specializing Log with inheritance.
>2) How do I get the file name from out?
You don't. Consider this instead since what you want is very hard to get right with the current design:
Because it seems you want a file log and a non-file log to do fundamentally different things, you can factor out the common parts and place them in a base class, then derive from the base class into specialized classes for generic ostreams and file streams. The difference being that the generic Log will accept an already open stream without assuming anything while the FileLog will maintain an internal file stream of its own.
Some members cannot be assigned to, and references are among these. Since _out is a reference, by the time you get inside the constructor body, you can no longer initialize it. You should favor an initializer list rather than assignment in the body because it always works even for those troublesome types, and is never less efficient than assignment in the constructor body.
>!out.good() does not seem to catch me deleting the file while the program is running
The current design of your Log suggests that out is always available and valid between the constructor and the destructor. If you're going to assume that the stream is a file stream (a very bad idea if it may not be), you would be better off specializing Log with inheritance.
>2) How do I get the file name from out?
You don't. Consider this instead since what you want is very hard to get right with the current design:
C Syntax (Toggle Plain Text)
class LogBase { bool _includeDate; public: void setIncludeDate(bool inclDate) { _includeDate = inclDate; } virtual ostream& operator<<(const string& s) = 0; }; class Log: public LogBase { ostream& _out; public: Log(ostream& out = cout): _out(out) {} ostream& operator<<(const string& s) { return _out<< s; } private: Log operator=(const Log&); }; class FileLog: public LogBase { const string _filename; ofstream _out; public: FileLog(const string& file): _filename(file), _out(file.c_str()) { if (!_out.is_open()) throw runtime_error("File open failure"); } ostream& operator<<(const string& s) { return _out<< s; } private: FileLog(const FileLog&); FileLog operator=(const FileLog&); };
I'm here to prove you wrong.
Narue,
I've attached my Log.cpp and Log.h. I'd be glad to hear your critique - (be gentle..)
What I'm trying to do now is create a class that can handle multiple FileLog's, so that I can do the following syntax and be able to have the MultiLog 'write' to all the logs inside of it (without the caller needing to manage how many logs there are, etc.)..
My question is this - how do I write the << operator such that it writes to multiple ofstreams, given that the << operator needs to return an ostream itself? (Meaning if I have the following code in MultiLog )
I've attached my Log.cpp and Log.h. I'd be glad to hear your critique - (be gentle..)
What I'm trying to do now is create a class that can handle multiple FileLog's, so that I can do the following syntax and be able to have the MultiLog 'write' to all the logs inside of it (without the caller needing to manage how many logs there are, etc.)..
C Syntax (Toggle Plain Text)
FileLog fl ("LogOne.txt"); FileLog fl2( "LogTwo.txt" ); MulitiLog ml; ml.add( fl ); ml.add( fl2); ml<<"Write this to both logs"<<endl; ml<<"blah1"< " blah2"<<endl;
My question is this - how do I write the << operator such that it writes to multiple ofstreams, given that the << operator needs to return an ostream itself? (Meaning if I have the following code in MultiLog )
C Syntax (Toggle Plain Text)
class MultiLog { vector<Log*> logs; public: //etc addLog, other functions. ostream & operator<<(const string &s ) { for ( int i=0;i< logs.size();i++) { *logs[i]<<s; } //WHAT DO I RETURN HERE? (considering I want to handle endl, etc...) } };
>how do I write the << operator such that it writes to multiple ofstreams,
>given that the << operator needs to return an ostream itself?
Did I mention how much I enjoy your insightful questions? You're pushing yourself and the language, and it's inspiring to watch.
But, to answer your question, you can't using basic iostreams. The most straightforward solution is to start doing your own thing using similar rules as the standard streams. For example, if returning an ostream& doesn't cut it, redefine operator<< to return a MultiLog&. If you still want to use manipulators, define the controlling operator<< for manipulators to take and return a MultiLog&. Consider this example:
The trick is knowing when to follow the rules (almost always), when to bend the rules (such as above), and when to shatter them (very rarely, if ever).
>given that the << operator needs to return an ostream itself?
Did I mention how much I enjoy your insightful questions? You're pushing yourself and the language, and it's inspiring to watch.

But, to answer your question, you can't using basic iostreams. The most straightforward solution is to start doing your own thing using similar rules as the standard streams. For example, if returning an ostream& doesn't cut it, redefine operator<< to return a MultiLog&. If you still want to use manipulators, define the controlling operator<< for manipulators to take and return a MultiLog&. Consider this example:
C Syntax (Toggle Plain Text)
#include <iostream> #include <string> using namespace std; namespace jsw { class DualLog { ostream& _o1; ostream& _o2; public: DualLog(ostream& o1, ostream& o2) : _o1(o1), _o2(o2) {}; // Redefine the no-argument manipulator controller DualLog& operator<<(DualLog& (*manip)(DualLog&)) { return manip(*this); } DualLog& operator<<(const string& s) { _o1<< s; _o2<< s; return *this; } // For proper definition of endl void flush() { _o1.flush(); _o2.flush(); } }; // DualLog specialized manipulator DualLog& endl(DualLog& log) { log<<"\n"; log.flush(); return log; } } int main() { jsw::DualLog log(cout, cerr); log<<"This is a test"<<jsw::endl; log<<"Another test"<<jsw::endl; log<<"Last test"<<jsw::endl; }
I'm here to prove you wrong.
Narue,
The problem is that I don't know how many ostreams (more likely ofstreams) that I will have - I won't necessarily have 2. (So I won't be able to initialize the ofstreams in a constructor).
I want to be able to have unlimited ofstreams, using an addLog( Log * ) type function. I would then iterate through the logs that I have accumulated and call the << or the writeLog( string ) function of each log object. I want to maintain the actual writing to the stream in the Log function as I have some options like including the datetime stamp, etc.
Also, what's this section about? Never saw this (*manip) stuff...
// Redefine the no-argument manipulator controller
DualLog& operator<<(DualLog& (*manip)(DualLog&))
{
return manip(*this);
}
Also, I noticed you are doing "\n" to simulate endl. To be platform independent, I should do some sort of #ifdef on whether I'm on windows to do \r\n? (Does the 'regular' endl work that way?)
The problem is that I don't know how many ostreams (more likely ofstreams) that I will have - I won't necessarily have 2. (So I won't be able to initialize the ofstreams in a constructor).
I want to be able to have unlimited ofstreams, using an addLog( Log * ) type function. I would then iterate through the logs that I have accumulated and call the << or the writeLog( string ) function of each log object. I want to maintain the actual writing to the stream in the Log function as I have some options like including the datetime stamp, etc.
Also, what's this section about? Never saw this (*manip) stuff...
// Redefine the no-argument manipulator controller
DualLog& operator<<(DualLog& (*manip)(DualLog&))
{
return manip(*this);
}
Also, I noticed you are doing "\n" to simulate endl. To be platform independent, I should do some sort of #ifdef on whether I'm on windows to do \r\n? (Does the 'regular' endl work that way?)
>I won't necessarily have 2.
You can have as many as you want. My code is always just an example.
>I want to be able to have unlimited ofstreams
So use a vector of pointers to ostream, or something equivalent:
>Never saw this (*manip) stuff...
That's because it's hidden from you. What you quoted was a function taking a function as its argument.
>To be platform independent, I should
Do exactly what I did. The character literal '\n' will do the right thing regardless of the platform.
You can have as many as you want. My code is always just an example.
>I want to be able to have unlimited ofstreams
So use a vector of pointers to ostream, or something equivalent:
C Syntax (Toggle Plain Text)
#include <iostream> #include <vector> class A { std::vector<std::ostream*> _o; public: void add(std::ostream *o) { _o.push_back(o); } std::ostream& operator[](int i) { return *_o[i]; } }; int main() { A a; a.add(&std::cout); a.add(&std::cerr); a[0]<<"This is a test"<<std::endl; a[1]<<"Another test"<<std::endl; }
That's because it's hidden from you. What you quoted was a function taking a function as its argument.
>To be platform independent, I should
Do exactly what I did. The character literal '\n' will do the right thing regardless of the platform.
I'm here to prove you wrong.
![]() |
Other Threads in the C Forum
- Previous Thread: Read unlimited no. of people.
- Next Thread: Please help
| Thread Tools | Search this Thread |
* ansi api array arrays bash binarysearch calculate centimeter changingto char character convert copyanyfile copypdffile createcopyoffile createprocess() csyntax directory dynamic fflush file floatingpointvalidation fork forloop frequency getlasterror getlogicaldrivestrin givemetehcodez graphics gtkgcurlcompiling gtkwinlinux hardware highest homework i/o ide inches initialization intmain() iso km license linked linkedlist linux linuxsegmentationfault list logical_drives loopinsideloop. lowest match matrix microsoft motherboard mqqueue multi mysql oddnumber odf open opendocumentformat openwebfoundation pdf pointer pointers posix power program programming pyramidusingturboccodes read recursion recv recvblocked repetition reversing scanf scheduling segmentationfault send shape single socketprogramming stack standard strchr string strings suggestions test testautomation unix urboc user variable whythiscodecausesegmentationfault win32api windows.h windowsapi






