Non-intrusive interface implementations

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';
}