Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/stdx/ct_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,11 @@ template <typename T> consteval auto name_for() {
template <typename T> consteval auto constant_name_for() {
return cts_t<name_for<T>()>{};
}

namespace detail {
template <std::size_t N>
// NOLINTNEXTLINE(*-avoid-c-arrays)
map_value_helper(char const (&)[N]) -> map_value_helper<ct_string<N>>;
} // namespace detail
} // namespace v1
} // namespace stdx
79 changes: 50 additions & 29 deletions include/stdx/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ template <typename... Fs> overload(Fs...) -> overload<Fs...>;
[[noreturn]] inline auto unreachable() -> void { __builtin_unreachable(); }

namespace detail {
template <auto V> struct value_t {
template <typename T> struct map_value_helper {
consteval explicit(false) map_value_helper(auto t) : value(t) {}

T value;
};
template <typename T> map_value_helper(T) -> map_value_helper<T>;

template <auto V> struct map_value_t {
constexpr static inline auto value = V;
};

Expand All @@ -38,10 +45,13 @@ template <typename K, typename V> struct type_pair {
using value_type = V;
};
template <typename K, typename V> using tt_pair = type_pair<K, V>;
template <auto K, typename V> using vt_pair = tt_pair<detail::value_t<K>, V>;
template <typename K, auto V> using tv_pair = tt_pair<K, detail::value_t<V>>;
template <auto K, auto V>
using vv_pair = tt_pair<detail::value_t<K>, detail::value_t<V>>;
template <detail::map_value_helper K, typename V, typename = void>
using vt_pair = tt_pair<detail::map_value_t<K.value>, V>;
template <typename K, detail::map_value_helper V>
using tv_pair = tt_pair<K, detail::map_value_t<V.value>>;
template <detail::map_value_helper K, detail::map_value_helper V>
using vv_pair =
tt_pair<detail::map_value_t<K.value>, detail::map_value_t<V.value>>;

template <typename... Ts> using type_map = shrink_t<detail::type_map<Ts...>>;

Expand All @@ -57,50 +67,61 @@ template <typename V, typename Default, typename K>
constexpr static auto reverse_lookup(type_pair<K, V>) -> K;
} // namespace detail

template <typename M, typename K, typename Default = void>
struct missing_t {
friend constexpr auto operator==(missing_t const &, missing_t const &)
-> bool = default;
};
constexpr inline auto missing = missing_t{};

template <typename M, typename K, typename Default = missing_t>
using type_lookup_t = decltype(detail::lookup<K, Default>(expand<M>()));

template <typename M, typename V, typename Default = void>
template <typename M, typename V, typename Default = missing_t>
using reverse_type_lookup_t =
decltype(detail::reverse_lookup<V, Default>(expand<M>()));

template <typename M, auto K, typename Default = void>
template <typename M, detail::map_value_helper K, typename Default = missing_t>
using value_lookup_t =
decltype(detail::lookup<detail::value_t<K>, Default>(expand<M>()));
decltype(detail::lookup<detail::map_value_t<K.value>, Default>(
expand<M>()));

template <typename M, auto V, typename Default = void>
template <typename M, detail::map_value_helper V, typename Default = missing_t>
using reverse_value_lookup_t =
decltype(detail::reverse_lookup<detail::value_t<V>, Default>(expand<M>()));
decltype(detail::reverse_lookup<detail::map_value_t<V.value>, Default>(
expand<M>()));

namespace detail {
template <typename T>
using is_not_void = std::bool_constant<not std::is_void_v<T>>;
using is_present = std::bool_constant<not std::same_as<T, missing_t>>;
}

template <typename M, typename K, auto Default = 0>
template <typename M, typename K, detail::map_value_helper Default = missing>
constexpr static auto type_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::lookup<K, void>(expand<M>())),
detail::value_t<Default>>::value;
type_or_t<detail::is_present,
decltype(detail::lookup<K, missing_t>(expand<M>())),
detail::map_value_t<Default.value>>::value;

template <typename M, typename V, auto Default = 0>
template <typename M, typename V, detail::map_value_helper Default = missing>
constexpr static auto reverse_type_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::reverse_lookup<V, void>(expand<M>())),
detail::value_t<Default>>::value;
type_or_t<detail::is_present,
decltype(detail::reverse_lookup<V, missing_t>(expand<M>())),
detail::map_value_t<Default.value>>::value;

template <typename M, auto K, auto Default = 0>
template <typename M, detail::map_value_helper K,
detail::map_value_helper Default = missing>
constexpr static auto value_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::lookup<detail::value_t<K>, void>(expand<M>())),
detail::value_t<Default>>::value;
type_or_t<detail::is_present,
decltype(detail::lookup<detail::map_value_t<K.value>, missing_t>(
expand<M>())),
detail::map_value_t<Default.value>>::value;

