1 /++ 2 This internal module, with the help of the luad.conversions package, takes care of converting between D and Lua types. 3 4 The conversion rules are as follows, where conversion goes both ways: 5 $(DL 6 $(DT boolean 7 $(DD $(D bool)) 8 ) 9 $(DT number 10 $(DD $(D lua_Integer) (default $(D int))) 11 $(DD $(D lua_Number) (default $(D double))) 12 ) 13 $(DT string 14 $(DD $(D string), $(D const(char)[]), $(D char[])) 15 $(DD $(D const(char)*)) 16 $(DD $(D char)) 17 $(DD $(D immutable(void)[]), $(D const(void)[]), $(D void[]) (binary data)) 18 ) 19 $(DT table 20 $(DD associative arrays (see $(DPMODULE2 conversions,assocarrays))) 21 $(DD arrays (see $(DPMODULE2 conversions,arrays))) 22 $(DD structs (see $(DPMODULE2 conversions,structs))) 23 $(DD $(DPREF table,LuaTable)) 24 ) 25 $(DT function (see $(DPMODULE2 conversions,functions)) 26 $(DD function pointers) 27 $(DD delegates) 28 $(DD $(DPREF lfunction,LuaFunction)) 29 ) 30 $(DT userdata 31 $(DD classes (see $(DPMODULE2 conversions,classes))) 32 ) 33 $(DT nil 34 $(DD the special identifier $(D nil)) 35 $(DD $(D null) class references) 36 ) 37 $(DT any of the above 38 $(DD $(DPREF base,LuaObject)) 39 $(DD $(DPREF dynamic,LuaDynamic)) 40 $(DD $(D Algebraic), when given a compatible value (see $(DPMODULE2 conversions,variant))) 41 ) 42 ) 43 44 The conversions are checked in the specified order. For example, even though $(D bool) is implicitly convertible 45 to $(D lua_Integer), it will be converted to a boolean because boolean has precedence. 46 47 $(D wchar) and $(D dchar) are explicitly disallowed. Lua strings consist of 8-bit characters, if you want to push UTF-16 or UTF-32 strings, convert to UTF-8 first. 48 49 Additionally, the following types are pushable to Lua, but can't be retrieved back: 50 $(DL 51 $(DT function 52 $(DD $(D lua_CFunction)) 53 ) 54 ) 55 +/ 56 module luad.stack; 57 58 import std.range; 59 import std.traits; 60 import std.typecons; 61 62 import luad.c.all; 63 64 import luad.base; 65 import luad.table; 66 import luad.lfunction; 67 import luad.dynamic; 68 69 import luad.conversions.functions; 70 import luad.conversions.arrays; 71 import luad.conversions.structs; 72 import luad.conversions.assocarrays; 73 import luad.conversions.classes; 74 import luad.conversions.variant; 75 76 /** 77 * Push a value of any type to the stack. 78 * Params: 79 * L = stack to push to 80 * value = value to push 81 */ 82 void pushValue(T)(lua_State* L, T value) 83 { 84 static if(is(T : LuaObject)) 85 value.push(); 86 87 else static if(is(T == LuaDynamic)) 88 value.object.push(); 89 90 else static if(is(T == Nil)) 91 lua_pushnil(L); 92 93 else static if(is(T == bool)) 94 lua_pushboolean(L, cast(bool)value); 95 96 else static if(is(T == char)) 97 lua_pushlstring(L, &value, 1); 98 99 else static if(is(T : lua_Integer)) 100 lua_pushinteger(L, value); 101 102 else static if(is(T : lua_Number)) 103 lua_pushnumber(L, value); 104 105 else static if(is(T : const(char)[])) 106 lua_pushlstring(L, value.ptr, value.length); 107 108 else static if(isVoidArray!T) 109 lua_pushlstring(L, cast(const(char)*)value.ptr, value.length); 110 111 else static if(is(T : const(char)*)) 112 lua_pushstring(L, value); 113 114 else static if(isVariant!T) 115 pushVariant(L, value); 116 117 else static if(isAssociativeArray!T) 118 pushAssocArray(L, value); 119 120 else static if(isArray!T) 121 pushArray(L, value); 122 123 else static if(is(T == struct)) 124 pushStruct(L, value); 125 126 // luaCFunction's are directly pushed 127 else static if(is(T == lua_CFunction) && functionLinkage!T == "C") 128 lua_pushcfunction(L, value); 129 130 // other functions are wrapped 131 else static if(isSomeFunction!T) 132 pushFunction(L, value); 133 134 else static if(is(T == class)) 135 { 136 if(value is null) 137 lua_pushnil(L); 138 else 139 pushClassInstance(L, value); 140 } 141 else 142 static assert(false, "Unsupported type `" ~ T.stringof ~ "` in stack push operation"); 143 } 144 145 template isVoidArray(T) 146 { 147 enum isVoidArray = is(T == void[]) || 148 is(T == const(void)[]) || 149 is(T == const(void[])) || 150 is(T == immutable(void)[]) || 151 is(T == immutable(void[])); 152 } 153 154 /** 155 * Get the associated Lua type for T. 156 * Returns: Lua type for T 157 */ 158 template luaTypeOf(T) 159 { 160 static if(is(T == bool)) 161 enum luaTypeOf = LUA_TBOOLEAN; 162 163 else static if(is(T == Nil)) 164 enum luaTypeOf = LUA_TNIL; 165 166 else static if(is(T : const(char)[]) || is(T : const(char)*) || is(T == char) || isVoidArray!T) 167 enum luaTypeOf = LUA_TSTRING; 168 169 else static if(is(T : lua_Integer) || is(T : lua_Number)) 170 enum luaTypeOf = LUA_TNUMBER; 171 172 else static if(isSomeFunction!T || is(T == LuaFunction)) 173 enum luaTypeOf = LUA_TFUNCTION; 174 175 else static if(isArray!T || isAssociativeArray!T || is(T == struct) || is(T == LuaTable)) 176 enum luaTypeOf = LUA_TTABLE; 177 178 else static if(is(T : Object)) 179 enum luaTypeOf = LUA_TUSERDATA; 180 181 else 182 static assert(false, "No Lua type defined for `" ~ T.stringof ~ "`"); 183 } 184 185 // generic type mismatch message 186 private void defaultTypeMismatch(lua_State* L, int idx, int expectedType) 187 { 188 luaL_error(L, "expected %s, got %s", lua_typename(L, expectedType), luaL_typename(L, idx)); 189 } 190 191 // type mismatch for function arguments of unexpected type 192 private void argumentTypeMismatch(lua_State* L, int idx, int expectedType) 193 { 194 luaL_typerror(L, idx, lua_typename(L, expectedType)); 195 } 196 197 /** 198 * Get a value of any type from the stack. 199 * Params: 200 * T = type of value 201 * typeMismatchHandler = function called to produce an error in case of an invalid conversion. 202 * L = stack to get from 203 * idx = value stack index 204 */ 205 T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int idx) 206 { 207 debug //ensure unchanged stack 208 { 209 int _top = lua_gettop(L); 210 scope(success) assert(lua_gettop(L) == _top); 211 } 212 213 //ambiguous types 214 static if(is(T == wchar) || is(T : const(wchar)[]) || 215 is(T == dchar) || is(T : const(dchar)[])) 216 { 217 static assert("Ambiguous type " ~ T.stringof ~ " in stack push operation. Consider converting before pushing."); 218 } 219 220 static if(!is(T == LuaObject) && !is(T == LuaDynamic) && !isVariant!T) 221 { 222 int type = lua_type(L, idx); 223 enum expectedType = luaTypeOf!T; 224 225 //if a class reference, return null for nil values 226 static if(is(T : Object)) 227 { 228 if(type == LuaType.Nil) 229 return null; 230 } 231 232 if(type != expectedType) 233 typeMismatchHandler(L, idx, expectedType); 234 } 235 236 static if(is(T == LuaFunction)) // WORKAROUND: bug #6036 237 { 238 LuaFunction func; 239 func.object = LuaObject(L, idx); 240 return func; 241 } 242 else static if(is(T == LuaDynamic)) // ditto 243 { 244 LuaDynamic obj; 245 obj.object = LuaObject(L, idx); 246 return obj; 247 } 248 else static if(is(T : LuaObject)) 249 return T(L, idx); 250 251 else static if(is(T == Nil)) 252 return nil; 253 254 else static if(is(T == bool)) 255 return lua_toboolean(L, idx); 256 257 else static if(is(T == char)) 258 return *lua_tostring(L, idx); // TODO: better define this 259 260 else static if(is(T : lua_Integer)) 261 return cast(T)lua_tointeger(L, idx); 262 263 else static if(is(T : lua_Number)) 264 return cast(T)lua_tonumber(L, idx); 265 266 else static if(is(T : const(char)[]) || isVoidArray!T) 267 { 268 size_t len; 269 const(char)* str = lua_tolstring(L, idx, &len); 270 static if(is(T == char[]) || is(T == void[])) 271 return str[0 .. len].dup; 272 else 273 return str[0 .. len].idup; 274 } 275 else static if(is(T : const(char)*)) 276 return lua_tostring(L, idx); 277 278 else static if(isAssociativeArray!T) 279 return getAssocArray!T(L, idx); 280 281 else static if(isArray!T) 282 return getArray!T(L, idx); 283 284 else static if(isVariant!T) 285 { 286 if(!isAllowedType!T(L, idx)) 287 luaL_error(L, "Type not allowed in Variant: %s", luaL_typename(L, idx)); 288 289 return getVariant!T(L, idx); 290 } 291 else static if(is(T == struct)) 292 return getStruct!T(L, idx); 293 294 else static if(isSomeFunction!T) 295 return getFunction!T(L, idx); 296 297 else static if(is(T : Object)) 298 return getClassInstance!T(L, idx); 299 300 else 301 { 302 static assert(false, "Unsupported type `" ~ T.stringof ~ "` in stack read operation"); 303 } 304 } 305 306 /** 307 * Same as calling getValue!(T, typeMismatchHandler)(L, -1), then popping one value from the stack. 308 * See_Also: $(MREF getValue) 309 */ 310 T popValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L) 311 { 312 scope(success) lua_pop(L, 1); 313 return getValue!(T, typeMismatchHandler)(L, -1); 314 } 315 316 /** 317 * Pop a number of elements from the stack. 318 * Params: 319 * T = element type 320 * L = stack to pop from 321 * n = number of elements to pop 322 * Returns: 323 * array of popped elements, or a $(D null) array if n = 0 324 */ 325 T[] popStack(T = LuaObject)(lua_State* L, size_t n) 326 { 327 if(n == 0) // Don't allocate an array in this case 328 return null; 329 330 auto stack = new T[n]; 331 foreach(i; 0 .. n) 332 { 333 stack[i] = getValue!T(L, cast(int)(-n + i)); 334 } 335 336 lua_pop(L, cast(int)n); 337 return stack; 338 } 339 340 /// Get a function argument from the stack. 341 auto getArgument(T, int narg)(lua_State* L, int idx) 342 { 343 alias ParameterTypeTuple!T Args; 344 345 static if(narg == -1) // varargs causes this 346 alias ForeachType!(Args[$-1]) Arg; 347 else 348 alias Args[narg] Arg; 349 350 enum isVarargs = variadicFunctionStyle!T == Variadic.typesafe; 351 352 static if(isVarargs && narg == Args.length-1) 353 { 354 alias Args[narg] LastArg; 355 alias ForeachType!LastArg ElemType; 356 357 auto top = lua_gettop(L); 358 auto size = top - idx + 1; 359 LastArg result = new LastArg(size); 360 foreach(i; 0 .. size) 361 { 362 result[i] = getArgument!(T, -1)(L, idx + i); 363 } 364 return result; 365 } 366 else static if(is(Arg == const(char)[]) || is(Arg == const(void)[]) || 367 is(Arg == const(char[])) || is(Arg == const(void[]))) 368 { 369 if(lua_type(L, idx) != LUA_TSTRING) 370 argumentTypeMismatch(L, idx, LUA_TSTRING); 371 372 size_t len; 373 const(char)* cstr = lua_tolstring(L, idx, &len); 374 return cstr[0 .. len]; 375 } 376 else 377 return getValue!(Arg, argumentTypeMismatch)(L, idx); 378 } 379 380 template isVariableReturnType(T : LuaVariableReturn!U, U) 381 { 382 enum isVariableReturnType = true; 383 } 384 385 template isVariableReturnType(T) 386 { 387 enum isVariableReturnType = false; 388 } 389 390 /// Used for getting a suitable nresults argument to $(D lua_call) or $(D lua_pcall). 391 template returnTypeSize(T) 392 { 393 static if(isVariableReturnType!T) 394 enum returnTypeSize = LUA_MULTRET; 395 396 else static if(isTuple!T) 397 enum returnTypeSize = T.Types.length; 398 399 else static if(isStaticArray!T) 400 enum returnTypeSize = T.length; 401 402 else static if(is(T == void)) 403 enum returnTypeSize = 0; 404 405 else 406 enum returnTypeSize = 1; 407 } 408 409 /** 410 * Pop return values from stack. 411 * Defaults to $(MREF popValue), but has special handling for $(DPREF2 conversions, functions, LuaVariableReturn), 412 * $(STDREF typecons, Tuple), static arrays and $(D void). 413 * Params: 414 * nret = number of return values 415 * Returns: 416 * Return value, collection of return values, or nothing 417 */ 418 T popReturnValues(T)(lua_State* L, size_t nret) 419 { 420 static if(isVariableReturnType!T) 421 return variableReturn(popStack!(ElementType!(T.WrappedType))(L, nret)); 422 423 else static if(isTuple!T) 424 { 425 if(nret < T.Types.length) 426 luaL_error(L, "expected %d return values, got %d", T.Types.length, nret); 427 428 return popTuple!T(L); 429 } 430 else static if(isStaticArray!T) 431 { 432 T ret; 433 fillStaticArray(L, ret); 434 return ret; 435 } 436 else static if(is(T == void)) 437 return; 438 439 else 440 { 441 if(nret < 1) 442 luaL_error(L, "expected return value of type %s, got nil", lua_typename(L, luaTypeOf!T)); 443 444 return popValue!T(L); 445 } 446 } 447 448 /** 449 * Push return values to the stack. 450 * Defaults to $(MREF pushValue), but has special handling for $(DPREF2 conversions, functions, LuaVariableReturn), 451 * $(STDREF typecons, Tuple) and static arrays. 452 */ 453 int pushReturnValues(T)(lua_State* L, T value) 454 { 455 static if(isVariableReturnType!T) 456 { 457 enum calculateLength = !hasLength!(typeof(value.returnValues)); 458 459 static if(calculateLength) 460 int length; 461 462 foreach(obj; value.returnValues) 463 { 464 pushValue(L, obj); 465 466 static if(calculateLength) 467 ++length; 468 } 469 470 static if(calculateLength) 471 return length; 472 else 473 return cast(int)value.returnValues.length; 474 } 475 else static if(isTuple!T) 476 { 477 pushTuple(L, value); 478 return cast(int)T.Types.length; 479 } 480 else static if(isStaticArray!T) 481 { 482 pushStaticArray(L, value); 483 return cast(int)value.length; 484 } 485 else 486 { 487 pushValue(L, value); 488 return 1; 489 } 490 } 491 492 /// Pops a $(STDREF typecons, Tuple) from the values at the top of the stack. 493 T popTuple(T)(lua_State* L) if(isTuple!T) 494 { 495 T tup; 496 foreach(i, Elem; T.Types) 497 tup[i] = getValue!Elem(L, cast(int)(-T.Types.length + i)); 498 499 lua_pop(L, T.Types.length); 500 return tup; 501 } 502 503 /// Pushes all the values in a $(STDREF typecons, Tuple) to the stack. 504 void pushTuple(T)(lua_State* L, ref T tup) if(isTuple!T) 505 { 506 foreach(i, Elem; T.Types) 507 pushValue(L, tup[i]); 508 } 509 510 /** 511 * Call a Lua function and handle its return values. 512 * Params: 513 * T = type of return value or container of return values 514 * nargs = number of arguments 515 * Returns: 516 * Zero, one or all return values as $(D T), taking into account $(D void), 517 * $(DPREF2 conversions, functions, LuaVariableReturn) and $(STDREF typecons, Tuple) returns 518 */ 519 T callWithRet(T)(lua_State* L, int nargs) 520 { 521 static if(isVariableReturnType!T) 522 auto frame = lua_gettop(L) - nargs - 1; // the size of the stack before arguments and the function 523 524 lua_call(L, nargs, returnTypeSize!T); 525 526 static if(isVariableReturnType!T) 527 auto nret = lua_gettop(L) - frame; 528 else 529 auto nret = returnTypeSize!T; 530 531 return popReturnValues!T(L, nret); 532 } 533 534 private extern(C) int printf(const(char)* fmt, ...); 535 536 /// Print the Lua stack to $(D stdout). 537 void printStack(lua_State* L) 538 { 539 auto top = lua_gettop(L); 540 541 foreach(n; 0 .. top) 542 { 543 auto str = luaL_tolstring(L, n + 1, null); 544 printf("\t[%d] %s (%s)\r\n", n + 1, str, luaL_typename(L, n + 1)); 545 } 546 547 lua_pop(L, top); // luaL_tolstring always pushes one 548 } 549 550 version(unittest) import luad.testing; 551 552 unittest 553 { 554 lua_State* L = luaL_newstate(); 555 scope(success) lua_close(L); 556 557 // pushValue and popValue 558 //number 559 pushValue(L, cast(ubyte)123); 560 assert(lua_isnumber(L, -1) && (popValue!ubyte(L) == 123)); 561 562 pushValue(L, cast(short)123); 563 assert(lua_isnumber(L, -1) && (popValue!short(L) == 123)); 564 565 pushValue(L, 123); 566 assert(lua_isnumber(L, -1) && (popValue!int(L) == 123)); 567 568 pushValue(L, 123UL); 569 assert(lua_isnumber(L, -1) && (popValue!ulong(L) == 123)); 570 571 pushValue(L, 1.2f); 572 assert(lua_isnumber(L, -1) && (popValue!float(L) == 1.2f)); 573 574 pushValue(L, 1.23); 575 assert(lua_isnumber(L, -1) && (popValue!double(L) == 1.23)); 576 577 //string 578 string istr = "foobar"; 579 pushValue(L, istr); 580 assert(lua_isstring(L, -1) && (popValue!string(L) == "foobar")); 581 582 char[] str = "baz".dup; 583 pushValue(L, str); 584 assert(lua_isstring(L, -1) && (popValue!(char[])(L) == "baz")); 585 586 const(char)* cstr = "hi"; 587 pushValue(L, cstr); 588 assert(lua_isstring(L, -1) && (strcmp(cstr, popValue!(const(char)*)(L)) == 0)); 589 590 //char 591 pushValue(L, '\t'); 592 assert(lua_isstring(L, -1) && getValue!string(L, -1) == "\t"); 593 assert(popValue!char(L) == '\t'); 594 595 //boolean 596 pushValue(L, true); 597 assert(lua_isboolean(L, -1) && (popValue!bool(L) == true)); 598 599 assert(lua_gettop(L) == 0, "bad popValue semantics for primitives"); 600 601 //void arrays 602 immutable void[] iarr = "foobar"; 603 pushValue(L, iarr); 604 assert(lua_isstring(L, -1) && popValue!(typeof(iarr))(L) == "foobar"); 605 606 void[] arr ="baz".dup; 607 pushValue(L, arr); 608 assert(lua_isstring(L, -1) && popValue!(void[])(L) == "baz"); 609 610 //popStack 611 extern(C) static int luacfunc(lua_State* L) 612 { 613 return 0; 614 } 615 616 pushValue(L, &luacfunc); 617 pushValue(L, "test"); 618 pushValue(L, 123); 619 pushValue(L, true); 620 621 assert(lua_gettop(L) == 4); 622 623 auto stack = popStack(L, lua_gettop(L)); 624 assert(lua_gettop(L) == 0); 625 assert(stack[0].type == LuaType.Function); 626 assert(stack[1].type == LuaType.String); 627 assert(stack[2].type == LuaType.Number); 628 assert(stack[3].type == LuaType.Boolean); 629 }