Skip to content

Commit

Permalink
Merge pull request #62 from bluescarni/pr/iter
Browse files Browse the repository at this point in the history
Doc improvements
  • Loading branch information
bluescarni authored May 21, 2024
2 parents 51382ea + 09be4d8 commit 7d4d4f8
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ tanuki
> classics and in the folklore and legends of various places in Japan. They are reputed to be mischievous and jolly,
> masters of disguise and shapeshifting but somewhat gullible and absent-minded.
tanuki is a small, single-header and self-contained C++20 toolkit for
tanuki is a small, single-header and self-contained C++20/23 toolkit for
[type-erasure](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure).
Main features include:

Expand Down
7 changes: 7 additions & 0 deletions doc/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,25 @@ Configuration options

.. cpp:var:: wrap_semantics semantics = wrap_semantics::value

This option selects the semantics for the :cpp:class:`wrap` class.

.. cpp:var:: bool copyable = true

This option selects whether or not the :cpp:class:`wrap` class is copy constructible/assignable.

This option is ignored when employing :ref:`reference semantics <ref_semantics>`.

.. cpp:var:: bool movable = true

This option selects whether or not the :cpp:class:`wrap` class is move constructible/assignable.

This option is ignored when employing :ref:`reference semantics <ref_semantics>`.

.. cpp:var:: bool swappable = true

This option selects whether or not the :cpp:class:`wrap` class is swappable.

This option is ignored when employing :ref:`reference semantics <ref_semantics>`.

.. cpp:var:: inline constexpr auto default_config = config{}

Expand Down
3 changes: 2 additions & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tanuki
classics and in the folklore and legends of various places in Japan. They are reputed to be mischievous and jolly,
masters of disguise and shapeshifting but somewhat gullible and absent-minded.

tanuki is a small, single-header and self-contained C++20 toolkit for
tanuki is a small, single-header and self-contained C++20/23 toolkit for
`type-erasure <https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure>`__.
Main features include:

Expand All @@ -28,6 +28,7 @@ default implementations, single/multiple inheritance, etc.) with C++20 concepts.
.. toctree::
:maxdepth: 1

installation.rst
tutorials.rst
case_studies.rst
api_reference.rst
18 changes: 18 additions & 0 deletions doc/installation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Installation
============

tanuki currently consists of a self-contained single header, with no dependencies
apart from the standard library. Just grab the ``include/tanuki.hpp`` header and copy it
somewhere in your project.

Supported platforms/compilers
-----------------------------

tanuki requires a C++ compiler with decent support for several C++20 features.
Currently the following compilers are tested in the CI pipeline:

- GCC >= 10.4,
- clang >= 14,
- MSVC 2022.

The CI pipeline is run on several platforms, including x86-64/arm64 Linux, x86-64 OSX and Windows.
2 changes: 2 additions & 0 deletions doc/ref_interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ Second, we can use the custom configuration instance to define another wrap type
foo_iface_impl calling foo()
.. _ref_interface23:

Reference interfaces in C++23
-----------------------------

Expand Down
6 changes: 3 additions & 3 deletions doc/simple_interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ an implementation for the ``void foo() const`` function:
:lines: 5-12

The ``Holder`` template parameter is a class defined in the tanuki library which stores the
value we are type-erasing as the ``m_value`` data member. ``Holder`` derives from ``foo1_iface_impl``
and thus we can reach the ``m_value`` data member via the cast
value we are type-erasing as the ``_tanuki_value`` data member. ``Holder`` derives from ``foo1_iface_impl``
and thus we can reach the ``_tanuki_value`` data member via the cast
``static_cast<const Holder *>(this)`` leveraging the
`curiously recurring template pattern (CRTP) <https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`__.
In other words, this implementation of the ``void foo() const`` function will invoke
Expand Down Expand Up @@ -106,7 +106,7 @@ function (such as, e.g., an ``int``)? The compiler will loudly complain:
.. code-block:: console
simple_interface.cpp:34:23: error: request for member ‘foo’ in [...] which is of non-class type ‘const int’
34 | m_value.foo();
34 | _tanuki_value.foo();
This is of course not ideal, for at least a couple of reasons:

Expand Down
16 changes: 16 additions & 0 deletions doc/std_function.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ the pointer to the interface via the :cpp:func:`~wrap::iface_ptr()` function, wh
the interface's call operator. The arguments are perfectly forwarded, and expression SFINAE is used
(via the trailing ``decltype(...)``) to disable the call operator if it is malformed.

Note that the visual complexity of this call operator is mainly due to CRTP limitations when employing C++20.
In :ref:`C++23 <ref_interface23>`, the call operator could be simplified to something like this:

.. code-block:: c++

