Re: Determine if operator<< is implemented for a class
On Mar 29, 12:30 am, dec4106 <dec4...@gmail.com> wrote:
Part of the functionality we'd like to have for a logging class would
be to write out the value of an object passed in. Something like:
template <typename T>
void MyLog::writeError(string msg, T obj)
{
outputStream << msg << obj;
}
This won't compile if operator<< isn't defined for T. Is there a way
to have the compiler determine whether or not operator<< is defined
for a class, and if not call a different function?
Yes, this is possible. The problem is to create a type_trait that
determines whether some type can be streamed to an ostream. My
solution to this is given below:
------ BEGIN CODE ------
#include <iosfwd>
#include <string> // Needed to get a std::operator<<
namespace hide_stream_operator {
template <class T, class C, class Tr>
void operator<<( std::basic_ostream<C, Tr>&, T const& );
using std::operator<<;
template <
class T,
class C = char,
class Tr = std::char_traits<char> >
class can_stream_out {
typedef char yes[1];
typedef char no[2];
static std::basic_ostream<C, Tr>& os;
template <class U>
static T& mkref();
template <class U>
static yes& test( char (*)[sizeof(os << mkref<U>()) > 1] );
template <class U>
static no& test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
}
using hide_stream_operator::can_stream_out;
------ END CODE ------
It's important that this lives in its own namespace rather than
sharing a namespace with any other components. This is because it
introduces a greedy broken overload of operator<<. This is needed to
ensure that os << mkref<U>() has something to fall back to if a real
overload doesn't exist. This greedy operator<< returns void; applying
sizeof to a void expression is illegal, though some compilers allow it
in some situations and define it to be 1 (0 would also seem a
possibility, though I've not seen that). This causes the first test
function to declare an array of size zero, which is not allowed, and
SFINAE removes it from the overload set. All sensible operator<<s
return either an ostream or a proxy that contains a reference to an
ostream; either way, the size of the return must be greater than 1
(assuming sizeof(char) != sizeof(void*) which is true on every
architecture I've heard of). If you're using old compilers, you may
struggle to get this to work.
The second step is to use something like boost::enable_if to
selectively handle the types. For example,
template <class T>
boost::enable_if< can_stream_out<T> >
write_error(string const& msg, T const& obj) {
os << msg << obj;
}
template <class T>
boost::disable_if< can_stream_out<T> >
write_error(string const& msg, T const& obj) {
os << msg << "[Type: " << typeid(T).name() << "]";
}
Richard Smith
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]