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
:
namespace ns
{
// An existing OO interface.
struct my_iface {
virtual ~my_iface() = default;
virtual int foo() const = 0;
};
} // namespace ns
In order to provide a non-intrusive tanuki implementation for my_iface
, we need to implement
a partial specialisation of the iface_impl
struct for my_iface
:
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
That is, the iface_impl
struct template depends on four parameters: the first one is the
the interface for which we are providing an implementation, while the remaining three are the
customary Base
, Holder
and T
arguments whose meaning has been explained in previous tutorials.
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:
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';
}
The final answer is 42
Full code listing#
#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';
}