Skip to content

Commit a3f72f7

Browse files
committed
🎨 Adjust type shrink/expand functions
Problem: - Sometimes it would be more convenient to call `shrink(x)` or `expand(y)` with existing values rather than using e.g. `shrink_t<std::remove_cvref_t<decltype(x)>>` and the `expand_t` equivalent. Solution: - Add appropriate `shrink` and `expand` functions.
1 parent 2b1e4a3 commit a3f72f7

2 files changed

Lines changed: 71 additions & 11 deletions

File tree

include/stdx/type_traits.hpp

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,22 +244,71 @@ CONSTEVAL auto shrink() {
244244
}
245245

246246
template <typename T>
247-
concept is_shrunk = requires(T t) {
247+
concept is_shrunk = requires(T const &t) {
248248
{ t()() } -> is_shrinkwrapped;
249249
};
250250

251251
template <typename T> CONSTEVAL auto maybe_expand() -> T;
252252
template <is_shrunk T>
253253
CONSTEVAL auto maybe_expand() -> typename decltype(T{}()())::type;
254+
255+
template <typename T> constexpr auto maybe_expand(T &&t) -> decltype(auto) {
256+
return T(std::forward<T>(t));
257+
}
258+
template <is_shrunk T>
259+
constexpr auto maybe_expand(T &&) ->
260+
typename decltype(std::remove_cvref_t<T>{}()())::type {
261+
using R = typename decltype(std::remove_cvref_t<T>{}()())::type;
262+
static_assert(std::is_default_constructible_v<R>,
263+
"expand(T) cannot default-construct the return value: maybe "
264+
"use expand_t<T> instead?");
265+
static_assert(
266+
std::is_empty_v<R>,
267+
"Danger! expand(T) would return a default-constructed but non-empty "
268+
"object: use expand_t<T>{} instead if this is what you want");
269+
return R{};
270+
}
254271
} // namespace detail
255272

256-
template <typename T> CONSTEVAL auto shrink() -> decltype(detail::shrink<T>());
273+
template <typename T> CONSTEVAL auto shrink() -> decltype(detail::shrink<T>()) {
274+
static_assert(
275+
always_false_v<T>,
276+
"shrink<T>() should not be called outside an unevaluated context");
277+
}
278+
template <typename T>
279+
CONSTEVAL auto shrink(T const &) -> decltype(detail::shrink<T>()) {
280+
return {};
281+
}
257282

258283
template <typename T>
259-
CONSTEVAL auto expand() -> decltype(detail::maybe_expand<T>());
284+
CONSTEVAL auto expand() -> decltype(detail::maybe_expand<T>()) {
285+
using R = decltype(detail::maybe_expand<T>());
286+
static_assert(
287+
always_false_v<R>,
288+
"expand<T>() should not be called outside an unevaluated context");
289+
}
290+
template <typename T> constexpr auto expand(T &&t) -> decltype(auto) {
291+
return detail::maybe_expand(std::forward<T>(t));
292+
}
293+
260294
#else
261-
template <typename T> CONSTEVAL auto shrink() -> T;
262-
template <typename T> CONSTEVAL auto expand() -> T;
295+
template <typename T> CONSTEVAL auto shrink() -> T {
296+
static_assert(
297+
always_false_v<T>,
298+
"shrink<T>() should not be called outside an unevaluated context");
299+
}
300+
template <typename T> constexpr auto shrink(T &&t) -> decltype(auto) {
301+
return T(std::forward<T>(t));
302+
}
303+
304+
template <typename T> CONSTEVAL auto expand() -> T {
305+
static_assert(
306+
always_false_v<T>,
307+
"expand<T>() should not be called outside an unevaluated context");
308+
}
309+
template <typename T> constexpr auto expand(T &&t) -> decltype(auto) {
310+
return T(std::forward<T>(t));
311+
}
263312
#endif
264313

265314
template <typename T> using shrink_t = decltype(shrink<T>());

test/type_traits.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,21 +245,32 @@ TEST_CASE("non-structural types", "[type_traits]") {
245245
STATIC_REQUIRE(not stdx::is_structural_v<non_structural::S>);
246246
}
247247

248-
#if __cplusplus >= 202002L
249248
namespace {
250249
template <typename...> struct long_type_name {};
250+
using A = long_type_name<int, int, int, int, int, int, int, int>;
251+
using B = long_type_name<A, A, A, A, A, A, A, A>;
252+
using C = long_type_name<B, B, B, B, B, B, B, B>;
251253
} // namespace
252254

253-
TEST_CASE("type shrinkage", "[type_traits]") {
254-
using A = long_type_name<int, int, int, int, int, int, int, int>;
255-
using B = long_type_name<A, A, A, A, A, A, A, A>;
256-
using C = long_type_name<B, B, B, B, B, B, B, B>;
255+
TEST_CASE("type shrinkage (by type)", "[type_traits]") {
257256
using X = stdx::shrink_t<C>;
257+
#if __cplusplus >= 202002L
258258
STATIC_CHECK(stdx::type_as_string<X>().size() <
259259
stdx::type_as_string<C>().size());
260-
STATIC_CHECK(std::same_as<stdx::expand_t<X>, C>);
260+
#endif
261+
STATIC_CHECK(std::is_same_v<stdx::expand_t<X>, C>);
261262
}
263+
264+
TEST_CASE("type shrinkage (by value)", "[type_traits]") {
265+
auto c = C{};
266+
auto x = stdx::shrink(c);
267+
auto y = stdx::expand(x);
268+
#if __cplusplus >= 202002L
269+
STATIC_CHECK(stdx::type_as_string<decltype(x)>().size() <
270+
stdx::type_as_string<C>().size());
262271
#endif
272+
STATIC_CHECK(std::is_same_v<decltype(y), C>);
273+
}
263274

264275
TEST_CASE("nth type in pack", "[type_traits]") {
265276
STATIC_REQUIRE(

0 commit comments

Comments
 (0)