Last active
October 11, 2021 10:58
-
-
Save gatchamix/d345e653a3c7880d8fde47b0e6439f3a to your computer and use it in GitHub Desktop.
constexpr switch/case/default
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <utility> | |
#include <limits> | |
#include <array> | |
// | |
template <auto V_> | |
struct auto_t | |
{ | |
using value_type = decltype(V_); | |
constexpr operator value_type() | |
{ return V_; } | |
}; | |
template <auto V> | |
auto constexpr auto_v = auto_t<V>{}; | |
// | |
template <auto... Vs_> | |
struct auto_list | |
{ static auto constexpr size = sizeof...(Vs_); }; | |
template <auto... Vs> | |
auto constexpr auto_list_v = auto_list<Vs...>{}; | |
template <auto... L, auto... R> | |
auto constexpr operator+(auto_list<L...>, auto_list<R...>) | |
{ return auto_list_v<L..., R...>; } | |
// | |
template <auto... Vs> | |
auto constexpr common_type(auto_list<Vs...>) | |
-> std::common_type_t<decltype(Vs)...>; | |
template <class T> | |
using common_type_t = decltype(common_type(T{})); | |
template <auto... Vs> | |
auto constexpr min(auto_list<Vs...>) | |
{ | |
using type = common_type_t<auto_list<Vs...>>; | |
auto val = std::numeric_limits<type>::max(); | |
(((val > Vs) ? val = Vs : 0), ...); | |
return val; | |
} | |
template <auto... Vs> | |
auto constexpr max(auto_list<Vs...>) | |
{ | |
using type = common_type_t<auto_list<Vs...>>; | |
auto val = std::numeric_limits<type>::min(); | |
(((val < Vs) ? val = Vs : 0), ...); | |
return val; | |
} | |
namespace detail | |
{ | |
template <auto V, auto... Vs> | |
auto constexpr is_unique_impl__(auto_list<V, Vs...> vs = {}) | |
{ | |
if constexpr (sizeof...(Vs) == 0) | |
{ return true; } | |
else | |
{ return ((V != Vs) && ...) && is_unique_impl__<Vs...>(); } | |
} | |
} | |
template <auto... Vs> | |
auto constexpr is_unique(auto_list<Vs...> vs) | |
{ | |
if constexpr (vs.size < 2) | |
{ return true; } | |
else | |
{ return detail::is_unique_impl__(vs); } | |
} | |
// | |
template <class Keys_, class Vals_, auto Def_ = key_t{}, auto Limit_ = 128> | |
struct flat_map | |
{ | |
constexpr flat_map(Keys_ = {}, Vals_ = {}, auto_t<Def_> = {}, auto_t<Limit_> = {}) | |
{ static_assert(is_unique(Keys_{}), "keys must be unique"); } | |
private: | |
using key_t = std::conditional_t<std::is_signed_v<common_type_t<Vals_>>, | |
std::make_signed_t<size_t>, std::size_t>; | |
using val_t = common_type_t<Vals_>; | |
static auto constexpr MIN = min(Keys_{}); | |
static auto constexpr MAX = max(Keys_{}); | |
static auto constexpr BIG = MAX-MIN+1 > Limit_; | |
static auto constexpr N = BIG ? Keys_::size : MAX-MIN+1; | |
template <auto... Vs, auto... Is> | |
static auto constexpr get__(key_t v, auto_list<Vs...>, std::index_sequence<Is...>) | |
{ | |
auto fun = Def_; | |
(((v == Vs) ? fun = table_[Is] : 0) || ...); | |
return fun; | |
} | |
public: | |
auto constexpr operator[](key_t i) const | |
{ | |
if (std::size_t(i-MIN) > MAX-MIN) | |
{ return static_cast<val_t>(Def_); } | |
if constexpr (BIG) | |
{ return get__(i, Keys_{}, std::make_index_sequence<N>{}); } | |
else | |
{ return table_[i - MIN]; } | |
} | |
private: | |
template <auto... Vs, auto... Fs> | |
static auto constexpr make_table__(auto_list<Vs...>, auto_list<Fs...>) | |
{ | |
if constexpr (BIG) | |
{ | |
return std::array<val_t, N>{ Fs... }; | |
} | |
else | |
{ | |
auto arr = std::array<val_t, N>{}; | |
for (auto& f : arr) f = Def_; | |
((arr[Vs - MIN] = Fs), ...); | |
return arr; | |
} | |
} | |
static auto constexpr table_ = make_table__(Keys_{}, Vals_{}); | |
}; | |
// | |
template <class Vals_, class Funcs_, auto Def_, auto Limit_ = 128> | |
struct switch_impl : flat_map<Vals_, Funcs_, Def_, Limit_> | |
{ | |
template <class... Args> | |
auto constexpr operator()(key_t i, Args&&... args) const | |
{ return (this->operator[](i))(std::forward<Args>(args)...); } | |
}; | |
template <class Vals_, class Funcs_, auto Def_ = nullptr> | |
struct options_ | |
{ | |
constexpr options_(Vals_ = {}, Funcs_ = {}, auto_t<Def_> = {}) | |
{} | |
template <class Vals, class Funcs, auto Def> | |
auto constexpr operator()(options_<Vals, Funcs, Def>) const | |
{ | |
auto constexpr has_def = std::is_same_v<decltype(Def_), std::nullptr_t>; | |
auto constexpr new_def = std::is_same_v<decltype(Def), std::nullptr_t>; | |
static_assert(has_def || new_def, "multiple instances of default_"); | |
using vals = decltype(Vals_{} + Vals{}); | |
using funcs = decltype(Funcs_{} + Funcs{}); | |
auto constexpr def = has_def ? Def : Def_; | |
return options_<vals, funcs, def>{}; | |
} | |
template <class Options> | |
auto constexpr operator+(Options&& o) const | |
{ return this->operator()(std::forward<Options>(o)); } | |
}; | |
template <auto Limit, class Vals, class Funcs, auto Def> | |
auto constexpr switch_(options_<Vals, Funcs, Def>, auto_t<Limit> = {}) | |
{ return switch_impl<Vals, Funcs, Def, Limit>{}; } | |
template <class Vals, class Funcs, auto Def> | |
auto constexpr switch_(options_<Vals, Funcs, Def>) | |
{ return switch_impl<Vals, Funcs, Def>{}; } | |
template <auto F, auto... Vs> | |
auto constexpr case_ = (options_{ auto_list_v<Vs>, auto_list_v<F> } + ...); | |
template <auto F> | |
auto constexpr default_ = options_{ auto_list_v<>, auto_list_v<>, auto_v<F> } ; | |
// | |
auto constexpr foo1() | |
{ return 1; } | |
auto constexpr foo2() | |
{ return 2; } | |
auto constexpr dfoo() | |
{ return 3; } | |
template <auto V> | |
auto constexpr vfoo() | |
{ return V; } | |
template <auto... Vs> | |
auto constexpr bar(auto_list<Vs...>) | |
{ | |
return | |
switch_ | |
( | |
((default_<&dfoo>)) | |
((case_<&vfoo<Vs>, Vs> + ...)) | |
((case_<&foo1, (Vs + 10)...>)) | |
((case_<&foo2, 10>)) | |
); | |
} | |
int main(int argc, char **argv) | |
{ | |
return bar(auto_list_v<1, 2, 3, 4, 5>)(argv[0][0]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment