1 module fuzzycopy; 2 3 template canBeConvertedWithTo(F,T) { 4 import std.traits : isIntegral, isFloatingPoint, isArray; 5 import std.range : ElementEncodingType; 6 7 static if(isIntegral!F && isIntegral!T) { 8 enum canBeConvertedWithTo = true; 9 } else static if(isIntegral!F && isFloatingPoint!T) { 10 enum canBeConvertedWithTo = true; 11 } else static if(isFloatingPoint!F && isIntegral!T) { 12 enum canBeConvertedWithTo = true; 13 } else static if(isArray!F && isArray!T) { 14 alias FType = ElementEncodingType!F; 15 alias TType = ElementEncodingType!T; 16 enum canBeConvertedWithTo = canBeConvertedWithTo!(FType,TType); 17 } else { 18 enum canBeConvertedWithTo = false; 19 } 20 } 21 22 unittest { 23 static assert(canBeConvertedWithTo!(int,byte)); 24 static assert(canBeConvertedWithTo!(byte,float)); 25 static assert(!canBeConvertedWithTo!(string,float)); 26 static assert(!canBeConvertedWithTo!(int[],float)); 27 static assert(canBeConvertedWithTo!(int[],float[])); 28 static assert(canBeConvertedWithTo!(byte[],double[])); 29 static assert(canBeConvertedWithTo!(double[],ubyte[])); 30 } 31 32 enum FuzzyCP { 33 Fuzzy, 34 AllFrom, // Does not work currently 35 AllTo, 36 Both // Does not work currently 37 } 38 39 string buildFromAllSwitchCase(T)() { 40 import std.conv : to; 41 import std.traits : isFunction; 42 import std.format : format; 43 44 string ret = "switch(mem) {\n"; 45 size_t idx = 0; 46 static foreach(mem; __traits(allMembers, T)) { 47 static if(mem != "this" && !isFunction!(mixin(format("T.%s", mem)))) { 48 ret ~= "case \"" ~ mem ~ "\":\n"; 49 ret ~= "fromMemberVisited[" ~ to!string(idx) ~ "] = true;\n"; 50 ret ~= "break;\n"; 51 ++idx; 52 } 53 } 54 55 ret ~= "default: assert(false, mem);\n"; 56 ret ~= "}\n"; 57 return ret; 58 } 59 60 private size_t numberOfMember(T)() { 61 import std.traits : isFunction; 62 import std.format : format; 63 64 size_t ret = 0; 65 static foreach(mem; __traits(allMembers, T)) { 66 static if(mem != "this" && !isFunction!(mixin(format("T.%s", mem)))) { 67 ++ret; 68 } 69 } 70 71 return ret; 72 } 73 74 void fuzzyCP(F,T, FuzzyCP FC = FuzzyCP.Fuzzy)(auto ref F from, auto ref T target) { 75 import std.traits : hasMember, isImplicitlyConvertible, isFunction; 76 import std.format : format; 77 78 static if(FC == FuzzyCP.Both || FC == FuzzyCP.AllTo) { 79 bool[numberOfMember!T()] fromMemberVisited; 80 } 81 foreach(mem; __traits(allMembers, typeof(from))) { 82 // we can not copy function so don't try 83 static if(mem != "this" && !isFunction!(mixin(format("F.%s", mem)))) { 84 alias FromType = typeof(__traits(getMember, from, mem)); 85 //pragma(msg, mem ~ " " ~ FromType.stringof); 86 87 // check if target has a value of the same name 88 static if(hasMember!(T, mem)) { 89 static import std.conv; 90 alias ToType = typeof(__traits(getMember, target, mem)); 91 92 // recursive if it is a struct 93 static if(is(FromType == struct) && is(ToType == struct)) { 94 fuzzyCP!(FromType,ToType,FC)( 95 __traits(getMember, from, mem), 96 __traits(getMember, target, mem) 97 ); 98 //pragma(msg, buildFromAllSwitchCase!F()); 99 //mixin(buildFromAllSwitchCase!F()); 100 // assign if it is assignable 101 } else static if(isImplicitlyConvertible!(FromType,ToType)) { 102 __traits(getMember, target, mem) = std.conv.to!(ToType)( 103 __traits(getMember, from, mem) 104 ); 105 } else static if(canBeConvertedWithTo!(FromType,ToType)) { 106 __traits(getMember, target, mem) = std.conv.to!(ToType)( 107 __traits(getMember, from, mem) 108 ); 109 } else static if(FC == FuzzyCP.Both || FC == FuzzyCP.AllFrom) { 110 import std.format : format; 111 static assert(false, format( 112 "fuzzyCP is using '%s' Mode and From '%s' and To '%s' have" 113 ~ " a member '%s' but From.%s can not be converted target " 114 ~ "To.%s", 115 FC, F.stringof, T.stringof, mem, FromType.stringof, 116 ToType.stringof)); 117 } 118 // for FromAll and Both To needs a member 119 } else static if(FC == FuzzyCP.Both || FC == FuzzyCP.AllFrom) { 120 import std.format : format; 121 static assert(false, format( 122 "fuzzyCP is using %s Mode and %s has no member named %s", 123 FC, T.stringof, mem)); 124 } 125 } 126 } 127 } 128 129 unittest { 130 struct Foo { 131 int a = 10; 132 int b = 20; 133 } 134 135 struct Bar { 136 int a; 137 int b; 138 } 139 140 Foo foo; 141 assert(foo.a == 10); 142 assert(foo.b == 20); 143 144 Bar bar; 145 146 fuzzyCP(foo, bar); 147 148 assert(bar.a == 10); 149 assert(bar.b == 20); 150 } 151 152 unittest { 153 struct Foo { 154 int a = 99; 155 int b = 77; 156 } 157 158 struct Bar { 159 Foo foo; 160 string s = "hello"; 161 } 162 163 struct NotFoo { 164 long a; 165 long b; 166 } 167 168 struct NotBar { 169 NotFoo foo; 170 string s; 171 } 172 173 Bar bar; 174 NotBar nBar; 175 176 fuzzyCP(bar, nBar); 177 178 assert(nBar.foo.a == 99); 179 assert(nBar.foo.b == 77); 180 assert(nBar.s == "hello"); 181 } 182 183 unittest { 184 struct Foo { 185 int a = 99; 186 int b = 77; 187 188 int fun2() { 189 return b; 190 } 191 } 192 193 struct Bar { 194 Foo foo; 195 string s = "hello"; 196 string fun() { 197 return s; 198 } 199 } 200 201 struct NotFoo { 202 byte a; 203 ushort b; 204 } 205 206 struct NotBar { 207 NotFoo foo; 208 string s; 209 } 210 211 Bar bar; 212 NotBar nBar; 213 214 fuzzyCP!(Bar,NotBar,FuzzyCP.Both)(bar, nBar); 215 216 assert(nBar.foo.a == 99); 217 assert(nBar.foo.b == 77); 218 assert(nBar.s == "hello"); 219 } 220 221 unittest { 222 import std.exception : assertThrown; 223 struct Foo { 224 long l = 126; 225 } 226 227 struct Bar { 228 byte l; 229 } 230 231 Foo f; 232 Bar b; 233 234 fuzzyCP(f, b); 235 assert(b.l == 126); 236 237 f.l = 128; 238 assertThrown(fuzzyCP(f, b)); 239 240 static assert(!__traits(compiles, fuccyCP!(Foo,Bar,FuzzyCP.FromAll))); 241 static assert(!__traits(compiles, fuccyCP!(Foo,Bar,FuzzyCP.ToAll))); 242 }