Inheritance

If you wrap both a class and a child of that class, Pyd is smart enough to make the resulting Python classes have a parent-child relationship. Any methods of the parent class will automatically be available to the child class. If the child class overloads any of those methods, it is important that the user wrap them in the module's init function. For example:

import std.stdio;

class Base {
    void foo() { writefln("Base.foo"); }
    void bar() { writefln("Base.bar"); }
}

class Derived : Base {
    void foo() { writefln("Derived.foo"); }
}

These would be exposed to Python by putting this code in PydMain after the call to module_init:

wrapped_class!(Base) b;
b.def!(Base.foo);
b.def!(Base.bar);
finalize_class(b);

wrapped_class!(Derived) d;
d.def!(Derived.foo);
finalize_class(d);

When used in Python, we get the expected behavior:

>>> issubclass(Derived, Base)
True
>>> b = Base()
>>> d = Derived()
>>> b.foo()
Base.foo
>>> b.bar()
Base.bar
>>> d.foo()
Derived.foo
>>> d.bar()
Base.bar

There is one weakness in the default behavior. Take a function like the following:

void polymorphic_call(Base b) {
    b.foo();
}

And in Python:

class PyClass(Base):
    def foo(self):
        print "PyClass.foo"

>>> p = PyClass()
>>> polymorphic_call(p)
Base.foo

Optimally, we would want polymorphic_call to call PyClass.foo. This requires some additional work on the D side of things. To get this behavior, then rather than expose Base directly, we must expose a wrapper class:

class BaseWrap : Base {
    mixin OverloadShim;
    void foo() {
        get_overload(&super.foo, "foo");
    }
    void bar() {
        get_overload(&super.bar, "bar");
    }
}

The OverloadShim template has but a single member, the get_overload function.

ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t);
  • dg should be a delegate to the parent class's method.
  • name should be the name of the method as Python understands it to be. (There's no efficient way to derive this automatically based on only the delegate.)
  • t is a tuple argument. These arguments will be passed on to the actual function call, be it the parent class's implementation or a Python subclass's implementation.

get_overload returns whatever the method does.

Now, we must replace the old wrapping of Base with this:

wrapped_class!(BaseWrap, "Base") w;
w.def!(BaseWrap.foo);
w.def!(BaseWrap.bar);
finalize_class(w);

Now our subclass will perform just like we expect:

>>> p = PyClass()
>>> polymorphic_call(p)
PyClass.foo