1 /// Everything to do with functions.
2 module lumars.function_;
3 
4 import bindbc.lua, lumars;
5 
6 // Mostly for internal use - controls how luaCWrapperSmart calls the underlying D function.
7 enum LuaFuncWrapperType
8 {
9     isAliasFunc,
10     isDelegate,
11     isFunction
12 }
13 
14 /++
15  + When used as the _last_ parameter of a function: Allows the function to capture any amount of additional arguments
16  + passed in from Lua.
17  +
18  + When used as the return type of a function: Allows the function to return a dynamic amount of values to Lua.
19  + ++/
20 struct LuaVariadic 
21 {
22     alias array this;
23     LuaValue[] array;
24 }
25 
26 // (replacing with Tuple soon, not documenting)
27 deprecated("Deprecated in favour of using std.typecons.Tuple")
28 struct LuaMultiReturn(T...)
29 {
30     alias ValueTuple = T;
31     alias values this;
32     T values;
33 
34     this()(T values)
35     {
36         this.values = values;
37     }
38 }
39 
40 /++
41  + See: `luaCWrapperSmart`
42  +
43  + In essence though, marking a function with this UDA means that the function wants to take
44  + full control of interfacing with Lua, instead of having `luaCWrapperSmart` do all the heavy lifting.
45  +
46  + This is needed in some specific cases, such as `luaOverloads`.
47  + ++/
48 struct LuaBasicFunction{}
49 
50 /++
51  + Calls a `LuaFunc` or `LuaFuncWeak` in protected mode, which means an exception is thrown
52  + if the function produces a LUA error.
53  +
54  + Notes:
55  +  As with all functions that return a `LuaValue`, strings are copied onto the GC heap so are safe to
56  +  copy around.
57  +
58  +  As with all functions that return a `LuaValue`, the "weak" variants of data types are never returned.
59  +
60  +  Calling this function on a `LuaFuncWeak` will permanently pop the function off the stack. This is because
61  +  weak references don't have the ability to push their values.
62  +
63  + Params:
64  +  results = The maximum number of results expected to be returned by the function.
65  +            Any values that are not provided by the function are instead set to `LuaValue(LuaNil())`
66  +
67  + Returns:
68  +  A static array of `LuaValue`s representing the values returned by the LUA function.
69  + ++/
70 template pcall(size_t results)
71 {
72     static if(results > 0)
73     {
74         LuaValue[results] pcall(LuaFuncT, Args...)(LuaFuncT func, Args args)
75         {
76             cast(void)func.push();
77 
78             static foreach(arg; args)
79                 func.lua.push(arg);
80 
81             if(func.lua.pcall(args.length, results, 0) != LuaStatus.ok)
82             {
83                 const error = func.lua.get!string(-1);
84                 func.lua.pop(1);
85                 throw new Exception(error);
86             }
87 
88             typeof(return) ret;
89             static foreach(i; 1..results+1)
90                 ret[$-i] = func.lua.get!LuaValue(cast(int)-i);
91 
92             func.lua.pop(results);
93             return ret;
94         }
95     }
96     else
97     {
98         void pcall(LuaFuncT, Args...)(LuaFuncT func, Args args)
99         {
100             cast(void)func.push();
101 
102             static foreach(arg; args)
103                 func.lua.push(arg);
104 
105             if(func.lua.pcall(args.length, 0, 0) != LuaStatus.ok)
106             {
107                 const error = func.lua.get!string(-1);
108                 func.lua.pop(1);
109                 throw new Exception(error);
110             }
111         }
112     }
113 }
114 
115 private mixin template LuaFuncFuncs()
116 {
117     /++
118      + Binds this lua function into a statically typed wrapper.
119      +
120      + Params:
121      +  ReturnT = The singular return value (or void) produced by this function.
122      +  Params  = The parameters that this function takes.
123      + 
124      + Returns:
125      +  The statically typed wrapper.
126      +
127      + See_Also:
128      +  `LuaBoundFunc`
129      + ++/
130     auto bind(alias ReturnT, Params...)()
131     {
132         auto bound = LuaBoundFunc!(typeof(this), ReturnT, Params).init;
133         bound.func = this;
134         return bound;
135     }
136 }
137 
138 /++
139  + The struct used as the wrapper produced by the `LuaFunc.bind` and `LuaFuncWeak.bind` functions.
140  +
141  + This struct implements `opCall` so it can be used like a normal function.
142  +
143  + Params:
144  +  LuaFuncT = Either `LuaFunc` or `LuaFuncWeak`.
145  +  ReturnT  = The singular return value (or void) produced by this function.
146  +  Params   = The parameters that this function takes.
147  + ++/
148 struct LuaBoundFunc(alias LuaFuncT, alias ReturnT, Params...)
149 {
150     /// The underlying function
151     LuaFuncT func;
152 
153     /++
154      + Allows this wrapper to be called like a normal function.
155      +
156      + Params:
157      +  params = The parameters to pass through.
158      +
159      + Returns:
160      +  Either nothing (`ResultT == void`) or the returned value, statically ensured to be of type `ReturnT`.
161      + ++/
162     ReturnT opCall(Params params)
163     {
164         import std.typecons : isTuple;
165 
166         static if(is(ReturnT == void))
167             this.func.pcall!0(params);
168         else static if (isTuple!ReturnT)
169         {
170             auto results = this.func.pcall!(ReturnT.Types.length)(params);
171             foreach (r; results)
172             {
173                 this.func.lua.push(r);
174             }
175             scope(exit) this.func.lua.pop(ReturnT.Types.length);
176             return this.func.lua.get!ReturnT(-1);
177         }
178         else
179         {
180             auto result = this.func.pcall!1(params)[0];
181             this.func.lua.push(result);
182             scope(exit) this.func.lua.pop(1);
183             return this.func.lua.get!ReturnT(-1);
184         }
185     }
186 
187     /// Allows taking a pointer to the `opCall` function, so a LUA function can be passed around like a D one!
188     alias asDelegate = opCall;
189 }
190 
191 /++
192  + A weak reference to a lua function that currently exists on the LUA stack.
193  +
194  + Notes:
195  +  As with all weak references, while they're marginally more efficient, they're harder to use, and their
196  +  pop and push functions are no-ops.
197  + ++/
198 struct LuaFuncWeak 
199 {
200     mixin LuaFuncFuncs;
201 
202     private
203     {
204         LuaState* _lua;
205         int _index;
206     }
207 
208     /++
209      + Creates a new `LuaFuncWeak` that references a function at `index`.
210      +
211      + Throws:
212      +  `Exception` if the value at `index` in the stack isn't a function.
213      +
214      + Params:
215      +  lua   = The lua state to use.
216      +  index = The index of the function.
217      + ++/
218     this(LuaState* lua, int index)
219     {
220         lua.enforceType(LuaValue.Kind.func, index);
221         this._index = index;
222         this._lua = lua;
223     }
224 
225     /// This function is a no-op and exists to make generic code easier to write.
226     ///
227     /// Returns:
228     ///  The index on the stack of the function being referenced.
229     @safe @nogc 
230     int push() nothrow pure const
231     {
232         return this._index;
233     }
234 
235     /// This function is a no-op and exists to make generic code easier to write.
236     void pop()
237     {
238         this.lua.enforceType(LuaValue.Kind.func, this._index);
239     }
240 
241     /// Returns: The underlying `LuaState`.
242     @property @safe @nogc
243     LuaState* lua() nothrow pure
244     {
245         return this._lua;
246     }
247 }
248 
249 /++
250  + A strong reference to a LUA function.
251  +
252  + Notes:
253  +  This struct contains a ref-counted store used to keep track of both the `LuaState` as well as the table reference.
254  +
255  +  As with all strong references, the original value does not need to exist on the LUA stack, and this struct may be used
256  +  to continously refer to the value. 
257  + ++/
258 struct LuaFunc 
259 {
260     import std.typecons : RefCounted;
261     mixin LuaFuncFuncs;
262 
263     private
264     {
265         static struct State
266         {
267             LuaState* lua;
268             int ref_;
269             bool isWrapper;
270             
271             ~this()
272             {
273                 if(this.lua && !this.isWrapper)
274                     luaL_unref(this.lua.handle, LUA_REGISTRYINDEX, this.ref_);
275             }
276         }
277         RefCounted!State _state;
278     }
279 
280     /++
281      + Creates a new `LuaFunc` using the function on the top of the LUA stack as the referenced value.
282      + This function pops the original value off the stack.
283      + ++/
284     static LuaFunc makeRef(LuaState* lua)
285     {
286         lua.enforceType(LuaValue.Kind.func, -1);
287         RefCounted!State state;
288         state.lua = lua;
289         state.ref_ = luaL_ref(lua.handle, LUA_REGISTRYINDEX);
290         state.isWrapper = lua._isWrapper;
291 
292         return LuaFunc(state);
293     }
294 
295     /++
296      + Creates a new `LuaFunc` using the provided `func` as the referenced value.
297      + ++/
298     static LuaFunc makeNew(LuaState* lua, lua_CFunction func)
299     {
300         lua.push(func);
301         return LuaFunc.makeRef(lua);
302     }
303 
304     /++
305      + Pushes the function onto the stack.
306      +
307      + Returns:
308      +  The positive index of the pushed function.
309      + ++/
310     @nogc
311     int push() nothrow
312     {
313         lua_rawgeti(this._state.lua.handle, LUA_REGISTRYINDEX, this._state.ref_);
314         return this._state.lua.top;
315     }
316     
317     /++
318      + Pops the stack, ensuring that the top value is a function.
319      + ++/
320     void pop()
321     {
322         this._state.lua.enforceType(LuaValue.Kind.func, -1);
323         this._state.lua.pop(1);
324     }
325 
326     /// Returns: The underlying LUA state.
327     LuaState* lua()
328     {
329         return this._state.lua;
330     }
331 }
332 
333 /++
334  + The bare minimum wrapper needed to allow LUA to call a D function.
335  +
336  + Notes:
337  +  Any throwables will instead be converted into a lua_error.
338  +
339  + Params:
340  +  Func = The D function to wrap. This function must take a `LuaState*` as its only parameter, and it can optionally return an int
341  +         to signify how many values it has returned.
342  + ++/
343 extern(C)
344 int luaCWrapperBasic(alias Func)(lua_State* state) nothrow
345 {
346     import std.exception : assumeWontThrow;
347     import std.format : format;
348     scope LuaState wrapper;
349 
350     try wrapper = LuaState(state);
351     catch(Throwable ex) // @suppress(dscanner.suspicious.catch_em_all)
352         return 0;
353 
354     try
355     {
356         static if(is(typeof(Func(&wrapper)) == int))
357             return Func(&wrapper);
358         else
359         {
360             Func(&wrapper);
361             return 0;
362         }
363     }
364     catch(Throwable e) // Can't allow any Throwable to execute normally as the backtrace code will crash. // @suppress(dscanner.suspicious.catch_em_all)
365     {
366         try
367         {
368             wrapper.push(e.msg);
369             wrapper.traceback();
370 
371             const str = wrapper.get!string(-1).assumeWontThrow;
372             wrapper.pop(1);
373 
374             wrapper.error(str);
375             return 0;
376         }
377         catch(Throwable e2) // @suppress(dscanner.suspicious.catch_em_all)
378         {
379             wrapper.error(e.msg~"\n[WARN] Traceback code failed: "~e2.msg);
380             return 0;
381         }
382     }
383 }
384 
385 /++
386  + A higher level wrapper that allows most D functions to be naturally interact with LUA.
387  +
388  + This is your go-to wrapper as it's capable of exposing most functions to LUA.
389  +
390  + Notes:
391  +  Any throwables will instead be converted into a lua_error.
392  +
393  +  The return value (if any) of `Func` will automatically be converted into a LUA value.
394  +
395  +  The parameters of `Func` will automatically be converted from the values passed by LUA.
396  +
397  +  `Func` may optionally ask for the lua state by specifying `LuaState*` as its $(B first) parameter.
398  +
399  +  `Func` may be made variadic by specifying `LuaVariadic` as its $(B last) parameter.
400  +
401  +   If `Func` is annotated with `@LuaBasicFunction`, then this function actually acts the same as
402  +   `luaCWrapperBasic`. The reason for this is so that we don't have to have a bunch of conditional
403  +   logic in the other parts of the code to select between the two wrappers, but instead we can just put the conditional logic
404  +   here to seamlessly support this usecase throughout the code.
405  +
406  + Params:
407  +  Func = The D function to wrap.
408  +  Type = User code shouldn't ever need to set this, please leave it as the default.
409  +
410  + Example:
411  +  `luaState.register!(std.path.buildPath!(string[]))("buildPath")`
412  + ++/
413 extern(C)
414 int luaCWrapperSmart(alias Func, LuaFuncWrapperType Type = LuaFuncWrapperType.isAliasFunc)(lua_State* state) nothrow
415 {
416     import std.traits : getUDAs;
417 
418     static if(__traits(compiles, getUDAs!(Func, LuaBasicFunction)))
419         enum IsBasicFunction = getUDAs!(Func, LuaBasicFunction).length > 0;
420     else
421         enum IsBasicFunction = false;
422 
423     static if(IsBasicFunction)
424         return luaCWrapperBasic!Func(state);
425     else
426     {
427         return luaCWrapperBasic!(
428             luaCWrapperSmartImpl!(Func, Type)
429         )(state);
430     }
431 }
432 
433 private int luaCWrapperSmartImpl(
434     alias Func,
435     LuaFuncWrapperType Type = LuaFuncWrapperType.isAliasFunc
436 )(
437     LuaState* lua
438 )
439 {
440     import std.format : format;
441     import std.traits : Parameters, ReturnType, isInstanceOf, ParameterDefaults;
442     import std.meta   : AliasSeq, staticIndexOf, Reverse;
443     import std.typecons : isTuple;
444 
445     alias Params = Parameters!Func;
446     alias Defaults = AliasSeq!(ParameterDefaults!Func);
447 
448     static if(Params.length)
449     {
450         const ParamsLength = 
451             Params.length
452             - (is(Params[0] == LuaState*) ? 1 : 0)
453             - (is(Params[$-1] == LuaVariadic) ? 1 : 0);
454 
455         const ParamsMinLength = staticIndexOf!(void, Defaults) == -1 ? 0 : (ParamsLength - staticIndexOf!(void, Reverse!Defaults));
456     }
457     else
458     {
459         const ParamsLength = 0;
460         const ParamsMinLength = 0;
461     }
462 
463     enum HasVariadic = Params.length > 0 && is(Params[$-1] == LuaVariadic);
464     enum HasDefault = ParamsMinLength != ParamsLength;
465 
466     Params params;
467 
468     const argsGiven = lua.top();
469     if(!HasVariadic && (argsGiven < ParamsMinLength || argsGiven > ParamsLength))
470         static if (HasDefault)
471             throw new LuaArgumentException("Expected %s ~ %s args, but was given %s.".format(ParamsMinLength, ParamsLength, argsGiven));
472         else
473             throw new LuaArgumentException("Expected exactly %s args, but was given %s.".format(ParamsLength, argsGiven));
474     else if(HasVariadic && argsGiven < ParamsMinLength)
475         throw new LuaArgumentException("Expected at least %s args, but was given %s.".format(ParamsMinLength, argsGiven));
476     
477     static if(is(Params[0] == LuaState*))
478     {
479         params[0] = lua;
480         static foreach(i; 0..ParamsLength)
481         if (i < argsGiven)
482             params[i+1] = lua.get!(Params[i+1])(i+1);
483     }
484     else
485     {
486         static foreach(i; 0..ParamsLength)
487         if (i < argsGiven)
488             params[i] = lua.get!(Params[i])(i+1);
489     }
490 
491     static if(HasVariadic)
492     foreach(i; 0..argsGiven-ParamsLength)
493         params[$-1] ~= lua.get!LuaValue(cast(int)(i+ParamsLength+1));
494 
495     static foreach(i; ParamsMinLength..ParamsLength)
496     if (i >= argsGiven)
497     {
498         static if(is(Params[0] == LuaState*))
499             params[i+1] = Defaults[i+1];
500         else
501             params[i] = Defaults[i];
502     }
503 
504     alias RetT = ReturnType!Func;
505 
506     static if(Type == LuaFuncWrapperType.isDelegate)
507     {
508         alias FuncWithContext = RetT function(Params, void*);
509 
510         auto context = lua_touserdata(lua.handle, lua_upvalueindex(1));
511         auto func    = lua_touserdata(lua.handle, lua_upvalueindex(2));
512         auto dFunc   = cast(FuncWithContext)func;
513         
514         static if(is(RetT == void))
515         {
516             dFunc(params, context);
517             return 0;
518         }
519         else static if(isInstanceOf!(LuaMultiReturn, RetT))
520         {
521             auto multiRet = dFunc(params, context);
522             static foreach(i; 0..multiRet.ValueTuple.length)
523                 lua.push(multiRet[i]);
524             return multiRet.ValueTuple.length;
525         }
526         else static if(is(RetT == LuaVariadic))
527         {
528             auto multiRet = dFunc(params, context);
529             foreach(value; multiRet)
530                 lua.push(value);
531             return cast(int)multiRet.length;
532         }
533         else static if(isTuple!RetT)
534         {
535             auto multiRet = dFunc(params);
536             static foreach(i; 0..multiRet.length)
537                 lua.push(multiRet[i]);
538             return multiRet.length;
539         }
540         else
541         {
542             lua.push(dFunc(params, context));
543             return 1;
544         }
545     }
546     else static if(Type == LuaFuncWrapperType.isFunction)
547     {
548         auto func = cast(Func)lua_touserdata(lua.handle, lua_upvalueindex(1));
549         static if(is(RetT == void))
550         {
551             func(params);
552             return 0;
553         }
554         else static if(isInstanceOf!(LuaMultiReturn, RetT))
555         {
556             auto multiRet = func(params);
557             static foreach(i; 0..multiRet.ValueTuple.length)
558                 lua.push(multiRet[i]);
559             return multiRet.ValueTuple.length;
560         }
561         else static if(is(RetT == LuaVariadic))
562         {
563             auto multiRet = func(params);
564             foreach(value; multiRet)
565                 lua.push(value);
566             return cast(int)multiRet.length;
567         }
568         else static if(isTuple!RetT)
569         {
570             auto multiRet = func(params);
571             static foreach(i; 0..multiRet.length)
572                 lua.push(multiRet[i]);
573             return multiRet.length;
574         }
575         else
576         {
577             lua.push(func(params));
578             return 1;
579         }
580     }
581     else
582     {
583         static if(is(RetT == void))
584         {
585             Func(params);
586             return 0;
587         }
588         else static if(isInstanceOf!(LuaMultiReturn, RetT))
589         {
590             auto multiRet = Func(params);
591             static foreach(i; 0..multiRet.ValueTuple.length)
592                 lua.push(multiRet[i]);
593             return multiRet.ValueTuple.length;
594         }
595         else static if(is(RetT == LuaVariadic))
596         {
597             auto multiRet = Func(params);
598             foreach(value; multiRet)
599                 lua.push(value);
600             return cast(int)multiRet.length;
601         }
602         else static if(isTuple!RetT)
603         {
604             auto multiRet = Func(params);
605             static foreach(i; 0..multiRet.length)
606                 lua.push(multiRet[i]);
607             return multiRet.length;
608         }
609         else
610         {
611             lua.push(Func(params));
612             return 1;
613         }
614     }
615 }
616 
617 /++
618  + A function that wraps around other functions in order to provide runtime overloading support.
619  +
620  + Notes:
621  +  Parameter binding logic is exactly the same as `luaCWrapperSmart`.
622  +
623  +  From the 0th `Overload` to the last, this function will exhaustively call each function until one successfully
624  +  has its arguments bound.
625  +
626  +  If no overloads could succesfully be matched, then an exception will be thrown.
627  +
628  +  All overloads must provide the same return type. I hope to make this more flexible in the future.
629  +
630  +  To be more specific, a function fails to bind its arguments if it throws `LuaTypeException` or `LuaArgumentException`.
631  +  So please be aware of this when writing overloads.
632  + ++/
633 @LuaBasicFunction
634 int luaOverloads(Overloads...)(LuaState* state)
635 {
636     static foreach(Overload; Overloads)
637     {{
638         bool compilerThinksThisIsUnreachable = true;
639         if(compilerThinksThisIsUnreachable)
640         {
641             // TODO: This needs a much better mechanism, this is super dodgy.
642             try return luaCWrapperSmartImpl!Overload(state);
643             catch(LuaTypeException) {}
644             catch(LuaArgumentException) {}
645         }
646     }}
647 
648     throw new Exception("No overload matched the given arguments.");
649 }
650 
651 unittest
652 {
653     auto l = LuaState(null);
654     l.doString("return function(...) return ... end");
655     auto f = l.get!LuaFuncWeak(-1);
656     auto result = f.pcall!1("Henlo!");
657     assert(result[0].textValue == "Henlo!");
658 }
659 
660 unittest
661 {
662     auto l = LuaState(null);
663     l.push(&luaCWrapperSmart!(
664         (string a, int[] b, bool[string] c)
665         {
666             assert(a == "Hello");
667             assert(b == [4, 2, 0]);
668             assert(c["true"]);
669             return true;
670         }
671     ));
672     auto f = l.get!LuaFunc(-1);
673     auto result = f.pcall!1("Hello", [4, 2, 0], ["true": true]);
674     assert(result[0].booleanValue);
675     
676     auto f2 = f.bind!(bool, string, int[], bool[string])();
677     assert(f2("Hello", [4, 2, 0], ["true": true]));
678 
679     alias F = bool delegate(string, int[], bool[string]);
680     F f3 = &f2.asDelegate;
681     assert(f3("Hello", [4, 2, 0], ["true": true]));
682 }
683 
684 unittest
685 {
686     static string func(string a, int b)
687     {
688         assert(a == "bc");
689         assert(b == 123);
690         return "doe ray me";
691     }
692 
693     auto l = LuaState(null);
694     l.push(&func);
695     auto f = LuaFuncWeak(&l, -1);
696     auto fb = f.bind!(string, string, int);
697     assert(fb("bc", 123) == "doe ray me");
698 }
699 
700 unittest
701 {
702     version(LDC)
703     {
704         pragma(msg, "WARNING: This unittest is currently broken under LDC");
705     }
706     else
707     {
708         int closedValue;
709         void del(string a)
710         {
711             assert(a == "bc");
712             closedValue = 123;
713         }
714 
715         auto l = LuaState(null);
716         l.push(&del);
717         auto f = LuaFuncWeak(&l, -1);
718         f.pcall!0("bc");
719         assert(closedValue == 123);
720     }
721 }
722 
723 unittest
724 {
725     import std.exception : assertThrown, assertNotThrown;
726 
727     auto l = LuaState(null);
728     l.register("test", &luaCWrapperSmart!((string s){  }));
729 
730     auto f = l.globalTable.get!LuaFunc("test");
731     f.pcall!0().assertThrown;
732     f.pcall!0("abc").assertNotThrown;
733     f.pcall!0("abc", "123").assertThrown;
734 }
735 
736 unittest
737 {
738     static struct S
739     {
740         int i;
741 
742         void test(int value)
743         {
744             assert(value == i);
745         }
746 
747         void bind(LuaState* l)
748         {
749             l.register!("test", function (int value) => S(200).test(value))("api");
750         }
751     }
752 
753     auto lua = new LuaState(null);
754     S s;
755     s.bind(lua);
756     lua.doString("api.test(200)");
757 }
758 
759 unittest
760 {
761     auto lua = new LuaState(null);
762     lua.register("test", &luaCWrapperSmart!((int a, string b, LuaVariadic c){
763         assert(a == 1);
764         assert(b == "2");
765         assert(c.length == 3);
766     }));
767     lua.doString("test(1, '2', 3, true, {})");
768 
769     lua.register("test", &luaCWrapperSmart!((LuaState* l, int a, string b, LuaVariadic c){
770         assert(a == 1);
771         assert(b == "2");
772         assert(c.length == 3);
773     }));
774     lua.doString("test(1, '2', 3, true, {})");
775 }
776 
777 unittest
778 {
779     import std.typecons : tuple;
780 
781     auto lua = new LuaState(null);
782     lua.register!(
783         luaOverloads!(
784             (int a) { assert(a == 1); return a; },
785             (string a) { assert(a == "2"); return a; },
786             (int a, string b) { assert(a == 1); assert(b == "2"); return tuple(a, b); }
787         )
788     )("overloaded");
789     lua.doString(`
790         assert(overloaded(1) == 1)
791         assert(overloaded("2") == "2")
792         a,b = overloaded(1, "2")
793         assert(a == 1)
794         assert(b == "2")
795     `);
796 }
797 
798 unittest
799 {
800     import std.typecons : tuple;
801 
802     static auto multiReturn()
803     {
804         return tuple(20, "40", true);
805     }
806 
807     auto lua = new LuaState(null);
808     lua.register!multiReturn("multiReturn");
809     lua.doString(`
810         local i, s, b = multiReturn()
811         assert(i == 20)
812         assert(s == "40")
813         assert(b)
814     `);
815 }
816 
817 unittest
818 {
819     static LuaVariadic multiReturn()
820     {
821         return LuaVariadic([LuaValue(20), LuaValue("40"), LuaValue(true)]);
822     }
823 
824     auto lua = new LuaState(null);
825     lua.register!multiReturn("multiReturn");
826     lua.doString(`
827         local i, s, b = multiReturn()
828         assert(i == 20)
829         assert(s == "40")
830         assert(b)
831     `);
832 }
833 
834 unittest
835 {
836     import std.typecons : Tuple;
837     static Tuple!(int, string, bool) multiReturn()
838     {
839         return typeof(return)(20, "40", true);
840     }
841 
842     auto lua = new LuaState(null);
843     lua.register!multiReturn("multiReturn");
844     lua.doString(`
845         local i, s, b = multiReturn()
846         assert(i == 20)
847         assert(s == "40")
848         assert(b)
849     `);
850 
851     alias Employee = Tuple!(int, "ID", string, "Name", bool, "FullTime");
852 
853     lua.doString(`
854         function employee1()
855             return 15305, "Domain", true
856         end
857 
858         function employee2()
859             return { ID = 15605, Name = "Range", FullTime = false }
860         end
861     `);
862 
863     auto f = lua.globalTable.get!LuaFunc("employee1").bind!(Employee);
864     auto g = lua.globalTable.get!LuaFunc("employee2").bind!(Employee);
865     auto employee1 = f();
866     auto employee2 = g();
867     assert(employee1.ID == 15305);
868     assert(employee1.Name == "Domain");
869     assert(employee1.FullTime);
870     assert(employee2.ID == 15605);
871     assert(employee2.Name == "Range");
872     assert(!employee2.FullTime);
873 }
874 
875 unittest
876 {
877     import std.exception : assertThrown;
878     import std.typecons  : Tuple;
879 
880     alias Employee = Tuple!(int, "ID", string, "Name", bool, "FullTime");
881 
882     auto lua = new LuaState(null);
883     lua.doString(`
884         function partialEmployee()
885             return 15305, "Domain"
886         end
887     `);
888 
889     auto partialEmployee = lua.globalTable.get!LuaFunc("partialEmployee").bind!(Employee);
890     assertThrown!LuaTypeException(partialEmployee());
891 }
892 
893 unittest
894 {
895     import std.typecons : tuple;
896 
897     auto lua = new LuaState(null);
898     lua.register!(
899         "normal", (){ return 1; },
900         "overloaded", luaOverloads!(
901             (int a) { assert(a == 1); return a; },
902             (string a) { assert(a == "2"); return a; },
903             (int a, string b) { assert(a == 1); assert(b == "2"); return tuple(a, b); }
904         )
905     )("lib");
906 
907     lua.doString(`
908         assert(lib.normal() == 1)
909 
910         assert(lib.overloaded(1) == 1)
911         assert(lib.overloaded("2") == "2")
912         a,b = lib.overloaded(1, "2")
913         assert(a == 1)
914         assert(b == "2")
915     `);
916 }
917 
918 unittest
919 {
920     auto lua = LuaState(null);
921     
922     @LuaBasicFunction
923     static int basic(LuaState* lua)
924     {
925         return lua.top(); // lua.top will be the amount of parameters we have. So return all params.
926     }
927     lua.register!basic("basic");
928 
929     lua.doString(`
930         assert(basic(1) == 1)
931 
932         a,b = basic(1, "2")
933         assert(a == 1 and b == "2")
934     `);
935 }
936 
937 unittest
938 {
939     import std.algorithm : canFind;
940     import std.exception : collectExceptionMsg;
941 
942     auto lua = LuaState(null);
943     
944     static void err()
945     {
946         throw new Exception("err");
947     }
948     lua.register!err("err");
949 
950     const msg = lua.doString(`err()`).collectExceptionMsg;
951     assert(msg.canFind("stack traceback:"));
952 }
953 
954 unittest
955 {
956     auto lua = new LuaState(null);
957     lua.register!(
958         "defaultParams", (int a, int b = 1, int c = 2) { return a+b+c; },
959         "defaultParams2", (LuaState*lua, int a, int b = 1, int c = 2) { return a+b+c; }
960     )("lib");
961 
962     lua.doString(`
963         assert(lib.defaultParams(1) == 4)
964         assert(lib.defaultParams(1, 2) == 5)
965         assert(lib.defaultParams(1, 3, 5) == 9)
966         assert(lib.defaultParams2(1) == 4)
967         assert(lib.defaultParams2(1, 2) == 5)
968         assert(lib.defaultParams2(1, 3, 5) == 9)
969     `);
970 }
971 
972 unittest
973 {
974     auto lua = new LuaState(null);
975     lua.register!(
976         "p0", () { },
977         "p1", (int a) { },
978         "p1o1", (int a, int b = 0) { },
979         "o1", (int a = 0) { },
980     )("lib");
981 
982     lua.doString(`
983         assert(pcall(lib.p0))
984         assert(not pcall(lib.p0, 1))
985 
986         assert(not pcall(lib.p1))
987         assert(pcall(lib.p1, 1))
988         assert(not pcall(lib.p1, 1, 2))
989 
990         assert(not pcall(lib.p1o1))
991         assert(pcall(lib.p1o1, 1))
992         assert(pcall(lib.p1o1, 1, 2))
993         assert(not pcall(lib.p1o1, 1, 2, 3))
994 
995         assert(pcall(lib.o1))
996         assert(pcall(lib.o1, 1))
997         assert(not pcall(lib.o1, 1, 2))
998     `);
999 }
1000 
1001 unittest
1002 {
1003     class A{}
1004     auto lua = new LuaState(null);
1005     lua.register!(
1006         "array", (int[] a) { assert(a is null); return 1; },
1007         "class", (A a) { assert(a is null); return 1; }
1008     )("test");
1009 
1010     lua.doString(`
1011         assert(test.array(nil) == 1)
1012         assert(test.class(nil) == 1)
1013     `);
1014 }