Handling std::endl in an overloaded "<<" function.

Hi All,

I'm trying to build a logging utility. It creates an object that can be used just like a std::cout. The added benefit of this object is that it can simultaneously output to a log file, console, add headers to the start of lines, etc based on a mask that is set.

The part that is not working right now is the fact that it cannot accept std::endl. My code is below (R0mai helped):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class logger {
public:
	logger( const char* filename);	// Opens a file for use with ofstream fout.
	~logger();			// Flushes and closes the logging file.
	logger& operator()(unsigned mask_); // Allows us to set the mask in the same line as the messages

	template<class LogInputTemplate>
	friend logger& operator<<( logger& log, const LogInputTemplate& output );

private:
	std::ofstream fout;
	unsigned mask;
};

template<class LogInputTemplate>
logger& operator<<( logger& log, const LogInputTemplate& output ) {
	if (log.mask & 0xf0)	std::cout << output;
	if (log.mask & 0x0f)	log.fout << output;
	return log;
}

Now, if I use the following in my code, I want it to compile:
1
2
logger MyLog("MyLog.txt");
MyLog(0xff)<<"Hello World"<< std::endl;

It works fine unless I use the std::endl. Then I get the following compilation error:
 error C2914: 'operator <<' : cannot deduce template argument as function argument is ambiguous
 error C2676: binary '<<' : 'logger' does not define this operator or a conversion to a type acceptable to the predefined operator


Now, I've looked up std::endl and in ostream.h it appears to be defined by a series of templates, classes, inlines and defines that don't mean a lot to me other than it places a \n and then flushes. So really, I'm dealing with something that doesn't fit into a template class.

I checked out this site which seems to suggest that std::endl is an ostream& object.
http://cplusplus.com/reference/iostream/manipulators/endl/

Therefore I figure I can add one more overloading function to carry out the duties of endl:
1
2
3
4
5
6
logger& operator<<( logger& log, std::ostream& os)
{
	if (log.mask & 0xf0)	std::cout << std::endl;
	if (log.mask & 0x0f)	log.fout << std::endl;
	return log;
}


However now I get the following error:
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)
logger.h(13): could be 'logger &operator <<(logger &,std::ostream &) while trying to match the argument list '(logger, overloaded-function)


I consider this a partial success because it at least recognizes the function that I've been trying to direct the endl to. Does anyone know what I can do from here?

Last edited on

Slite modification
1
2
3
4
5
6
7
template<class LogInputTemplate>
logger& operator<<(ostream& os ,  logger& log, const LogInputTemplate& output ) 
{
	if (log.mask & 0xf0)	os << output;
	if (log.mask & 0x0f)	log.fout << output;
	return log;
}
When I try that I get:
logger.h(49): error C2804: binary 'operator <<' has too many parameters
.

Why did you think this would work? I'm very curious if there is something down this path that I can explore.
Soory for not testing it before i thought it worked .
but i suppose you have to write oveloaded '<<' function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class LogInputTemplate>
ostream& operator<<(ostream& os ,  const LogInputTemplate& output ) 
{
	if (log.mask & 0xf0)	os << output;
	
	return os;
}

and 

template<class LogInputTemplate>
logger& operator<<(  logger& log,  const LogInputTemplate& output  ) 
{
	
	if (log.mask & 0x0f)	log.fout << output;
	return log;
}
Thanks for trying.

It took me a while to compile (I forgot to put std:: to the left of ostream&) but once it worked, I tried out the <<std::endl and got the following compilation error:


logger.cpp(78): error C2914: 'operator <<' : cannot deduce template argument as function argument is ambiguous
logger.cpp(78): error C2914: 'operator <<' : cannot deduce template argument as function argument is ambiguous
logger.cpp(78): error C2676: binary '<<' : 'logger' does not define this operator or a conversion to a type acceptable to the predefined operator


I think I am going to make an obscure class and create an object called "endl" in its own namespace. Then I'll be able to recognize when that particular class comes in. I'll just have to instruct users to use the endl in my namespace instead of the one in std.
Success!

I just created the following:
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
class BlankType {};
namespace sl
{
	extern BlankType endl;
}

class logger {
public:
	logger(const char* filename);
	~logger();
	logger& operator()(unsigned mask_);

	template<class LogInputTemplate>
	friend logger& operator<<(logger& log, const LogInputTemplate& output);
	friend logger& operator<<(logger& log, const BlankType& C);
private:
	std::ofstream fout;
	unsigned mask;
	bool nextline;
};

template<class LogInputTemplate2>
logger& operator<<(  logger& log,  const LogInputTemplate2& output  ) 
{
	if (log.mask & 0xf0) std::cout << output;
	if (log.mask & 0x0f) log.fout << output;
	return log;
}

logger& operator<<(logger& log, const BlankType& C)
{
	if (log.mask & 0xf0) std::cout << '\n' << std::flush;
	if (log.mask & 0x0f) log.fout << '\n' << std::flush;
	log.nextline = true;
	return log;
}
extern logger SimLog;


Then in my main source code I can just:
SimLog(0xff) << "HelloWorld" << sl::endl;

