-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #60 from bluescarni/pr/nonintrusive
Noniontrusive testing and docs
- Loading branch information
Showing
8 changed files
with
192 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
.. _nonintrusive: | ||
|
||
.. cpp:namespace-push:: tanuki | ||
|
||
Non-intrusive interface implementations | ||
======================================= | ||
|
||
In all the examples seen so far, implementations for tanuki interfaces have always | ||
been specified by adding an ``impl`` template alias in the body of the interface class. | ||
This is an *intrusive* approach, in the sense that it requires modifying the definition | ||
of the interface. | ||
|
||
In order to be able to adapt existing object-oriented interfaces without having to modify them | ||
by adding the ``impl`` alias, tanuki also supports a non-intrusive way of specifying | ||
interface implementations. Let us see it in action. | ||
|
||
Consider the following object-oriented interface ``my_iface`` defined in some namespace ``ns``: | ||
|
||
.. literalinclude:: ../tutorial/nonintrusive.cpp | ||
:language: c++ | ||
:lines: 6-15 | ||
|
||
In order to provide a non-intrusive tanuki implementation for ``my_iface``, we need to implement | ||
a partial specialisation of the :cpp:struct:`iface_impl` struct for ``my_iface``: | ||
|
||
.. literalinclude:: ../tutorial/nonintrusive.cpp | ||
:language: c++ | ||
:lines: 17-29 | ||
|
||
Here we are specifying an implementation for all value types ``T``, but, as explained | ||
in previous tutorials, we could also easily provide a partially-specialised implementation, | ||
constrain the implementation only for value types modelling | ||
certain requirements, provide an empty default implementation, etc. | ||
|
||
We are now able to wrap ``my_iface`` in the usual way: | ||
|
||
.. literalinclude:: ../tutorial/nonintrusive.cpp | ||
:language: c++ | ||
:lines: 31-40 | ||
|
||
.. code-block:: console | ||
The final answer is 42 | ||
Full code listing | ||
----------------- | ||
|
||
.. literalinclude:: ../tutorial/nonintrusive.cpp | ||
:language: c++ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ Tutorials | |
wrap_reference.rst | ||
custom_construct.rst | ||
composite_interfaces.rst | ||
nonintrusive.rst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#include <concepts> | ||
|
||
#include <tanuki/tanuki.hpp> | ||
|
||
#include <catch2/catch_test_macros.hpp> | ||
|
||
// NOLINTBEGIN(cert-err58-cpp,misc-use-anonymous-namespace,cppcoreguidelines-avoid-do-while) | ||
|
||
namespace ns | ||
{ | ||
|
||
struct my_iface { | ||
virtual ~my_iface() = default; | ||
virtual int foo() const = 0; | ||
}; | ||
|
||
// Also include an interface with an impl template | ||
// to check the non-intrusive representation takes | ||
// the precedence. | ||
struct my_iface2 { | ||
virtual ~my_iface2() = default; | ||
virtual int bar() const = 0; | ||
|
||
template <typename Base, typename Holder, typename T> | ||
struct impl { | ||
}; | ||
}; | ||
|
||
} // namespace ns | ||
|
||
namespace tanuki | ||
{ | ||
|
||
// Empty default nonintrusive implementation. | ||
template <typename Base, typename Holder, typename T> | ||
struct iface_impl<ns::my_iface, Base, Holder, T> { | ||
}; | ||
|
||
// Specialisation for int value type. | ||
template <typename Base, typename Holder> | ||
struct iface_impl<ns::my_iface, Base, Holder, int> : public Base { | ||
int foo() const final | ||
{ | ||
return 42; | ||
} | ||
}; | ||
|
||
template <typename Base, typename Holder, typename T> | ||
struct iface_impl<ns::my_iface2, Base, Holder, T> { | ||
}; | ||
|
||
// Specialisation for int value type. | ||
template <typename Base, typename Holder> | ||
struct iface_impl<ns::my_iface2, Base, Holder, int> : public Base { | ||
int bar() const final | ||
{ | ||
return 43; | ||
} | ||
}; | ||
|
||
} // namespace tanuki | ||
|
||
TEST_CASE("basics") | ||
{ | ||
using wrap_t = tanuki::wrap<ns::my_iface>; | ||
|
||
wrap_t w(123); | ||
REQUIRE(w->foo() == 42); | ||
|
||
REQUIRE(!std::constructible_from<wrap_t, long>); | ||
|
||
using wrap2_t = tanuki::wrap<ns::my_iface2>; | ||
|
||
wrap2_t w2(123); | ||
REQUIRE(w2->bar() == 43); | ||
|
||
REQUIRE(!std::constructible_from<wrap2_t, long>); | ||
|
||
// Composite interface. | ||
using wrap3_t = tanuki::wrap<tanuki::composite_iface<ns::my_iface, ns::my_iface2>>; | ||
|
||
wrap3_t w3(123); | ||
REQUIRE(w3->foo() == 42); | ||
REQUIRE(w3->bar() == 43); | ||
|
||
REQUIRE(!std::constructible_from<wrap3_t, long>); | ||
} | ||
|
||
// NOLINTEND(cert-err58-cpp,misc-use-anonymous-namespace,cppcoreguidelines-avoid-do-while) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include <iostream> | ||
#include <string> | ||
|
||
#include <tanuki/tanuki.hpp> | ||
|
||
namespace ns | ||
{ | ||
|
||
// An existing OO interface. | ||
struct my_iface { | ||
virtual ~my_iface() = default; | ||
virtual int foo() const = 0; | ||
}; | ||
|
||
} // namespace ns | ||
|
||
namespace tanuki | ||
{ | ||
|
||
// Non-intrusive implementation for the ns::my_iface interface. | ||
template <typename Base, typename Holder, typename T> | ||
struct iface_impl<ns::my_iface, Base, Holder, T> : public Base { | ||
int foo() const override | ||
{ | ||
return 42; | ||
} | ||
}; | ||
|
||
} // namespace tanuki | ||
|
||
int main() | ||
{ | ||
// Define a wrap for ns::my_iface. | ||
using wrap_t = tanuki::wrap<ns::my_iface>; | ||
|
||
wrap_t w1(123); | ||
wrap_t w2(std::string("hello world!")); | ||
|
||
std::cout << "The final answer is " << w1->foo() << '\n'; | ||
} |