.. _Inheritance: Inheritance =========== Rice supports creating child classes via the ``define_class`` method: .. code-block:: cpp class Base { public: virtual void foo(); }; class Derived : public Base { }; extern "C" void Init_test() { Data_Type rb_cBase = define_class("Base") .define_method("foo", &Base::foo); Data_Type rb_cDerived = define_class("Derived"); } This is done by adding a second template parameter to ``define_class`` that specifies that ``Derived`` inherits from ``Base``. Rice does not support multiple inheritance. Directors ---------- Inheritance becomes much more complex if you want to create Ruby classes that inherit from wrapped C++ classes. This introduces several problems: * Ruby classes should be able to override C++ virtual methods * Overridden virtual methods should be able to call ``super`` and invoke the overridden C++ method * C++ code calling the virtual methods should invoke the overridden version in Ruby Rice supports these use cases through the use of ``Director`` classes. ``Directors`` are proxies that can correctly dispatch method invocations up or down a Class hierarchy. .. note:: The name Director comes from SWIG - for more information see https://www.swig.org/Doc4.0/SWIGPlus.html#SWIGPlus_director_classes_introduction. Take the following class: .. code-block:: cpp class VirtualBase { public: VirtualBase(); virtual int doWork(); virtual int processWorker() = 0; }; Due to the abstract nature of this class, we cannot directly wrap it in Rice, as any C++ compiler will complain about trying to instantiate a virtual class. Even without the pure virtual function, any call to ``VirtualBase::doWork`` will stop at the C++ level and execution will not pass down into any Ruby subclasses. To properly wrap both of these methods, use a ``Rice::Director`` subclass as a proxy and use this new proxy class as the type to wrap with ``define_class``: .. code-block:: cpp #include class VirtualBaseProxy : public VirtualBase, public Rice::Director { public: VirtualBaseProxy(Object self) : Rice::Director(self) { } virtual int doWork() { int result = getSelf().call("do_work"); return detail::From_Ruby().convert(result); } int default_doWork() { return VirtualBase::doWork(); } virtual int processWorker() { int result = getSelf().call("process_worker"); return detail::From_Ruby().convert(result); } int default_processWorker() { raisePureVirtual(); } }; There is a lot going on here, so we'll go through each part. .. code-block:: cpp class VirtualBaseProxy : public Virtualbase, public Rice::Director { } First, the class needs to subclass both the virtual class in question and ``Rice::Director``. .. code-block:: cpp public: VirtualBaseProxy(Object self) : Rice::Director(self) { } For ``Rice::Director`` to work its magic, every instance of this class needs to have a handle to its Ruby instance. The constructor must take a ``Rice::Object`` as the first argument and pass it to ``Rice::Director``. Next we implement ``doWork``. The director class overrides its by forwarding the invocation to the Ruby instance. .. code-block:: cpp virtual int doWork() { int result = getSelf().call("do_work"); return detail::From_Ruby().convert(result); } int default_doWork() { return VirtualBase::doWork(); } It director also implements ``default_doWork`` which enables Ruby to call the overridden virtual C++ method. The ``default_`` prefix is a naming convention to help keep straight which methods perform which functions. If Ruby should never call the C++ method then the ``default_`` implementation should call ``raisePureVirtual()``: .. code-block:: cpp int default_processWorker() { raisePureVirtual(); } The method ``raisePureVirtual()`` exists to allow wrapping a pure virtual method into Ruby (and ensuring compilation is possible) while also making sure any users of this extension are informed quickly if there is nothing callable on the C++ side. Once the Director class is built, it's time to wrap it into Ruby: .. code-block:: cpp extern "C" void Init_virtual() { define_class("VirtualBase") .define_director() .define_constructor(Constructor()) .define_method("do_work", &VirtualBaseProxy::default_doWork) .define_method("process_worker", &VirtualBaseProxy::default_processWorker); } There are couple of new things in this code. First, is the addition of the ``define_director`` call which takes the ``VirtualBaseProxy`` as a template parameter. Second, the ``Constructor`` template parameter must also be the ``VirtualBaseProxy`` to allow proper object construction and destruction of the derived objects. Third, the ``define_method`` calls should point to the ``default_*`` implementations.