But one question:
If there is a file with:
1
2
3
4
using namespace std;
using namespace sl;
...
SimLog(0xff) << endl;

What would happen?
Last edited on
I would recommend you derive from std::ostream, or even better use boost::iostreams. However the following code achieves what you want I think.

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

#include <iostream>

struct logger
{
	std::ostream &loggingStream;
	logger(std::ostream &ls) : loggingStream(ls) {}

};

logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) {
	manip(l.loggingStream);
	return l;
}

template<typename T>
logger &operator<<(logger &l, const T &t) {
	l.loggingStream << t;
	return l;
}

int main() {
	logger l(std::cout);

	l << "Hello" << std::endl;
	return 0;
}


But one question:
If there is a file with:
1
2
3
4
using namespace std;
using namespace sl;
...
SimLog(0xff) << endl;

What would happen?


Why don't you try it?

I would recommend you derive from std::ostream,


I have always wanted to know how to do this, but I can't seem to find any information on it. Do you have any references?

I've seen some example classes which supposedly do it, but they didn't work with VS. I assume they must've worked on gcc, but I never tried.

It frustrates me that it's so hard to derive from such a fundamental class. You'd think they would have made it simple.
It is surprisingly dark magic.

You can go simply and just derive from std::ostream directly, but you can be complete and derive from the base class with all the template trappings.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <ostream>

template <typename CharT, typename Traits>
class basic_logger: public std::basic_ostream <CharT, Traits> 
{
  public:
    // You must redeclare these types:
     typedef CharT                        char_type;
     typedef typename Traits::int_type    int_type;
     typedef typename Traits::pos_type    pos_type;
     typedef typename Traits::off_type    off_type;
     typedef Traits                       traits_type;

  // Everything else is your stuff.
  ...
};

typedef basic_logger <char>    logger;
typedef basic_logger <wchar_t> wlogger;
...

You might want to consider working with a new filebuf type, which is where this kind of thing actually belongs. Then your overloaded basic_logger class simply instanciates with a loggerbuf instead of the standard filebuf.

Yeah, I know it is a lot to work with. Good luck!

[edit] BTW, you should put stuff in your own namespace, too...
Last edited on
Thank kev82. I can now use the std::endl, std::ends, and std::flush output manipulators. To do this, I added:

1
2
3
4
5
logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) {
	if (log.mask & 0xf0) manip(std::cout);
	if (log.mask & 0x0f) manip(log.fout);
	return l;
}


I'm not sure if I'm comfortable with the "dark magic" of deriving from std::basic_ostream yet so I'll try and find other solutions for my remaining items.

Two more questions since this development seems to open a whole new world with manip:
1. How about if I want to use basic format flag such as std::boolalpha, std::hex or std::scientific?
2. How about if I want to use parameterized manipulators such as std::setprecision() or std::setw()?

I am going to do some experimenting tonight and let you know how it goes.

EDIT: *Giggle. Question 1 was solved by adding a function that replaces ostream with ios_base. It works!

Now question 2 might take a new approach as I may need to play with struct std::_Smanip. Any methods here would be awesome



EDIT 2: Tonight is my night!!! The following single line of code works for any manipulator that takes an argument:

logger& operator<<(logger&log, std::_Smanip<std::streamsize> MySmanip) { return log; }

Part of how I solved Question 2 came from a previous post... Thanks Bazzy!
http://www.cplusplus.com/forum/beginner/5794/
Last edited on
@disch

No, I don't think there is one solid really good reference on this - I've just googled and pieced things together. If I was trying to achieve the same effect as the OP, then this is probably what I'd do, just for simplicity more than anything else:

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
#include <sstream>
#include <iostream>

class MultiTargetBuf : public std::stringbuf
{
private:
	int flags;
public:
	MultiTargetBuf(int flags) : std::stringbuf(), flags(flags) {}
	void setFlags(int f) { flags = f; }
protected:
	virtual int sync() {
		if(flags & 0xf0) {
			std::cout << "\n(stream1)\n" << str() << std::flush;
		}
		if(flags & 0x0f) {
			std::cout << "\n(stream2)\n" << str() << std::flush;
		}
			
		str("");
		return 0;
	}
};

class Logger : public std::ostream
{
public:
	Logger() : std::ostream(new MultiTargetBuf(0)) {}
	~Logger() {
		delete rdbuf();
		rdbuf(NULL);
	}
	Logger &operator()(int flags) {
		rdbuf()->pubsync();
		dynamic_cast<MultiTargetBuf *>(rdbuf())->setFlags(flags);
		return *this;
	}
};

int main() {
	Logger l;
	l << "no one can hear this" << std::endl;
	l(0x0f) << "hello from one stream" << std::endl;
	l(0xf0) << "hello from the other stream" << std::endl;
	l(0xff) << "hello from both streams" << std::endl;
	return 0;
}


I also agree with Duoas though, this stuff is not particularly easy or welcoming. I still recommend learning boost::iostreams.
Reference:
Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference
by Angelika Langer and Klaus Kreft
http://www.amazon.com/Standard-IOStreams-Locales-Programmers-Reference/dp/0201183951
http://www.angelikalanger.com/iostreams.html
Topic archived. No new replies allowed.