template <typename M, auto V, auto Default = 0>
template <typename M, detail::map_value_helper V,
detail::map_value_helper Default = missing>
constexpr static auto reverse_value_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::reverse_lookup<detail::value_t<V>, void>(
expand<M>())),
detail::value_t<Default>>::value;
type_or_t<detail::is_present,
decltype(detail::reverse_lookup<detail::map_value_t<V.value>,
missing_t>(expand<M>())),
detail::map_value_t<Default.value>>::value;

template <typename T, typename U>
[[nodiscard]] constexpr auto forward_like(U &&u) noexcept -> decltype(auto) {
Expand Down
104 changes: 96 additions & 8 deletions test/type_map.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <stdx/ct_string.hpp>
#include <stdx/utility.hpp>

#include <catch2/catch_test_macros.hpp>
Expand All @@ -18,7 +19,7 @@ TEST_CASE("look up type in map", "[type map]") {

TEST_CASE("look up type not in map", "[type map]") {
using M = stdx::type_map<stdx::type_pair<A, X>, stdx::type_pair<B, Y>>;
STATIC_REQUIRE(std::is_same_v<stdx::type_lookup_t<M, Z>, void>);
STATIC_REQUIRE(std::is_same_v<stdx::type_lookup_t<M, Z>, stdx::missing_t>);
STATIC_REQUIRE(std::is_same_v<stdx::type_lookup_t<M, Z, int>, int>);
}

Expand All @@ -30,10 +31,23 @@ TEST_CASE("look up type in map (by value)", "[type map]") {

TEST_CASE("look up type not in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::vt_pair<0, X>, stdx::vt_pair<1, Y>>;
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, 2>, void>);
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, 2>, stdx::missing_t>);
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, 2, int>, int>);
}

TEST_CASE("look up type in map (by string value)", "[type map]") {
using M = stdx::type_map<stdx::vt_pair<"A", X>, stdx::vt_pair<"B", Y>>;
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, "A">, X>);
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, "B">, Y>);
}

TEST_CASE("look up type not in map (by string value)", "[type map]") {
using M = stdx::type_map<stdx::vt_pair<"A", X>, stdx::vt_pair<"B", Y>>;
STATIC_REQUIRE(
std::is_same_v<stdx::value_lookup_t<M, "C">, stdx::missing_t>);
STATIC_REQUIRE(std::is_same_v<stdx::value_lookup_t<M, "C", int>, int>);
}

