operator<<

Hello everybody

I'm a bit confused regarding operator overloading. More specifically, I'm trying to overload the insert operator for the ofstream class so that when I apply it to an object created from one of my classes I can handle the output the way I want. To make a long story short here is an example of what I'm trying to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

ofstream& operator<<(ofstream& out, const bool& val){
	string retval;
	if (val)
		retval="true";
	else 
		retval="false";
	out << retval;
	return out;
}

int main(int argc, char **argv){
	ofstream logfile;
	
	logfile.open("test.log", ios::binary | ios::trunc);
		
	logfile << (bool)1 << "-" << (bool)0 << "-"<< endl;	
		
	logfile.close();
}


This is the content of the test.log file after program execution:

true-0-

The operator<< function should kick in when program execution runs in an ofstream object on the left side of the << operator that has a boolean variable on it's right side. This behaviour seems to take place only the first time the << operator is used.the second time that the << operator is encountered it's default implementation is called.I'm really puzzled :-)

Any clue?

Thank you very much in advance for your help.

andros.
the << "-" part calls an ostream &operator <<, not an ofstream &operator << so after it you will have a general ostream

You may find useful manipulators: http://www.cplusplus.com/reference/iostream/manipulators/boolalpha.html
Last edited on
Thank you for the hint Bazzy.

Maybe I'll solve my little problem if I overload operator<< also for the more generic ostream class objects.

I'll give it a try...

andros
You should only overload it for ostream, or more generally basic_ostream (if you need wide character support). Since all the fundamental types already are supported by the streams, you should only have to overload it for user-defined types, in which case the correct way is to make it a friend of the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class my_class {
  friend std::ostream& operator<<( std::ostream& os, const my_class& mc ) {
        // stuff here
        return os;
  }

  // Or, more generally:
  template< typename charT, typename Traits > friend
  std::basic_ostream<charT, Traits>& operator<<(
     std::basic_ostream<charT, Traits>& os, const my_class& mc ) {
         // stuff here
         return os;
    }
};

Thanks jsmith.

Although I think that now I've better understood how operators overloading should be implemented, I still have a doubt.

To learn c++ I'm developing a logging class. Let's say that I need to output log messages (i.e.: strings) using a basic_ostream object and that I would like to be able to change text formatting at will, so that I may be able to output plain or html formatted text.

The log class users shoudn't care about adding formatting characters, the code should handle reformatting for example by transforming string containing "\n" into strings containing "\n<br>" on the fly during the output phase.

I wanted to realize this by overloading operator << for basic_ostream (lhs) and string (rhs) and then , if html mode was enabled, parsing the string to perfomr the needed text sostitutions before outputting it. But in this case if I overload operator<< as described, the compiler complains about the existence of more than one operator<< for the operands...

andros.
I'll give you a solution (that you probably won't like), but I'm not _the_ expert on streams, so there might be a better way: create your own stream object that works like cout/cerr. In there you will need to completely reimplement << for all the fundamental types. You'll want to derive your object from ostream (I think, or maybe basic_ostream) so that any user-defined stream output operators are still usable. You'll then want to write one or two manipulators that toggle between HTML and text.

eg, as a user I'd expect to be able to do something like:

clog << html << "This is *boldfaced*\n" << text << "And this is *not*.\n";

and expect something like


This is boldfaced<br>
And this is *not*.
Last edited on
Thanks for the prompt reply jsmith!

Implementing a custom stream object might be a good thing to do in order to learn c++ and theoretically I should only reimplement the << operator only for the types (either basic or user defined) for whoose I need a particular behaviour. I'll try...

andros.
Now I'm confused...

This is how I've tried to implement a custom stream object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* ===================================================
    LogStream.h
   =================================================== */
   
#ifndef LOGSTREAM_
#define LOGSTREAM_

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cstring>
#include <ctime>

using namespace std;  
 
class LogStream: public ofstream{	
	private:
	public:
		LogStream();
		~LogStream();
		
	  LogStream& operator<<(const time_t&);
	  
	  string format_time_t(const time_t& rawtime);
};

/*==================================================        
   LogStream.cpp                                               
  ================================================== */

#include "LogStream.h"

LogStream::LogStream(){
}

LogStream::~LogStream(){
}

string LogStream::format_time_t(const time_t& rawtime){
  struct tm * timeinfo;	
  string result;
  if (rawtime != -1){ // rawtime vale -1 se il livello di log a cui si desidera scrivere รจ < del livello di log impostato per quel log
    //timeinfo = localtime ( &rawtime );
    //result = asctime (timeinfo);
    //result.resize(result.size()-1);
    struct tm * ptm= localtime(&rawtime);
    
    stringstream day,month,year,hour,min,sec;
    day << setfill('0') << setw(2) << ptm->tm_mday;
    month << setfill('0') << setw(2) << ((ptm->tm_mon)+1);
    year << setfill('0') << setw(4) << 1900 + ptm->tm_year;
    hour << setfill('0') << setw(2) << ptm->tm_hour;
    min << setfill('0') << setw(2) << ptm->tm_min;
    sec <<  setfill('0') << setw(2) << ptm->tm_sec;

    result = day.str() + "-" + month.str() + "-" + year.str() + " " + hour.str() + ":" + min.str() +  ":" + sec.str();
    
	  return result;
  }	
}
 
LogStream& LogStream::operator<<(const time_t& rawtime){
	*this << format_time_t(rawtime);
	return *this;
}

main (int argc, char** argv){
	time_t rawtime;
	
	LogStream log;
	log.open("mylog.txt", ios::binary | ios::trunc);
	log << time (&rawtime) << " - " << time (&rawtime)	;	
	log.close();
}
#endif 


As you can see from the above code, I've overloaded operator<< to handle time_t values on rhs. this is the content of the mylog.txt file:

more mylog.txt
06-04-2009 15:13:08-1239023588

Obiouvsly the overloaded operator << is called only once when line 73 is executed.

Again, I'm confused... :-)

andros.
I think it's the same problem.

You should not be deriving from ofstream, but perhaps ostream instead.

no luck,

If I derive from ostream I get this error while compiling the code:

LogStream.cpp:73: error: 'class LogStream' has no member named 'open'
LogStream.cpp:75: error: 'class LogStream' has no member named 'close'


which sounds correct because the more general ostream class doesn't have methods to handle files. Casting from ostream to ofstream in lines 72 e 73 gives more (uglier) errors (and I do not think is correct to cast a superclass to a subclass...).

:-(

andros.
Did you change your operator<< methods to take ostreams and return ostreams (as in the original reply)?
Topic archived. No new replies allowed.