1 /** 2 * Defines DuckPointer, which allows quacking without templates. 3 */ 4 module quack.pointer; 5 import quack.extends, quack.mixins; 6 7 import tested; 8 9 /** 10 * Convience wrapper for creating a DuckPointer. 11 */ 12 template duck( Pointee ) if( isExtendable!Pointee ) 13 { 14 DuckPointer!Pointee duck( Pointer )( Pointer* p ) 15 { 16 return DuckPointer!Pointee( p ); 17 } 18 } 19 20 /** 21 * Pointer to any type that `extends` Pointee. 22 * 23 * To make a DuckPointer for a mixin, it's recommended to define a struct 24 * that only implements the mixin. 25 * 26 * Params: 27 * Pointee = The type the pointee refers to. 28 */ 29 template DuckPointer( Pointee ) if( isExtendable!Pointee ) 30 { 31 struct DuckPointer 32 { 33 public: 34 mixin( ImplementMembers ); 35 36 this( Pointer )( Pointer* p ) 37 { 38 impl = cast(void*)p; 39 destroyer = ptr => typeid(Pointer).destroy( ptr ); 40 41 // Assign pointers 42 foreach( member; __traits( allMembers, Pointee ) ) 43 { 44 import std.algorithm: among; 45 import std.traits: isSomeFunction; 46 static if( !member.among( "this", "~this" ) ) 47 { 48 static if( __traits( hasMember, this, "__" ~ member ) ) 49 { 50 // We store a pointer, so store that. 51 mixin( "this.__" ~ member ) = &__traits( getMember, p, member ); 52 } 53 else 54 { 55 // No pointer, assign the reference directly. 56 mixin( "this." ~ member ) = &__traits( getMember, p, member ); 57 } 58 } 59 } 60 } 61 62 ~this() 63 { 64 destroyer( impl ); 65 } 66 67 private: 68 void* impl; 69 void function( void* ) destroyer; 70 } 71 72 // The code to implement the pass throughs. 73 private enum ImplementMembers = { 74 string members = ""; 75 76 foreach( member; __traits( allMembers, Pointee ) ) 77 { 78 import std.algorithm: among; 79 import std.string: replace; 80 static if( !member.among( "this", "~this" ) ) 81 { 82 // If member is a field, this'll fail citing a need for a `this` pointer. 83 static if( __traits( compiles, &__traits( getMember, Pointee, member ) ) ) 84 { 85 members ~= "public " ~ typeof( &__traits( getMember, Pointee, member ) ).stringof.replace( "function", "delegate" ) ~ 86 " " ~ member ~ ";"; 87 } 88 else 89 { 90 // If it's a var, store a pointer to it, and return a reference to the dereferenced pointer. 91 members ~= "private " ~ typeof( __traits( getMember, Pointee, member ) ).stringof ~ "* __" ~ member ~ ";"; 92 members ~= "public @property ref " ~ typeof( __traits( getMember, Pointee, member ) ).stringof ~ " " ~ member ~ "() { 93 return *__" ~ member ~ "; }"; 94 } 95 } 96 } 97 98 return members; 99 } (); 100 } 101 /// 102 @name( "DuckPointer Structs" ) 103 unittest 104 { 105 struct IFace1 106 { 107 int getX() { return 12; } 108 } 109 struct Struct1 110 { 111 int x; 112 int getX() { return x; } 113 } 114 115 Struct1 s1; 116 s1.x = 42; 117 DuckPointer!IFace1 ptr1 = duck!IFace1( &s1 ); 118 assert( ptr1.getX() == 42 ); 119 } 120 /// 121 @name( "DuckPointer Template Mixin" ) 122 unittest 123 { 124 struct MyMixinImpl 125 { 126 mixin MyMixin!(); 127 } 128 alias MixinPtr = DuckPointer!MyMixinImpl; 129 130 struct Struct1 131 { 132 mixin MyMixin!(); 133 } 134 135 Struct1 s1; 136 s1.x = 42; 137 MixinPtr ptr1 = MixinPtr( &s1 ); 138 assert( ptr1.x == 42 ); 139 assert( ptr1.getX() == 42 ); 140 ++ptr1.x; 141 assert( ptr1.x == 43 ); 142 assert( ptr1.getX() == 43 ); 143 assert( s1.x == 43 ); 144 } 145 version( unittest ) 146 private mixin template MyMixin() 147 { 148 int x; 149 int getX() { return x; } 150 } 151 /// 152 @name( "DuckPointer String Mixin" ) 153 unittest 154 { 155 enum myMixin = q{ 156 int x; 157 int getX() { return x; } 158 }; 159 struct MyMixinImpl 160 { 161 mixin( myMixin ); 162 } 163 alias MixinPtr = DuckPointer!MyMixinImpl; 164 165 struct Struct1 166 { 167 mixin( myMixin ); 168 } 169 170 Struct1 s1; 171 s1.x = 42; 172 MixinPtr ptr1 = MixinPtr( &s1 ); 173 assert( ptr1.x == 42 ); 174 assert( ptr1.getX() == 42 ); 175 ++ptr1.x; 176 assert( ptr1.x == 43 ); 177 assert( ptr1.getX() == 43 ); 178 assert( s1.x == 43 ); 179 }