Simulating multiple inheritance with mixin templates

D does not have multiple inheritance like C++. Instead, it follows a Java-style pattern of one base class, multiple interfaces. However, using mixin templates, we can simulate all aspects of multiple inheritance.

How to do it…

We need to execute the following steps to simulate multiple inheritance with mixin templates:

  1. Write an interface and mixin template instead of a base class.
  2. Write your usage class, which inherits from the interfaces and mixes in the mixin templates to the body.
  3. You may override functions from the mixin template by writing new ones with the same name in the class body.

The code is as follows:

import std.stdio : writeln;
interface Base1 {
    void foo();
}

mixin template Base1_Impl() {
  void foo() { writeln("default foo impl"); }
}

interface Base2 {
  void bar();
  void baz();
}

mixin template Base2_Impl() {
  int member;
  void bar() { writeln("default bar impl"); }
  void baz() { writeln("default bazimpl"); }
}

classMyObject : Base1, Base2 {
  mixin Base1_Impl!();
  mixin Base2_Impl!();

  void baz() {
    writeln("custom bazimpl");
  }
}

void main() {
  MyObjectobj = new MyObject();

  Base1 base1 = obj; // works on all interfaces
  Base2 base2 = obj; // works on all interfaces

  obj.foo(); // call methods through object
  obj.baz(); // note that the custom one is called
  base2.bar(); // or through interface
  base2.baz();
}

On running the preceding program, we will get the following output:

default foo impl
custombazimpl
default bar impl
custombazimpl

Note

If you override one member of an overload set, you must overload them all.

How it works…

Inheriting and implementing multiple interfaces will allow your object to implicitly convert to the various interface types. The problem is they cannot have default virtual implementations. That's where the mixin template comes in.

A mixin template (not to be confused with the mixins string we used before) is a parameterized list of declarations which is inserted into the mixin site, almost as if you copied and pasted the code and compiled it.

Here, we paired a mixin template with each interface, putting the interface implementation in the template. An interesting feature of these templates is that they will not conflict with names written in the object. As a result, we can selectively override functions from the mixin template, as we did with the custom baz implementation.

Note

Since the customization is done by name, if you want to customize one member of an overload set, you must customize them all. That is, if we had baz(int) in addition to baz(), we'd have to write custom implementations of both.

When customizing functions, you may also wish to call the default implementation. To do this, give the mixin template a name and then refer to it by that name:

mixin Base2_Impl!() b2; // name it

void baz() {
  writeln("custom bazimpl");
  b2.baz(); // refer to the function by full name
  }

The other functions will still appear in the object—you will not have to refer to them by the full b2.bar name. The name is only used when disambiguating.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.59.145