Skip to content
Open
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
22 changes: 13 additions & 9 deletions include/stdx/ct_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,25 @@ template <ct_string S> consteval auto operator""_ctst() { return cts_t<S>{}; }
} // namespace ct_string_literals
} // namespace literals

template <ct_string Name> struct with_name {
constexpr static auto name = Name;
template <ct_string S> consteval auto shrink() -> decltype(shrink_impl<S>()) {
return {};
}

namespace detail {
template <auto Name> struct with_name {
constexpr static auto name = expand(Name);
friend constexpr auto operator==(with_name, with_name) -> bool = default;
};

namespace detail {
template <typename T>
concept is_ct_string =
stdx::is_value_specialization_of_v<std::remove_cvref_t<T>, ct_string>;
}
} // namespace detail

template <ct_string Name> using with_name = detail::with_name<shrink<Name>()>;
template <auto Name>
requires detail::is_shrunk_value<decltype(Name)>
using with_shrunk_name = detail::with_name<Name>;

template <typename T>
concept named = requires {
Expand Down Expand Up @@ -242,10 +251,5 @@ template <std::size_t N>
// NOLINTNEXTLINE(*-avoid-c-arrays)
map_value_helper(char const (&)[N]) -> map_value_helper<ct_string<N>>;
} // namespace detail

template <ct_string S>
consteval auto shrink() -> decltype(detail::shrink<S>()) {
return {};
}
} // namespace v1
} // namespace stdx
45 changes: 31 additions & 14 deletions include/stdx/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,6 @@ template <typename T>
concept is_shrinkwrapped_value =
requires { typename T::is_shrinkwrapped_value; };

template <typename T,
auto F = []()->auto (*)() -> shrinkwrap<T> { return nullptr; }>
consteval auto shrink() {
return F;
}

template <auto T,
auto F = []()->auto (*)() -> shrinkwrap_value<T> { return nullptr; }>
consteval auto shrink() {
return F;
}

template <typename T>
concept is_shrunk = requires(T const &t) {
{ t()() } -> is_shrinkwrapped;
Expand All @@ -256,6 +244,35 @@ concept is_shrunk_value = requires(T const &t) {
{ t()() } -> is_shrinkwrapped_value;
};

} // namespace detail
} // namespace v1
} // namespace stdx

namespace stdx {
template <typename T,
auto F = []()->auto (*)() -> detail::shrinkwrap<T> {
return nullptr;
}>
consteval auto shrink_impl() {
if constexpr (detail::is_shrunk<T> or detail::is_shrunk_value<T>) {
return T{};
} else {
return F;
}
}

template <auto V,
auto F = []()->auto (*)() -> detail::shrinkwrap_value<V> {
return nullptr;
}>
consteval auto shrink_impl() {
return F;
}
} // namespace stdx

namespace stdx {
inline namespace v1 {
namespace detail {
template <typename T> consteval auto maybe_expand() -> T;
template <is_shrunk T>
consteval auto maybe_expand() -> typename decltype(T{}()())::type;
Expand All @@ -282,13 +299,13 @@ template <is_shrunk_value T> constexpr auto maybe_expand(T &&) {
}
} // namespace detail

template <typename T> consteval auto shrink() -> decltype(detail::shrink<T>()) {
template <typename T> consteval auto shrink() -> decltype(shrink_impl<T>()) {
static_assert(
always_false_v<T>,
"shrink<T>() should not be called outside an unevaluated context");
}
template <typename T>
consteval auto shrink(T const &) -> decltype(detail::shrink<T>()) {
consteval auto shrink(T const &) -> decltype(shrink_impl<T>()) {
return {};
}

Expand Down
13 changes: 13 additions & 0 deletions test/ct_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,19 @@ TEST_CASE("named object", "[ct_string]") {
STATIC_CHECK(not stdx::same_name_q<T>::template fn<U>::value);
}

TEST_CASE("named object with shrunk name", "[ct_string]") {
using namespace stdx::ct_string_literals;
constexpr auto n = stdx::shrink<"test">();
using T = stdx::with_shrunk_name<n>;
STATIC_CHECK(stdx::named<T>);
STATIC_CHECK(stdx::name_of_v<T> == "test"_cts);
STATIC_CHECK(stdx::constant_name_of_v<T> == "test"_ctst);

STATIC_CHECK(stdx::matching_name<T, "test">);
STATIC_CHECK(stdx::matching_name_t<T, "test">::value);
STATIC_CHECK(stdx::matching_name_q<"test">::template fn<T>::value);
}

TEST_CASE("name or type fallback", "[ct_string]") {
using namespace stdx::ct_string_literals;
using T = stdx::with_name<"test">;
Expand Down
22 changes: 22 additions & 0 deletions test/type_traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ TEST_CASE("type shrinkage (by type)", "[type_traits]") {
STATIC_CHECK(std::is_same_v<stdx::expand_t<X>, C>);
}

TEST_CASE("type shrinkage is idempotent", "[type_traits]") {
using X = stdx::shrink_t<C>;
using Y = stdx::shrink_t<X>;
STATIC_CHECK(std::is_same_v<X, Y>);
}

TEST_CASE("two shrunk types are considered the same", "[type_traits]") {
using X = stdx::shrink_t<C>;
using Y = stdx::shrink_t<C>;
Expand All @@ -260,6 +266,14 @@ TEST_CASE("type shrinkage (by value)", "[type_traits]") {
STATIC_CHECK(std::is_same_v<decltype(y), C>);
}

TEST_CASE("type shrinkage (by value) is idempotent", "[type_traits]") {
auto c = C{};
auto x = stdx::shrink(c);
auto y = stdx::shrink(x);
STATIC_CHECK(std::is_same_v<decltype(x), decltype(y)>);
STATIC_CHECK(x == y);
}

TEST_CASE("value shrinkage for ct_strings", "[type_traits]") {
using namespace stdx::literals;
constexpr auto x = stdx::shrink<"hello"_cts>();
Expand All @@ -272,6 +286,14 @@ TEST_CASE("two shrunk ct_strings are considered the same type",
constexpr auto x = stdx::shrink<"hello">();
constexpr auto y = stdx::shrink<"hello">();
STATIC_CHECK(std::is_same_v<decltype(x), decltype(y)>);
STATIC_CHECK(x == y);
}

TEST_CASE("ct_string shrinkage is idempotent", "[type_traits]") {
constexpr auto x = stdx::shrink<"hello">();
constexpr auto y = stdx::shrink(x);
STATIC_CHECK(std::is_same_v<decltype(x), decltype(y)>);
STATIC_CHECK(x == y);
}

namespace {
Expand Down