1 /*
2 Copyright (c) 2013, Pierre KRIEGER
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the <organization> nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 #ifndef INCLUDE_LUACONTEXT_HPP
29 #define INCLUDE_LUACONTEXT_HPP
30 
31 #include <algorithm>
32 #include <array>
33 #include <cassert>
34 #include <cmath>
35 #include <cstring>
36 #include <functional>
37 #include <limits>
38 #include <list>
39 #include <map>
40 #include <memory>
41 #include <random>
42 #include <set>
43 #include <stdexcept>
44 #include <string>
45 #include <sstream>
46 #include <tuple>
47 #include <type_traits>
48 #include <unordered_map>
49 #include <boost/any.hpp>
50 #include <boost/format.hpp>
51 #include <boost/mpl/distance.hpp>
52 #include <boost/mpl/transform.hpp>
53 #include <boost/optional.hpp>
54 #include <boost/variant.hpp>
55 #include <boost/type_traits.hpp>
56 #include <lua.hpp>
57 
58 #if defined(_MSC_VER) && _MSC_VER < 1900
59 #   include "misc/exception.hpp"
60 #endif
61 
62 #ifdef __GNUC__
63 #   define ATTR_UNUSED __attribute__((unused))
64 #else
65 #   define ATTR_UNUSED
66 #endif
67 
68 #define LUACONTEXT_GLOBAL_EQ "e5ddced079fc405aa4937b386ca387d2"
69 #define EQ_FUNCTION_NAME "__eq"
70 #define TOSTRING_FUNCTION_NAME "__tostring"
71 
72 /**
73  * Defines a Lua context
74  * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions),
75  * we only provide few functions like readVariable and writeVariable.
76  *
77  * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert
78  * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types
79  * it wants. These arguments may only be of basic types (int, float, etc.) or std::string.
80  */
81 
82 #if defined(__GNUC__) && !defined(__clang__)
83 #pragma GCC diagnostic push
84 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
85 #endif
86 
87 class LuaContext {
88     struct ValueInRegistry;
89     template<typename TFunctionObject, typename TFirstParamType> struct Binder;
90     template<typename T> struct IsOptional;
91     enum Globals_t { Globals }; // tag for "global variables"
92 public:
93     /**
94      * @param openDefaultLibs True if luaL_openlibs should be called
95      */
LuaContext(bool openDefaultLibs=true)96     explicit LuaContext(bool openDefaultLibs = true)
97     {
98         // luaL_newstate can return null if allocation failed
99         mState = luaL_newstate();
100         if (mState == nullptr)
101             throw std::bad_alloc();
102 
103         // setting the panic function
104         lua_atpanic(mState, [](lua_State* state) -> int {
105             const std::string str = lua_tostring(state, -1);
106             lua_pop(state, 1);
107             assert(false && "lua_atpanic triggered");
108             exit(0);
109         });
110 
111         // opening default library if required to do so
112         if (openDefaultLibs)
113             luaL_openlibs(mState);
114 
115          writeGlobalEq();
116     }
117 
writeGlobalEq()118     void writeGlobalEq() {
119       const auto eqFunction = [](lua_State* lua) -> int {
120         try {
121           lua_pushstring(lua, "__eq");
122           lua_gettable(lua, -2);
123           /* if not found, return false */
124           if (lua_isnil(lua, -1)) {
125             lua_pop(lua, -2);
126             lua_pushboolean(lua, false);
127             return 1;
128           }
129           lua_insert(lua, lua_gettop(lua)-2);
130           return callRaw(lua, PushedObject{lua, 3}, 1).release();
131         } catch(...) {
132           Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
133           luaError(lua);
134         }
135       };
136       lua_pushcfunction(mState, eqFunction);
137       lua_setglobal(mState, LUACONTEXT_GLOBAL_EQ);
138     };
139 
140     /**
141      * Move constructor
142      */
LuaContext(LuaContext && s)143     LuaContext(LuaContext&& s) :
144         mState(s.mState)
145     {
146         s.mState = luaL_newstate();
147     }
148 
149     /**
150      * Move operator
151      */
operator =(LuaContext && s)152     LuaContext& operator=(LuaContext&& s) noexcept
153     {
154         std::swap(mState, s.mState);
155         return *this;
156     }
157 
158     /**
159      * Copy is forbidden
160      */
161     LuaContext(const LuaContext&) = delete;
162 
163     /**
164      * Copy is forbidden
165      */
166     LuaContext& operator=(const LuaContext&) = delete;
167 
168     /**
169      * Destructor
170      */
~LuaContext()171     ~LuaContext() noexcept
172     {
173         assert(mState);
174         lua_close(mState);
175     }
176 
177     /**
178      * Thrown when an error happens during execution of lua code (like not enough parameters for a function)
179      */
180     class ExecutionErrorException : public std::runtime_error
181     {
182     public:
ExecutionErrorException(const std::string & msg)183         ExecutionErrorException(const std::string& msg) :
184             std::runtime_error(msg)
185         {
186         }
187     };
188 
189     /**
190      * Thrown when a syntax error happens in a lua script
191      */
192     class SyntaxErrorException : public std::runtime_error
193     {
194     public:
SyntaxErrorException(const std::string & msg)195         SyntaxErrorException(const std::string& msg) :
196             std::runtime_error(msg)
197         {
198         }
199     };
200 
201     /**
202      * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string
203      */
204     class WrongTypeException : public std::runtime_error
205     {
206     public:
WrongTypeException(const std::string & luaType_,const std::type_info & destination_)207         WrongTypeException(const std::string& luaType_, const std::type_info& destination_) :
208             std::runtime_error("Trying to cast a lua variable from \"" + luaType_ + "\" to \"" + destination_.name() + "\""),
209             luaType(luaType_),
210             destination(destination_)
211         {
212         }
213 
214         std::string luaType;
215         const std::type_info& destination;
216     };
217 
218     /**
219      * Function object that can call a function stored by Lua
220      * This type is copiable and movable, but not constructible. It can only be created through readVariable.
221      * @tparam TFunctionType    Function type (eg. "int (int, bool)")
222      */
223     template<typename TFunctionType>
224     class LuaFunctionCaller;
225 
226     /**
227      * Opaque type that identifies a Lua object
228      */
229     struct LuaObject {
230         LuaObject() = default;
LuaObjectLuaContext::LuaObject231         LuaObject(lua_State* state, int index=-1) {
232             this->objectInRegistry = std::make_shared<LuaContext::ValueInRegistry>(state, index);
233         }
234         std::shared_ptr<LuaContext::ValueInRegistry> objectInRegistry;
235     };
236 
237     /**
238      * Opaque type that identifies a Lua thread
239      */
240     struct ThreadID {
241         ThreadID() = default;
ThreadIDLuaContext::ThreadID242         ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { }
operator =LuaContext::ThreadID243         ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; }
244     public:
245         friend LuaContext;
246         lua_State* state;
247         std::unique_ptr<ValueInRegistry> threadInRegistry;
248     };
249 
250     /**
251      * Type that is considered as an empty array
252      */
253     enum EmptyArray_t { EmptyArray };
254 
255     /**
256      * Type for a metatable
257      */
258     enum Metatable_t { Metatable };
259 
260     /**
261      * Executes lua code from the stream
262      * @param code      A stream that Lua will read its code from
263      */
executeCode(std::istream & code)264     void executeCode(std::istream& code)
265     {
266         auto toCall = load(mState, code);
267         call<std::tuple<>>(mState, std::move(toCall));
268     }
269 
270     /**
271      * Executes lua code from the stream and returns a value
272      * @param code      A stream that Lua will read its code from
273      * @tparam TType    The type that the executing code should return
274      */
275     template<typename TType>
executeCode(std::istream & code)276     auto executeCode(std::istream& code)
277         -> TType
278     {
279         auto toCall = load(mState, code);
280         return call<TType>(mState, std::move(toCall));
281     }
282 
283     /**
284      * Executes lua code given as parameter
285      * @param code      A string containing code that will be executed by Lua
286      */
executeCode(const std::string & code)287     void executeCode(const std::string& code)
288     {
289         executeCode(code.c_str());
290     }
291 
292     /*
293      * Executes Lua code from the stream and returns a value
294      * @param code      A string containing code that will be executed by Lua
295      * @tparam TType    The type that the executing code should return
296      */
297     template<typename TType>
executeCode(const std::string & code)298     auto executeCode(const std::string& code)
299         -> TType
300     {
301         return executeCode<TType>(code.c_str());
302     }
303 
304     /**
305      * Executes Lua code
306      * @param code      A string containing code that will be executed by Lua
307      */
executeCode(const char * code)308     void executeCode(const char* code)
309     {
310         auto toCall = load(mState, code);
311         call<std::tuple<>>(mState, std::move(toCall));
312     }
313 
314     /*
315      * Executes Lua code from the stream and returns a value
316      * @param code      A string containing code that will be executed by Lua
317      * @tparam TType    The type that the executing code should return
318      */
319     template<typename TType>
executeCode(const char * code)320     auto executeCode(const char* code)
321         -> TType
322     {
323         auto toCall = load(mState, code);
324         return call<TType>(mState, std::move(toCall));
325     }
326 
327     /**
328      * Executes lua code from the stream
329      * @param code      A stream that Lua will read its code from
330      */
executeCode(const ThreadID & thread,std::istream & code)331     void executeCode(const ThreadID& thread, std::istream& code)
332     {
333         auto toCall = load(thread.state, code);
334         call<std::tuple<>>(thread.state, std::move(toCall));
335     }
336 
337     /**
338      * Executes lua code from the stream and returns a value
339      * @param code      A stream that Lua will read its code from
340      * @tparam TType    The type that the executing code should return
341      */
342     template<typename TType>
executeCode(const ThreadID & thread,std::istream & code)343     auto executeCode(const ThreadID& thread, std::istream& code)
344         -> TType
345     {
346         auto toCall = load(thread.state, code);
347         return call<TType>(thread.state, std::move(toCall));
348     }
349 
350     /**
351      * Executes lua code given as parameter
352      * @param code      A string containing code that will be executed by Lua
353      */
executeCode(const ThreadID & thread,const std::string & code)354     void executeCode(const ThreadID& thread, const std::string& code)
355     {
356         executeCode(thread, code.c_str());
357     }
358 
359     /*
360      * Executes Lua code from the stream and returns a value
361      * @param code      A string containing code that will be executed by Lua
362      * @tparam TType    The type that the executing code should return
363      */
364     template<typename TType>
executeCode(const ThreadID & thread,const std::string & code)365     auto executeCode(const ThreadID& thread, const std::string& code)
366         -> TType
367     {
368         return executeCode<TType>(thread, code.c_str());
369     }
370 
371     /**
372      * Executes Lua code
373      * @param code      A string containing code that will be executed by Lua
374      */
executeCode(const ThreadID & thread,const char * code)375     void executeCode(const ThreadID& thread, const char* code)
376     {
377         auto toCall = load(thread.state, code);
378         call<std::tuple<>>(thread.state, std::move(toCall));
379     }
380 
381     /*
382      * Executes Lua code from the stream and returns a value
383      * @param code      A string containing code that will be executed by Lua
384      * @tparam TType    The type that the executing code should return
385      */
386     template<typename TType>
executeCode(const ThreadID & thread,const char * code)387     auto executeCode(const ThreadID& thread, const char* code)
388         -> TType
389     {
390         auto toCall = load(thread.state, code);
391         return call<TType>(thread.state, std::move(toCall));
392     }
393 
394     /**
395      * Tells that Lua will be allowed to access an object's function
396      * This is the version "registerFunction(name, &Foo::function)"
397      */
398     template<typename TPointerToMemberFunction>
registerFunction(const std::string & name,TPointerToMemberFunction pointer)399     auto registerFunction(const std::string& name, TPointerToMemberFunction pointer)
400         -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
401     {
402         registerFunctionImpl(name, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
403     }
404 
405     /**
406      * Tells that Lua will be allowed to access an object's function
407      * This is the version with an explicit template parameter: "registerFunction<void (Foo::*)()>(name, [](Foo&) { })"
408      * @param fn                Function object which takes as first parameter a reference to the object
409      * @tparam TFunctionType    Pointer-to-member function type
410      */
411     template<typename TFunctionType, typename TType>
registerFunction(const std::string & functionName,TType fn)412     void registerFunction(const std::string& functionName, TType fn)
413     {
414         static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
415         registerFunctionImpl(functionName, std::move(fn), tag<TFunctionType>{});
416     }
417 
418     /**
419      * Tells that Lua will be allowed to access an object's function
420      * This is the alternative version with an explicit template parameter: "registerFunction<Foo, void (*)()>(name, [](Foo&) { })"
421      * @param fn                Function object which takes as first parameter a reference to the object
422      * @tparam TObject          Object to register this function to
423      * @tparam TFunctionType    Function type
424      */
425     template<typename TObject, typename TFunctionType, typename TType>
registerFunction(const std::string & functionName,TType fn)426     void registerFunction(const std::string& functionName, TType fn)
427     {
428         static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
429         registerFunctionImpl(functionName, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
430     }
431 
432     /**
433      * Wrappers for registering "__eq" function in case we want to change this to something else some day
434      */
435 
436     template<typename TPointerToMemberFunction>
registerEqFunction(TPointerToMemberFunction pointer)437     auto registerEqFunction(TPointerToMemberFunction pointer)
438         -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
439     {
440         registerFunctionImpl(EQ_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
441     }
442 
443     template<typename TFunctionType, typename TType>
registerEqFunction(TType fn)444     void registerEqFunction(TType fn)
445     {
446         static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
447         registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
448     }
449 
450     template<typename TObject, typename TFunctionType, typename TType>
registerEqFunction(TType fn)451     void registerEqFunction(TType fn)
452        {
453         static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
454         registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
455     }
456 
457     /**
458      * Wrappers for registering "__tostring" function in case we want to change this to something else some day
459      */
460 
461     template<typename TPointerToMemberFunction>
registerToStringFunction(TPointerToMemberFunction pointer)462     auto registerToStringFunction(TPointerToMemberFunction pointer)
463         -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
464     {
465         registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
466     }
467 
468     template<typename TFunctionType, typename TType>
registerToStringFunction(TType fn)469     void registerToStringFunction(TType fn)
470     {
471         static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
472         registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
473     }
474 
475     template<typename TObject, typename TFunctionType, typename TType>
registerToStringFunction(TType fn)476     void registerToStringFunction(TType fn)
477        {
478         static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
479         registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
480     }
481 
482     /**
483      * Inverse operation of registerFunction
484      * @tparam TType Type whose function belongs to
485      */
486     template<typename TType>
unregisterFunction(const std::string &)487     void unregisterFunction(const std::string& /*functionName*/)
488     {
489         lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType)));
490         lua_pushnil(mState);
491         lua_settable(mState, LUA_REGISTRYINDEX);
492         checkTypeRegistration(mState, &typeid(TType));
493 
494         lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType*)));
495         lua_pushnil(mState);
496         lua_settable(mState, LUA_REGISTRYINDEX);
497         checkTypeRegistration(mState, &typeid(TType*));
498 
499         lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(std::shared_ptr<TType>)));
500         lua_pushnil(mState);
501         lua_settable(mState, LUA_REGISTRYINDEX);
502         checkTypeRegistration(mState, &typeid(std::shared_ptr<TType>));
503     }
504 
505     /**
506      * Registers a member variable
507      * This is the version "registerMember(name, &Foo::member)"
508      */
509     template<typename TObject, typename TVarType>
registerMember(const std::string & name,TVarType TObject::* member)510     void registerMember(const std::string& name, TVarType TObject::*member)
511     {
512         // implementation simply calls the custom member with getter and setter
513         const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; };
514         const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; };
515         registerMember<TVarType (TObject::*)>(name, getter, setter);
516     }
517 
518     /**
519      * Registers a member variable
520      * This is the version "registerMember<Foo, int>(name, getter, setter)"
521      * @tparam TObject       Type to register the member to
522      * @tparam TVarType      Type of the member
523      * @param name           Name of the member to register
524      * @param readFunction   Function of type "TVarType (const TObject&)"
525      * @param writeFunction_  Function of type "void (TObject&, const TVarType&)"
526      */
527     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMember(const std::string & name,TReadFunction readFunction,TWriteFunction writeFunction_)528     void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
529     {
530         registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
531     }
532 
533     /**
534      * Registers a member variable
535      * This is the version "registerMember<int (Foo::*)>(name, getter, setter)"
536      * @tparam TMemberType   Pointer to member object representing the type
537      * @param name           Name of the member to register
538      * @param readFunction   Function of type "TVarType (const TObject&)"
539      * @param writeFunction_  Function of type "void (TObject&, const TVarType&)"
540      */
541     template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
registerMember(const std::string & name,TReadFunction readFunction,TWriteFunction writeFunction_)542     void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
543     {
544         static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
545         registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction_));
546     }
547 
548     /**
549      * Registers a non-modifiable member variable
550      * This is the version "registerMember<Foo, int>(name, getter)"
551      * @tparam TObject       Type to register the member to
552      * @tparam TVarType      Type of the member
553      * @param name           Name of the member to register
554      * @param readFunction   Function of type "TVarType (const TObject&)"
555      */
556     template<typename TObject, typename TVarType, typename TReadFunction>
registerMember(const std::string & name,TReadFunction readFunction)557     void registerMember(const std::string& name, TReadFunction readFunction)
558     {
559         registerMemberImpl<TObject,TVarType>(name, std::move(readFunction));
560     }
561 
562     /**
563      * Registers a non-modifiable member variable
564      * This is the version "registerMember<int (Foo::*)>(name, getter)"
565      * @tparam TMemberType   Pointer to member object representing the type
566      * @param name           Name of the member to register
567      * @param readFunction   Function of type "TVarType (const TObject&)"
568      */
569     template<typename TMemberType, typename TReadFunction>
registerMember(const std::string & name,TReadFunction readFunction)570     void registerMember(const std::string& name, TReadFunction readFunction)
571     {
572         static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
573         registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction));
574     }
575 
576     /**
577      * Registers a dynamic member variable
578      * This is the version "registerMember<Foo, int>(getter, setter)"
579      * @tparam TObject       Type to register the member to
580      * @tparam TVarType      Type of the member
581      * @param readFunction   Function of type "TVarType (const TObject&, const std::string&)"
582      * @param writeFunction_  Function of type "void (TObject&, const std::string&, const TVarType&)"
583      */
584     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMember(TReadFunction readFunction,TWriteFunction writeFunction_)585     void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
586     {
587         registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
588     }
589 
590     /**
591      * Registers a dynamic member variable
592      * This is the version "registerMember<int (Foo::*)>(getter, setter)"
593      * @tparam TMemberType   Pointer to member object representing the type
594      * @param readFunction   Function of type "TVarType (const TObject&, const std::string&)"
595      * @param writeFunction_  Function of type "void (TObject&, const std::string&, const TVarType&)"
596      */
597     template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
registerMember(TReadFunction readFunction,TWriteFunction writeFunction_)598     void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
599     {
600         static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
601         registerMemberImpl(tag<TMemberType>{}, std::move(readFunction), std::move(writeFunction_));
602     }
603 
604     /**
605      * Registers a dynamic non-modifiable member variable
606      * This is the version "registerMember<Foo, int>(getter)"
607      * @tparam TObject       Type to register the member to
608      * @tparam TVarType      Type of the member
609      * @param readFunction   Function of type "TVarType (const TObject&, const std::string&)"
610      */
611     template<typename TObject, typename TVarType, typename TReadFunction>
registerMember(TReadFunction readFunction)612     void registerMember(TReadFunction readFunction)
613     {
614         registerMemberImpl<TObject, TVarType>(std::move(readFunction));
615     }
616 
617     /**
618      * Registers a dynamic non-modifiable member variable
619      * This is the version "registerMember<int (Foo::*)>(getter)"
620      * @tparam TMemberType   Pointer to member object representing the type
621      * @param readFunction   Function of type "TVarType (const TObject&, const std::string&)"
622      */
623     template<typename TMemberType, typename TReadFunction>
registerMember(TReadFunction readFunction)624     void registerMember(TReadFunction readFunction)
625     {
626         static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
627         registerMemberImpl(tag<TMemberType>{}, std::move(readFunction));
628     }
629 
630     /**
631      * Creates a new thread
632      * A Lua thread is not really a thread, but rather an "execution stack".
633      * You can destroy the thread by calling destroyThread
634      * @sa destroyThread
635      */
createThread()636     auto createThread()
637         -> ThreadID
638     {
639         ThreadID result;
640 
641         result.state = lua_newthread(mState);
642         result.threadInRegistry = std::unique_ptr<ValueInRegistry>(new ValueInRegistry(mState));
643         lua_pop(mState, 1);
644 
645         return result;
646     }
647 
648     /**
649      * Destroys a thread created with createThread
650      * @sa createThread
651      */
destroyThread(ThreadID & id)652     void destroyThread(ThreadID& id)
653     {
654         id.threadInRegistry.reset();
655     }
656 
657     /**
658      * Reads the content of a Lua variable
659      *
660      * @tparam TType                Type requested for the read
661      * @throw WrongTypeException    When the variable is not convertible to the requested type
662      * @sa writeVariable
663      *
664      * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers
665      * Additionally supported:
666      *  - LuaFunctionCaller<FunctionType>, which is an alternative to std::function
667      *  - references to custom objects, in which case it will return the object in-place
668      *
669      * After the variable name, you can add other parameters.
670      * If the variable is an array, it will instead get the element of that array whose offset is the second parameter.
671      * Same applies for third, fourth, etc. parameters.
672     */
673     template<typename TType, typename... TTypes>
674     TType readVariable(const std::string& name, TTypes&&... elements) const
675     {
676         lua_getglobal(mState, name.c_str());
677         lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
678         return readTopAndPop<TType>(mState, PushedObject{mState, 1});
679     }
680 
681     /**
682      * @sa readVariable
683      */
684     template<typename TType, typename... TTypes>
685     TType readVariable(const char* name, TTypes&&... elements) const
686     {
687         lua_getglobal(mState, name);
688         lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
689         return readTopAndPop<TType>(mState, PushedObject{mState, 1});
690     }
691 
692     /**
693      * @sa readVariable
694      */
695     template<typename TType, typename... TTypes>
696     TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const
697     {
698         lua_getglobal(thread.state, name.c_str());
699         lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
700         return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
701     }
702 
703     /**
704      * @sa readVariable
705      */
706     template<typename TType, typename... TTypes>
707     TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const
708     {
709         lua_getglobal(thread.state, name);
710         lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
711         return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
712     }
713 
714     /**
715      * Changes the content of a Lua variable
716      *
717      * Accepted values are:
718      * - all base types (char, short, int, float, double, bool)
719      * - std::string
720      * - enums
721      * - std::vector<>
722      * - std::vector<std::pair<>>, std::map<> and std::unordered_map<> (the key and value must also be accepted values)
723      * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple)
724      * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>)
725      * - nullptr (writes nil)
726      * - any object
727      *
728      * All objects are passed by copy and destroyed by the garbage collector if necessary.
729      */
730     template<typename... TData>
writeVariable(TData &&...data)731     void writeVariable(TData&&... data) noexcept {
732         static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable");
733         typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
734             RealDataType;
735         static_assert(!std::is_same<typename Tupleizer<RealDataType>::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
736 
737         setTable<RealDataType>(mState, Globals, std::forward<TData>(data)...);
738     }
739 
740     /**
741      * Equivalent to writeVariable(varName, ..., std::function<TFunctionType>(data));
742      * This version is more efficient than writeVariable if you want to write functions
743      */
744     template<typename TFunctionType, typename... TData>
writeFunction(TData &&...data)745     void writeFunction(TData&&... data) noexcept {
746         static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
747 
748         setTable<TFunctionType>(mState, Globals, std::forward<TData>(data)...);
749     }
750 
751     /**
752      * Same as the other writeFunction, except that the template parameter is automatically detected
753      * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas)
754      */
755     template<typename... TData>
writeFunction(TData &&...data)756     void writeFunction(TData&&... data) noexcept {
757         static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
758         typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
759             RealDataType;
760         typedef typename FunctionTypeDetector<RealDataType>::type
761             DetectedFunctionType;
762 
763         return writeFunction<DetectedFunctionType>(std::forward<TData>(data)...);
764     }
765 
766 
767 private:
768     // the state is the most important variable in the class since it is our interface with Lua
769     //  - registered members and functions are stored in tables at offset &typeid(type) of the registry
770     //    each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2
771     //    offset 3 is unused, setter members at offset 4, default setter at offset 5
772     lua_State*                  mState;
773 
774 
775     /**************************************************/
776     /*                 PUSH OBJECT                    */
777     /**************************************************/
778     struct PushedObject {
PushedObjectLuaContext::PushedObject779         PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {}
~PushedObjectLuaContext::PushedObject780         ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); }
781 
782         PushedObject& operator=(const PushedObject&) = delete;
783         PushedObject(const PushedObject&) = delete;
operator =LuaContext::PushedObject784         PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; }
PushedObjectLuaContext::PushedObject785         PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; }
786 
operator +LuaContext::PushedObject787         PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return obj; }
operator +=LuaContext::PushedObject788         void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; }
789 
getStateLuaContext::PushedObject790         auto getState() const -> lua_State* { return state; }
getNumLuaContext::PushedObject791         auto getNum() const -> int { return num; }
792 
releaseLuaContext::PushedObject793         int release() { const auto n = num; num = 0; return n; }
popLuaContext::PushedObject794         void pop() { if (num >= 1) lua_pop(state, num); num = 0; }
popLuaContext::PushedObject795         void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; }
796 
797     private:
798         lua_State* state;
799         int num = 0;
800     };
801 
802 
803     /**************************************************/
804     /*                     MISC                       */
805     /**************************************************/
806     // type used as a tag
807     template<typename T>
808     struct tag {};
809 
810     // tag for "the registry"
811     enum RegistryTag { Registry };
812 
813     // this function takes a value representing the offset to look into
814     // it will look into the top element of the stack and replace the element by its content at the given index
815     template<typename OffsetType1, typename... OffsetTypeOthers>
lookIntoStackTop(lua_State * state,OffsetType1 && offset1,OffsetTypeOthers &&...offsetOthers)816     static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) {
817         static_assert(Pusher<typename std::decay<OffsetType1>::type>::minSize == 1 && Pusher<typename std::decay<OffsetType1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
818         auto p1 = Pusher<typename std::decay<OffsetType1>::type>::push(state, offset1);
819         lua_gettable(state, -2);
820         lua_remove(state, -2);
821         p1.release();
822 
823         lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
824     }
825 
826     template<typename... OffsetTypeOthers>
lookIntoStackTop(lua_State * state,Metatable_t,OffsetTypeOthers &&...offsetOthers)827     static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) {
828         lua_getmetatable(state, -1);
829         lua_remove(state, -2);
830 
831         lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
832     }
833 
lookIntoStackTop(lua_State *)834     static void lookIntoStackTop(lua_State*) {
835     }
836 
837     // equivalent of lua_settable with t[k]=n, where t is the value at the index in the template parameter, k is the second parameter, n is the last parameter, and n is pushed by the function in the first parameter
838     // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array
839     // the dataPusher MUST push only one thing on the stack
840     // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack
841     template<typename TDataType, typename TIndex, typename TData>
setTable(lua_State * state,const PushedObject &,TIndex && index,TData && data)842     static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept
843     {
844         static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
845         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
846 
847         auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
848         auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
849 
850         lua_settable(state, -3);
851         p1.release();
852         p2.release();
853     }
854 
855     template<typename TDataType, typename TData>
setTable(lua_State * state,const PushedObject &,const std::string & index,TData && data)856     static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept
857     {
858         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
859 
860         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
861         lua_setfield(state, -2, index.c_str());
862         p1.release();
863     }
864 
865     template<typename TDataType, typename TData>
setTable(lua_State * state,const PushedObject &,const char * index,TData && data)866     static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept
867     {
868         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
869 
870         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
871         lua_setfield(state, -2, index);
872         p1.release();
873     }
874 
875     template<typename TDataType, typename TData>
setTable(lua_State * state,const PushedObject &,Metatable_t,TData && data)876     static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept
877     {
878         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
879 
880         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
881         lua_setmetatable(state, -2);
882         p1.release();
883     }
884 
885     template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,PushedObject &,TIndex1 && index1,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)886     static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
887         -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
888     {
889         static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
890 
891         auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
892         lua_gettable(state, -2);
893 
894         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
895     }
896 
897     template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,PushedObject && pushedTable,TIndex1 && index1,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)898     static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
899         -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
900     {
901         static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
902 
903         auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + std::move(pushedTable);
904         lua_gettable(state, -2);
905 
906         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
907     }
908 
909     template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,PushedObject & pushedObject,Metatable_t,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)910     static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
911     {
912         if (lua_getmetatable(state, -1) == 0)
913         {
914             lua_newtable(state);
915             PushedObject p1{state, 1};
916 
917             setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
918 
919             lua_setmetatable(state, -2);
920             p1.release();
921         }
922         else
923         {
924             setTable<TDataType>(state, pushedObject, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
925         }
926     }
927 
928     template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,PushedObject && pushedObject,Metatable_t,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)929     static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
930     {
931         if (lua_getmetatable(state, -1) == 0)
932         {
933             lua_newtable(state);
934             PushedObject p1{state, 1};
935 
936             setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
937 
938             lua_setmetatable(state, -2);
939             p1.release();
940         }
941         else
942         {
943             setTable<TDataType>(state, std::move(pushedObject), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
944         }
945     }
946 
947     template<typename TDataType, typename TIndex, typename TData>
setTable(lua_State * state,RegistryTag,TIndex && index,TData && data)948     static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept
949     {
950         static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
951         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
952 
953         auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
954         auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
955 
956         lua_settable(state, LUA_REGISTRYINDEX);
957         p1.release();
958         p2.release();
959     }
960 
961     template<typename TDataType, typename TData>
setTable(lua_State * state,RegistryTag,const std::string & index,TData && data)962     static void setTable(lua_State* state, RegistryTag, const std::string& index, TData&& data) noexcept
963     {
964         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
965 
966         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
967         lua_setfield(state, LUA_REGISTRYINDEX, index.c_str());
968         p1.release();
969     }
970 
971     template<typename TDataType, typename TData>
setTable(lua_State * state,RegistryTag,const char * index,TData && data)972     static void setTable(lua_State* state, RegistryTag, const char* index, TData&& data) noexcept
973     {
974         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
975 
976         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
977         lua_setfield(state, LUA_REGISTRYINDEX, index);
978         p1.release();
979     }
980 
981     template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,RegistryTag,TIndex1 && index1,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)982     static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
983     {
984         static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
985 
986         auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
987         lua_gettable(state, LUA_REGISTRYINDEX);
988 
989         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
990     }
991 
992     template<typename TDataType, typename TIndex, typename TData>
setTable(lua_State * state,Globals_t,TIndex && index,TData && data)993     static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept
994     {
995         static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
996         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
997 
998 
999 #       if LUA_VERSION_NUM >= 502
1000 
1001             lua_pushglobaltable(state);
1002             PushedObject p3{state, 1};
1003             auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
1004             auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1005             lua_settable(state, -3);
1006 
1007 #       else
1008 
1009             auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
1010             auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1011             lua_settable(state, LUA_GLOBALSINDEX);
1012 
1013 #       endif
1014 
1015         p1.release();
1016         p2.release();
1017     }
1018 
1019     template<typename TDataType, typename TData>
setTable(lua_State * state,Globals_t,const std::string & index,TData && data)1020     static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept
1021     {
1022         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
1023 
1024         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1025         lua_setglobal(state, index.c_str());
1026         p1.release();
1027     }
1028 
1029     template<typename TDataType, typename TData>
setTable(lua_State * state,Globals_t,const char * index,TData && data)1030     static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept
1031     {
1032         static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
1033 
1034         auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1035         lua_setglobal(state, index);
1036         p1.release();
1037     }
1038 
1039     template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
setTable(lua_State * state,Globals_t,TIndex1 && index1,TIndex2 && index2,TIndex3 && index3,TIndices &&...indices)1040     static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1041     {
1042         static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
1043 
1044 #       if LUA_VERSION_NUM >= 502
1045 
1046             lua_pushglobaltable(state);
1047             auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + PushedObject{state, 1};
1048             lua_gettable(state, -2);
1049 
1050 #       else
1051 
1052             auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
1053             lua_gettable(state, LUA_GLOBALSINDEX);
1054 
1055 #       endif
1056 
1057         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1058     }
1059 
1060     // TODO: g++ reports "ambiguous overload"
1061     /*template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
1062     static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1063     {
1064         lua_getglobal(state, index);
1065         PushedObject p1{state, 1};
1066 
1067         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1068     }
1069 
1070     template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
1071     static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1072     {
1073         lua_getglobal(state, index.c_str());
1074         PushedObject p1{state, 1};
1075 
1076         setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1077     }*/
1078 
1079     // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value
1080     // warning: first parameter is the number of parameters, not the parameter index
1081     // if read generates an exception, stack is poped anyway
1082     template<typename TReturnType>
readTopAndPop(lua_State * state,PushedObject object)1083     static auto readTopAndPop(lua_State* state, PushedObject object)
1084         -> TReturnType
1085     {
1086         auto val = Reader<typename std::decay<TReturnType>::type>::read(state, -object.getNum());
1087         if (!val.is_initialized())
1088             throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)};
1089         return val.get();
1090     }
1091 
1092     // checks that the offsets for a type's registrations are set in the registry
checkTypeRegistration(lua_State * state,const std::type_info * type)1093     static void checkTypeRegistration(lua_State* state, const std::type_info* type)
1094     {
1095         lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1096         lua_gettable(state, LUA_REGISTRYINDEX);
1097         if (!lua_isnil(state, -1)) {
1098             lua_pop(state, 1);
1099             return;
1100         }
1101         lua_pop(state, 1);
1102 
1103         lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1104         lua_newtable(state);
1105 
1106         lua_pushinteger(state, 0);
1107         lua_newtable(state);
1108         lua_settable(state, -3);
1109 
1110         lua_pushinteger(state, 1);
1111         lua_newtable(state);
1112         lua_settable(state, -3);
1113 
1114         lua_pushinteger(state, 3);
1115         lua_newtable(state);
1116         lua_settable(state, -3);
1117 
1118         lua_pushinteger(state, 4);
1119         lua_newtable(state);
1120         lua_settable(state, -3);
1121 
1122         lua_settable(state, LUA_REGISTRYINDEX);
1123     }
1124 
1125     //
1126 #   ifdef _MSC_VER
1127         __declspec(noreturn)
1128 #   else
1129         [[noreturn]]
1130 #   endif
luaError(lua_State * state)1131     static void luaError(lua_State* state)
1132     {
1133         lua_error(state);
1134         assert(false);
1135         std::terminate();   // removes compilation warning
1136     }
1137 
1138 
1139     /**************************************************/
1140     /*            FUNCTIONS REGISTRATION              */
1141     /**************************************************/
1142     // the "registerFunction" public functions call this one
1143     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<TObject>,tag<TRetValue (TOtherParams...)>)1144     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TObject>, tag<TRetValue (TOtherParams...)>)
1145     {
1146         static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value || std::is_union<TObject>::value , "registerFunction can only be used for a class a union or a pointer");
1147 
1148         checkTypeRegistration(mState, &typeid(TObject));
1149         setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, function);
1150 
1151         checkTypeRegistration(mState, &typeid(TObject*));
1152         setTable<TRetValue(TObject*, TOtherParams...)>(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1153 
1154         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1155         setTable<TRetValue(std::shared_ptr<TObject>, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 0, functionName, [=](const std::shared_ptr<TObject>& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1156     }
1157 
1158     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<const TObject>,tag<TRetValue (TOtherParams...)> fTypeTag)1159     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<const TObject>, tag<TRetValue (TOtherParams...)> fTypeTag)
1160     {
1161         registerFunctionImpl(functionName, function, tag<TObject>{}, fTypeTag);
1162 
1163         checkTypeRegistration(mState, &typeid(TObject const*));
1164         setTable<TRetValue(TObject const*, TOtherParams...)>(mState, Registry, &typeid(TObject const*), 0, functionName, [=](TObject const* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1165 
1166         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1167         setTable<TRetValue(std::shared_ptr<TObject const>, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 0, functionName, [=](const std::shared_ptr<TObject const>& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1168     }
1169 
1170     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<TRetValue (TObject::*)(TOtherParams...)>)1171     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...)>)
1172     {
1173         registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1174     }
1175 
1176     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<TRetValue (TObject::*)(TOtherParams...)const>)1177     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const>)
1178     {
1179         registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1180     }
1181 
1182     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<TRetValue (TObject::*)(TOtherParams...)volatile>)1183     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) volatile>)
1184     {
1185         registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1186     }
1187 
1188     template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
registerFunctionImpl(const std::string & functionName,TFunctionType function,tag<TRetValue (TObject::*)(TOtherParams...)const volatile>)1189     void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const volatile>)
1190     {
1191         registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1192     }
1193 
1194     // the "registerMember" public functions call this one
1195     template<typename TObject, typename TVarType, typename TReadFunction>
registerMemberImpl(const std::string & name,TReadFunction readFunction)1196     void registerMemberImpl(const std::string& name, TReadFunction readFunction)
1197     {
1198         static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value, "registerMember can only be called on a class or a pointer");
1199 
1200         checkTypeRegistration(mState, &typeid(TObject));
1201         setTable<TVarType (TObject&)>(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) {
1202             return readFunction(object);
1203         });
1204 
1205         checkTypeRegistration(mState, &typeid(TObject*));
1206         setTable<TVarType (TObject*)>(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) {
1207             assert(object);
1208             return readFunction(*object);
1209         });
1210 
1211         checkTypeRegistration(mState, &typeid(TObject const*));
1212         setTable<TVarType (TObject const*)>(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) {
1213             assert(object);
1214             return readFunction(*object);
1215         });
1216 
1217         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1218         setTable<TVarType (std::shared_ptr<TObject>)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 1, name, [readFunction](const std::shared_ptr<TObject>& object) {
1219             assert(object);
1220             return readFunction(*object);
1221         });
1222 
1223         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1224         setTable<TVarType (std::shared_ptr<TObject const>)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 1, name, [readFunction](const std::shared_ptr<TObject const>& object) {
1225             assert(object);
1226             return readFunction(*object);
1227         });
1228     }
1229 
1230     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMemberImpl(const std::string & name,TReadFunction readFunction,TWriteFunction writeFunction_)1231     void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1232     {
1233         registerMemberImpl<TObject,TVarType>(name, readFunction);
1234 
1235         setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) {
1236             writeFunction_(object, value);
1237         });
1238 
1239         setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) {
1240             assert(object);
1241             writeFunction_(*object, value);
1242         });
1243 
1244         setTable<void (std::shared_ptr<TObject>, TVarType)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 4, name, [writeFunction_](std::shared_ptr<TObject> object, const TVarType& value) {
1245             assert(object);
1246             writeFunction_(*object, value);
1247         });
1248     }
1249 
1250     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMemberImpl(tag<TVarType (TObject::*)>,const std::string & name,TReadFunction readFunction,TWriteFunction writeFunction_)1251     void registerMemberImpl(tag<TVarType (TObject::*)>, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1252     {
1253         registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
1254     }
1255 
1256     template<typename TObject, typename TVarType, typename TReadFunction>
registerMemberImpl(tag<TVarType (TObject::*)>,const std::string & name,TReadFunction readFunction)1257     void registerMemberImpl(tag<TVarType(TObject::*)>, const std::string& name, TReadFunction readFunction)
1258     {
1259         registerMemberImpl<TObject, TVarType>(name, std::move(readFunction));
1260     }
1261 
1262     // the "registerMember" public functions call this one
1263     template<typename TObject, typename TVarType, typename TReadFunction>
registerMemberImpl(TReadFunction readFunction)1264     void registerMemberImpl(TReadFunction readFunction)
1265     {
1266         checkTypeRegistration(mState, &typeid(TObject));
1267         setTable<TVarType (TObject const&, std::string)>(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) {
1268             return readFunction(object, name);
1269         });
1270 
1271         checkTypeRegistration(mState, &typeid(TObject*));
1272         setTable<TVarType (TObject*, std::string)>(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) {
1273             assert(object);
1274             return readFunction(*object, name);
1275         });
1276 
1277         checkTypeRegistration(mState, &typeid(TObject const*));
1278         setTable<TVarType (TObject const*, std::string)>(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) {
1279             assert(object);
1280             return readFunction(*object, name);
1281         });
1282 
1283         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1284         setTable<TVarType (std::shared_ptr<TObject>, std::string)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 2, [readFunction](const std::shared_ptr<TObject>& object, const std::string& name) {
1285             assert(object);
1286             return readFunction(*object, name);
1287         });
1288 
1289         checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1290         setTable<TVarType (std::shared_ptr<TObject const>, std::string)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 2, [readFunction](const std::shared_ptr<TObject const>& object, const std::string& name) {
1291             assert(object);
1292             return readFunction(*object, name);
1293         });
1294     }
1295 
1296     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMemberImpl(TReadFunction readFunction,TWriteFunction writeFunction_)1297     void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction_)
1298     {
1299         registerMemberImpl<TObject,TVarType>(readFunction);
1300 
1301         setTable<void (TObject&, std::string, TVarType)>(mState, Registry, &typeid(TObject), 5, [writeFunction_](TObject& object, const std::string& name, const TVarType& value) {
1302             writeFunction_(object, name, value);
1303         });
1304 
1305         setTable<void (TObject*, std::string, TVarType)>(mState, Registry, &typeid(TObject*), 2, [writeFunction_](TObject* object, const std::string& name, const TVarType& value) {
1306             assert(object);
1307             writeFunction_(*object, name, value);
1308         });
1309 
1310         setTable<void (std::shared_ptr<TObject>, std::string, TVarType)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 2, [writeFunction_](const std::shared_ptr<TObject>& object, const std::string& name, const TVarType& value) {
1311             assert(object);
1312             writeFunction_(*object, name, value);
1313         });
1314     }
1315 
1316     template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
registerMemberImpl(tag<TVarType (TObject::*)>,TReadFunction readFunction,TWriteFunction writeFunction_)1317     void registerMemberImpl(tag<TVarType (TObject::*)>, TReadFunction readFunction, TWriteFunction writeFunction_)
1318     {
1319         registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
1320     }
1321 
1322     template<typename TObject, typename TVarType, typename TReadFunction>
registerMemberImpl(tag<TVarType (TObject::*)>,TReadFunction readFunction)1323     void registerMemberImpl(tag<TVarType(TObject::*)>, TReadFunction readFunction)
1324     {
1325         registerMemberImpl<TObject, TVarType>(std::move(readFunction));
1326     }
1327 
1328 
1329     /**************************************************/
1330     /*              LOADING AND CALLING               */
1331     /**************************************************/
1332     // this function loads data from the stream and pushes a function at the top of the stack
1333     // throws in case of syntax error
load(lua_State * state,std::istream & code)1334     static PushedObject load(lua_State* state, std::istream& code) {
1335         // since the lua_load function requires a static function, we use this structure
1336         // the Reader structure is at the same time an object storing an istream and a buffer,
1337         //   and a static function provider
1338         struct Reader {
1339             Reader(std::istream& str) : stream(str) {}
1340             std::istream&           stream;
1341             std::array<char,512>    buffer;
1342 
1343             // read function ; "data" must be an instance of Reader
1344             static const char* read(lua_State* /*l*/, void* data, size_t* size) {
1345                 assert(size != nullptr);
1346                 assert(data != nullptr);
1347                 Reader& me = *static_cast<Reader*>(data);
1348                 if (me.stream.eof())    { *size = 0; return nullptr; }
1349 
1350                 me.stream.read(me.buffer.data(), me.buffer.size());
1351                 *size = static_cast<size_t>(me.stream.gcount());    // gcount could return a value larger than a size_t, but its maximum is me.buffer.size() so there's no problem
1352                 return me.buffer.data();
1353             }
1354         };
1355 
1356         // we create an instance of Reader, and we call lua_load
1357         Reader reader{code};
1358         const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk"
1359 #           if LUA_VERSION_NUM >= 502
1360                 , nullptr
1361 #           endif
1362         );
1363 
1364         // now we have to check return value
1365         if (loadReturnValue != 0) {
1366             // there was an error during loading, an error message was pushed on the stack
1367             const std::string errorMsg = lua_tostring(state, -1);
1368             lua_pop(state, 1);
1369             if (loadReturnValue == LUA_ERRMEM)
1370                 throw std::bad_alloc();
1371             else if (loadReturnValue == LUA_ERRSYNTAX)
1372                 throw SyntaxErrorException{errorMsg};
1373             throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1374         }
1375 
1376         return PushedObject{state, 1};
1377     }
1378 
1379     // this function loads data and pushes a function at the top of the stack
1380     // throws in case of syntax error
load(lua_State * state,const char * code)1381     static PushedObject load(lua_State* state, const char* code) {
1382         auto loadReturnValue = luaL_loadstring(state, code);
1383 
1384         // now we have to check return value
1385         if (loadReturnValue != 0) {
1386             // there was an error during loading, an error message was pushed on the stack
1387             const std::string errorMsg = lua_tostring(state, -1);
1388             lua_pop(state, 1);
1389             if (loadReturnValue == LUA_ERRMEM)
1390                 throw std::bad_alloc();
1391             else if (loadReturnValue == LUA_ERRSYNTAX)
1392                 throw SyntaxErrorException{errorMsg};
1393             throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1394         }
1395 
1396         return PushedObject{state, 1};
1397     }
1398 
1399     // this function calls what is on the top of the stack and removes it (just like lua_call)
1400     // if an exception is triggered, the top of the stack will be removed anyway
1401     template<typename TReturnType, typename... TParameters>
call(lua_State * state,PushedObject toCall,TParameters &&...input)1402     static auto call(lua_State* state, PushedObject toCall, TParameters&&... input)
1403         -> TReturnType
1404     {
1405         typedef typename Tupleizer<TReturnType>::type
1406             RealReturnType;
1407 
1408         // we push the parameters on the stack
1409         auto inArguments = Pusher<std::tuple<TParameters&&...>>::push(state, std::forward_as_tuple(std::forward<TParameters>(input)...));
1410 
1411         //
1412         const int outArgumentsCount = std::tuple_size<RealReturnType>::value;
1413         auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount);
1414 
1415         // pcall succeeded, we pop the returned values and return them
1416         return readTopAndPop<TReturnType>(state, std::move(outArguments));
1417     }
1418 
gettraceback(lua_State * L)1419     static int gettraceback(lua_State* L) {
1420         lua_getglobal(L, "debug"); // stack: error, debug library
1421         lua_getfield(L, -1, "traceback"); // stack: error, debug library, debug.traceback function
1422         lua_remove(L, -2); // stack: error, debug.traceback function
1423         lua_pushstring(L, ""); // stack: error, debug.traceback, ""
1424         lua_pushinteger(L, 2); // stack: error, debug.traceback, "", 2
1425         lua_call(L, 2, 1); // stack: error, traceback
1426         lua_createtable(L, 2, 0); // stack: error, traceback, {}
1427         lua_insert(L, 1); // stack: {}, error, traceback
1428         lua_rawseti(L, 1, 2); // stack: {[2]=traceback}, error
1429         lua_rawseti(L, 1, 1); // stack: {[1]=error,[2]=traceback}
1430         return 1; // return the table
1431     }
1432 
1433     // this function just calls lua_pcall and checks for errors
callRaw(lua_State * state,PushedObject functionAndArguments,const int outArguments)1434     static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments)
1435     {
1436         // provide traceback handler
1437         int tbindex = lua_gettop(state) - (functionAndArguments.getNum() - 1);
1438         lua_pushcfunction(state, gettraceback);
1439 
1440         // move it back up, before our function and arguments
1441         lua_insert(state, tbindex);
1442 
1443         // calling pcall automatically pops the parameters and pushes output
1444         const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, tbindex);
1445         functionAndArguments.release();
1446 
1447         lua_remove(state, tbindex); // remove traceback function
1448 
1449 
1450         // if pcall failed, analyzing the problem and throwing
1451         if (pcallReturnValue != 0) {
1452 
1453             // stack top: {error, traceback}
1454             lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error
1455             lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback
1456             lua_remove(state, -3); // stack top: error, traceback
1457 
1458             PushedObject traceBackRef{state, 1};
1459             const auto traceBack = readTopAndPop<std::string>(state, std::move(traceBackRef)); // stack top: error
1460             PushedObject errorCode{state, 1};
1461 
1462             // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack
1463             if (pcallReturnValue == LUA_ERRMEM) {
1464                 throw std::bad_alloc{};
1465 
1466             } else if (pcallReturnValue == LUA_ERRRUN) {
1467                 if (lua_isstring(state, 1)) {
1468                     // the error is a string
1469                     const auto str = readTopAndPop<std::string>(state, std::move(errorCode));
1470                     throw ExecutionErrorException{str+traceBack};
1471 
1472                 } else {
1473                     // an exception_ptr was pushed on the stack
1474                     // rethrowing it with an additional ExecutionErrorException
1475                     try {
1476                         if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
1477                             std::rethrow_exception(exp);
1478                         }
1479                     } catch(const std::exception& e) {
1480                         std::throw_with_nested(ExecutionErrorException{std::string{"Exception thrown by a callback function: "} + e.what()});
1481                     } catch(...) {
1482                         std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua. "+traceBack});
1483                     }
1484                     throw ExecutionErrorException{"Unknown Lua error"};
1485                 }
1486             }
1487         }
1488 
1489         return PushedObject{state, outArguments};
1490     }
1491 
1492 
1493     /**************************************************/
1494     /*                PUSH FUNCTIONS                  */
1495     /**************************************************/
1496     template<typename T>
push(lua_State * state,T && value)1497     static PushedObject push(lua_State* state, T&& value)
1498     {
1499         return Pusher<typename std::decay<T>::type>::push(state, std::forward<T>(value));
1500     }
1501 
1502     // the Pusher structures allow you to push a value on the stack
1503     //  - static const int minSize : minimum size on the stack that the value can have
1504     //  - static const int maxSize : maximum size on the stack that the value can have
1505     //  - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack
1506 
1507     // implementation for custom objects
1508     template<typename TType, typename = void>
1509     struct Pusher {
1510         static const int minSize = 1;
1511         static const int maxSize = 1;
1512 
1513         template<typename TType2>
pushLuaContext::Pusher1514         static PushedObject push(lua_State* state, TType2&& value) noexcept {
1515             // this function is called when lua's garbage collector wants to destroy our object
1516             // we simply call its destructor
1517             const auto garbageCallbackFunction = [](lua_State* lua) -> int {
1518                 assert(lua_gettop(lua) == 1);
1519                 TType* ptr = static_cast<TType*>(lua_touserdata(lua, 1));
1520                 assert(ptr);
1521                 ptr->~TType();
1522                 return 0;
1523             };
1524 
1525             // this function will be stored in __index in the metatable
1526             const auto indexFunction = [](lua_State* lua) -> int {
1527                 try {
1528                     assert(lua_gettop(lua) == 2);
1529                     assert(lua_isuserdata(lua, 1));
1530 
1531                     // searching for a handler
1532                     lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1533                     lua_gettable(lua, LUA_REGISTRYINDEX);
1534                     assert(!lua_isnil(lua, -1));
1535 
1536                     // looking into getter functions
1537                     lua_pushinteger(lua, 0);
1538                     lua_gettable(lua, -2);
1539                     lua_pushvalue(lua, 2);
1540                     lua_gettable(lua, -2);
1541                     if (!lua_isnil(lua, -1))
1542                         return 1;
1543                     lua_pop(lua, 2);
1544 
1545                     // looking into getter members
1546                     lua_pushinteger(lua, 1);
1547                     lua_gettable(lua, -2);
1548                     lua_pushvalue(lua, 2);
1549                     lua_gettable(lua, -2);
1550                     if (!lua_isnil(lua, -1)) {
1551                         lua_pushvalue(lua, 1);
1552                         return callRaw(lua, PushedObject{lua, 2}, 1).release();
1553                     }
1554                     lua_pop(lua, 2);
1555 
1556                     // looking into default getter
1557                     lua_pushinteger(lua, 2);
1558                     lua_gettable(lua, -2);
1559                     if (lua_isnil(lua, -1))
1560                         return 1;
1561                     lua_pushvalue(lua, 1);
1562                     lua_pushvalue(lua, 2);
1563                     return callRaw(lua, PushedObject{lua, 3}, 1).release();
1564 
1565                 } catch (...) {
1566                     Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1567                     luaError(lua);
1568                 }
1569             };
1570 
1571             // this function will be stored in __newindex in the metatable
1572             const auto newIndexFunction = [](lua_State* lua) -> int {
1573                 try {
1574                     assert(lua_gettop(lua) == 3);
1575                     assert(lua_isuserdata(lua, 1));
1576 
1577                     // searching for a handler
1578                     lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1579                     lua_rawget(lua, LUA_REGISTRYINDEX);
1580                     assert(!lua_isnil(lua, -1));
1581 
1582                     // looking into setter members
1583                     lua_pushinteger(lua, 4);
1584                     lua_rawget(lua, -2);
1585                     lua_pushvalue(lua, 2);
1586                     lua_rawget(lua, -2);
1587                     if (!lua_isnil(lua, -1)) {
1588                         lua_pushvalue(lua, 1);
1589                         lua_pushvalue(lua, 3);
1590                         callRaw(lua, PushedObject{lua, 3}, 0);
1591                         lua_pop(lua, 2);
1592                         return 0;
1593                     }
1594                     lua_pop(lua, 2);
1595 
1596                     // looking into default setter
1597                     lua_pushinteger(lua, 5);
1598                     lua_rawget(lua, -2);
1599                     if (lua_isnil(lua, -1))
1600                     {
1601                         lua_pop(lua, 2);
1602                         lua_pushstring(lua, "No setter found");
1603                         luaError(lua);
1604                     }
1605                     lua_pushvalue(lua, 1);
1606                     lua_pushvalue(lua, 2);
1607                     lua_pushvalue(lua, 3);
1608                     callRaw(lua, PushedObject{lua, 4}, 0);
1609                     lua_pop(lua, 1);
1610                     return 0;
1611 
1612                 } catch (...) {
1613                     Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1614                     luaError(lua);
1615                 }
1616             };
1617 
1618             const auto toStringFunction = [](lua_State* lua) -> int {
1619                try {
1620                     assert(lua_gettop(lua) == 1);
1621                     assert(lua_isuserdata(lua, 1));
1622                     lua_pushstring(lua, "__tostring");
1623                     lua_gettable(lua, 1);
1624                     if (lua_isnil(lua, -1))
1625                     {
1626                         const void *ptr = lua_topointer(lua, -2);
1627                         lua_pop(lua, 1);
1628                         lua_pushstring(lua, (boost::format("userdata 0x%08x") % reinterpret_cast<intptr_t>(ptr)).str().c_str());
1629                         return 1;
1630                     }
1631                     lua_pushvalue(lua, 1);
1632 		    return callRaw(lua, PushedObject{lua, 2}, 1).release();
1633                 } catch (...) {
1634                     Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1635                     luaError(lua);
1636                 }
1637             };
1638 
1639 
1640             // writing structure for this type into the registry
1641             checkTypeRegistration(state, &typeid(TType));
1642 
1643             // creating the object
1644             // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
1645             //   and that's what we do with placement-new
1646             const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
1647             new (pointerLocation) TType(std::forward<TType2>(value));
1648             PushedObject obj{state, 1};
1649 
1650             // creating the metatable (over the object on the stack)
1651             // lua_settable pops the key and value we just pushed, so stack management is easy
1652             // all that remains on the stack after these function calls is the metatable
1653             lua_newtable(state);
1654             PushedObject pushedTable{state, 1};
1655 
1656             // using the garbage collecting function we created above
1657             if (!boost::has_trivial_destructor<TType>::value)
1658             {
1659                 lua_pushstring(state, "__gc");
1660                 lua_pushcfunction(state, garbageCallbackFunction);
1661                 lua_settable(state, -3);
1662             }
1663 
1664             // the _typeid index of the metatable will store the type_info*
1665             lua_pushstring(state, "_typeid");
1666             lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
1667             lua_settable(state, -3);
1668 
1669             // using the index function we created above
1670             lua_pushstring(state, "__index");
1671             lua_pushcfunction(state, indexFunction);
1672             lua_settable(state, -3);
1673 
1674             // using the newindex function we created above
1675             lua_pushstring(state, "__newindex");
1676             lua_pushcfunction(state, newIndexFunction);
1677             lua_settable(state, -3);
1678 
1679             lua_pushstring(state, "__tostring");
1680             lua_pushcfunction(state, toStringFunction);
1681             lua_settable(state, -3);
1682 
1683             lua_pushstring(state, "__eq");
1684             lua_getglobal(state, LUACONTEXT_GLOBAL_EQ);
1685             lua_settable(state, -3);
1686 
1687 
1688             // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1689             // lua_setmetatable will bind the two together and pop the metatable
1690             // our custom type remains on the stack (and that's what we want since this is a push function)
1691             lua_setmetatable(state, -2);
1692             pushedTable.release();
1693 
1694             return obj;
1695         }
1696     };
1697 
1698     // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1699     template<typename... TTypes>
1700     struct PusherTotalMinSize;
1701 
1702     // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1703     template<typename... TTypes>
1704     struct PusherTotalMaxSize;
1705 
1706     // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1707     template<typename... TTypes>
1708     struct PusherMinSize;
1709 
1710     // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1711     template<typename... TTypes>
1712     struct PusherMaxSize;
1713 
1714 
1715     /**************************************************/
1716     /*                READ FUNCTIONS                  */
1717     /**************************************************/
1718     // the "Reader" structures allow to read data from the stack
1719     // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1720     // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1721 
1722     template<typename TType, typename = void>
1723     struct Reader {
1724         typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1725             ReturnType;
1726 
readLuaContext::Reader1727         static auto read(lua_State* state, int index)
1728             -> boost::optional<ReturnType>
1729         {
1730             if (!test(state, index))
1731                 return boost::none;
1732             return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
1733         }
1734 
1735     private:
testLuaContext::Reader1736         static bool test(lua_State* state, int index)
1737         {
1738             if (!lua_isuserdata(state, index))
1739                 return false;
1740             if (!lua_getmetatable(state, index))
1741                 return false;
1742 
1743             // now we have our metatable on the top of the stack
1744             // retrieving its _typeid member
1745             lua_pushstring(state, "_typeid");
1746             lua_gettable(state, -2);
1747             const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
1748             const auto typeIDToCompare = &typeid(TType);
1749 
1750             // if wrong typeid, returning false
1751             lua_pop(state, 2);
1752             if (storedTypeID != typeIDToCompare)
1753                 return false;
1754 
1755             return true;
1756         }
1757     };
1758 
1759     /**
1760      * This functions reads multiple values starting at "index" and passes them to the callback
1761      */
1762     template<typename TRetValue, typename TCallback>
readIntoFunction(lua_State *,tag<TRetValue>,TCallback && callback,int)1763     static auto readIntoFunction(lua_State* /*state*/, tag<TRetValue>, TCallback&& callback, int /*index*/)
1764         -> TRetValue
1765     {
1766         return callback();
1767     }
1768     template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
readIntoFunction(lua_State * state,tag<TRetValue> retValueTag,TCallback && callback,int index,tag<TFirstType>,tag<TTypes>...othersTags)1769     static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1770         -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1771     {
1772         if (index >= 0) {
1773             Binder<TCallback, const TFirstType&> binder{ callback, {} };
1774             return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1775         }
1776 
1777         const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1778         if (!firstElem)
1779             throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
1780 
1781         Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1782         return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1783     }
1784     template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
readIntoFunction(lua_State * state,tag<TRetValue> retValueTag,TCallback && callback,int index,tag<TFirstType>,tag<TTypes>...othersTags)1785     static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1786         -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1787     {
1788         if (index >= 0)
1789             throw std::logic_error("Wrong number of parameters");
1790 
1791         const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1792         if (!firstElem)
1793             throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
1794 
1795         Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1796         return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1797     }
1798 
1799 
1800     /**************************************************/
1801     /*                   UTILITIES                    */
1802     /**************************************************/
1803     // structure that will ensure that a certain value is stored somewhere in the registry
1804     struct ValueInRegistry {
1805         // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
ValueInRegistryLuaContext::ValueInRegistry1806         ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_}
1807         {
1808             lua_pushlightuserdata(lua, this);
1809             lua_pushvalue(lua, -1 + index);
1810             lua_settable(lua, LUA_REGISTRYINDEX);
1811         }
1812 
1813         // removing the function from the registry
~ValueInRegistryLuaContext::ValueInRegistry1814         ~ValueInRegistry()
1815         {
1816             lua_pushlightuserdata(lua, this);
1817             lua_pushnil(lua);
1818             lua_settable(lua, LUA_REGISTRYINDEX);
1819         }
1820 
1821         // loads the value and puts it at the top of the stack
popLuaContext::ValueInRegistry1822         PushedObject pop()
1823         {
1824             lua_pushlightuserdata(lua, this);
1825             lua_gettable(lua, LUA_REGISTRYINDEX);
1826             return PushedObject{lua, 1};
1827         }
1828 
1829         ValueInRegistry(const ValueInRegistry&) = delete;
1830         ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1831 
1832     private:
1833         lua_State* lua;
1834     };
1835 
1836     // binds the first parameter of a function object
1837     template<typename TFunctionObject, typename TFirstParamType>
1838     struct Binder {
1839         TFunctionObject function;
1840         TFirstParamType param;
1841 
1842         template<typename... TParams>
operator ()LuaContext::Binder1843         auto operator()(TParams&&... params)
1844             -> decltype(function(param, std::forward<TParams>(params)...))
1845         {
1846             return function(param, std::forward<TParams>(params)...);
1847         }
1848     };
1849 
1850     // turns a type into a tuple
1851     // void is turned into std::tuple<>
1852     // existing tuples are untouched
1853     template<typename T>
1854     struct Tupleizer;
1855 
1856     // this structure takes a pointer to a member function type and returns the base function type
1857     template<typename TType>
1858     struct RemoveMemberPointerFunction { typedef void type; };          // required because of a compiler bug
1859 
1860     // this structure takes any object and detects its function type
1861     template<typename TObjectType>
1862     struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1863 
1864     // this structure takes a function arguments list and has the "min" and the "max" static const member variables, whose value equal to the min and max number of parameters for the function
1865     // the only case where "min != max" is with boost::optional at the end of the list
1866     template<typename... TArgumentsList>
1867     struct FunctionArgumentsCounter {};
1868 
1869     // true is the template parameter is a boost::optional
1870     template<typename T>
1871     struct IsOptional : public std::false_type {};
1872 };
1873 
1874 /// @deprecated
1875 static LuaContext::EmptyArray_t ATTR_UNUSED
1876     LuaEmptyArray {};
1877 /// @deprecated
1878 static LuaContext::Metatable_t ATTR_UNUSED
1879     LuaMetatable {};
1880 
1881 /**************************************************/
1882 /*            PARTIAL IMPLEMENTATIONS             */
1883 /**************************************************/
1884 template<>
readTopAndPop(lua_State *,PushedObject)1885 inline auto LuaContext::readTopAndPop<void>(lua_State* /*state*/, PushedObject /*obj*/)
1886     -> void
1887 {
1888 }
1889 
1890 // this structure takes a template parameter T
1891 // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1892 // we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1893 template<typename T>
1894 struct LuaContext::Tupleizer                        { typedef std::tuple<T>         type; };
1895 template<typename... Args>
1896 struct LuaContext::Tupleizer<std::tuple<Args...>>   { typedef std::tuple<Args...>   type; };
1897 template<>
1898 struct LuaContext::Tupleizer<void>                  { typedef std::tuple<>          type; };
1899 
1900 // this structure takes any object and detects its function type
1901 template<typename TRetValue, typename... TParameters>
1902 struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)>             { typedef TRetValue type(TParameters...); };
1903 template<typename TObjectType>
1904 struct LuaContext::FunctionTypeDetector<TObjectType*>                           { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1905 
1906 // this structure takes a pointer to a member function type and returns the base function type
1907 template<typename TType, typename TRetValue, typename... TParameters>
1908 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)>                    { typedef TRetValue type(TParameters...); };
1909 template<typename TType, typename TRetValue, typename... TParameters>
1910 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const>              { typedef TRetValue type(TParameters...); };
1911 template<typename TType, typename TRetValue, typename... TParameters>
1912 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile>           { typedef TRetValue type(TParameters...); };
1913 template<typename TType, typename TRetValue, typename... TParameters>
1914 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile>     { typedef TRetValue type(TParameters...); };
1915 
1916 // implementation of PusherTotalMinSize
1917 template<typename TFirst, typename... TTypes>
1918 struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1919 template<>
1920 struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1921 
1922 // implementation of PusherTotalMaxSize
1923 template<typename TFirst, typename... TTypes>
1924 struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1925 template<>
1926 struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1927 
1928 // implementation of PusherMinSize
1929 template<typename TFirst, typename TSecond, typename... TTypes>
1930 struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...>
1931 {
1932     static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize
1933                             ?
1934                             PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size
1935                             :
1936                             PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1937 };
1938 
1939 template<typename TFirst>
1940 struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
1941 
1942 // implementation of PusherMaxSize
1943 template<typename TFirst, typename... TTypes>
1944 struct LuaContext::PusherMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize > PusherTotalMaxSize<TTypes...>::size ? Pusher<typename std::decay<TFirst>::type>::maxSize : PusherMaxSize<TTypes...>::size; };
1945 template<>
1946 struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1947 
1948 // implementation of FunctionArgumentsCounter
1949 template<typename TFirst, typename... TParams>
1950 struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1951     typedef FunctionArgumentsCounter<TParams...>
1952         SubType;
1953     static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1954     static const int max = 1 + SubType::max;
1955 };
1956 template<>
1957 struct LuaContext::FunctionArgumentsCounter<> {
1958     static const int min = 0;
1959     static const int max = 0;
1960 };
1961 
1962 // implementation of IsOptional
1963 template<typename T>
1964 struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1965 
1966 // implementation of LuaFunctionCaller
1967 template<typename TFunctionType>
1968 class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1969 template<typename TRetValue, typename... TParams>
1970 class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1971 {
1972 public:
operator ()(TParams &&...params) const1973     TRetValue operator()(TParams&&... params) const
1974     {
1975         auto obj = valueHolder->pop();
1976         return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
1977     }
1978 
1979 private:
1980     std::shared_ptr<ValueInRegistry>    valueHolder;
1981     lua_State*                          state;
1982 
1983 private:
1984     friend LuaContext;
LuaFunctionCaller(lua_State * state_,int index)1985     explicit LuaFunctionCaller(lua_State* state_, int index) :
1986         valueHolder(std::make_shared<ValueInRegistry>(state_, index)),
1987         state(state_)
1988     {}
1989 };
1990 
1991 
1992 /**************************************************/
1993 /*                PUSH FUNCTIONS                  */
1994 /**************************************************/
1995 // specializations of the Pusher structure
1996 
1997 // opaque Lua references
1998 template<>
1999 struct LuaContext::Pusher<LuaContext::LuaObject> {
2000     static const int minSize = 1;
2001     static const int maxSize = 1;
2002 
pushLuaContext::Pusher2003     static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept {
2004         if (value.objectInRegistry.get()) {
2005             PushedObject obj = value.objectInRegistry->pop();
2006             return obj;
2007         } else {
2008             lua_pushnil(state);
2009             return PushedObject{state, 1};
2010         }
2011     }
2012 };
2013 
2014 // boolean
2015 template<>
2016 struct LuaContext::Pusher<bool> {
2017     static const int minSize = 1;
2018     static const int maxSize = 1;
2019 
pushLuaContext::Pusher2020     static PushedObject push(lua_State* state, bool value) noexcept {
2021         lua_pushboolean(state, value);
2022         return PushedObject{state, 1};
2023     }
2024 };
2025 
2026 // string
2027 template<>
2028 struct LuaContext::Pusher<std::string> {
2029     static const int minSize = 1;
2030     static const int maxSize = 1;
2031 
pushLuaContext::Pusher2032     static PushedObject push(lua_State* state, const std::string& value) noexcept {
2033         lua_pushlstring(state, value.c_str(), value.length());
2034         return PushedObject{state, 1};
2035     }
2036 };
2037 
2038 // const char*
2039 template<>
2040 struct LuaContext::Pusher<const char*> {
2041     static const int minSize = 1;
2042     static const int maxSize = 1;
2043 
pushLuaContext::Pusher2044     static PushedObject push(lua_State* state, const char* value) noexcept {
2045         lua_pushstring(state, value);
2046         return PushedObject{state, 1};
2047     }
2048 };
2049 
2050 // const char[N]
2051 template<int N>
2052 struct LuaContext::Pusher<const char[N]> {
2053     static const int minSize = 1;
2054     static const int maxSize = 1;
2055 
pushLuaContext::Pusher2056     static PushedObject push(lua_State* state, const char* value) noexcept {
2057         lua_pushstring(state, value);
2058         return PushedObject{state, 1};
2059     }
2060 };
2061 
2062 // floating numbers
2063 template<typename T>
2064 struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
2065     static const int minSize = 1;
2066     static const int maxSize = 1;
2067 
pushLuaContext::Pusher2068     static PushedObject push(lua_State* state, T value) noexcept {
2069         lua_pushnumber(state, value);
2070         return PushedObject{state, 1};
2071     }
2072 };
2073 
2074 // integers
2075 template<typename T>
2076 struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2077     static const int minSize = 1;
2078     static const int maxSize = 1;
2079 
pushLuaContext::Pusher2080     static PushedObject push(lua_State* state, T value) noexcept {
2081         lua_pushinteger(state, value);
2082         return PushedObject{state, 1};
2083     }
2084 };
2085 
2086 // nil
2087 template<>
2088 struct LuaContext::Pusher<std::nullptr_t> {
2089     static const int minSize = 1;
2090     static const int maxSize = 1;
2091 
pushLuaContext::Pusher2092     static PushedObject push(lua_State* state, std::nullptr_t) noexcept {
2093         lua_pushnil(state);
2094         return PushedObject{state, 1};
2095     }
2096 };
2097 
2098 // empty arrays
2099 template<>
2100 struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
2101     static const int minSize = 1;
2102     static const int maxSize = 1;
2103 
pushLuaContext::Pusher2104     static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
2105         lua_newtable(state);
2106         return PushedObject{state, 1};
2107     }
2108 };
2109 
2110 // std::type_info* is a lightuserdata
2111 template<>
2112 struct LuaContext::Pusher<const std::type_info*> {
2113     static const int minSize = 1;
2114     static const int maxSize = 1;
2115 
pushLuaContext::Pusher2116     static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
2117         lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
2118         return PushedObject{state, 1};
2119     }
2120 };
2121 
2122 // thread
2123 template<>
2124 struct LuaContext::Pusher<LuaContext::ThreadID> {
2125     static const int minSize = 1;
2126     static const int maxSize = 1;
2127 
pushLuaContext::Pusher2128     static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
2129         lua_pushthread(value.state);
2130         return PushedObject{state, 1};
2131     }
2132 };
2133 
2134 // maps
2135 template<typename TKey, typename TValue>
2136 struct LuaContext::Pusher<std::map<TKey,TValue>> {
2137     static const int minSize = 1;
2138     static const int maxSize = 1;
2139 
pushLuaContext::Pusher2140     static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
2141         static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2142         static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2143 
2144         auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2145 
2146         for (auto i = value.begin(), e = value.end(); i != e; ++i)
2147             setTable<TValue>(state, obj, i->first, i->second);
2148 
2149         return obj;
2150     }
2151 };
2152 
2153 // unordered_maps
2154 template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2155 struct LuaContext::Pusher<std::unordered_map<TKey,TValue,THash,TKeyEqual>> {
2156     static const int minSize = 1;
2157     static const int maxSize = 1;
2158 
pushLuaContext::Pusher2159     static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue,THash,TKeyEqual>& value) noexcept {
2160         static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2161         static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2162 
2163         auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2164 
2165         for (auto i = value.begin(), e = value.end(); i != e; ++i)
2166             setTable<TValue>(state, obj, i->first, i->second);
2167 
2168         return obj;
2169     }
2170 };
2171 
2172 // vectors of pairs
2173 template<typename TType1, typename TType2>
2174 struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
2175     static const int minSize = 1;
2176     static const int maxSize = 1;
2177 
pushLuaContext::Pusher2178     static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
2179         static_assert(Pusher<typename std::decay<TType1>::type>::minSize == 1 && Pusher<typename std::decay<TType1>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2180         static_assert(Pusher<typename std::decay<TType2>::type>::minSize == 1 && Pusher<typename std::decay<TType2>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2181 
2182         auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2183 
2184         for (auto i = value.begin(), e = value.end(); i != e; ++i)
2185             setTable<TType2>(state, obj, i->first, i->second);
2186 
2187         return obj;
2188     }
2189 };
2190 
2191 // vectors
2192 template<typename TType>
2193 struct LuaContext::Pusher<std::vector<TType>> {
2194     static const int minSize = 1;
2195     static const int maxSize = 1;
2196 
pushLuaContext::Pusher2197     static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
2198         static_assert(Pusher<typename std::decay<TType>::type>::minSize == 1 && Pusher<typename std::decay<TType>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2199 
2200         auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2201 
2202         for (unsigned int i = 0; i < value.size(); ++i)
2203             setTable<TType>(state, obj, i + 1, value[i]);
2204 
2205         return obj;
2206     }
2207 };
2208 
2209 // unique_ptr
2210 template<typename TType>
2211 struct LuaContext::Pusher<std::unique_ptr<TType>> {
2212     static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2213     static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2214 
pushLuaContext::Pusher2215     static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2216         return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2217     }
2218 };
2219 
2220 // enum
2221 template<typename TEnum>
2222 struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2223     #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2224         typedef typename std::underlying_type<TEnum>::type
2225             RealType;
2226     #else
2227         // implementation when std::underlying_type is not supported
2228         typedef unsigned long
2229             RealType;
2230     #endif
2231 
2232     static const int minSize = Pusher<RealType>::minSize;
2233     static const int maxSize = Pusher<RealType>::maxSize;
2234 
pushLuaContext::Pusher2235     static PushedObject push(lua_State* state, TEnum value) noexcept {
2236         return Pusher<RealType>::push(state, static_cast<RealType>(value));
2237     }
2238 };
2239 
2240 // any function
2241 // this specialization is not directly called, but is called by other specializations
2242 template<typename TReturnType, typename... TParameters>
2243 struct LuaContext::Pusher<TReturnType (TParameters...)>
2244 {
2245     static const int minSize = 1;
2246     static const int maxSize = 1;
2247 
2248     // counts the number of arguments
2249     typedef FunctionArgumentsCounter<TParameters...>
2250         LocalFunctionArgumentsCounter;
2251 
2252     // this is the version of "push" for non-trivially destructible function objects
2253     template<typename TFunctionObject>
pushLuaContext::Pusher2254     static auto push(lua_State* state, TFunctionObject fn) noexcept
2255         -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2256     {
2257         // TODO: is_move_constructible not supported by some compilers
2258         //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2259 
2260         // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2261         // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2262         // so we use userdata instead
2263 
2264         // this function is called when the lua script tries to call our custom data type
2265         // we transfer execution to the "callback" function below
2266         const auto callCallback = [](lua_State* lua) -> int {
2267             assert(lua_gettop(lua) >= 1);
2268             assert(lua_isuserdata(lua, 1));
2269             auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2270             assert(function);
2271 
2272             return callback(lua, function, lua_gettop(lua) - 1).release();
2273         };
2274 
2275         // this one is called when lua's garbage collector no longer needs our custom data type
2276         // we call the function object's destructor
2277         const auto garbageCallback = [](lua_State* lua) -> int {
2278             assert(lua_gettop(lua) == 1);
2279             auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2280             assert(function);
2281             function->~TFunctionObject();
2282             return 0;
2283         };
2284 
2285         // creating the object
2286         // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2287         //   and that's what we do with placement-new
2288         const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2289         new (functionLocation) TFunctionObject(std::move(fn));
2290 
2291         // creating the metatable (over the object on the stack)
2292         // lua_settable pops the key and value we just pushed, so stack management is easy
2293         // all that remains on the stack after these function calls is the metatable
2294         lua_newtable(state);
2295         lua_pushstring(state, "__call");
2296         lua_pushcfunction(state, callCallback);
2297         lua_settable(state, -3);
2298 
2299         lua_pushstring(state, "__gc");
2300         lua_pushcfunction(state, garbageCallback);
2301         lua_settable(state, -3);
2302 
2303         // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2304         // lua_setmetatable will bind the two together and pop the metatable
2305         // our custom function remains on the stack (and that's what we want)
2306         lua_setmetatable(state, -2);
2307 
2308         return PushedObject{state, 1};
2309     }
2310 
2311     // this is the version of "push" for trivially destructible objects
2312     template<typename TFunctionObject>
pushLuaContext::Pusher2313     static auto push(lua_State* state, TFunctionObject fn) noexcept
2314         -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2315     {
2316         // TODO: is_move_constructible not supported by some compilers
2317         //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2318 
2319         // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2320         // since "fn" doesn't need to be destroyed, we simply push it on the stack
2321 
2322         // this is the cfunction that is the callback
2323         const auto function = [](lua_State* state_) -> int
2324         {
2325             // the function object is an upvalue
2326             const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state_, lua_upvalueindex(1)));
2327             return callback(state_, toCall, lua_gettop(state_)).release();
2328         };
2329 
2330         // we copy the function object onto the stack
2331         const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2332         new (functionObjectLocation) TFunctionObject(std::move(fn));
2333 
2334         // pushing the function with the function object as upvalue
2335         lua_pushcclosure(state, function, 1);
2336         return PushedObject{state, 1};
2337     }
2338 
2339     // this is the version of "push" for pointer to functions
pushLuaContext::Pusher2340     static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2341         -> PushedObject
2342     {
2343         // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2344         // since "fn" doesn't need to be destroyed, we simply push it on the stack
2345 
2346         // this is the cfunction that is the callback
2347         const auto function = [](lua_State* state_) -> int
2348         {
2349             // the function object is an upvalue
2350             const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state_, lua_upvalueindex(1)));
2351             return callback(state_, toCall, lua_gettop(state_)).release();
2352         };
2353 
2354         // we copy the function object onto the stack
2355         lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
2356 
2357         // pushing the function with the function object as upvalue
2358         lua_pushcclosure(state, function, 1);
2359         return PushedObject{state, 1};
2360     }
2361 
2362     // this is the version of "push" for references to functions
pushLuaContext::Pusher2363     static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2364         -> PushedObject
2365     {
2366         return push(state, &fn);
2367     }
2368 
2369 private:
2370     // callback that calls the function object
2371     // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2372     template<typename TFunctionObject>
callbackLuaContext::Pusher2373     static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2374         -> PushedObject
2375     {
2376         // checking if number of parameters is correct
2377         if (argumentsCount < LocalFunctionArgumentsCounter::min) {
2378             // if not, using lua_error to return an error
2379             luaL_where(state, 1);
2380             lua_pushstring(state, "This function requires at least ");
2381             lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
2382             lua_pushstring(state, " parameter(s)");
2383             lua_concat(state, 4);
2384             luaError(state);
2385 
2386         } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
2387             // if not, using lua_error to return an error
2388             luaL_where(state, 1);
2389             lua_pushstring(state, "This function requires at most ");
2390             lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
2391             lua_pushstring(state, " parameter(s)");
2392             lua_concat(state, 4);
2393             luaError(state);
2394         }
2395 
2396         // calling the function
2397         try {
2398             return callback2(state, *toCall, argumentsCount);
2399 
2400         } catch (const WrongTypeException& ex) {
2401             // wrong parameter type, using lua_error to return an error
2402             luaL_where(state, 1);
2403             lua_pushstring(state, "Unable to convert parameter from ");
2404             lua_pushstring(state, ex.luaType.c_str());
2405             lua_pushstring(state, " to ");
2406             lua_pushstring(state, ex.destination.name());
2407             lua_concat(state, 5);
2408             luaError(state);
2409 
2410         } catch (const std::exception& e) {
2411           luaL_where(state, 1);
2412           lua_pushstring(state, "Caught exception: ");
2413           lua_pushstring(state, e.what());
2414           lua_concat(state, 3);
2415           luaError(state);
2416         } catch (...) {
2417             Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
2418             luaError(state);
2419         }
2420     }
2421 
2422     template<typename TFunctionObject>
callback2LuaContext::Pusher2423     static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2424         -> typename std::enable_if<!std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2425     {
2426         // pushing the result on the stack and returning number of pushed elements
2427         typedef Pusher<typename std::decay<TReturnType>::type>
2428             P;
2429         return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
2430     }
2431 
2432     template<typename TFunctionObject>
callback2LuaContext::Pusher2433     static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2434         -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2435     {
2436         readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
2437         return PushedObject{state, 0};
2438     }
2439 };
2440 
2441 // C function pointers
2442 template<typename TReturnType, typename... TParameters>
2443 struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2444 {
2445     // using the function-pushing implementation
2446     typedef Pusher<TReturnType (TParameters...)>
2447         SubPusher;
2448     static const int minSize = SubPusher::minSize;
2449     static const int maxSize = SubPusher::maxSize;
2450 
2451     template<typename TType>
pushLuaContext::Pusher2452     static PushedObject push(lua_State* state, TType value) noexcept {
2453         return SubPusher::push(state, value);
2454     }
2455 };
2456 
2457 // C function references
2458 template<typename TReturnType, typename... TParameters>
2459 struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2460 {
2461     // using the function-pushing implementation
2462     typedef Pusher<TReturnType(TParameters...)>
2463         SubPusher;
2464     static const int minSize = SubPusher::minSize;
2465     static const int maxSize = SubPusher::maxSize;
2466 
2467     template<typename TType>
pushLuaContext::Pusher2468     static PushedObject push(lua_State* state, TType value) noexcept {
2469         return SubPusher::push(state, value);
2470     }
2471 };
2472 
2473 // std::function
2474 template<typename TReturnType, typename... TParameters>
2475 struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2476 {
2477     // using the function-pushing implementation
2478     typedef Pusher<TReturnType (TParameters...)>
2479         SubPusher;
2480     static const int minSize = SubPusher::minSize;
2481     static const int maxSize = SubPusher::maxSize;
2482 
pushLuaContext::Pusher2483     static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2484         return SubPusher::push(state, value);
2485     }
2486 };
2487 
2488 // boost::variant
2489 template<typename... TTypes>
2490 struct LuaContext::Pusher<boost::variant<TTypes...>>
2491 {
2492     static const int minSize = PusherMinSize<TTypes...>::size;
2493     static const int maxSize = PusherMaxSize<TTypes...>::size;
2494 
pushLuaContext::Pusher2495     static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
2496         PushedObject obj{state, 0};
2497         VariantWriter writer{state, obj};
2498         value.apply_visitor(writer);
2499         return obj;
2500     }
2501 
2502 private:
2503     struct VariantWriter : public boost::static_visitor<> {
2504         template<typename TType>
operator ()LuaContext::Pusher::VariantWriter2505         void operator()(TType value) noexcept
2506         {
2507             obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
2508         }
2509 
VariantWriterLuaContext::Pusher::VariantWriter2510         VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {}
2511         lua_State* state;
2512         PushedObject& obj;
2513     };
2514 };
2515 
2516 // boost::optional
2517 template<typename TType>
2518 struct LuaContext::Pusher<boost::optional<TType>> {
2519     typedef Pusher<typename std::decay<TType>::type>
2520         UnderlyingPusher;
2521 
2522     static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2523     static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2524 
pushLuaContext::Pusher2525     static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
2526         if (value) {
2527             return UnderlyingPusher::push(state, value.get());
2528         } else {
2529             lua_pushnil(state);
2530             return PushedObject{state, 1};
2531         }
2532     }
2533 };
2534 
2535 // tuple
2536 template<typename... TTypes>
2537 struct LuaContext::Pusher<std::tuple<TTypes...>> {
2538     // TODO: NOT EXCEPTION SAFE /!\ //
2539     static const int minSize = PusherTotalMinSize<TTypes...>::size;
2540     static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2541 
pushLuaContext::Pusher2542     static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2543         return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2544     }
2545 
pushLuaContext::Pusher2546     static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
2547         return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
2548     }
2549 
2550 private:
2551     template<int N>
push2LuaContext::Pusher2552     static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2553         typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2554 
2555         return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2556             push2(state, value, std::integral_constant<int,N+1>{});
2557     }
2558 
2559     template<int N>
push2LuaContext::Pusher2560     static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
2561         typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2562 
2563         return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
2564             push2(state, std::move(value), std::integral_constant<int,N+1>{});
2565     }
2566 
push2LuaContext::Pusher2567     static int push2(lua_State* /*state*/, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2568         return 0;
2569     }
2570 
push2LuaContext::Pusher2571     static int push2(lua_State* /*state*/, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2572         return 0;
2573     }
2574 };
2575 
2576 /**************************************************/
2577 /*                READ FUNCTIONS                  */
2578 /**************************************************/
2579 // specializations of the Reader structures
2580 
2581 // opaque Lua references
2582 template<>
2583 struct LuaContext::Reader<LuaContext::LuaObject>
2584 {
readLuaContext::Reader2585     static auto read(lua_State* state, int index)
2586         -> boost::optional<LuaContext::LuaObject>
2587     {
2588         LuaContext::LuaObject obj(state, index);
2589         return obj;
2590     }
2591 };
2592 
2593 // reading null
2594 template<>
2595 struct LuaContext::Reader<std::nullptr_t>
2596 {
readLuaContext::Reader2597     static auto read(lua_State* state, int index)
2598         -> boost::optional<std::nullptr_t>
2599     {
2600         if (!lua_isnil(state, index))
2601             return boost::none;
2602         return nullptr;
2603     }
2604 };
2605 
2606 // integrals
2607 template<typename TType>
2608 struct LuaContext::Reader<
2609             TType,
2610             typename std::enable_if<std::is_integral<TType>::value>::type
2611         >
2612 {
readLuaContext::Reader2613     static auto read(lua_State* state, int index)
2614         -> boost::optional<TType>
2615     {
2616 #       if LUA_VERSION_NUM >= 502
2617 
2618             int success;
2619             auto value = lua_tointegerx(state, index, &success);
2620             if (success == 0)
2621                 return boost::none;
2622             return static_cast<TType>(value);
2623 
2624 #       else
2625 
2626             if (!lua_isnumber(state, index))
2627                 return boost::none;
2628             return static_cast<TType>(lua_tointeger(state, index));
2629 
2630 #       endif
2631     }
2632 };
2633 
2634 // floating points
2635 template<typename TType>
2636 struct LuaContext::Reader<
2637             TType,
2638             typename std::enable_if<std::is_floating_point<TType>::value>::type
2639         >
2640 {
readLuaContext::Reader2641     static auto read(lua_State* state, int index)
2642         -> boost::optional<TType>
2643     {
2644 #       if LUA_VERSION_NUM >= 502
2645 
2646             int success;
2647             auto value = lua_tonumberx(state, index, &success);
2648             if (success == 0)
2649                 return boost::none;
2650             return static_cast<TType>(value);
2651 
2652 #       else
2653 
2654             if (!lua_isnumber(state, index))
2655                 return boost::none;
2656             return static_cast<TType>(lua_tonumber(state, index));
2657 
2658 #       endif
2659     }
2660 };
2661 
2662 // boolean
2663 template<>
2664 struct LuaContext::Reader<bool>
2665 {
readLuaContext::Reader2666     static auto read(lua_State* state, int index)
2667         -> boost::optional<bool>
2668     {
2669         if (!lua_isboolean(state, index))
2670             return boost::none;
2671         return lua_toboolean(state, index) != 0;
2672     }
2673 };
2674 
2675 // string
2676 // lua_tostring returns a temporary pointer, but that's not a problem since we copy
2677 //   the data into a std::string
2678 template<>
2679 struct LuaContext::Reader<std::string>
2680 {
readLuaContext::Reader2681     static auto read(lua_State* state, int index)
2682         -> boost::optional<std::string>
2683     {
2684         std::string result;
2685 
2686         // lua_tolstring might convert the variable that would confuse lua_next, so we
2687         //   make a copy of the variable.
2688         lua_pushvalue(state, index);
2689 
2690         size_t len;
2691         const auto val = lua_tolstring(state, -1, &len);
2692 
2693         if (val != nullptr)
2694           result.assign(val, len);
2695 
2696         lua_pop(state, 1);
2697 
2698         return val != nullptr ? boost::optional<std::string>{ std::move(result) } : boost::none;
2699     }
2700 };
2701 
2702 // enums
2703 template<typename TType>
2704 struct LuaContext::Reader<
2705             TType,
2706             typename std::enable_if<std::is_enum<TType>::value>::type
2707         >
2708 {
readLuaContext::Reader2709     static auto read(lua_State* state, int index)
2710         -> boost::optional<TType>
2711     {
2712         if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
2713             return boost::none;
2714         return static_cast<TType>(lua_tointeger(state, index));
2715     }
2716 };
2717 
2718 // LuaFunctionCaller
2719 template<typename TRetValue, typename... TParameters>
2720 struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2721 {
2722     typedef LuaFunctionCaller<TRetValue (TParameters...)>
2723         ReturnType;
2724 
readLuaContext::Reader2725     static auto read(lua_State* state, int index)
2726         -> boost::optional<ReturnType>
2727     {
2728         if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
2729             return boost::none;
2730         return ReturnType(state, index);
2731     }
2732 };
2733 
2734 // function
2735 template<typename TRetValue, typename... TParameters>
2736 struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2737 {
readLuaContext::Reader2738     static auto read(lua_State* state, int index)
2739         -> boost::optional<std::function<TRetValue (TParameters...)>>
2740     {
2741 		if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
2742 		{
2743 			std::function<TRetValue (TParameters...)> f{*val};
2744 			return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
2745 		}
2746 
2747         return boost::none;
2748     }
2749 };
2750 
2751 // vector of pairs
2752 template<typename TType1, typename TType2>
2753 struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2754 {
readLuaContext::Reader2755     static auto read(lua_State* state, int index)
2756         -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2757     {
2758         if (!lua_istable(state, index))
2759             return boost::none;
2760 
2761         std::vector<std::pair<TType1, TType2>> result;
2762 
2763         // we traverse the table at the top of the stack
2764         lua_pushnil(state);     // first key
2765         while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2766             // now a key and its value are pushed on the stack
2767             try {
2768                 auto val1 = Reader<TType1>::read(state, -2);
2769                 auto val2 = Reader<TType2>::read(state, -1);
2770 
2771                 if (!val1.is_initialized() || !val2.is_initialized()) {
2772                     lua_pop(state, 2);      // we remove the value and the key
2773                     return {};
2774                 }
2775 
2776                 result.push_back({ val1.get(), val2.get() });
2777                 lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2778 
2779             } catch(...) {
2780                 lua_pop(state, 2);      // we remove the value and the key
2781                 return {};
2782             }
2783         }
2784 
2785         return { std::move(result) };
2786     }
2787 };
2788 
2789 // map
2790 template<typename TKey, typename TValue>
2791 struct LuaContext::Reader<std::map<TKey,TValue>>
2792 {
readLuaContext::Reader2793     static auto read(lua_State* state, int index)
2794         -> boost::optional<std::map<TKey,TValue>>
2795     {
2796         if (!lua_istable(state, index))
2797             return boost::none;
2798 
2799         std::map<TKey,TValue> result;
2800 
2801         // we traverse the table at the top of the stack
2802         lua_pushnil(state);     // first key
2803         while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2804             // now a key and its value are pushed on the stack
2805             try {
2806                 auto key = Reader<TKey>::read(state, -2);
2807                 auto value = Reader<TValue>::read(state, -1);
2808 
2809                 if (!key.is_initialized() || !value.is_initialized()) {
2810                     lua_pop(state, 2);      // we remove the value and the key
2811                     return {};
2812                 }
2813 
2814                 result.insert({ key.get(), value.get() });
2815                 lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2816 
2817             } catch(...) {
2818                 lua_pop(state, 2);      // we remove the value and the key
2819                 return {};
2820             }
2821         }
2822 
2823         return { std::move(result) };
2824     }
2825 };
2826 
2827 // unordered_map
2828 template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2829 struct LuaContext::Reader<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2830 {
readLuaContext::Reader2831     static auto read(lua_State* state, int index)
2832         -> boost::optional<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2833     {
2834         if (!lua_istable(state, index))
2835             return boost::none;
2836 
2837         std::unordered_map<TKey,TValue,THash,TKeyEqual> result;
2838 
2839         // we traverse the table at the top of the stack
2840         lua_pushnil(state);     // first key
2841         while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2842             // now a key and its value are pushed on the stack
2843             try {
2844                 auto key = Reader<TKey>::read(state, -2);
2845                 auto value = Reader<TValue>::read(state, -1);
2846 
2847                 if (!key.is_initialized() || !value.is_initialized()) {
2848                     lua_pop(state, 2);      // we remove the value and the key
2849                     return {};
2850                 }
2851 
2852                 result.insert({ key.get(), value.get() });
2853                 lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2854 
2855             } catch(...) {
2856                 lua_pop(state, 2);      // we remove the value and the key
2857                 return {};
2858             }
2859         }
2860 
2861         return { std::move(result) };
2862     }
2863 };
2864 
2865 // optional
2866 // IMPORTANT: optional means "either nil or the value of the right type"
2867 //  * if the value is nil, then an optional containing an empty optional is returned
2868 //  * if the value is of the right type, then an optional containing an optional containing the value is returned
2869 //  * if the value is of the wrong type, then an empty optional is returned
2870 template<typename TType>
2871 struct LuaContext::Reader<boost::optional<TType>>
2872 {
readLuaContext::Reader2873     static auto read(lua_State* state, int index)
2874         -> boost::optional<boost::optional<TType>>
2875     {
2876         if (lua_isnil(state, index))
2877             return boost::optional<TType>{boost::none};
2878         if (auto&& other = Reader<TType>::read(state, index))
2879             return std::move(other);
2880         return boost::none;
2881     }
2882 };
2883 
2884 // variant
2885 template<typename... TTypes>
2886 struct LuaContext::Reader<boost::variant<TTypes...>>
2887 {
2888 	typedef boost::variant<TTypes...>
2889 		ReturnType;
2890 
2891 private:
2892     // class doing operations for a range of types from TIterBegin to TIterEnd
2893     template<typename TIterBegin, typename TIterEnd, typename = void>
2894     struct VariantReader
2895     {
2896         using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2897 
readLuaContext::Reader::VariantReader2898         static auto read(lua_State* state, int index)
2899             -> boost::optional<ReturnType>
2900         {
2901             // note: using SubReader::read triggers a compilation error when used with a reference
2902             if (const auto val = SubReader::read(state, index))
2903                 return boost::variant<TTypes...>{*val};
2904             return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
2905         }
2906     };
2907 
2908     // specialization of class above being called when list of remaining types is empty
2909     template<typename TIterBegin, typename TIterEnd>
2910     struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2911     {
readLuaContext::Reader::VariantReader2912         static auto read(lua_State* /*state*/, int /*index*/)
2913             -> boost::optional<ReturnType>
2914         {
2915             return boost::none;
2916         }
2917     };
2918 
2919     // this is the main type
2920     typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2921         MainVariantReader;
2922 
2923 public:
readLuaContext::Reader2924     static auto read(lua_State* state, int index)
2925         -> boost::optional<ReturnType>
2926     {
2927         return MainVariantReader::read(state, index);
2928     }
2929 };
2930 
2931 // reading a tuple
2932 // tuple have an additional argument for their functions, that is the maximum size to read
2933 // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2934 template<>
2935 struct LuaContext::Reader<std::tuple<>>
2936 {
2937     static auto read(lua_State* /*state*/, int /*index*/, int /*maxSize*/ = 0)
2938         -> boost::optional<std::tuple<>>
2939     {
2940         return std::tuple<>{};
2941     }
2942 };
2943 
2944 template<typename TFirst, typename... TOthers>
2945 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2946         typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type       // TODO: replace by std::is_default_constructible when it works on every compiler
2947     >
2948 {
2949     // this is the "TFirst is NOT default constructible" version
2950 
2951 	typedef std::tuple<TFirst, TOthers...>
2952 		ReturnType;
2953 
readLuaContext::Reader2954     static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2955         -> boost::optional<ReturnType>
2956     {
2957         if (maxSize <= 0)
2958             return boost::none;
2959 
2960         auto firstVal = Reader<TFirst>::read(state, index);
2961         auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2962 
2963         if (!firstVal || !othersVal)
2964             return boost::none;
2965 
2966         return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2967     }
2968 };
2969 
2970 template<typename TFirst, typename... TOthers>
2971 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2972         typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type        // TODO: replace by std::is_default_constructible when it works on every compiler
2973     >
2974 {
2975     // this is the "TFirst is default-constructible" version
2976 
2977 	typedef std::tuple<TFirst, TOthers...>
2978 		ReturnType;
2979 
readLuaContext::Reader2980     static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2981         -> boost::optional<ReturnType>
2982     {
2983         auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2984         if (!othersVal)
2985             return boost::none;
2986 
2987         if (maxSize <= 0)
2988             return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2989 
2990         auto firstVal = Reader<TFirst>::read(state, index);
2991         if (!firstVal)
2992             return boost::none;
2993 
2994         return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2995     }
2996 };
2997 
2998 #if defined(__GNUC__) && !defined(__clang__)
2999 #pragma GCC diagnostic pop
3000 #endif
3001 
3002 #endif
3003