How to detect a special templated param

B

Bob Altman

Hi all,

I'm writing a simple unmanaged C++ debug class (named, appropriately enough,
"debug") that lets me write text to a file. I use an overloaded << operator
to write text to the file, like this:

debug::Write() << "Hello" << ios::hex << 23 << ios::endl;

I've included a simplified version of my class definition below. My
question is this: I am using a templated function to implement the
overloaded << operator. When ios::endl is specified as the right hand
parameter to the << operator, I need to do some special processing. How can
I do this?

TIA - Bob

The class looks kind of like this (much simplified):

#include <iostream>
#include <fstream>
#include <iomanip>

class debug
{
private:
static debug m_debug;
static std::blush:fstream m_outStream;

public:
// Begin a write operation to the file
static debug& Write()
{
// Open the file for append
m_outStream.open("debug.txt", ios::blush:ut | ios::app);

// Write the beginning of the line
m_outStream << "Some text: ";

// Return a reference to a debug object
return m_debug;
}

// Overloaded << operator
template <class T> debug& operator<<(const T& param) const
{
if (param == ios::endl) { // This line obviously won't compile
<Do special processing to close the file>
} else {
// Write the parameter to the output stream
m_outStream << param;
}

return m_debug;
}
};

// Declare the static data
debug debug::m_debug;
std::blush:fstream debug::m_outStream;
 
B

Bob Altman

Bob Altman said:
Hi all,

I'm writing a simple unmanaged C++ debug class (named, appropriately
enough, "debug") that lets me write text to a file. I use an overloaded
<< operator to write text to the file, like this:

debug::Write() << "Hello" << ios::hex << 23 << ios::endl;

I guess I should add that I'm not stuck on using the endl manipulator to
signal the last parameter; it just seemed to make sense that way. I'd be
happy (but not quite as happy ;-) to expose something in my class that I
could put on the right side of the << operator, which I could then detect in
the operator<<() code.

- Bob
 
B

Bob Altman

Well, I thought of a way to do it, but I'm not wild about it. I'd still
love it if someone with a deeper knowledge of C++ could suggest a better
solution.

Here's the solution I thought of: I could expose a static End function that
returns a type that can be the right side of an ostream's << operator (for
example, an int). The End function would perform the final tasks and close
the file. In order to write a message to my debug log file I'd use this
syntax:

debug::Write() << "This is a message" << debug::End();

There is a minor drawback to this solution: The operator<< function will be
called *after* End has finished closing the file. The operator<< function
would need to detect that the file is closed and not try to write to the
internal file stream. That a little extra complexity (yeah, I know, it's
just a couple lines of code) and an extra subroutine call that I'd like to
avoid (this is part of a high-performance, real-time application).
 
J

Jeffrey Tan[MSFT]

Hi Bob,

Regarding ios::endl, do you mean std::endl?

You may provide an overload for: ostream& (*pf)(ostream&) and test: pf ==
static_cast<ostream& (*)(ostream&)>(endl) (Implicit Callable Functions(ICF)
will have to be disabled for this test to be always meaningful). If so,
you've got endl. If not, you don't have endl (maybe you have ends or
flush), so forward pf to the template (that would involve calling
operator<<<>(pf)).

Let's explain the confusing part a bit. First, let's understand how endl
works. Although endl is familiar from Hello World, it actually operates in
a subtle manner. cout and pals (from <iostream>) are global objects;
although they benefit from a small amount of special Standardese, they're
basically like any other global object. This makes people think that endl
is also a global object, but it is not. It is actually a function template
(from <ostream>; technically you must include this to get endl), with the
signature:

template <class charT, class traits> basic_ostream<charT, traits>&
endl(basic_ostream<charT, traits>& os)

which inserts a newline and then flushes. basic_ostream<charT, traits>
then has:

basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&
(*pf)(basic_ostream<charT, traits>&));

This streaming operator returns pf(*this), invoking the function pointer on
the this-object.

At this point, but how can we pass *the name of a function template* (i.e.
endl) to this operator<<(), which wants a function pointer? The answer is,
this operator<<() is a member function of a class template, and you have a
specific instantiation of that class template handy (cout, wcout, an
ofstream, a wofstream, etc.). So, the signature of this operator<<() is
fixed; it is a member function of a class template, not a member function
template itself. When we pass the name of a function template (i.e. endl)
as an argument (i.e. pf) to a function (i.e. operator<<()), template
argument deduction is triggered. So, if we call cout << endl, the compiler
figures out that we want the narrow instantiation of endl (taking
ostream&); if we call wcout << endl, the compiler figures out that we want
the wide instantiation of endl (taking wostream&).

