Quick-and-dirty C++11 SFINAE:
template<typename T,
typename = decltype(
std::declval<std::ostream&>() << std::declval<T const&>()
)
>
std::string toString(T const& t)
{
std::ostringstream out;
// Beware of no error checking here
out << t;
return out.str();
}
template<typename T,
typename... Ignored
>
std::string toString(T const& t, Ignored const&..., ...)
{
static_assert( sizeof...(Ignored) == 0
, "Incorrect usage: only one parameter allowed" );
/* handle any which way here */
}
If you want you can also check that the return type of stream << val
is indeed convertible to std::ostream&
:
template<typename T,
typename Result = decltype(
std::declval<std::ostream&>() << std::declval<T const&>()
),
typename std::enable_if<
std::is_convertible<Result, std::ostream&>::value,
int
>::type = 0
>
As for a not so quick-and-dirty solution I'd introduce an is_stream_insertable
trait, which implementation can make use of the very same tricks used here.
Be aware that std::integral_constant<bool, B>
has a conversion operator to bool
, this might explain some of the things you have observed. I also do not recommend mixing the C++11 Standard types and traits with Boost: don't mix up std::true_type
with boost::true_type
! Which is not to say that you shouldn't use e.g. Boost.TypeTraits at all with C++11, but try to be consistent and only use one of two at a time.