I need to print all kind of string data within double quotation, and others without double quotation.
This are my functions to check if the argument is a string.
template<class T>
struct is_str : std::integral_constant<bool, false> {};
template<> struct is_str<char*> : std::integral_constant<bool, true> {};
template<> struct is_str<wchar_t*> : std::integral_constant<bool, true> {};
template<> struct is_str<const char*> : std::integral_constant<bool, true> {};
template<> struct is_str<const wchar_t*> : std::integral_constant<bool, true> {};
template<> struct is_str<std::string> : std::integral_constant<bool, true> {};
template<> struct is_str<const std::string> : std::integral_constant<bool, true> {};
template<> struct is_str<std::wstring> : std::integral_constant<bool, true> {};
template<> struct is_str<const std::wstring> : std::integral_constant<bool, true> {};
And in the printer function, I use the above functions like this
template<typename T>
std::enable_if_t<is_str<T>::value>, std::ostream&>
operator<<(std::ostream& xx, const T& ar)
{
xx << "\"" << ar << "\"";
return xx;
}
template<typename T>
std::enable_if_t<!is_str<T>::value>, std::ostream&>
operator<<(std::ostream& xx, const T& ar)
{
xx << ar;
return xx;
}
It doesn't compile with the error
unrecognized template declaration/definition
Can someone please tell me how to fix this or better way to handle this situation?
Graham Best is right but there is another (bigger, IMHO) problem.
You're not defining operator<<
; you're redefining it calling itself.
As far I know, it's impossible (you redefine a function, the compiler doesn't know which version call) and when you write (inside the redefined operator)
xx << "\"" << ar << "\"";
which version of operator<<()
should be used? The new redefined (causing a loop recursion) or the old one?
I think that the best way to exit from this problem is avoid to redefine operator<<()
and define a simple function; by example, the following print()
template<typename T>
std::enable_if_t<is_str<T>::value, std::string> print (T const & ar)
{
std::ostringstream oss;
oss << "\"" << ar << "\"";
return oss.str();
}
template<typename T>
std::enable_if_t<!is_str<T>::value, T const &> print (T const & ar)
{ return ar; }
and use it in this way
std::cout << print(std::string{"abc"}) << std::endl; // add quotes
std::cout << print(1) << std::endl; // no quotes
En passant: there are a couple of classes that can be useful to write a more compact code: std::true_type
, defined as std::integral_constant<bool, true>
, and std::false_type
, defined as std::integral_constant<bool, false>
.
So you can define is_str
as follows
template<class T>
struct is_str : std::false_type {};
template<> struct is_str<char*> : std::true_type {};
template<> struct is_str<wchar_t*> : std::true_type {};
// ...
OBSERVE also that if you write
std::cout << print("abc") << std::endl; // add quotes
the print()
doesn't add the quotes.
This is because "abc"
isn't a char const *
; it's a const char[4]
.
So, if you want add the quotes to char[N]
and wchar_t[N]
too, you should add the following specializations
template<std::size_t N> struct is_str<char[N]> : std::true_type {};
template<std::size_t N> struct is_str<wchar_t[N]> : std::true_type {};