1 /* 2 Copyright (c) 2015-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Prototype-based OOP system for structs. 31 * 32 * Description: 33 * Supports multiple inheritance, parametric polymorphism (struct interfaces), 34 * interface inheritance 35 * 36 * Copyright: Timur Gafarov 2015-2021. 37 * License: $(LINK2 https://boost.org/LICENSE_1_0.txt, Boost License 1.0). 38 * Authors: Timur Gafarov 39 */ 40 module dlib.core.oop; 41 42 import std.traits; 43 44 /** 45 * Inheritance mixin 46 * 47 * Description: 48 * Inserts structs specified by PT type tuple as members and a dispatcher method 49 * that statically transfers any method calls and member accesses to corresponding 50 * child struct. 51 */ 52 mixin template Inherit(PT...) 53 { 54 PT _parent; 55 alias _parentTypeTuple = PT; 56 alias _parent this; 57 58 template opDispatch(string s) 59 { 60 enum e = tupleElemWithMember!(s, PT); 61 62 static if (hasMethod!(typeof(_parent[e]), s)) 63 { 64 @property auto ref opDispatch(T...)(T params) 65 { 66 return __traits(getMember, _parent[e], s)(params); 67 } 68 } 69 else 70 { 71 @property auto ref opDispatch() 72 { 73 return __traits(getMember, _parent[e], s); 74 } 75 76 @property auto ref opDispatch(T)(T val) 77 { 78 return __traits(getMember, _parent[e], s) = val; 79 } 80 } 81 } 82 } 83 84 /// 85 unittest 86 { 87 struct Foo 88 { 89 int x = 100; 90 int foo() { return 11; } 91 } 92 93 struct Bar 94 { 95 int y = 99; 96 int bar() { return 22; } 97 } 98 99 struct TestObj 100 { 101 mixin Inherit!(Foo, Bar); 102 } 103 104 TestObj obj; 105 106 obj.x *= 2; 107 obj.y = 10; 108 109 assert(obj.x == 200); 110 assert(obj.y == 10); 111 112 assert(obj.foo() == 11); 113 assert(obj.bar() == 22); 114 } 115 116 /** 117 * Returns index of a tuple element which has specified member (s) 118 */ 119 size_t tupleElemWithMember(string s, T...)() 120 { 121 foreach(i, type; T) 122 { 123 static if (__traits(hasMember, type, s)) 124 return i; 125 } 126 assert(0); 127 } 128 129 /** 130 * Test if type (T) has specified method (m), local or derived 131 */ 132 bool hasMethod(T, string m)() 133 { 134 static if (__traits(hasMember, T, m)) 135 { 136 static if (isMethod!(__traits(getMember, T, m))) 137 { 138 return true; 139 } 140 } 141 142 static if (__traits(hasMember, T, "_parentTypeTuple")) 143 { 144 foreach(i, parentType; T._parentTypeTuple) 145 { 146 static if (hasMethod!(parentType, m)) 147 { 148 return true; 149 } 150 } 151 152 return false; 153 } 154 else 155 { 156 return false; 157 } 158 } 159 160 /// 161 unittest 162 { 163 struct Foo 164 { 165 int bar() { return 0; } 166 } 167 168 assert(hasMethod!(Foo, "bar")() == true); 169 assert(hasMethod!(Foo, "foo")() == false); 170 } 171 172 /** 173 * Check if given type (T) corresponds to given signature (I) 174 * 175 * Description: 176 * Returns true if struct T has the same members and methods as struct I. 177 * This allows to use structs as static interfaces in generic code. 178 */ 179 /* 180 bool implements(T, I)() 181 { 182 foreach(i, name; __traits(allMembers, I)) 183 { 184 static if (name == "_parentTypeTuple") {} 185 else static if (name == "_parent") 186 { 187 foreach(ParI; I._parentTypeTuple) 188 { 189 static if (!implements!(T, ParI)) 190 return false; 191 } 192 } 193 else static if (isMethod!(__traits(getMember, I, name))) 194 { 195 static if (!hasMethod!(T, name)) 196 return false; 197 } 198 else 199 { 200 static if (!__traits(hasMember, T, name)) 201 return false; 202 else 203 { 204 alias t1 = typeof(__traits(getMember, T, name)); 205 alias t2 = typeof(__traits(getMember, I, name)); 206 static if (!is(t1 == t2)) 207 return false; 208 } 209 } 210 } 211 return true; 212 } 213 214 /// 215 unittest 216 { 217 struct Foo 218 { 219 int bar() { return 10; } 220 } 221 222 struct FooInterface 223 { 224 int bar() { return 0; } 225 } 226 227 assert(implements!(Foo, FooInterface)() == true); 228 } 229 */ 230 231 /** 232 * Test if F is a method 233 */ 234 template isMethod(alias F) 235 { 236 enum isMethod = 237 isSomeFunction!F && 238 !isFunctionPointer!F && 239 !isDelegate!F; 240 }