overloading Operator << to accept endl

Please support our C advertiser: Programming Forums - DaniWeb Sister Site
Reply

Join Date: Feb 2005
Posts: 466
Reputation: winbatch is on a distinguished road 
Solved Threads: 18
winbatch's Avatar
winbatch winbatch is offline Offline
Posting Pro in Training

Re: overloading Operator << to accept endl

 
0
  #11
May 6th, 2005
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) {};
Reply With Quote Quick reply to this message  
Join Date: Feb 2005
Posts: 466
Reputation: winbatch is on a distinguished road 
Solved Threads: 18
winbatch's Avatar
winbatch winbatch is offline Offline
Posting Pro in Training

Re: overloading Operator << to accept endl

 
0
  #12
May 8th, 2005
Narue,

I rewrote the class as follows:
  1. #include "MainInclude.h"
  2. #ifndef __NEW_LOG__
  3. #define __NEW_LOG__
  4. class NewLog
  5. {
  6. private:
  7. ostream& out;
  8.  
  9. bool is_file;
  10. bool includeDate;
  11. public:
  12. NewLog(ostream& myout = cout) : out(myout)
  13. {
  14. includeDate = false;
  15.  
  16. }
  17.  
  18. NewLog( ofstream &o) : out(o)
  19. {
  20. includeDate = false;
  21.  
  22. if ( out.fail() )
  23. {
  24. throw DHException ("Could not open output file for writing...");
  25. }
  26. }
  27.  
  28.  
  29. void setIncludeDate( bool inclDate )
  30. {
  31. includeDate = inclDate;
  32. }
  33. ostream& operator<<(const string s)
  34. {
  35.  
  36. if ( !out.good() )
  37. {
  38. throw DHException ("Could not open output file for writing...");
  39. }
  40.  
  41. if ( includeDate )
  42. {
  43.  
  44. out<<Date::getFormattedDate()<<"->";
  45. }
  46.  
  47. return out<< s;
  48.  
  49. }
  50.  
  51. };
  52. #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..)
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,733
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 737
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: overloading Operator << to accept endl

 
0
  #13
May 8th, 2005
>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:
  1. class LogBase {
  2. bool _includeDate;
  3. public:
  4. void setIncludeDate(bool inclDate) { _includeDate = inclDate; }
  5. virtual ostream& operator<<(const string& s) = 0;
  6. };
  7.  
  8. class Log: public LogBase {
  9. ostream& _out;
  10. public:
  11. Log(ostream& out = cout): _out(out) {}
  12. ostream& operator<<(const string& s) { return _out<< s; }
  13. private:
  14. Log operator=(const Log&);
  15. };
  16.  
  17. class FileLog: public LogBase {
  18. const string _filename;
  19. ofstream _out;
  20. public:
  21. FileLog(const string& file): _filename(file), _out(file.c_str())
  22. {
  23. if (!_out.is_open())
  24. throw runtime_error("File open failure");
  25. }
  26.  
  27. ostream& operator<<(const string& s) { return _out<< s; }
  28. private:
  29. FileLog(const FileLog&);
  30. FileLog operator=(const FileLog&);
  31. };
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.
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Join Date: Feb 2005
Posts: 466
Reputation: winbatch is on a distinguished road 
Solved Threads: 18
winbatch's Avatar
winbatch winbatch is offline Offline
Posting Pro in Training

Re: overloading Operator << to accept endl

 
0
  #14
May 8th, 2005
Thanks again, I will try it out. When finished, I'll post the final code. (I want to write some other methods that can handle printf semantics, etc.)
Reply With Quote Quick reply to this message  
Join Date: Feb 2005
Posts: 466
Reputation: winbatch is on a distinguished road 
Solved Threads: 18
winbatch's Avatar
winbatch winbatch is offline Offline
Posting Pro in Training

Re: overloading Operator << to accept endl

 
0
  #15
May 12th, 2005
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.)..
  1. FileLog fl ("LogOne.txt");
  2. FileLog fl2( "LogTwo.txt" );
  3.  
  4. MulitiLog ml;
  5. ml.add( fl );
  6. ml.add( fl2);
  7.  
  8. ml<<"Write this to both logs"<<endl;
  9. 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 )
  1. class MultiLog
  2. {
  3. vector<Log*> logs;
  4. public:
  5. //etc addLog, other functions.
  6. ostream & operator<<(const string &s )
  7. {
  8. for ( int i=0;i< logs.size();i++)
  9. {
  10. *logs[i]<<s;
  11. }
  12.  
  13. //WHAT DO I RETURN HERE? (considering I want to handle endl, etc...)
  14.  
  15. }
  16.  
  17. };
Attached Files
File Type: cpp Log.cpp (2.0 KB, 7 views)
File Type: h Log.h (2.4 KB, 8 views)
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,733
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 737
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: overloading Operator << to accept endl

 
0
  #16
May 12th, 2005
>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:
  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. namespace jsw {
  7. class DualLog {
  8. ostream& _o1;
  9. ostream& _o2;
  10. public:
  11. DualLog(ostream& o1, ostream& o2)
  12. : _o1(o1), _o2(o2)
  13. {};
  14.  
  15. // Redefine the no-argument manipulator controller
  16. DualLog& operator<<(DualLog& (*manip)(DualLog&))
  17. {
  18. return manip(*this);
  19. }
  20.  
  21. DualLog& operator<<(const string& s)
  22. {
  23. _o1<< s;
  24. _o2<< s;
  25.  
  26. return *this;
  27. }
  28.  
  29. // For proper definition of endl
  30. void flush()
  31. {
  32. _o1.flush();
  33. _o2.flush();
  34. }
  35. };
  36.  
  37. // DualLog specialized manipulator
  38. DualLog& endl(DualLog& log)
  39. {
  40. log<<"\n";
  41. log.flush();
  42.  
  43. return log;
  44. }
  45. }
  46.  
  47. int main()
  48. {
  49. jsw::DualLog log(cout, cerr);
  50.  
  51. log<<"This is a test"<<jsw::endl;
  52. log<<"Another test"<<jsw::endl;
  53. log<<"Last test"<<jsw::endl;
  54. }
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).
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Join Date: Feb 2005
Posts: 466
Reputation: winbatch is on a distinguished road 
Solved Threads: 18
winbatch's Avatar
winbatch winbatch is offline Offline
Posting Pro in Training

Re: overloading Operator << to accept endl

 
0
  #17
May 12th, 2005
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?)
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,733
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 737
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: overloading Operator << to accept endl

 
0
  #18
May 12th, 2005
>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:
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. class A {
  5. std::vector<std::ostream*> _o;
  6. public:
  7. void add(std::ostream *o) { _o.push_back(o); }
  8. std::ostream& operator[](int i) { return *_o[i]; }
  9. };
  10.  
  11. int main()
  12. {
  13. A a;
  14.  
  15. a.add(&std::cout);
  16. a.add(&std::cerr);
  17.  
  18. a[0]<<"This is a test"<<std::endl;
  19. a[1]<<"Another test"<<std::endl;
  20. }
>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.
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Reply

This thread is more than three months old.
Perhaps start a new thread instead?
Message:



Other Threads in the C Forum
Thread Tools Search this Thread



About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC