Zero-dependency allocation-free mapping enum values to names for C++11
/** zero-dependency allocation-free mapping enum values to names */ | |
namespace enumnames { | |
typedef const char* (*name)(); | |
bool match(const char*, const char*) noexcept; /* to be provided by the user */ | |
template<typename T, T K, name V> | |
struct tuple { | |
typedef T key_t; | |
static constexpr T key = K; | |
static constexpr name val = V; | |
}; | |
template<typename ... L> | |
struct map; | |
template<typename L> | |
struct map<L> { | |
static constexpr typename L::key_t get(const char*) noexcept { | |
return L::key; /* always returning last element */ | |
} | |
static constexpr const char* get(typename L::key_t key) noexcept { | |
return (L::val != nullptr) && (key == L::key) ? L::val() : ""; | |
} | |
}; | |
template<typename L, typename ... R> | |
struct map<L,R...> { | |
static typename L::key_t get(const char* val) noexcept { | |
static_assert(L::val != nullptr, "Only last element may have null name"); | |
return match(val, L::val()) ? L::key : map<R...>::get(val); | |
} | |
static constexpr const char* get(typename L::key_t key) noexcept { | |
return (key == L::key) ? L::val() : map<R...>::get(key); | |
} | |
}; | |
template<typename T, typename ... L> | |
struct names { | |
static T get(const char* nam) noexcept { | |
return M::get(nam); | |
} | |
static constexpr const char* get(T key) noexcept { | |
return M::get(key); | |
} | |
private: | |
typedef map<L...> M; | |
}; | |
} |
/* main.cpp - usage example for enumnames */ | |
#include <iostream> | |
#include <cstring> | |
#include "enumnames.hpp" | |
enum class fasion { | |
fancy, | |
classic, | |
sporty, | |
etno, | |
emo, | |
__last__ = emo, | |
__unknown__ = -1 | |
}; | |
/* needed for iterations only */ | |
static inline constexpr fasion operator+(fasion a, int b) noexcept { | |
return static_cast<fasion>(static_cast<int>(a) + b); | |
} | |
static inline constexpr int operator+(fasion a) noexcept { | |
return static_cast<int>(a); | |
} | |
#define NAME(s) static inline constexpr const char* s() noexcept {return #s;} | |
namespace name { | |
NAME(fancy) | |
NAME(classic) | |
NAME(sporty) | |
NAME(etno) | |
NAME(emo) | |
} | |
template<fasion K, enumnames::name V> | |
struct _ : enumnames::tuple<fasion, K,V> {}; | |
typedef enumnames::names<fasion, | |
_<fasion::fancy, name::fancy>, | |
_<fasion::classic, name::classic>, | |
_<fasion::sporty, name::sporty>, | |
_<fasion::etno, name::etno>, | |
_<fasion::emo, name::emo>, | |
_<fasion::__unknown__, nullptr> | |
> fasion_names; | |
using namespace std; | |
int main() { | |
std::string str; | |
cout << "Enter fasion from the list:" << endl; | |
for(fasion i = fasion::fancy; i <= fasion::__last__; i = i+1) | |
cout << fasion_names::get(i) << endl; | |
cin >> str; | |
fasion selected = fasion_names::get(str.data()); | |
cout << "Selected :" << +selected << " (" << fasion_names::get(selected) << ")"; | |
} | |
namespace enumnames { | |
bool match(const char* a, const char* b) noexcept { | |
return strcasecmp(a,b) == 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
I have changed so much your answer on StackOverflow that I am not sure you will accept the changes:
This gist provides a simple mapping based on C++ variadic templates.
This is a C++17-simplified version of the type-based map from the gist:
An example usage:
The
map<KeyValues...>
can be used in both directions:fasion_names::get(fasion::emo)
fasion_names::get("emo")
This example is available on godbolt.org
Result from
gcc-7 -std=c++1z -Ofast -S