atilaneves / tardy

Runtime polymorphism without inheritance (structs, ints, classes, ...)
25 stars 3 forks source link

tardy - runtime polymorphism without inheritance

Build Status Coverage

What?

import tardy;

interface ITransformer {
    int transform(int) @safe pure const;
}
alias Transformer = Polymorphic!ITransformer;

int xform(Transformer t) {
    return t.transform(3);
}

struct Adder {
    int i;
    int transform(int j) @safe pure const { return i + j; }
}

struct Plus1 {
    int transform(int i) @safe pure const { return i + 1; }
}

unittest {
    assert(xform(Transformer(Adder(2))) == 5);
    assert(xform(Transformer(Adder(3))) == 6);

    assert(xform(Transformer(Plus1())) == 4);
}

Why?

Traditional inheritance-based runtime polymorphism has a few drawbacks:

Louis Dionne explained it better and in more detail in his talk.

Tardy makes it so:

Creating instances

Instances may be created by passing a value to Polymorphic's constructor or by emplacing them and having Polymorphic call the instance type's contructor itself. The examples above show how to construct using a value. To explicitly instantiate a particular type:

auto t = Polymorphic!MyInterface.create!MyType(arg0, arg1, arg2, ...);

One can also pass modules to create where Polymorphic should look for UFCS candidates:

// Using the `Transfomer` example above, and assuming there's a
// UFCS function in one of "mymod0" or "mymod1",
// this constructs an `int` "instance"

auto t = Transformer.create!("mymod0", "mymod1")(42);

Specifying function attributes for the copy constructor and destructor

The vtable type is constructed at compile-time by reflecting on the interface passed as the first template parameter passed to Polymorphic. To not overly constrain what users may do with their types (@safe, pure, ...), the default is @safe, but attributes can be specified for each of these compiler-generated member functions:

interface MyInterface {
    import std.traits: FA = FunctionAttribute;
    enum CopyConstructorAttrs = FA.safe | FA.pure_;
    enum DestructorAttrs = FA.pure_ | FA.nogc;
}