template <typename Wrap, typename... FArgs>
auto operator()(this const Wrap &self,
FArgs &&...fargs) -> decltype(iface_ptr(self)->operator()(std::forward<FArgs>(fargs)...))
{
if (is_invalid(self)) {
throw std::bad_function_call{};
}

return iface_ptr(self)->operator()(std::forward<FArgs>(fargs)...);
}

Next, we take a look at the ``bool`` conversion operator:

.. literalinclude:: ../tutorial/std_function.cpp
Expand Down
2 changes: 1 addition & 1 deletion doc/wrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The ``wrap`` class
==================

.. cpp:class:: template <typename IFace, auto Cfg = default_config> requires valid_config<Cfg> wrap
.. cpp:class:: template <typename IFace, auto Cfg = default_config> requires std::is_class_v<IFace> && std::same_as<IFace, std::remove_cv_t<IFace>> && valid_config<Cfg> wrap

.. cpp:function:: wrap()

Expand Down
42 changes: 21 additions & 21 deletions include/tanuki/tanuki.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ concept iface_has_impl = requires() {
// are checked in the generic ctors of the wrap class.
template <typename T, typename IFace, wrap_semantics Sem>
struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holder<T, IFace, Sem>, T, Sem> {
TANUKI_NO_UNIQUE_ADDRESS T m_value;
TANUKI_NO_UNIQUE_ADDRESS T _tanuki_value;

// Make sure we don't end up accidentally copying/moving
// this class.
Expand Down Expand Up @@ -609,15 +609,15 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
explicit holder(U &&x) noexcept(
std::is_nothrow_constructible_v<T, U &&>
&& detail::nothrow_default_initializable<detail::impl_from_iface<IFace, holder<T, IFace, Sem>, T, Sem>>)
: m_value(std::forward<U>(x))
: _tanuki_value(std::forward<U>(x))
{
}
template <typename... U>
requires(sizeof...(U) != 1u)
explicit holder(U &&...x) noexcept(
std::is_nothrow_constructible_v<T, U &&...>
&& detail::nothrow_default_initializable<detail::impl_from_iface<IFace, holder<T, IFace, Sem>, T, Sem>>)
: m_value(std::forward<U>(x)...)
: _tanuki_value(std::forward<U>(x)...)
{
}

Expand All @@ -636,7 +636,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
}
[[nodiscard]] void *_tanuki_value_ptr() noexcept final
{
return std::addressof(m_value);
return std::addressof(_tanuki_value);
}

[[nodiscard]] bool _tanuki_value_is_reference() const noexcept final
Expand All @@ -654,7 +654,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
// there is a default-constructible interface implementation.
if constexpr (std::is_copy_constructible_v<T>) {
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
return new holder(m_value);
return new holder(_tanuki_value);
}

// NOTE: we should never reach this point as we are using this function
Expand All @@ -666,7 +666,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
[[nodiscard]] std::shared_ptr<detail::value_iface<IFace, Sem>> _tanuki_shared_clone_holder() const final
{
if constexpr (std::is_copy_constructible_v<T>) {
return std::make_shared<holder>(m_value);
return std::make_shared<holder>(_tanuki_value);
} else {
// NOTE: this is the one case in which we might end up here at runtime. This function
// is used only in the implementation of the copy() function to force deep copy
Expand All @@ -687,7 +687,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
#endif

// NOLINTNEXTLINE(cppcoreguidelines-owning-memory,clang-analyzer-cplusplus.PlacementNew)
return ::new (ptr) holder(m_value);
return ::new (ptr) holder(_tanuki_value);
}

// NOTE: we should never reach this point as we are using this function
Expand All @@ -705,7 +705,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
#endif

// NOLINTNEXTLINE(cppcoreguidelines-owning-memory,clang-analyzer-cplusplus.PlacementNew)
return ::new (ptr) holder(std::move(m_value));
return ::new (ptr) holder(std::move(_tanuki_value));
}

// NOTE: we should never reach this point as we are using this function
Expand All @@ -716,7 +716,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde

// Copy/move assignment and swap primitives.

// Copy-assign m_value into the m_value of v_iface.
// Copy-assign _tanuki_value into the _tanuki_value of v_iface.
void _tanuki_copy_assign_value_to(detail::value_iface<IFace, Sem> *v_iface) const final
{
if constexpr (std::is_copy_assignable_v<T>) {
Expand All @@ -726,7 +726,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
// the same T, the conversion chain should boil down to T * -> void * -> T *, which
// does not require laundering.
assert(typeid(T) == v_iface->_tanuki_value_type_index());
*static_cast<T *>(v_iface->_tanuki_value_ptr()) = m_value;
*static_cast<T *>(v_iface->_tanuki_value_ptr()) = _tanuki_value;
return;
}

Expand All @@ -735,12 +735,12 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
// the creation of a copyable value semantics wrap from a non-copyable value.
detail::unreachable(); // LCOV_EXCL_LINE
}
// Move-assign m_value into the m_value of v_iface.
// Move-assign _tanuki_value into the _tanuki_value of v_iface.
void _tanuki_move_assign_value_to(detail::value_iface<IFace, Sem> *v_iface) && noexcept final
{
if constexpr (std::is_move_assignable_v<T>) {
assert(typeid(T) == v_iface->_tanuki_value_type_index());
*static_cast<T *>(v_iface->_tanuki_value_ptr()) = std::move(m_value);
*static_cast<T *>(v_iface->_tanuki_value_ptr()) = std::move(_tanuki_value);
return;
}

Expand All @@ -749,11 +749,11 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
// the creation of a movable value semantics wrap from a non-movable value.
detail::unreachable(); // LCOV_EXCL_LINE
}
// Copy-assign the object of type T assumed to be stored in ptr into m_value.
// Copy-assign the object of type T assumed to be stored in ptr into _tanuki_value.
void _tanuki_copy_assign_value_from(const void *ptr) final
{
if constexpr (std::is_copy_assignable_v<T>) {
m_value = *static_cast<const T *>(ptr);
_tanuki_value = *static_cast<const T *>(ptr);
return;
}

Expand All @@ -765,7 +765,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
void _tanuki_move_assign_value_from(void *ptr) noexcept final
{
if constexpr (std::is_move_assignable_v<T>) {
m_value = std::move(*static_cast<T *>(ptr));
_tanuki_value = std::move(*static_cast<T *>(ptr));
return;
}

Expand All @@ -774,14 +774,14 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
// the creation of a movable value semantics wrap from a non-movable value.
detail::unreachable(); // LCOV_EXCL_LINE
}
// Swap m_value with the m_value of v_iface.
// Swap _tanuki_value with the _tanuki_value of v_iface.
void _tanuki_swap_value(detail::value_iface<IFace, Sem> *v_iface) noexcept final
{
if constexpr (std::swappable<T>) {
assert(typeid(T) == v_iface->_tanuki_value_type_index());

using std::swap;
swap(m_value, *static_cast<T *>(v_iface->_tanuki_value_ptr()));
swap(_tanuki_value, *static_cast<T *>(v_iface->_tanuki_value_ptr()));

return;
}
Expand Down Expand Up @@ -809,7 +809,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface<IFace, holde
void serialize(Archive &ar, unsigned)
{
ar &boost::serialization::base_object<detail::value_iface<IFace, Sem>>(*this);
ar & m_value;
ar & _tanuki_value;
}

#endif
Expand Down Expand Up @@ -1213,7 +1213,7 @@ concept copy_move_swap_consistent = (Cfg.semantics == wrap_semantics::reference)

// The wrap class.
template <typename IFace, auto Cfg = default_config>
requires valid_config<Cfg>
requires std::is_class_v<IFace> && std::same_as<IFace, std::remove_cv_t<IFace>> && valid_config<Cfg>
class TANUKI_VISIBLE wrap : private detail::wrap_storage<IFace, Cfg.static_size, Cfg.static_align, Cfg.semantics>,
// NOTE: the reference interface is not supposed to hold any data: it will always
// be def-inited (even when copying/moving a wrap object), its assignment operators
Expand Down Expand Up @@ -2028,7 +2028,7 @@ template <typename Holder, typename U>

using T = typename detail::holder_value<Holder>::type;

const auto &val = static_cast<const Holder *>(h)->m_value;
const auto &val = static_cast<const Holder *>(h)->_tanuki_value;

if constexpr (detail::is_reference_wrapper_v<T>) {
return val.get();
Expand All @@ -2045,7 +2045,7 @@ template <typename Holder, typename U>

using T = typename detail::holder_value<Holder>::type;

auto &val = static_cast<Holder *>(h)->m_value;
auto &val = static_cast<Holder *>(h)->_tanuki_value;

if constexpr (detail::is_reference_wrapper_v<T>) {
if constexpr (std::is_const_v<std::remove_reference_t<std::unwrap_reference_t<T>>>) {
Expand Down
2 changes: 1 addition & 1 deletion tutorial/simple_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ struct foo1_iface_impl : public Base {
void foo() const override
{
std::cout << "foo1_iface_impl calling foo()\n";
static_cast<const Holder *>(this)->m_value.foo();
static_cast<const Holder *>(this)->_tanuki_value.foo();
}
};

Expand Down

0 comments on commit 7d4d4f8

Please sign in to comment.