Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- struct ModelA {
- class Animal {
- TypeInfo eat() {
- return typeid(typeof(this));
- }
- Food eatsFood() {
- return null;
- }
- mixin fixCast!();
- }
- class Dog : Animal {
- override TypeInfo eat() {
- return typeid(typeof(this));
- }
- TypeInfo bark() {
- return typeid(typeof(this));
- }
- TypeInfo bite() {
- return typeid(typeof(this));
- }
- override Food eatsFood() {
- return new Food();
- }
- }
- class Food {
- mixin fixCast!();
- }
- }
- struct ModelB {
- class Animal {
- mixin inherit!ModelA;
- }
- class Dog : Animal {
- mixin inherit!ModelA;
- TypeInfo bark() {
- return typeid(typeof(this));
- }
- Food eatsFood() {
- return new Food();
- }
- }
- class Food {
- mixin inherit!ModelA;
- }
- }
- unittest {
- // Implicit conversions to ModelA:
- ModelB.Dog a = new ModelB.Dog();
- ModelA.Dog b = a;
- ModelB.Animal c = a;
- ModelA.Animal d = c;
- assert(a !is null);
- assert(b !is null);
- assert(c !is null);
- assert(d !is null);
- assert(a == b);
- assert(a == c);
- assert(a == d);
- assert(b == c);
- assert(b == d);
- assert(c == d);
- // Methods are inherited from the corresponding class in the base model:
- assert(a.eat() == typeid(ModelA.Dog));
- assert(b.eat() == typeid(ModelA.Dog));
- assert(c.eat() == typeid(ModelA.Dog));
- assert(d.eat() == typeid(ModelA.Dog));
- // bark() is overridden in ModelB.Dog, so that's the version what's being called:
- assert(a.bark() == typeid(ModelB.Dog));
- assert(b.bark() == typeid(ModelB.Dog));
- // bite(), however, is not overridden, so ModelA's version is used:
- assert(a.bite() == typeid(ModelA.Dog));
- assert(b.bite() == typeid(ModelA.Dog));
- // Managed to fix casting from ModelA to ModelB:
- assert(cast(ModelB.Dog)d !is null);
- assert(cast(ModelB.Animal)d !is null);
- //
- ModelB.Food food1 = a.eatsFood;
- ModelA.Food food3 = b.eatsFood;
- ModelA.Food food4 = d.eatsFood;
- // Can't do - ModelB.Animal does not override eatsFood(),
- // so this is ModelA.Animal's eatsFood() method.
- //ModelB.Food food2 = c.eatsFood;
- }
- mixin template fixCast() {
- static if (!__traits(hasMember, typeof(this), "_hasFixCast")) {
- enum _hasFixCast = true;
- inout(T) opCast(T)() inout if (is(T == class)) {
- auto tmp = _opCastImpl(typeid(T));
- return *cast(inout(T)*)&tmp;
- }
- inout(void)* _opCastImpl(TypeInfo_Class) inout {
- return null;
- }
- }
- }
- mixin template inherit(ParentModel) {
- static assert(is(typeof(this) == class), "inherit only works for classes");
- static assert(__traits(hasMember, ParentModel, typeof(this).stringof), "inherit works only for models matching the same class structure");
- alias ThisClass = typeof(this);
- alias ParentClass = __traits(getMember, ParentModel, typeof(this).stringof);
- static assert(__traits(hasMember, ParentClass, "_opCastImpl"), "Base model class doesn't correctly implement `opCast`. Please use `mixin fixCast!();` in `"~ParentClass.stringof~"`");
- private inout(ParentClass) _getProxyParentImpl() inout {
- if (_proxyParent !is null) {
- return _proxyParent;
- }
- import std.typecons : AutoImplement;
- static class Base : ParentClass {
- ThisClass zis;
- this(inout(ThisClass) a) inout {
- zis = a;
- }
- // Manually check if we're casting ThisClass or a supertype, otherwise return null.
- override inout(void)* _opCastImpl(TypeInfo_Class t) inout {
- auto thisType = typeid(ThisClass);
- while (thisType !is null && thisType != t) {
- thisType = thisType.base;
- }
- if (!t) return null;
- union A {
- ThisClass a;
- void* b;
- }
- inout(A) a = {zis};
- return a.b;
- }
- override bool opEquals(Object o) {
- if (*cast(void**)&o == *cast(void**)&zis) return true;
- auto tmp = this;
- return *cast(void**)&o == *cast(void**)&tmp;
- }
- override size_t toHash() @trusted nothrow {
- return zis.toHash();
- }
- }
- // AutoImplement should only implement methods that are overridden in ThisClass.
- template What(alias func) {
- import std.algorithm.comparison : among;
- enum funcName = __traits(identifier, func);
- static if (funcName.among(__traits(allMembers, ThisClass))) {
- alias thisFunc = __traits(getMember, ThisClass, __traits(identifier, func));
- enum What = __traits(isVirtualFunction, func) &&
- __traits(isVirtualFunction, thisFunc);
- } else {
- enum What = false;
- }
- }
- // AutoImplement should forward calls to ThisClass where it defines an override.
- template How(T, alias func) {
- import std.format : format;
- enum How = q{return zis.%1$s(args);}
- .format(__traits(identifier, func));
- }
- auto tmp = new inout(AutoImplement!(Base, How, What))(this);
- // Ugly as fuck. I'm sorry.
- *cast(ParentClass*)&_proxyParent = *cast(ParentClass*)&tmp;
- return tmp;
- }
- ParentClass _proxyParent;
- import std.traits : BaseClassesTuple;
- static if (__traits(hasMember, BaseClassesTuple!ThisClass[0], "_getProxyParent")) {
- override inout(ParentClass) _getProxyParent() inout {
- return _getProxyParentImpl();
- }
- } else {
- inout(ParentClass) _getProxyParent() inout {
- return _getProxyParentImpl();
- }
- }
- alias _getProxyParent this;
- override bool opEquals(Object o) {
- auto zis = _getProxyParent();
- if (*cast(void**)&o == *cast(void**)&zis) return true;
- auto tmp = this;
- return *cast(void**)&o == *cast(void**)&tmp;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement