Reputation: 6010
I have the following template:
namespace std {
template<typename Enum>
typename std::enable_if<std::is_enum<Enum>::value, std::ostream&>::type
operator<<(std::ostream& strm, Enum e)
{
return strm << helper_of<Enum>::to_string(e);
}
}
which helps google-test display human-readable diagnostics when comparing hobbits:
template <typename T> struct enumclass {}; // generic template
template <typename T>
using helper_of = typename enumclass<T>::helper; // more mnemonic
namespace MiddleEarth {
enum class Hobbit { Bilbo, Frodo, Sam };
struct HobbitHelper{
std::string to_string(Hobbit H);
Hobbit from_string(std::string const& s); // Hobbit-forming
};
template <> struct enumclass<Hobbit> {
using helper = HobbitHelper; // links Hobbit to its helper
}
}
The enable_if
is there to prevent this templated operator<<
from being applied to any old class (the naive version without enable_if is ambiguous for classes which already have streaming operators, e.g. std::string
).
However, if there is an enum which doesn't specialize enumclass
,
enum class Wizard { Gandalf, Radagast, Saruman };
const Wizard g = Wizard::Gandalf, s = Wizard::Saruman;
then the following fails to compile
EXPECT_EQ(g, s);
with error: no type named 'helper' in 'aws::enumclass<Wizard>'
because the compiler tries to apply the templated operator<<
to Wizard.
Is it possible to construct an enable_if
that would only apply this operator<< if there is a specialization of enumclass<Enum>
? Google-test then would fall back to display the raw bytes of Wizard and it would compile.
Failing that, is it possible to construct an enable_if
which would only allow types in a certain namespace (e.g. MiddleEarth)? This would solve the problem if Wizard is not in the MiddleEarth namespace. All the enums in MiddleEarth are supposed to have a specialization for enumclass
.
Upvotes: 2
Views: 931
Reputation: 302698
You can just move the helper_of
substitution into the template specification itself:
template <typename Enum,
typename Helper = helper_of<Enum>>
std::ostream& operator<<(std::ostream& strm, Enum e)
{
return strm << Helper::to_string(e);
}
That way, if the helper_of
substitution fails (that is, enumclass
isn't specialized for the given Enum
), the entire overload will be thrown out due to SFINAE rather than being a hard compile error - since now we're in the immediate context of the substitution itself.
Upvotes: 5