Skip to content

Commit

Permalink
Doc updates, implementation refinment, testing.
Browse files Browse the repository at this point in the history
bluescarni committed Apr 11, 2024
1 parent 853d22e commit 7d5b4d5
Showing 5 changed files with 28 additions and 27 deletions.
4 changes: 2 additions & 2 deletions doc/custom_construct.rst
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ The invalid state

A :cpp:class:`wrap` object is in the *invalid* state when it is empty, that is, it does not contain
any value. The validity of a :cpp:class:`wrap` can be checked via the :cpp:func:`~wrap::is_invalid()`
function.
and :cpp:func:`is_valid()` functions.

A :cpp:class:`wrap` object can become invalid in a variety of circumstances:

@@ -51,7 +51,7 @@ A :cpp:class:`wrap` object can become invalid in a variety of circumstances:
The only allowed operations on an invalid :cpp:class:`wrap` are:

- destruction,
- the invocation of :cpp:func:`~wrap::is_invalid()`,
- the invocation of :cpp:func:`~wrap::is_invalid()` and :cpp:func:`is_valid()`,
- copy/move assignment from, and swapping with, a valid :cpp:class:`wrap`,
- :ref:`emplacement <emplacement>`,
- generic assignment.
15 changes: 13 additions & 2 deletions doc/wrap.rst
Original file line number Diff line number Diff line change
@@ -101,8 +101,10 @@ The ``wrap`` class
This function will first destroy the value in *w* (if *w* is not already in the :ref:`invalid state <invalid_state>`).
It will then construct in *w* a value of type :cpp:type:`T` using the construction arguments :cpp:type:`Args`.

This function is enabled only if :cpp:type:`T` is not :cpp:class:`wrap` (that is, it is not possible to emplace
a :cpp:class:`wrap` into itself) and if an instance of :cpp:type:`T` can be constructed from :cpp:type:`Args`.
This function is enabled only if an instance of :cpp:type:`T` can be constructed from :cpp:type:`Args`.

Passing *w* as an argument in *args* (e.g., attempting to emplace *w* into itself) will lead to
undefined behaviour.

This function is ``noexcept`` if all these conditions are satisfied:

@@ -126,6 +128,15 @@ The ``wrap`` class

:return: ``true`` if *w* is currently employing static storage, ``false`` otherwise.

.. cpp:function:: [[nodiscard]] friend bool is_valid(const wrap &w) noexcept

This function will return ``false`` if *w* is in the :ref:`invalid state <invalid_state>`,
``true`` otherwise.

:param w: the input argument.

:return: the validity status for *w*.

.. cpp:function:: template <typename IFace, auto Cfg> bool has_dynamic_storage(const wrap<IFace, Cfg> &w) noexcept

Query the storage type of a :cpp:class:`wrap`.
23 changes: 2 additions & 21 deletions include/tanuki/tanuki.hpp
Original file line number Diff line number Diff line change
@@ -1089,11 +1089,6 @@ struct invalid_wrap_t {

inline constexpr invalid_wrap_t invalid_wrap{};

struct nested_wrap_t {
};

inline constexpr nested_wrap_t nested_wrap{};

// Helper to unwrap a std::reference_wrapper and remove reference
// and cv qualifiers from the result.
template <typename T>
@@ -1435,17 +1430,6 @@ class TANUKI_VISIBLE wrap : private detail::wrap_storage<IFace, Cfg.static_size,
ctor_impl<T>(std::forward<U>(args)...);
}

// Nested wrap constructor.
template <typename T>
requires std::default_initializable<ref_iface_t> && std::same_as<wrap, std::remove_cvref_t<T>>
&& detail::holder_constructible_from<detail::value_t_from_arg<T &&>, IFace, Cfg.semantics, T &&>
explicit wrap(nested_wrap_t,
T &&w) noexcept(noexcept(this->ctor_impl<detail::value_t_from_arg<T &&>>(std::forward<T>(w)))
&& detail::nothrow_default_initializable<ref_iface_t>)
{
ctor_impl<detail::value_t_from_arg<T &&>>(std::forward<T>(w));
}

wrap(const wrap &other) noexcept(Cfg.semantics == wrap_semantics::reference)
requires(Cfg.copyable || Cfg.semantics == wrap_semantics::reference) && std::default_initializable<ref_iface_t>
{
@@ -1708,9 +1692,6 @@ class TANUKI_VISIBLE wrap : private detail::wrap_storage<IFace, Cfg.static_size,
#endif

// Emplacement.
// NOTE: need to document that self-emplacement results in undefined
// behaviour because the first step taken by this function is self
// destruction.
template <typename T, typename... Args>
requires
// We must be able to construct the holder.
@@ -1751,7 +1732,7 @@ class TANUKI_VISIBLE wrap : private detail::wrap_storage<IFace, Cfg.static_size,
// The invalid state can also be explicitly set by constructing/assigning
// from invalid_wrap_t.
// The only valid operations on an invalid object are:
// - invocation of is_invalid(),
// - invocation of is_invalid()/is_valid(),
// - destruction,
// - copy/move assignment from, and swapping with, a valid wrap,
// - generic assignment,
@@ -1983,7 +1964,7 @@ template <typename Holder, typename U>
}

template <typename IFace, auto Cfg>
bool is_valid(const wrap<IFace, Cfg> &w) noexcept
[[nodiscard]] bool is_valid(const wrap<IFace, Cfg> &w) noexcept
{
return !is_invalid(w);
}
4 changes: 2 additions & 2 deletions test/ranges.hpp
Original file line number Diff line number Diff line change
@@ -197,7 +197,7 @@ struct generic_range_iface_impl<Base, Holder, T, V, R, RR, CR, CRR, It> : public
// functions contains a user-defined iterator of type ud_iter_t (and that will not be
// the case, leading to a runtime exception).
if constexpr (std::same_as<ud_iter_t, It<V, R, RR>>) {
return It<V, R, RR>(tanuki::nested_wrap, begin_end_impl::b(getval<Holder>(this)));
return It<V, R, RR>(std::in_place_type<It<V, R, RR>>, begin_end_impl::b(getval<Holder>(this)));
} else {
return make_generic_iterator<It>{}(begin_end_impl::b(getval<Holder>(this)));
}
@@ -214,7 +214,7 @@ struct generic_range_iface_impl<Base, Holder, T, V, R, RR, CR, CRR, It> : public
using ud_iter_t = decltype(begin_end_impl::b(getval<Holder>(this)));

if constexpr (std::same_as<ud_iter_t, It<V, CR, CRR>>) {
return It<V, CR, CRR>(tanuki::nested_wrap, begin_end_impl::b(getval<Holder>(this)));
return It<V, CR, CRR>(std::in_place_type<It<V, CR, CRR>>, begin_end_impl::b(getval<Holder>(this)));
} else {
return make_generic_iterator<It>{}(begin_end_impl::b(getval<Holder>(this)));
}
9 changes: 9 additions & 0 deletions test/test_basics.cpp
Original file line number Diff line number Diff line change
@@ -207,6 +207,15 @@ TEST_CASE("basics")

// Same on holder_align.
REQUIRE(!with_holder_align<nonwrappable, any_iface>);

// In-place nested construction.
wrap_t w4(4.), w5(std::in_place_type<wrap_t>, w4);
REQUIRE(!noexcept(wrap_t(std::in_place_type<wrap_t>, w4)));
REQUIRE(value_ref<double>(value_ref<wrap_t>(w5)) == 4.);

wrap_t w8(large{.str = "foobar"}), w9(std::in_place_type<wrap_t>, std::move(w8));
REQUIRE(value_ref<large>(value_ref<wrap_t>(w9)).str == "foobar");
REQUIRE(is_invalid(w8));
}

TEST_CASE("assignment")

0 comments on commit 7d5b4d5

Please sign in to comment.