SHARE
TWEET

Untitled

a guest Jul 18th, 2019 75 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. struct ModelA {
  2.     class Animal {
  3.         TypeInfo eat() {
  4.             return typeid(typeof(this));
  5.         }
  6.         Food eatsFood() {
  7.             return null;
  8.         }
  9.         mixin fixCast!();
  10.     }
  11.     class Dog : Animal {
  12.         override TypeInfo eat() {
  13.             return typeid(typeof(this));
  14.         }
  15.         TypeInfo bark() {
  16.             return typeid(typeof(this));
  17.         }
  18.         TypeInfo bite() {
  19.             return typeid(typeof(this));
  20.         }
  21.         override Food eatsFood() {
  22.             return new Food();
  23.         }
  24.     }
  25.     class Food {
  26.         mixin fixCast!();
  27.     }
  28. }
  29.  
  30. struct ModelB {
  31.     class Animal {
  32.         mixin inherit!ModelA;
  33.     }
  34.     class Dog : Animal {
  35.         mixin inherit!ModelA;
  36.         TypeInfo bark() {
  37.             return typeid(typeof(this));
  38.         }
  39.         Food eatsFood() {
  40.             return new Food();
  41.         }
  42.     }
  43.     class Food {
  44.         mixin inherit!ModelA;
  45.     }
  46. }
  47.  
  48. unittest {
  49.     // Implicit conversions to ModelA:
  50.     ModelB.Dog a = new ModelB.Dog();
  51.     ModelA.Dog b = a;
  52.     ModelB.Animal c = a;
  53.     ModelA.Animal d = c;
  54.  
  55.     assert(a !is null);
  56.     assert(b !is null);
  57.     assert(c !is null);
  58.     assert(d !is null);
  59.    
  60.     assert(a == b);
  61.     assert(a == c);
  62.     assert(a == d);
  63.     assert(b == c);
  64.     assert(b == d);
  65.     assert(c == d);
  66.    
  67.     // Methods are inherited from the corresponding class in the base model:
  68.     assert(a.eat() == typeid(ModelA.Dog));
  69.     assert(b.eat() == typeid(ModelA.Dog));
  70.     assert(c.eat() == typeid(ModelA.Dog));
  71.     assert(d.eat() == typeid(ModelA.Dog));
  72.  
  73.     // bark() is overridden in ModelB.Dog, so that's the version what's being called:
  74.     assert(a.bark() == typeid(ModelB.Dog));
  75.     assert(b.bark() == typeid(ModelB.Dog));
  76.  
  77.     // bite(), however, is not overridden, so ModelA's version is used:
  78.     assert(a.bite() == typeid(ModelA.Dog));
  79.     assert(b.bite() == typeid(ModelA.Dog));
  80.    
  81.     // Managed to fix casting from ModelA to ModelB:
  82.     assert(cast(ModelB.Dog)d !is null);
  83.     assert(cast(ModelB.Animal)d !is null);
  84.    
  85.     //
  86.     ModelB.Food food1 = a.eatsFood;
  87.     ModelA.Food food3 = b.eatsFood;
  88.     ModelA.Food food4 = d.eatsFood;
  89.    
  90.     // Can't do - ModelB.Animal does not override eatsFood(),
  91.     // so this is ModelA.Animal's eatsFood() method.
  92.     //ModelB.Food food2 = c.eatsFood;
  93. }
  94.  
  95. mixin template fixCast() {
  96.     static if (!__traits(hasMember, typeof(this), "_hasFixCast")) {
  97.         enum _hasFixCast = true;
  98.         inout(T) opCast(T)() inout if (is(T == class)) {
  99.             auto tmp = _opCastImpl(typeid(T));
  100.             return *cast(inout(T)*)&tmp;
  101.         }
  102.        
  103.         inout(void)* _opCastImpl(TypeInfo_Class) inout {
  104.             return null;
  105.         }
  106.     }
  107. }
  108.  
  109. mixin template inherit(ParentModel) {
  110.     static assert(is(typeof(this) == class), "inherit only works for classes");
  111.     static assert(__traits(hasMember, ParentModel, typeof(this).stringof), "inherit works only for models matching the same class structure");
  112.  
  113.     alias ThisClass = typeof(this);
  114.     alias ParentClass = __traits(getMember, ParentModel, typeof(this).stringof);
  115.    
  116.     static assert(__traits(hasMember, ParentClass, "_opCastImpl"), "Base model class doesn't correctly implement `opCast`. Please use `mixin fixCast!();` in `"~ParentClass.stringof~"`");
  117.    
  118.    
  119.     private inout(ParentClass) _getProxyParentImpl() inout {
  120.         if (_proxyParent !is null) {
  121.             return _proxyParent;
  122.         }
  123.    
  124.         import std.typecons : AutoImplement;
  125.        
  126.         static class Base : ParentClass {
  127.             ThisClass zis;
  128.             this(inout(ThisClass) a) inout {
  129.                 zis = a;
  130.             }
  131.            
  132.             // Manually check if we're casting ThisClass or a supertype, otherwise return null.
  133.             override inout(void)* _opCastImpl(TypeInfo_Class t) inout {
  134.                 auto thisType = typeid(ThisClass);
  135.                 while (thisType !is null && thisType != t) {
  136.                     thisType = thisType.base;
  137.                 }
  138.                
  139.                 if (!t) return null;
  140.                 union A {
  141.                     ThisClass a;
  142.                     void* b;
  143.                 }
  144.                 inout(A) a = {zis};
  145.                 return a.b;
  146.             }
  147.            
  148.             override bool opEquals(Object o) {
  149.                 if (*cast(void**)&o == *cast(void**)&zis) return true;
  150.                 auto tmp = this;
  151.                 return *cast(void**)&o == *cast(void**)&tmp;
  152.             }
  153.            
  154.             override size_t toHash() @trusted nothrow {
  155.                 return zis.toHash();
  156.             }
  157.         }
  158.        
  159.         // AutoImplement should only implement methods that are overridden in ThisClass.
  160.         template What(alias func) {
  161.             import std.algorithm.comparison : among;
  162.            
  163.             enum funcName = __traits(identifier, func);
  164.             static if (funcName.among(__traits(allMembers, ThisClass))) {
  165.                 alias thisFunc = __traits(getMember, ThisClass, __traits(identifier, func));
  166.                 enum What =  __traits(isVirtualFunction, func) &&
  167.                              __traits(isVirtualFunction, thisFunc);
  168.             } else {
  169.                 enum What = false;
  170.             }
  171.         }
  172.        
  173.         // AutoImplement should forward calls to ThisClass where it defines an override.
  174.         template How(T, alias func) {
  175.             import std.format : format;
  176.             enum How = q{return zis.%1$s(args);}
  177.                 .format(__traits(identifier, func));
  178.         }
  179.         auto tmp = new inout(AutoImplement!(Base, How, What))(this);
  180.         // Ugly as fuck. I'm sorry.
  181.         *cast(ParentClass*)&_proxyParent = *cast(ParentClass*)&tmp;
  182.         return tmp;
  183.     }
  184.    
  185.     ParentClass _proxyParent;
  186.    
  187.     import std.traits : BaseClassesTuple;
  188.     static if (__traits(hasMember, BaseClassesTuple!ThisClass[0], "_getProxyParent")) {
  189.         override inout(ParentClass) _getProxyParent() inout {
  190.             return _getProxyParentImpl();
  191.         }
  192.     } else {
  193.         inout(ParentClass) _getProxyParent() inout {
  194.             return _getProxyParentImpl();
  195.         }
  196.     }
  197.     alias _getProxyParent this;
  198.    
  199.     override bool opEquals(Object o) {
  200.         auto zis = _getProxyParent();
  201.         if (*cast(void**)&o == *cast(void**)&zis) return true;
  202.         auto tmp = this;
  203.         return *cast(void**)&o == *cast(void**)&tmp;
  204.     }
  205. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top