2015-08-14 5 views
0

У меня есть следующий код:Как связать класс с перечислением?

#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); 
} 

Как я могу устроить, чтобы вызвать соответствующий вспомогательный класс (HobbitHelper или Apprentice) в decoder? Я не могу объявить вложенный тип внутри перечисления, как если бы это был класс. Я также попытался получить хелпера из перечисления (поскольку сам помощник не имеет данных), но это тоже не допускается.

Любые идеи?

ответ

4

Вы можете просто иметь helper типа черта быть внешней и шаблонными на перечислимом типе, с явной специализацией для каждого 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; 

И тогда decode только доступ будет что:

template <typename T> 
T decoder(std::string s) 
{ 
    return helper_t<T>::decode(s); 
} 
+0

В то время как я вырос, чтобы не любить слово «помощник», это довольно элегантный ответ (поддержанный). – Jeff

+0

Это решение также можно распространять: «template <> struct helper : type_is {};' специализации могут быть в соответствующем заголовке. Переименование 'helper_t' в' helper_of' делает его еще более мнемоничным. – Bulletmagnet

1

Мое предложение было бы частично специализированным шаблоном, хотя ответ от @Barry может быть больше похож на то, что вы ищете.

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); 
} 
+0

Я вижу, что @Quentin отредактировал код, но почему? Он работал отлично ... –

+0

Uh. Я был уверен, что вы забыли аргументы в специализации, но они компилируются без них ... Извините за это! Редактировать: хорошо, я действительно чему-то научился. Спасибо за это :) – Quentin

1

Самый простой способ сделать это - использовать ADL. Вы можете использовать тег типа, чтобы заставить компилятор выглядеть в соответствующем пространстве имен.

Рассмотрим:

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>()); 
} 

Это решение используется на почти все C++ libraries- более или менее. В основном нет никаких дополнительных усилий. Мне даже не пришлось упомянуть Wizard.

+0

И Хоббит, и Мастер находятся в пространстве имен MiddleEarth :) – Bulletmagnet

+0

Я не вижу, чтобы это работало, если 'Hobbit decode (......)' не является свободной функцией, а статичным член, например 'HobbitHelper' (как в исходном вопросе). – Bulletmagnet

+0

@Bulletmagnet: использование таких функций, как статические члены классов, является плохой практикой в ​​C++, это бессмысленный Javaism и блокирует многие языки более полезными функциями, такими как ADL. Если вы помещаете такие функции в класс, у вас больше проблем, чем у небольшого беспорядочного синтаксиса вызова. – Puppy

1

Помимо хелперов проблема есть лучшее решение, чем каскад 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; 
} 
+0

Спасибо, но это не было вопросом. – Bulletmagnet

0

В конце концов я пошел с немного измененной версией Barry's answer

template <typename T> 
struct enumclass { 
}; 

template<> 
struct enumclass<Hobbit> { 
    using helper = HobbitHelper; 
}; 

template<> 
struct enumclass<Wizard> { 
    using helper = Apprentice; 
}; 

, потому что это позволяет мне писать более мнемонические

template <typename T> 
T 
decoder(std::string s) 
{ 
    return enumclass<T>::helper::decode(s); 
} 

Все специализации могут быть распределены (т.е. enumclass <Hobbit> находится в hobbit.h; enumclass<Wizard> находится в wizard.h). Все эти заголовки должны включать небольшой заголовок с неспециализированным шаблоном.

Смежные вопросы