Reputation: 6010
I have the following code:
#include <string>
enum class Hobbit {
// typedef HobbitHelper helper;
UNKNOWN = -1, Bilbo, Frodo, Samwise
};
struct HobbitHelper {
static Hobbit decode(std::string const& s) {
if (s == "Bilbo") {
return Hobbit::Bilbo;
}
else if (s == "Frodo") {
return Hobbit::Frodo;
}
else if (s == "Samwise") {
return Hobbit::Samwise;
}
else {
return Hobbit::UNKNOWN;
}
}
};
enum class Wizard {
// typedef Apprentice helper;
UNKNOWN = -1, Gandalf, Radagast, Saruman
};
struct Apprentice { // WizardHelper :)
static Wizard decode(std::string const& s) {
if (s == "Gandalf") {
return Wizard::Gandalf;
}
else if (s == "Radagast") {
return Wizard::Radagast;
}
else if (s == "Saruman") {
return Wizard::Saruman;
}
else {
return Wizard::UNKNOWN;
}
}
};
template <typename T>
T
decoder(std::string s)
{
return ??::decode(s);
// if the typedefs were allowed, I could use T::helper::decode()
}
int main()
{
std::string s{ "Rincewind" };
auto h = decoder<Hobbit>(s);
auto w = decoder<Wizard>(s);
}
How can I arrange to call the appropriate helper class (HobbitHelper or Apprentice) in decoder
? I can't declare a nested type inside the enum, as if it was a class. I also tried deriving the helper from the enum (since the helper itself has no data), but that isn't allowed either.
Any ideas?
Upvotes: 0
Views: 193
Reputation: 6010
In the end I went with a slightly modified version of Barry's answer
template <typename T>
struct enumclass {
};
template<>
struct enumclass<Hobbit> {
using helper = HobbitHelper;
};
template<>
struct enumclass<Wizard> {
using helper = Apprentice;
};
because it lets me write the more mnemonic
template <typename T>
T
decoder(std::string s)
{
return enumclass<T>::helper::decode(s);
}
All the specializations can be distributed (i.e. enumclass <Hobbit>
is in hobbit.h
; enumclass<Wizard>
is in wizard.h
). All these headers must include a small header with the unspecialized template.
Upvotes: 0
Reputation: 302767
You can just have the helper
type trait be external and templated on the enum type, with an explicit specialization for each enum
:
template <typename T> struct type_is { using type = T; };
template <typename > struct helper;
template <> struct helper<Hobbit> : type_is<HobbitHelper> { };
template <> struct helper<Wizard> : type_is<Apprentice> { };
template <typename T>
using helper_t = typename helper<T>::type;
And then decode
would just access that:
template <typename T>
T decoder(std::string s)
{
return helper_t<T>::decode(s);
}
Upvotes: 4
Reputation: 44238
Aside helper problem there is better solution than cascade if
:
static Hobbit decode(std::string const& s) {
static std::unordered_map<std::strinng,Hobbit> choice {
{ "Bilbo", Hobbit::Bilbo },
{ "Frodo", Hobbit::Frodo },
{ "Samwise", Hobbit::Samwise }
};
auto f = choice.find( s );
return f != choice.end() ? f->second : Hobbit::UNKNOWN;
}
Upvotes: 1
Reputation: 1509
My suggestion would be partial template specialization, although the answer from @Barry might be more like what you are looking for.
template <typename T>
T decoder(std::string s);
template<>
Hobbit decoder(std::string s)
{
return HobbitHelper::decode(s);
}
template<>
Wizard decoder(std::string s)
{
return Apprentice::decode(s);
}
Upvotes: 1
Reputation: 146910
The simplest way to do it is to use ADL. You can use a type tag to make the compiler look in the appropriate namespace.
Consider:
template<typename T> struct adl_tag {};
namespace MiddleEarth {
enum class Hobbit {
// typedef HobbitHelper helper;
UNKNOWN = -1, Bilbo, Frodo, Samwise
};
Hobbit decode(std::string const& s, adl_tag<Hobbit>) {
if (s == "Bilbo") {
return Hobbit::Bilbo;
}
else if (s == "Frodo") {
return Hobbit::Frodo;
}
else if (s == "Samwise") {
return Hobbit::Samwise;
}
else {
return Hobbit::UNKNOWN;
}
}
}
template<typename T> T decode(std::string s) {
return decode(s, adl_tag<T>());
}
This is the solution employed by pretty much all C++ libraries- more or less. There's basically no additional effort involved. I didn't even have to mention Wizard.
Upvotes: 1