Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Want kind of constexpr switch case on type

I am currently doing this trick to have a cstring based on a type:

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

But this code is not very good-looking, and if we want to check more types, this could be unreadable. Is it a way to do the same thing as if there were a switch case block?

Actually, the code is more complicated than that, because std::conditional need some type, so we need some class to do the trick:

struct LicenceName { static char constexpr * value = "licences"; };

for example.

like image 532
Boiethios Avatar asked Nov 22 '16 15:11

Boiethios


3 Answers

I think it would be easier using template specialization


Example code:

#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
}
like image 126
apple apple Avatar answered Nov 19 '22 14:11

apple apple


You don't need to resort to metaprogramming, plain ifs work just fine:

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

See it live on Coliru

like image 24
Quentin Avatar answered Nov 19 '22 12:11

Quentin


You could create constexpr array of strings plus tuple of list types to create mapping list type -> index -> name (if you need the mapping index -> types containing strings just use tuple instead of array). c++17 approach could look as follows:

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

[live demo]

If you want to maintain c++11 compatibility the approach would be just a little bit longer (I used here Casey's answer to implement index_of structure):

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

[live demo]

like image 3
W.F. Avatar answered Nov 19 '22 12:11

W.F.