diff --git a/include/stdx/type_traits.hpp b/include/stdx/type_traits.hpp index 515fa98..e35b729 100644 --- a/include/stdx/type_traits.hpp +++ b/include/stdx/type_traits.hpp @@ -68,6 +68,9 @@ constexpr bool is_callable_v = is_function_v or is_function_object_v; template struct type_identity { using type = T; + [[nodiscard]] friend constexpr auto operator==(type_identity const &, + type_identity const &) + -> bool = default; }; template using type_identity_t = typename type_identity::type; template diff --git a/include/stdx/utility.hpp b/include/stdx/utility.hpp index 4e91509..2665520 100644 --- a/include/stdx/utility.hpp +++ b/include/stdx/utility.hpp @@ -35,6 +35,11 @@ template struct map_value_t { constexpr static inline auto value = V.value; }; +template consteval auto unwrap_map_value(T t) -> T { return t; } +template consteval auto unwrap_map_value(map_value_t) { + return V.value; +} + template struct type_map : Ts... {}; } // namespace detail @@ -52,8 +57,6 @@ using tv_pair = tt_pair>; template using vv_pair = tt_pair, detail::map_value_t>; -template using type_map = shrink_t>; - namespace detail { template constexpr static auto lookup(...) -> Default; @@ -121,6 +124,36 @@ constexpr static auto reverse_value_lookup_v = missing_t>(expand())), detail::map_value_t>::value; +template +class type_map : public shrink_t> { + using map_t = shrink_t>; + + template consteval static auto try_lookup() { + using V = type_lookup_t; + if constexpr (requires { V{}; }) { + return detail::unwrap_map_value(V{}); + } else { + return type_identity_v; + } + } + + public: + template consteval auto operator[](K) const { + if constexpr (not same_as()), missing_t>) { + return try_lookup(); + } else if constexpr (requires { typename K::type; }) { + return try_lookup(); + } else { + return missing; + } + } + + template consteval auto operator()(K) const { + return this->operator[]( + detail::map_value_t{}); + } +}; + template [[nodiscard]] constexpr auto forward_like(U &&u) noexcept -> decltype(auto) { constexpr auto t_is_const = std::is_const_v>; diff --git a/test/type_map.cpp b/test/type_map.cpp index a4773df..df13846 100644 --- a/test/type_map.cpp +++ b/test/type_map.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -13,184 +14,273 @@ struct Z; TEST_CASE("look up type in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE(std::is_same_v, X>); - STATIC_REQUIRE(std::is_same_v, Y>); + STATIC_CHECK(std::is_same_v, X>); + STATIC_CHECK(std::is_same_v, Y>); } TEST_CASE("look up type not in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE(std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, stdx::missing_t>); + STATIC_CHECK(std::is_same_v, int>); } TEST_CASE("look up type in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, Y>>; - STATIC_REQUIRE(std::is_same_v, X>); - STATIC_REQUIRE(std::is_same_v, Y>); + STATIC_CHECK(std::is_same_v, X>); + STATIC_CHECK(std::is_same_v, Y>); } TEST_CASE("look up type not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, Y>>; - STATIC_REQUIRE(std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, stdx::missing_t>); + STATIC_CHECK(std::is_same_v, int>); } TEST_CASE("look up type in map (by string value)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<"B", Y>>; - STATIC_REQUIRE(std::is_same_v, X>); - STATIC_REQUIRE(std::is_same_v, Y>); + STATIC_CHECK(std::is_same_v, X>); + STATIC_CHECK(std::is_same_v, Y>); } TEST_CASE("look up type not in map (by string value)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<"B", Y>>; - STATIC_REQUIRE( - std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, stdx::missing_t>); + STATIC_CHECK(std::is_same_v, int>); } TEST_CASE("look up value in map (by type)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(stdx::type_lookup_v == 0); - STATIC_REQUIRE(stdx::type_lookup_v == 1); + STATIC_CHECK(stdx::type_lookup_v == 0); + STATIC_CHECK(stdx::type_lookup_v == 1); } TEST_CASE("look up value not in map (by type)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(stdx::type_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::type_lookup_v == 2); + STATIC_CHECK(stdx::type_lookup_v == stdx::missing); + STATIC_CHECK(stdx::type_lookup_v == 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>; - STATIC_REQUIRE(stdx::type_lookup_v == "X"_cts); - STATIC_REQUIRE(stdx::type_lookup_v == "Y"_cts); + STATIC_CHECK(stdx::type_lookup_v == "X"_cts); + STATIC_CHECK(stdx::type_lookup_v == "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>; - STATIC_REQUIRE(stdx::type_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::type_lookup_v == "Z"_cts); + STATIC_CHECK(stdx::type_lookup_v == stdx::missing); + STATIC_CHECK(stdx::type_lookup_v == "Z"_cts); } TEST_CASE("look up value in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::value_lookup_v == 10); - STATIC_REQUIRE(stdx::value_lookup_v == 11); + STATIC_CHECK(stdx::value_lookup_v == 10); + STATIC_CHECK(stdx::value_lookup_v == 11); } TEST_CASE("look up value not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::value_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::value_lookup_v == 3); + STATIC_CHECK(stdx::value_lookup_v == stdx::missing); + STATIC_CHECK(stdx::value_lookup_v == 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<"B", "Y">>; - STATIC_REQUIRE(stdx::value_lookup_v == "X"_cts); - STATIC_REQUIRE(stdx::value_lookup_v == "Y"_cts); + STATIC_CHECK(stdx::value_lookup_v == "X"_cts); + STATIC_CHECK(stdx::value_lookup_v == "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<"B", "Y">>; - STATIC_REQUIRE(stdx::value_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::value_lookup_v == "Z"_cts); + STATIC_CHECK(stdx::value_lookup_v == stdx::missing); + STATIC_CHECK(stdx::value_lookup_v == "Z"_cts); } TEST_CASE("reverse look up type in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE(std::is_same_v, A>); - STATIC_REQUIRE(std::is_same_v, B>); + STATIC_CHECK(std::is_same_v, A>); + STATIC_CHECK(std::is_same_v, B>); } TEST_CASE("reverse look up type not in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE( + STATIC_CHECK( std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, int>); } TEST_CASE("reverse look up type in map (by value)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(std::is_same_v, X>); - STATIC_REQUIRE(std::is_same_v, Y>); + STATIC_CHECK(std::is_same_v, X>); + STATIC_CHECK(std::is_same_v, Y>); } TEST_CASE("reverse look up type not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE( + STATIC_CHECK( std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE( - std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, int>); } TEST_CASE("reverse look up type in map (by string value)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(std::is_same_v, X>); - STATIC_REQUIRE(std::is_same_v, Y>); + STATIC_CHECK(std::is_same_v, X>); + STATIC_CHECK(std::is_same_v, Y>); } TEST_CASE("reverse look up type not in map (by string value)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE( + STATIC_CHECK( std::is_same_v, stdx::missing_t>); - STATIC_REQUIRE( + STATIC_CHECK( std::is_same_v, int>); } TEST_CASE("reverse look up value in map (by type)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, B>>; - STATIC_REQUIRE(stdx::reverse_type_lookup_v == 0); - STATIC_REQUIRE(stdx::reverse_type_lookup_v == 1); + STATIC_CHECK(stdx::reverse_type_lookup_v == 0); + STATIC_CHECK(stdx::reverse_type_lookup_v == 1); } TEST_CASE("reverse look up value not in map (by type)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, B>>; - STATIC_REQUIRE(stdx::reverse_type_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::reverse_type_lookup_v == 2); + STATIC_CHECK(stdx::reverse_type_lookup_v == stdx::missing); + STATIC_CHECK(stdx::reverse_type_lookup_v == 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<"B", B>>; - STATIC_REQUIRE(stdx::reverse_type_lookup_v == "A"_cts); - STATIC_REQUIRE(stdx::reverse_type_lookup_v == "B"_cts); + STATIC_CHECK(stdx::reverse_type_lookup_v == "A"_cts); + STATIC_CHECK(stdx::reverse_type_lookup_v == "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<"B", B>>; - STATIC_REQUIRE(stdx::reverse_type_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::reverse_type_lookup_v == "C"_cts); + STATIC_CHECK(stdx::reverse_type_lookup_v == stdx::missing); + STATIC_CHECK(stdx::reverse_type_lookup_v == "C"_cts); } TEST_CASE("reverse look up value in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::reverse_value_lookup_v == 0); - STATIC_REQUIRE(stdx::reverse_value_lookup_v == 1); + STATIC_CHECK(stdx::reverse_value_lookup_v == 0); + STATIC_CHECK(stdx::reverse_value_lookup_v == 1); } TEST_CASE("reverse look up value not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::reverse_value_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::reverse_value_lookup_v == 3); + STATIC_CHECK(stdx::reverse_value_lookup_v == stdx::missing); + STATIC_CHECK(stdx::reverse_value_lookup_v == 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<"Y", "B">>; - STATIC_REQUIRE(stdx::reverse_value_lookup_v == "X"_cts); - STATIC_REQUIRE(stdx::reverse_value_lookup_v == "Y"_cts); + STATIC_CHECK(stdx::reverse_value_lookup_v == "X"_cts); + STATIC_CHECK(stdx::reverse_value_lookup_v == "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<"Y", "B">>; - STATIC_REQUIRE(stdx::reverse_value_lookup_v == stdx::missing); - STATIC_REQUIRE(stdx::reverse_value_lookup_v == "Z"_cts); + STATIC_CHECK(stdx::reverse_value_lookup_v == stdx::missing); + STATIC_CHECK(stdx::reverse_value_lookup_v == "Z"_cts); +} + +namespace { +template struct S { + [[nodiscard]] friend constexpr auto operator==(S const &, S const &) + -> bool = default; +}; +} // namespace + +TEST_CASE("operator[] lookup (type -> type)", "[type map]") { + using M = stdx::type_map, S<100>>, + stdx::tt_pair, S<101>>>; + constexpr auto m = M{}; + STATIC_CHECK(m[S<0>{}] == S<100>{}); + STATIC_CHECK(m[S<1>{}] == S<101>{}); + STATIC_CHECK(m[S<2>{}] == stdx::missing); +} + +TEST_CASE("operator[] lookup (type -> type) (incomplete types)", "[type map]") { + using M = stdx::type_map, stdx::tt_pair>; + constexpr auto m = M{}; + STATIC_CHECK(m[stdx::type_identity_v] == stdx::type_identity_v); + STATIC_CHECK(m[stdx::type_identity_v] == stdx::type_identity_v); + STATIC_CHECK(m[stdx::type_identity_v] == stdx::missing); +} + +TEST_CASE("operator[] lookup (type -> value)", "[type map]") { + using M = stdx::type_map, 0>, stdx::tv_pair, 1>>; + constexpr auto m = M{}; + STATIC_CHECK(m[S<0>{}] == 0); + STATIC_CHECK(m[S<1>{}] == 1); + STATIC_CHECK(m[S<2>{}] == stdx::missing); +} + +TEST_CASE("operator[] lookup (type -> value) (incomplete types)", + "[type map]") { + using M = stdx::type_map, stdx::tv_pair>; + constexpr auto m = M{}; + STATIC_CHECK(m[stdx::type_identity_v] == 0); + STATIC_CHECK(m[stdx::type_identity_v] == 1); + STATIC_CHECK(m[stdx::type_identity_v] == stdx::missing); +} + +TEST_CASE("operator() lookup (value -> type)", "[type map]") { + using namespace stdx::literals; + using M = + stdx::type_map>, stdx::vt_pair<1u, S<101>>>; + constexpr auto m = M{}; + STATIC_CHECK(m(0_c) == S<100>{}); + STATIC_CHECK(m(1_c) == S<101>{}); + STATIC_CHECK(m(2_c) == stdx::missing); +} + +TEST_CASE("operator() lookup (value -> type) (incomplete types)", + "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vt_pair<1u, B>>; + constexpr auto m = M{}; + STATIC_CHECK(m(0_c) == stdx::type_identity_v); + STATIC_CHECK(m(1_c) == stdx::type_identity_v); + STATIC_CHECK(m(2_c) == stdx::missing); +} + +TEST_CASE("operator() lookup (value -> value)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<1u, 101>>; + constexpr auto m = M{}; + STATIC_CHECK(m(0_c) == 100); + STATIC_CHECK(m(1_c) == 101); + STATIC_CHECK(m(2_c) == stdx::missing); +} + +TEST_CASE("operator() lookup (string -> string)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<"B", "Y">>; + constexpr auto m = M{}; + STATIC_CHECK(m("A"_ctst) == "X"_cts); + STATIC_CHECK(m("B"_ctst) == "Y"_cts); + STATIC_CHECK(m("C"_ctst) == stdx::missing); +} + +TEST_CASE("multi-depth lookup", "[type map]") { + using namespace stdx::literals; + using P = stdx::type_map, stdx::vv_pair<"2", "M">>; + using Q = stdx::type_map, stdx::vv_pair<"2", "O">>; + using M = stdx::type_map, stdx::vt_pair<"B", Q>>; + constexpr auto m = M{}; + STATIC_CHECK(m("A"_ctst)("1"_ctst) == "L"_cts); + STATIC_CHECK(m("A"_ctst)("2"_ctst) == "M"_cts); + STATIC_CHECK(m("B"_ctst)("1"_ctst) == "N"_cts); + STATIC_CHECK(m("B"_ctst)("2"_ctst) == "O"_cts); }