1 /** 2 The most important data type in Godot. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.variant; 14 15 import godot.c; 16 import godot.core; 17 import godot.object; 18 import godot.d.meta; 19 import godot.d.reference; 20 21 import std.meta, std.traits; 22 import std.conv : text; 23 import std.range; 24 25 // ABI type should probably have its own `version`... 26 version(X86_64) 27 { 28 version(DigitalMars) 29 { 30 version(linux) version = GodotSystemV; 31 version(OSX) version = GodotSystemV; 32 version(Posix) version = GodotSystemV; 33 } 34 } 35 36 /** 37 A Variant takes up only 20 bytes and can store almost any engine datatype inside of it. Variants are rarely used to hold information for long periods of time, instead they are used mainly for communication, editing, serialization and moving data around. 38 */ 39 struct Variant 40 { 41 @nogc nothrow: 42 43 package(godot) godot_variant _godot_variant; 44 45 /// 46 enum Type 47 { 48 nil, 49 50 // atomic types 51 bool_, 52 int_, 53 real_, 54 string, 55 56 // math types 57 58 vector2,// 5 59 rect2, 60 vector3, 61 transform2d, 62 plane, 63 quat,// 10 64 aabb, 65 basis, 66 transform, 67 68 // misc types 69 color, 70 node_path,// 15 71 rid, 72 object, 73 dictionary, 74 array, 75 76 // arrays 77 pool_byte_array,// 20 78 pool_int_array, 79 pool_real_array, 80 pool_string_array, 81 pool_vector2_array, 82 pool_vector3_array,// 25 83 pool_color_array, 84 } 85 86 /// GDNative type that gets passed to the C functions 87 alias InternalType = AliasSeq! 88 ( 89 typeof(null), 90 91 godot_bool, 92 long, 93 double, 94 godot_string, 95 96 godot_vector2, 97 godot_rect2, 98 godot_vector3, 99 godot_transform2d, 100 godot_plane, 101 godot_quat, 102 godot_aabb, 103 godot_basis, 104 godot_transform, 105 106 godot_color, 107 godot_node_path, 108 godot_rid, 109 godot_object, 110 godot_dictionary, 111 godot_array, 112 113 godot_pool_byte_array, 114 godot_pool_int_array, 115 godot_pool_real_array, 116 godot_pool_string_array, 117 godot_pool_vector2_array, 118 godot_pool_vector3_array, 119 godot_pool_color_array, 120 ); 121 122 /// D type that this Variant implementation uses 123 alias DType = AliasSeq! 124 ( 125 typeof(null), 126 127 bool, 128 long, 129 double, 130 String, 131 132 Vector2,// 5 133 Rect2, 134 Vector3, 135 Transform2D, 136 Plane, 137 Quat,// 10 138 AABB, 139 Basis, 140 Transform, 141 142 // misc types 143 Color, 144 NodePath,// 15 145 RID, 146 GodotObject, 147 Dictionary, 148 Array, 149 150 // arrays 151 PoolByteArray,// 20 152 PoolIntArray, 153 PoolRealArray, 154 PoolStringArray, 155 PoolVector2Array, 156 PoolVector3Array,// 25 157 PoolColorArray, 158 ); 159 160 /// 161 enum Operator 162 { 163 //comparation 164 equal, 165 notEqual, 166 less, 167 lessEqual, 168 greater, 169 greaterEqual, 170 171 //mathematic 172 add, 173 substract, 174 multiply, 175 divide, 176 negate, 177 positive, 178 modulus, 179 stringConcat, 180 181 //bitwise 182 shiftLeft, 183 shiftRight, 184 bitAnd, 185 bitOr, 186 bitXor, 187 bitNegate, 188 189 //logic 190 and, 191 or, 192 xor, 193 not, 194 195 //containment 196 in_ 197 } 198 199 private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest); 200 201 private static GodotObject objectToGodot(T)(T o) 202 { 203 return o.getGodotObject; 204 } 205 private static R objectFromGodot(R)(in GodotObject o) 206 { 207 static if(is(R == const)) alias co = o; 208 else GodotObject co = cast(GodotObject)o._godot_object; // annoying hack to deal with const 209 210 return co.as!R; 211 } 212 213 /// function to convert T to an equivalent Godot type 214 template conversionToGodot(T) 215 { 216 static if(isGodotClass!T) alias conversionToGodot = objectToGodot!T; 217 else static if(is(T : GodotStringLiteral!s, string s)) alias conversionToGodot = (T t) => t.str(); 218 else static if(is(T : Ref!U, U)) alias conversionToGodot = objectToGodot!U; 219 else static if(isIntegral!T) alias conversionToGodot = (T t) => cast(long)t; 220 else static if(isFloatingPoint!T) alias conversionToGodot = (T t) => cast(double)t; 221 else static if(implicit!(T, const(char)[]) || implicit!(T, const(char)*)) 222 alias conversionToGodot = (T t) => String(t); 223 else static if((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T)) 224 alias conversionToGodot = (T t) 225 { 226 import std.algorithm.iteration; 227 Array ret = Array.empty_array; 228 static if(hasLength!T) 229 { 230 ret.resize(cast(int)t.length); 231 foreach(ei, e; t) ret[cast(int)ei] = e; 232 } 233 else t.each!(e => ret ~= e); 234 return ret; 235 }; 236 else alias conversionToGodot = void; // none 237 } 238 enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T); 239 alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T)); 240 241 /// function to convert a Godot-compatible type to T 242 template conversionFromGodot(T) 243 { 244 static if(isGodotClass!T || is(T : Ref!U, U)) alias conversionFromGodot = objectFromGodot!T; 245 else static if(isIntegral!T) alias conversionFromGodot = (long v) => cast(T)v; 246 else static if(isFloatingPoint!T) alias conversionFromGodot = (double v) => cast(T)v; 247 /* 248 TODO: overhaul this badly-designed conversion system. These should be 249 moved out of Variant, into conversion functions on the core types themselves. 250 */ 251 else static if(isStaticArray!T && compatibleFromGodot!(ElementType!T)) 252 alias conversionFromGodot = (in Array arr) 253 { 254 if(arr.length == T.length) 255 { 256 T ret; 257 foreach(i; 0..T.length) ret[i] = (arr[i]).as!(ElementType!T); 258 return ret; 259 } 260 else assert(0, "Array length doesn't match static array "~T.stringof); 261 }; 262 else alias conversionFromGodot = void; 263 } 264 enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T); 265 alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]); 266 267 enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1; 268 template compatibleToGodot(T) 269 { 270 static if(directlyCompatible!T) enum bool compatibleToGodot = true; 271 else enum bool compatibleToGodot = convertsToGodot!T; 272 } 273 template compatibleFromGodot(T) 274 { 275 static if(directlyCompatible!T) enum bool compatibleFromGodot = true; 276 else enum bool compatibleFromGodot = convertsFromGodot!T; 277 } 278 enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R); 279 280 281 /// All target Variant.Types that T could implicitly convert to, as indices 282 private template implicitTargetIndices(T) 283 { 284 private enum bool _implicit(size_t di) = implicit!(T, DType[di]); 285 alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length))); 286 } 287 288 /++ 289 Get the Variant.Type of a compatible D type. Incompatible types return nil. 290 +/ 291 public template variantTypeOf(T) 292 { 293 import std.traits, godot; 294 295 static if(directlyCompatible!T) 296 { 297 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)]; 298 } 299 else static if(convertsToGodot!T) 300 { 301 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!( 302 conversionToGodotType!T, DType)]; 303 } 304 else enum Type variantTypeOf = Type.nil; // so the template always returns a Type 305 } 306 307 unittest 308 { 309 static assert(allSatisfy!(compatible, DType)); 310 static assert(!compatible!Object); // D Object 311 312 static assert(directlyCompatible!GodotObject); 313 static assert(directlyCompatible!(const(GodotObject))); 314 import godot.camera; 315 static assert(!directlyCompatible!Camera); 316 static assert(compatibleFromGodot!Camera); 317 static assert(compatibleToGodot!Camera); 318 static assert(compatibleFromGodot!(const(Camera))); 319 static assert(compatibleToGodot!(const(Camera))); 320 } 321 322 private template FunctionAs(Type type) 323 { 324 private enum string name_ = text(type); 325 private enum string FunctionAs = (name_[$-1]=='_')?(name_[0..$-1]):name_; 326 } 327 private template FunctionNew(Type type) 328 { 329 private enum string name_ = text(type); 330 private enum string FunctionNew = (name_[$-1]=='_')?(name_[0..$-1]):name_; 331 } 332 333 this(this) 334 { 335 godot_variant other = _godot_variant; // source Variant still owns this 336 _godot_api.godot_variant_new_copy(&_godot_variant, &other); 337 } 338 339 static Variant nil() 340 { 341 Variant v = void; 342 _godot_api.godot_variant_new_nil(&v._godot_variant); 343 return v; 344 } 345 346 this(in ref Variant other) 347 { 348 _godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant); 349 } 350 351 this(T : typeof(null))(in T nil) 352 { 353 _godot_api.godot_variant_new_nil(&_godot_variant); 354 } 355 356 this(R)(auto ref R input) if(!is(R : Variant) && !is(R : typeof(null))) 357 { 358 static assert(compatibleToGodot!R, R.stringof~" isn't compatible with Variant."); 359 enum VarType = variantTypeOf!R; 360 361 mixin("auto Fn = _godot_api.godot_variant_new_"~FunctionNew!VarType~";"); 362 alias PassType = Parameters!Fn[1]; // second param is the value 363 364 alias IT = InternalType[VarType]; 365 366 // handle explicit conversions 367 static if(directlyCompatible!R) alias inputConv = input; 368 else auto inputConv = conversionToGodot!R(input); 369 370 static if(is(IT == Unqual!PassType)) Fn(&_godot_variant, cast(IT)inputConv); // value 371 else Fn(&_godot_variant, cast(IT*)&inputConv); // pointer 372 } 373 374 ~this() 375 { 376 _godot_api.godot_variant_destroy(&_godot_variant); 377 } 378 379 Type type() const 380 { 381 return cast(Type)_godot_api.godot_variant_get_type(&_godot_variant); 382 } 383 384 inout(T) as(T : Variant)() inout { return this; } 385 386 R as(R)() const if(!is(R == Variant) && !is(R==typeof(null)) && compatibleFromGodot!R) 387 { 388 static if(directlyCompatible!R) enum VarType = variantTypeOf!R; 389 else enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)]; 390 391 // HACK workaround for DMD issue #5570 392 version(GodotSystemV) enum sV = true; 393 else enum sV = false; 394 static if(VarType == Type.vector3 && sV) 395 { 396 godot_vector3 ret = void; 397 void* _func = cast(void*)_godot_api.godot_variant_as_vector3; 398 void* _this = cast(void*)&this; 399 400 asm @nogc nothrow 401 { 402 mov RDI, _this; 403 call _func; 404 405 mov ret[0], RAX; 406 mov ret[8], EDX; 407 } 408 return *cast(Vector3*)&ret; 409 } 410 else 411 { 412 InternalType[VarType] ret = mixin("_godot_api.godot_variant_as_"~FunctionAs!VarType~"(&_godot_variant)"); 413 414 static if(directlyCompatible!R) return *cast(DType[VarType]*)&ret; 415 else 416 { 417 return conversionFromGodot!R(*cast(DType[VarType]*)&ret); 418 } 419 } 420 } 421 422 pragma(inline, true) 423 void opAssign(T)(in auto ref T input) if(!is(T : Variant) && !is(T : typeof(null))) 424 { 425 import std.conv : emplace; 426 427 _godot_api.godot_variant_destroy(&_godot_variant); 428 emplace!(Variant)(&this, input); 429 } 430 431 pragma(inline, true) 432 void opAssign(T : typeof(null))(in T nil) 433 { 434 _godot_api.godot_variant_destroy(&_godot_variant); 435 _godot_api.godot_variant_new_nil(&_godot_variant); 436 } 437 438 pragma(inline, true) 439 void opAssign(T : Variant)(in T other) 440 { 441 _godot_api.godot_variant_destroy(&_godot_variant); 442 _godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant); 443 } 444 445 bool opEquals(in ref Variant other) const 446 { 447 return cast(bool)_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant); 448 } 449 450 int opCmp(in ref Variant other) const 451 { 452 if(_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant)) 453 return 0; 454 return _godot_api.godot_variant_operator_less(&_godot_variant, &other._godot_variant)? 455 -1 : 1; 456 } 457 458 bool booleanize() const 459 { 460 return cast(bool)_godot_api.godot_variant_booleanize(&_godot_variant); 461 } 462 463 auto toString() const 464 { 465 String str = as!String; 466 return str.data; 467 } 468 } 469