I thought I had figured out how to make a template which determines if a class/struct is stream-able to an ostream (e.g., "print-able") but there is a flaw in the template I wrote to do this. Looking to learn where I went wrong as well as any solutions to get the template to work the way I expect/want.
So my questions are
- Am I missing something pretty basic here or is this more complicated than the template takes into account?
- I have not looked at the template's interaction with rvalue references: will they be problematic for the template as well?
Full code here.
Here is the template
template<typename T, typename U>
class isOutputStreamable {
private:
template<typename V, typename W> static decltype(operator<<(std::declval<V>(), std::declval<W>()), std::true_type()) check_(int);
template<typename, typename> static std::false_type check_(...);
public:
static const bool value = decltype(check_<T, U>(0))::value;
};
The classes I am testing
struct Empty {
};
struct Streamer {
};
std::ostream& operator<<(std::ostream& ostream, const Streamer&) {
return ostream << "Streamer";
}
The static_asserts that work the way I expect
// Streamer should show as ostreamable
static_assert( isOutputStreamable< std::ostream&, const Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&, const Streamer >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&, Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&, Streamer >::value, "Goodness" );
// isOutputStreamable works with a value ostream for Streamer
static_assert( isOutputStreamable< std::ostream, const Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream, const Streamer >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream, Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream, Streamer >::value, "Goodness" );
// Empty should not show as ostreamable
static_assert( !isOutputStreamable< std::ostream&, const Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&, const Empty >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&, Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&, Empty >::value, "Goodness" );
// With a const ostream nothing should show as ostreamable
static_assert( !isOutputStreamable< const std::ostream&, const Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Streamer >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, Streamer >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Streamer >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , Streamer >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Empty >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, Empty >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Empty >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , Empty >::value, "Goodness" );
and then the static_asserts that do not
// Unexpected results: I wanted these to all fail
static_assert( isOutputStreamable< std::ostream, const Empty& >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream, const Empty >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream, Empty& >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream, Empty >::value, "Badness" );
Thanks in advance for any help.
Update
@Casey: Thanks for the help. I was so convinced that there was something wrong with the template I guess I completely missed the fact that it might actually be matching correctly. As for your suggestion about how to switch the template around this is certainly something that is easily do-able; however, I'm curious if there is not another solution that'll fit given what I'll say next.
The template is actually generated from what I had hoped would be a general macro to create these types of checker templates
#define BINARY_FUNCTION_EXISTS_(FUNCTION_NAME_, CHECK_NAME_) \
template<typename T, typename U> class CHECK_NAME_ {\
private:\
template<typename V, typename W> static decltype(FUNCTION_NAME_(std::declval<V>(), std::declval<W>()), std::true_type()) check_(int);\
template<typename, typename> static std::false_type check_(...);\
public:\
static const bool value = decltype(check_<T, U>(0))::value;\
};
Is it the case that this ostream-able template needs to be a special case because of the (undesirable in my case) matching template? I think the answer is yes but I've already been confused once on this and it does not hurt to ask. Thanks again.