So: if we provide an overload for ostream& (*pf)(ostream&), and call
some_debug << endl, first the compiler must perform overload resolution to
determine whether we want to call the template or non-template
operator<<(). Template argument deduction for the former will fail (it
takes const T&, but you're passing it the name of a function template,
endl, so it has no clue what T should be), and SFINAE kicks in, removing
the templated operator<<() from consideration. So the non-template
operator<<() will be selected, and template argument deduction will kick in
to determine which endl we want (arbitrarily, the narrow one). In the
body, you test whether pf is equal to the address of narrow endl (the
static_cast is necessary to disambiguous which endl you want, and ICF can
potentially hork you because it can make the addresses of different
functions equal, which is slightly nonconformant). If it's endl, you do
your thing, otherwise you have to forward it over to the general
operator<<(). Here, you need to use an empty set of angle brackets to
indicate that you want the template (or you'll get infinite recursion),
hence the strange-looking construct operator<<<>(pf).

Also, we should beware users writing debug::Write() << "meow" << endl <<
endl;

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
B

Ben Voigt [C++ MVP]

Bob said:
Well, I thought of a way to do it, but I'm not wild about it. I'd
still love it if someone with a deeper knowledge of C++ could suggest
a better solution.

Here's the solution I thought of: I could expose a static End
function that returns a type that can be the right side of an
ostream's << operator (for example, an int). The End function would
perform the final tasks and close the file. In order to write a
message to my debug log file I'd use this syntax:

debug::Write() << "This is a message" << debug::End();

There is a minor drawback to this solution: The operator<< function
will be called *after* End has finished closing the file. The
operator<< function would need to detect that the file is closed and
not try to write to the internal file stream. That a little extra
complexity (yeah, I know, it's just a couple lines of code) and an
extra subroutine call that I'd like to avoid (this is part of a
high-performance, real-time application).

So you do what the standard streams do for manipulators -- you pass the name
of a function, and let operator<< call it through a function pointer.

An even easier solution, though, is:

struct DebugEndMarker
{
} end;

Then usage looks like

debug::Write() << "Hello" << std::hex << 23 << debug::end;

and you provide an overload or specialization for your operator<< accepting
a parameter of type const DebugEndMarker&.
 
B

Bob Altman

Thanks Ben. I got your solution working lickety split. I'm still chewing
on Jeffrey's description of how to detect endl...

- Bob
 
B

Bob Altman

Thanks Jeffrey. Wow, that's really dense stuff! Amazingly (to me ;-) I was
able to get it working with a function like this:

debug& operator<<(ostream& (*pf)(ostream&)) const
{
if (pf == static_cast<ostream&(*)(ostream&)>(endl)) {
// Do special processing here
} else {
// Forward the function pointer to the templated << operator
operator<<<>(pf);
}
// All done
return m_debug;
}

So, if I understand this correctly, I've provided an overload for the
operator<< function that accepts a function pointer for a function whose
signature matches that of the endl function. When I put "endl" to the right
of an << operator, the compiler decides that I'm really trying to pass a
function pointer (since I've provided a function name and not an actual call
to a function), and it matches up the endl function signature with my
overloaded << operator.

This just leaves me with one question: What the heck is ICF and how do I
tell if it is enabled? I did a quick search through the MSDN help library
and couldn't find anything about it.
 
J

Jeffrey Tan[MSFT]

Hi Bob,

Thanks for your feedback.

Oh, it seems that there is some mistake in my reply. ICF means "Identical
Comdat Folding". Please refer to the links below to understand how to
turn-off it:
"/OPT (Optimizations)"
http://msdn2.microsoft.com/en-us/library/bxwfs976.aspx
"Why does the debugger show me the wrong function?"
http://blogs.msdn.com/oldnewthing/archive/2005/03/22/400373.aspx

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
B

Bob Altman

Amazing. Thank you!

Jeffrey, I want to really thank you for the mini-textbook that you posted on
this thread! That was really above and beyond my expectations. As is so
often the case in C++, the devil is in a whole bunch of picky details that I
never would have figured out by myself (like calling the function<<()
function with a pair of empty brackets to cause the compiler to choose the
templated version of the function).

Bob
 
J

Jeffrey Tan[MSFT]

You are welcome :)

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top