From d2dc481e8153a0abd72ef828df1a69ec9005ff38 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:01:32 +0200 Subject: [PATCH 1/6] Constrain the wrap IFace parameter to be a non-cv qualified class type. --- doc/wrap.rst | 2 +- include/tanuki/tanuki.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/wrap.rst b/doc/wrap.rst index 10fe046..aaaa62e 100644 --- a/doc/wrap.rst +++ b/doc/wrap.rst @@ -3,7 +3,7 @@ The ``wrap`` class ================== -.. cpp:class:: template requires valid_config wrap +.. cpp:class:: template requires std::is_class_v && std::same_as> && valid_config wrap .. cpp:function:: wrap() diff --git a/include/tanuki/tanuki.hpp b/include/tanuki/tanuki.hpp index 482d685..720d9ce 100644 --- a/include/tanuki/tanuki.hpp +++ b/include/tanuki/tanuki.hpp @@ -1213,7 +1213,7 @@ concept copy_move_swap_consistent = (Cfg.semantics == wrap_semantics::reference) // The wrap class. template - requires valid_config + requires std::is_class_v && std::same_as> && valid_config class TANUKI_VISIBLE wrap : private detail::wrap_storage, // 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 From 534c21fa30352fc9373c48584fa27d5076c1ce47 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:09:00 +0200 Subject: [PATCH 2/6] Doc tweaks. --- doc/config.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/config.rst b/doc/config.rst index b310cc5..d1d82b0 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -51,14 +51,19 @@ Configuration options This option selects whether or not the :cpp:class:`wrap` class is copy constructible/assignable. + This option is ignored when employing :ref:`reference 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 `. + .. 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 `. .. cpp:var:: inline constexpr auto default_config = config{} From 427507b39c2fa51f0e4dc5892a76da8e1ae95513 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:09:11 +0200 Subject: [PATCH 3/6] Rename m_value to _tanuki_value. --- doc/simple_interface.rst | 6 +++--- include/tanuki/tanuki.hpp | 40 +++++++++++++++++------------------ tutorial/simple_interface.cpp | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/doc/simple_interface.rst b/doc/simple_interface.rst index 7f12126..572ec6e 100644 --- a/doc/simple_interface.rst +++ b/doc/simple_interface.rst @@ -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(this)`` leveraging the `curiously recurring template pattern (CRTP) `__. In other words, this implementation of the ``void foo() const`` function will invoke @@ -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: diff --git a/include/tanuki/tanuki.hpp b/include/tanuki/tanuki.hpp index 720d9ce..c3e7a8c 100644 --- a/include/tanuki/tanuki.hpp +++ b/include/tanuki/tanuki.hpp @@ -578,7 +578,7 @@ concept iface_has_impl = requires() { // are checked in the generic ctors of the wrap class. template struct TANUKI_VISIBLE holder final : public detail::impl_from_iface, 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. @@ -609,7 +609,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface && detail::nothrow_default_initializable, T, Sem>>) - : m_value(std::forward(x)) + : _tanuki_value(std::forward(x)) { } template @@ -617,7 +617,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface && detail::nothrow_default_initializable, T, Sem>>) - : m_value(std::forward(x)...) + : _tanuki_value(std::forward(x)...) { } @@ -636,7 +636,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface) { // 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 @@ -666,7 +666,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface> _tanuki_shared_clone_holder() const final { if constexpr (std::is_copy_constructible_v) { - return std::make_shared(m_value); + return std::make_shared(_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 @@ -687,7 +687,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface *v_iface) const final { if constexpr (std::is_copy_assignable_v) { @@ -726,7 +726,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface void * -> T *, which // does not require laundering. assert(typeid(T) == v_iface->_tanuki_value_type_index()); - *static_cast(v_iface->_tanuki_value_ptr()) = m_value; + *static_cast(v_iface->_tanuki_value_ptr()) = _tanuki_value; return; } @@ -735,12 +735,12 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface *v_iface) && noexcept final { if constexpr (std::is_move_assignable_v) { assert(typeid(T) == v_iface->_tanuki_value_type_index()); - *static_cast(v_iface->_tanuki_value_ptr()) = std::move(m_value); + *static_cast(v_iface->_tanuki_value_ptr()) = std::move(_tanuki_value); return; } @@ -749,11 +749,11 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface) { - m_value = *static_cast(ptr); + _tanuki_value = *static_cast(ptr); return; } @@ -765,7 +765,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface) { - m_value = std::move(*static_cast(ptr)); + _tanuki_value = std::move(*static_cast(ptr)); return; } @@ -774,14 +774,14 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface *v_iface) noexcept final { if constexpr (std::swappable) { assert(typeid(T) == v_iface->_tanuki_value_type_index()); using std::swap; - swap(m_value, *static_cast(v_iface->_tanuki_value_ptr())); + swap(_tanuki_value, *static_cast(v_iface->_tanuki_value_ptr())); return; } @@ -809,7 +809,7 @@ struct TANUKI_VISIBLE holder final : public detail::impl_from_iface>(*this); - ar & m_value; + ar & _tanuki_value; } #endif @@ -2028,7 +2028,7 @@ template using T = typename detail::holder_value::type; - const auto &val = static_cast(h)->m_value; + const auto &val = static_cast(h)->_tanuki_value; if constexpr (detail::is_reference_wrapper_v) { return val.get(); @@ -2045,7 +2045,7 @@ template using T = typename detail::holder_value::type; - auto &val = static_cast(h)->m_value; + auto &val = static_cast(h)->_tanuki_value; if constexpr (detail::is_reference_wrapper_v) { if constexpr (std::is_const_v>>) { diff --git a/tutorial/simple_interface.cpp b/tutorial/simple_interface.cpp index 6b62823..f195df6 100644 --- a/tutorial/simple_interface.cpp +++ b/tutorial/simple_interface.cpp @@ -7,7 +7,7 @@ struct foo1_iface_impl : public Base { void foo() const override { std::cout << "foo1_iface_impl calling foo()\n"; - static_cast(this)->m_value.foo(); + static_cast(this)->_tanuki_value.foo(); } }; From 73f57ab64aebecdaf3db9976045aa9ac7bbeb0a3 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:26:23 +0200 Subject: [PATCH 4/6] Another couple of doc bits. --- README.md | 2 +- doc/index.rst | 2 +- doc/ref_interface.rst | 2 ++ doc/std_function.rst | 16 ++++++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 040a126..edd05c8 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/doc/index.rst b/doc/index.rst index e5281fd..2a146a0 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -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 `__. Main features include: diff --git a/doc/ref_interface.rst b/doc/ref_interface.rst index 05c3e03..a03b323 100644 --- a/doc/ref_interface.rst +++ b/doc/ref_interface.rst @@ -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 ----------------------------- diff --git a/doc/std_function.rst b/doc/std_function.rst index c7e479d..c645b90 100644 --- a/doc/std_function.rst +++ b/doc/std_function.rst @@ -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 `, the call operator could be simplified to something like this: + +.. code-block:: c++ + + template + auto operator()(this const Wrap &self, + FArgs &&...fargs) -> decltype(iface_ptr(self)->operator()(std::forward(fargs)...)) + { + if (is_invalid(self)) { + throw std::bad_function_call{}; + } + + return iface_ptr(self)->operator()(std::forward(fargs)...); + } + Next, we take a look at the ``bool`` conversion operator: .. literalinclude:: ../tutorial/std_function.cpp From b6710a1335224c720b8f6b7ddeb7452585185a8d Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:38:02 +0200 Subject: [PATCH 5/6] Add some installation instructions. --- doc/index.rst | 1 + doc/installation.rst | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 doc/installation.rst diff --git a/doc/index.rst b/doc/index.rst index 2a146a0..548da9a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -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 diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 0000000..71a91cf --- /dev/null +++ b/doc/installation.rst @@ -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. From 09be4d82d224bf628351d2233f4693c0ba9ae2f1 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Tue, 21 May 2024 09:47:34 +0200 Subject: [PATCH 6/6] Last minute addition. --- doc/config.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/config.rst b/doc/config.rst index d1d82b0..5687e62 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -47,6 +47,8 @@ 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.