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
12 changes: 5 additions & 7 deletions include/fn/detail/pack_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ template <::std::size_t I, typename T> struct _element {
template <typename, typename... Ts> struct pack_impl;

template <typename... Ts> struct _pack_append;
// A pack never holds a sum. The specialization is defined but has no `type`, so naming
// `append_type<sum>` is a substitution failure rather than a use of an incomplete type; and the
// two specializations must exclude each other, or both match a sum and the choice is ambiguous.
template <typename T, typename... Ts>
requires _some_sum<T>
struct _pack_append<T, Ts...>;
struct _pack_append<T, Ts...> {};
template <typename T, typename... Ts>
requires(not _some_pack<T>)
requires(not _some_pack<T>) && (not _some_sum<T>)
struct _pack_append<T, Ts...> {
using impl = pack_impl<::std::index_sequence_for<Ts..., T>, Ts..., T>;
using type = ::fn::pack<Ts..., T>;
Expand Down Expand Up @@ -98,11 +101,6 @@ struct pack_impl<::std::index_sequence<Is...>, Ts...> : _element<Is, Ts>... {
return type{static_cast<apply_const_lvalue_t<Self, Ts &&>>(FWD(self)._element<Is, Ts>::v)..., FWD(args)...};
});
}

template <typename T, typename Self>
static constexpr auto _append(Self &&self, auto &&...args) noexcept
requires _some_sum<T>
= delete;
};

} // namespace fn::detail
Expand Down
12 changes: 8 additions & 4 deletions include/fn/pack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ template <typename... Ts> struct pack : detail::pack_impl<::std::index_sequence_
*/
template <typename Arg>
[[nodiscard]] constexpr auto append(Arg &&arg) & noexcept -> append_type<Arg>
requires requires { append_type<Arg>{_impl::template _append<Arg>(*this, FWD(arg))}; }
requires(not some_in_place_type<Arg>)
&& requires { append_type<Arg>{_impl::template _append<Arg>(*this, FWD(arg))}; }
{
return {_impl::template _append<Arg>(*this, FWD(arg))};
}
Expand All @@ -120,7 +121,8 @@ template <typename... Ts> struct pack : detail::pack_impl<::std::index_sequence_
*/
template <typename Arg>
[[nodiscard]] constexpr auto append(Arg &&arg) const & noexcept -> append_type<Arg>
requires requires { append_type<Arg>{_impl::template _append<Arg>(*this, FWD(arg))}; }
requires(not some_in_place_type<Arg>)
&& requires { append_type<Arg>{_impl::template _append<Arg>(*this, FWD(arg))}; }
{
return {_impl::template _append<Arg>(*this, FWD(arg))};
}
Expand All @@ -134,7 +136,8 @@ template <typename... Ts> struct pack : detail::pack_impl<::std::index_sequence_
*/
template <typename Arg>
[[nodiscard]] constexpr auto append(Arg &&arg) && noexcept -> append_type<Arg>
requires requires { append_type<Arg>{_impl::template _append<Arg>(::std::move(*this), FWD(arg))}; }
requires(not some_in_place_type<Arg>)
&& requires { append_type<Arg>{_impl::template _append<Arg>(::std::move(*this), FWD(arg))}; }
{
return {_impl::template _append<Arg>(::std::move(*this), FWD(arg))};
}
Expand All @@ -148,7 +151,8 @@ template <typename... Ts> struct pack : detail::pack_impl<::std::index_sequence_
*/
template <typename Arg>
[[nodiscard]] constexpr auto append(Arg &&arg) const && noexcept -> append_type<Arg>
requires requires { append_type<Arg>{_impl::template _append<Arg>(::std::move(*this), FWD(arg))}; }
requires(not some_in_place_type<Arg>)
&& requires { append_type<Arg>{_impl::template _append<Arg>(::std::move(*this), FWD(arg))}; }
{
return {_impl::template _append<Arg>(::std::move(*this), FWD(arg))};
}
Expand Down
30 changes: 30 additions & 0 deletions tests/fn/pack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ template <fn::pack<int, double> P> struct pack_nttp final {};
template <fn::some_pack auto P> struct some_pack_nttp final {};
template <fn::some_pack auto P> auto read_nttp() { return fn::get<0>(P); }

template <typename V, typename T, typename... Args>
concept can_append_in_place = requires(V v, Args... args) { FWD(v).append(std::in_place_type<T>, args...); };

template <typename V, typename Arg>
concept can_append = requires(V v, Arg arg) { FWD(v).append(FWD(arg)); };

} // namespace

TEST_CASE("pack", "[pack]")
Expand Down Expand Up @@ -238,6 +244,30 @@ TEST_CASE("append value categories", "[pack][append]")
return i && j == 3 && b1.v == 14 && c.v == 30 && b2.v == 20;
}));
}

WHEN("constraints")
{
static_assert(can_append_in_place<T &, B, int>);
static_assert(can_append_in_place<T &, B, int, int>);
static_assert(not can_append_in_place<T &, B, char const *>); // B is not constructible from it

// in_place_type selects the element type, it is never itself an element: with no arguments and
// no default constructor there is nothing to construct, and the deduced-Arg overload must not
// pick the call up and append the tag instead
static_assert(not can_append_in_place<T &, B>);
static_assert(not can_append<T &, std::in_place_type_t<B> const &>);
// C has a default constructor, so the same call still means "construct the element"
static_assert(can_append_in_place<T &, C>);
static_assert(std::same_as<decltype(std::declval<T &>().append(std::in_place_type<C>)), T::append_type<C>>);

// A pack never holds a sum, in either spelling - and asking must answer, not hard-error
static_assert(not can_append<T &, fn::sum<int>>);
static_assert(not can_append<T &, fn::sum<int> &>);
static_assert(not can_append_in_place<T &, fn::sum<int>, fn::sum<int>>);
static_assert(not can_append_in_place<T &, fn::sum_for<bool, int>, fn::sum_for<bool, int>>);

SUCCEED();
Comment thread
Bronek marked this conversation as resolved.
}
}

TEST_CASE("pack with immovable data", "[pack][immovable]")
Expand Down