Skip to content

Instantly share code, notes, and snippets.

@gatchamix
Last active October 11, 2021 10:58
Show Gist options
  • Save gatchamix/d345e653a3c7880d8fde47b0e6439f3a to your computer and use it in GitHub Desktop.
Save gatchamix/d345e653a3c7880d8fde47b0e6439f3a to your computer and use it in GitHub Desktop.
constexpr switch/case/default
#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