c++ - ¿Quieres un tipo de caja de interruptor constexpr en el tipo
templates metaprogramming (3)
Creo que sería más fácil usar la especialización de plantillas.
Código de ejemplo:
#include <iostream>
struct A{};
struct B{};
struct C{};
struct D{};
template<typename T> constexpr const char* GetNameOfList();
//here you may want to make it return nullptr by default
template<>constexpr const char* GetNameOfList<A>(){return "A";}
template<>constexpr const char* GetNameOfList<B>(){return "B";}
template<>constexpr const char* GetNameOfList<C>(){return "C";}
int main(){
std::cout << GetNameOfList<A>() << ''/n'';
std::cout << GetNameOfList<B>() << ''/n'';
std::cout << GetNameOfList<C>() << ''/n'';
//std::cout << GetNameOfList<D>() << ''/n''; //compile error here
}
Actualmente estoy haciendo este truco para tener un cstring basado en un tipo:
template<class ListT> static char constexpr * GetNameOfList(void)
{
return
std::conditional<
std::is_same<ListT, LicencesList>::value, "licences",
std::conditional<
std::is_same<ListT, BundlesList>::value, "bundles",
std::conditional<
std::is_same<ListT, ProductsList>::value, "products",
std::conditional<
std::is_same<ListT, UsersList>::value, "users",
nullptr
>
>
>
>;
}
Pero este código no es muy atractivo, y si queremos verificar más tipos, esto podría ser ilegible. ¿Es una forma de hacer lo mismo como si hubiera un bloqueo de mayúsculas y minúsculas?
En realidad, el código es más complicado que eso, porque std :: conditional necesita algún tipo, así que necesitamos alguna clase para hacer el truco:
struct LicenceName { static char constexpr * value = "licences"; };
por ejemplo.
No es necesario recurrir a la metaprogramación, simplemente if
s funciona bien:
template<class ListT>
constexpr char const *GetNameOfList() {
if(std::is_same<ListT, A>::value) return "A";
if(std::is_same<ListT, B>::value) return "B";
if(std::is_same<ListT, C>::value) return "C";
if(std::is_same<ListT, D>::value) return "D";
return nullptr;
}
Podría crear un conjunto de cadenas constexpr más una tupla de tipos de lista para crear un list type -> index -> name
mapeo list type -> index -> name
(si necesita el index -> types containing strings
mapeo index -> types containing strings
solo usan tuple en lugar de un array). El enfoque de c ++ 17 podría verse como sigue:
#include <type_traits>
#include <tuple>
#include <utility>
#include <iostream>
struct LicencesList{};
struct BundlesList{};
struct ProductsList{};
struct UsersList{};
using ListTypes = std::tuple<LicencesList, BundlesList, ProductsList, UsersList>;
constexpr const char *NameList[] = {"licences", "bundles", "products", "users"};
template <class Tup, class, class = std::make_index_sequence<std::tuple_size<Tup>::value>>
struct index_of;
template <class Tup, class T, std::size_t... Is>
struct index_of<Tup, T, std::index_sequence<Is...>> {
static constexpr std::size_t value = ((std::is_same<std::tuple_element_t<Is, Tup>, T>::value * Is) + ...);
};
template<class ListT> static const char constexpr * GetNameOfList(void) {
return NameList[index_of<ListTypes, ListT>::value];
}
int main() {
constexpr const char *value = GetNameOfList<BundlesList>();
std::cout << value << std::endl;
}
Si desea mantener la compatibilidad con c ++ 11, el enfoque sería un poco más largo (aquí utilicé la respuesta de Casey para implementar index_of
estructura):
#include <type_traits>
#include <tuple>
#include <iostream>
struct LicencesList{};
struct BundlesList{};
struct ProductsList{};
struct UsersList{};
using ListTypes = std::tuple<LicencesList, BundlesList, ProductsList, UsersList>;
constexpr const char *NameList[] = {"licences", "bundles", "products", "users"};
template <class Tuple, class T>
struct index_of;
template <class T, class... Types>
struct index_of<std::tuple<T, Types...>, T> {
static const std::size_t value = 0;
};
template <class T, class U, class... Types>
struct index_of<std::tuple<U, Types...>, T> {
static const std::size_t value = 1 + index_of<std::tuple<Types...>, T>::value;
};
template<class ListT> static const char constexpr * GetNameOfList(void) {
return NameList[index_of<ListTypes, ListT>::value];
}
int main() {
constexpr const char *value = GetNameOfList<BundlesList>();
std::cout << value << std::endl;
}