TEST_CASE("look up value in map (by type)", "[type map]") {
using M = stdx::type_map<stdx::tv_pair<A, 0>, stdx::tv_pair<B, 1>>;
STATIC_REQUIRE(stdx::type_lookup_v<M, A> == 0);
Expand All @@ -42,10 +56,24 @@ TEST_CASE("look up value in map (by type)", "[type map]") {

TEST_CASE("look up value not in map (by type)", "[type map]") {
using M = stdx::type_map<stdx::tv_pair<A, 0>, stdx::tv_pair<B, 1>>;
STATIC_REQUIRE(stdx::type_lookup_v<M, Z> == 0);
STATIC_REQUIRE(stdx::type_lookup_v<M, Z> == stdx::missing);
STATIC_REQUIRE(stdx::type_lookup_v<M, Z, 2> == 2);
}

TEST_CASE("look up string value in map (by type)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::tv_pair<A, "X">, stdx::tv_pair<B, "Y">>;
STATIC_REQUIRE(stdx::type_lookup_v<M, A> == "X"_cts);
STATIC_REQUIRE(stdx::type_lookup_v<M, B> == "Y"_cts);
}

TEST_CASE("look up string value not in map (by type)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::tv_pair<A, "X">, stdx::tv_pair<B, "Y">>;
STATIC_REQUIRE(stdx::type_lookup_v<M, Z> == stdx::missing);
STATIC_REQUIRE(stdx::type_lookup_v<M, Z, "Z"> == "Z"_cts);
}

TEST_CASE("look up value in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::vv_pair<0, 10>, stdx::vv_pair<1, 11>>;
STATIC_REQUIRE(stdx::value_lookup_v<M, 0> == 10);
Expand All @@ -54,10 +82,24 @@ TEST_CASE("look up value in map (by value)", "[type map]") {

TEST_CASE("look up value not in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::vv_pair<0, 10>, stdx::vv_pair<1, 11>>;
STATIC_REQUIRE(stdx::value_lookup_v<M, 2> == 0);
STATIC_REQUIRE(stdx::value_lookup_v<M, 2> == stdx::missing);
STATIC_REQUIRE(stdx::value_lookup_v<M, 2, 3> == 3);
}

TEST_CASE("look up string value in map (by string value)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vv_pair<"A", "X">, stdx::vv_pair<"B", "Y">>;
STATIC_REQUIRE(stdx::value_lookup_v<M, "A"> == "X"_cts);
STATIC_REQUIRE(stdx::value_lookup_v<M, "B"> == "Y"_cts);
}

TEST_CASE("look up string value not in map (by string value)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vv_pair<"A", "X">, stdx::vv_pair<"B", "Y">>;
STATIC_REQUIRE(stdx::value_lookup_v<M, "C"> == stdx::missing);
STATIC_REQUIRE(stdx::value_lookup_v<M, "C", "Z"> == "Z"_cts);
}

TEST_CASE("reverse look up type in map", "[type map]") {
using M = stdx::type_map<stdx::type_pair<A, X>, stdx::type_pair<B, Y>>;
STATIC_REQUIRE(std::is_same_v<stdx::reverse_type_lookup_t<M, X>, A>);
Expand All @@ -66,7 +108,8 @@ TEST_CASE("reverse look up type in map", "[type map]") {

TEST_CASE("reverse look up type not in map", "[type map]") {
using M = stdx::type_map<stdx::type_pair<A, X>, stdx::type_pair<B, Y>>;
STATIC_REQUIRE(std::is_same_v<stdx::reverse_type_lookup_t<M, Z>, void>);
STATIC_REQUIRE(
std::is_same_v<stdx::reverse_type_lookup_t<M, Z>, stdx::missing_t>);
STATIC_REQUIRE(std::is_same_v<stdx::reverse_type_lookup_t<M, Z, int>, int>);
}

Expand All @@ -78,11 +121,26 @@ TEST_CASE("reverse look up type in map (by value)", "[type map]") {

TEST_CASE("reverse look up type not in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::tv_pair<X, 0>, stdx::tv_pair<Y, 1>>;
STATIC_REQUIRE(std::is_same_v<stdx::reverse_value_lookup_t<M, 2>, void>);
STATIC_REQUIRE(
std::is_same_v<stdx::reverse_value_lookup_t<M, 2>, stdx::missing_t>);
STATIC_REQUIRE(
std::is_same_v<stdx::reverse_value_lookup_t<M, 2, int>, int>);
}

TEST_CASE("reverse look up type in map (by string value)", "[type map]") {
using M = stdx::type_map<stdx::tv_pair<X, "A">, stdx::tv_pair<Y, "B">>;
STATIC_REQUIRE(std::is_same_v<stdx::reverse_value_lookup_t<M, "A">, X>);
STATIC_REQUIRE(std::is_same_v<stdx::reverse_value_lookup_t<M, "B">, Y>);
}

TEST_CASE("reverse look up type not in map (by string value)", "[type map]") {
using M = stdx::type_map<stdx::tv_pair<X, "A">, stdx::tv_pair<Y, "B">>;
STATIC_REQUIRE(
std::is_same_v<stdx::reverse_value_lookup_t<M, "C">, stdx::missing_t>);
STATIC_REQUIRE(
std::is_same_v<stdx::reverse_value_lookup_t<M, "C", int>, int>);
}

TEST_CASE("reverse look up value in map (by type)", "[type map]") {
using M = stdx::type_map<stdx::vt_pair<0, A>, stdx::vt_pair<1, B>>;
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, A> == 0);
Expand All @@ -91,10 +149,24 @@ TEST_CASE("reverse look up value in map (by type)", "[type map]") {

TEST_CASE("reverse look up value not in map (by type)", "[type map]") {
using M = stdx::type_map<stdx::vt_pair<0, A>, stdx::vt_pair<1, B>>;
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, Z> == 0);
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, Z> == stdx::missing);
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, Z, 2> == 2);
}

TEST_CASE("reverse look up string value in map (by type)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vt_pair<"A", A>, stdx::vt_pair<"B", B>>;
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, A> == "A"_cts);
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, B> == "B"_cts);
}

TEST_CASE("reverse look up string value not in map (by type)", "[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vt_pair<"A", A>, stdx::vt_pair<"B", B>>;
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, Z> == stdx::missing);
STATIC_REQUIRE(stdx::reverse_type_lookup_v<M, Z, "C"> == "C"_cts);
}

TEST_CASE("reverse look up value in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::vv_pair<0, 10>, stdx::vv_pair<1, 11>>;
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, 10> == 0);
Expand All @@ -103,6 +175,22 @@ TEST_CASE("reverse look up value in map (by value)", "[type map]") {

TEST_CASE("reverse look up value not in map (by value)", "[type map]") {
using M = stdx::type_map<stdx::vv_pair<0, 10>, stdx::vv_pair<1, 11>>;
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, 2> == 0);
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, 2> == stdx::missing);
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, 2, 3> == 3);
}

TEST_CASE("reverse look up string value in map (by string value)",
"[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vv_pair<"X", "A">, stdx::vv_pair<"Y", "B">>;
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, "A"> == "X"_cts);
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, "B"> == "Y"_cts);
}

TEST_CASE("reverse look up string value not in map (by string value)",
"[type map]") {
using namespace stdx::literals;
using M = stdx::type_map<stdx::vv_pair<"X", "A">, stdx::vv_pair<"Y", "B">>;
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, "C"> == stdx::missing);
STATIC_REQUIRE(stdx::reverse_value_lookup_v<M, "C", "Z"> == "Z"_cts);
}