2 Copyright (c) 2013, Pierre KRIEGER
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.
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.
28 #ifndef INCLUDE_LUACONTEXT_HPP
29 #define INCLUDE_LUACONTEXT_HPP
47 #include <type_traits>
48 #include <unordered_map>
49 #include <boost/any.hpp>
50 #include <boost/mpl/distance.hpp>
51 #include <boost/mpl/transform.hpp>
52 #include <boost/optional.hpp>
53 #include <boost/variant.hpp>
54 #include <boost/type_traits.hpp>
58 # include "misc/exception.hpp"
62 # define ATTR_UNUSED __attribute__((unused))
68 * Defines a Lua context
69 * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions),
70 * we only provide few functions like readVariable and writeVariable.
72 * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert
73 * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types
74 * it wants. These arguments may only be of basic types (int, float, etc.) or std::string.
77 struct ValueInRegistry;
78 template<typename TFunctionObject, typename TFirstParamType> struct Binder;
79 template<typename T> struct IsOptional;
80 enum Globals_t { Globals }; // tag for "global variables"
83 * @param openDefaultLibs True if luaL_openlibs should be called
85 explicit LuaContext(bool openDefaultLibs = true)
87 // luaL_newstate can return null if allocation failed
88 mState = luaL_newstate();
89 if (mState == nullptr)
90 throw std::bad_alloc();
92 // setting the panic function
93 lua_atpanic(mState, [](lua_State* state) -> int {
94 const std::string str = lua_tostring(state, -1);
96 assert(false && "lua_atpanic triggered");
100 // opening default library if required to do so
102 luaL_openlibs(mState);
108 LuaContext(LuaContext&& s) :
111 s.mState = luaL_newstate();
117 LuaContext& operator=(LuaContext&& s) noexcept
119 std::swap(mState, s.mState);
126 LuaContext(const LuaContext&) = delete;
131 LuaContext& operator=(const LuaContext&) = delete;
136 ~LuaContext() noexcept
143 * Thrown when an error happens during execution of lua code (like not enough parameters for a function)
145 class ExecutionErrorException : public std::runtime_error
148 ExecutionErrorException(const std::string& msg) :
149 std::runtime_error(msg)
155 * Thrown when a syntax error happens in a lua script
157 class SyntaxErrorException : public std::runtime_error
160 SyntaxErrorException(const std::string& msg) :
161 std::runtime_error(msg)
167 * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string
169 class WrongTypeException : public std::runtime_error
172 WrongTypeException(std::string luaType_, const std::type_info& destination_) :
173 std::runtime_error("Trying to cast a lua variable from \"" + luaType_ + "\" to \"" + destination_.name() + "\""),
175 destination(destination_)
180 const std::type_info& destination;
184 * Function object that can call a function stored by Lua
185 * This type is copiable and movable, but not constructible. It can only be created through readVariable.
186 * @tparam TFunctionType Function type (eg. "int (int, bool)")
188 template<typename TFunctionType>
189 class LuaFunctionCaller;
192 * Opaque type that identifies a Lua object
195 LuaObject() = default;
196 LuaObject(lua_State* state, int index=-1) {
197 this->objectInRegistry = std::make_shared<LuaContext::ValueInRegistry>(state, index);
199 std::shared_ptr<LuaContext::ValueInRegistry> objectInRegistry;
203 * Opaque type that identifies a Lua thread
206 ThreadID() = default;
207 ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { }
208 ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; }
212 std::unique_ptr<ValueInRegistry> threadInRegistry;
216 * Type that is considered as an empty array
218 enum EmptyArray_t { EmptyArray };
221 * Type for a metatable
223 enum Metatable_t { Metatable };
226 * Executes lua code from the stream
227 * @param code A stream that Lua will read its code from
229 void executeCode(std::istream& code)
231 auto toCall = load(mState, code);
232 call<std::tuple<>>(mState, std::move(toCall));
236 * Executes lua code from the stream and returns a value
237 * @param code A stream that Lua will read its code from
238 * @tparam TType The type that the executing code should return
240 template<typename TType>
241 auto executeCode(std::istream& code)
244 auto toCall = load(mState, code);
245 return call<TType>(mState, std::move(toCall));
249 * Executes lua code given as parameter
250 * @param code A string containing code that will be executed by Lua
252 void executeCode(const std::string& code)
254 executeCode(code.c_str());
258 * Executes Lua code from the stream and returns a value
259 * @param code A string containing code that will be executed by Lua
260 * @tparam TType The type that the executing code should return
262 template<typename TType>
263 auto executeCode(const std::string& code)
266 return executeCode<TType>(code.c_str());
271 * @param code A string containing code that will be executed by Lua
273 void executeCode(const char* code)
275 auto toCall = load(mState, code);
276 call<std::tuple<>>(mState, std::move(toCall));
280 * Executes Lua code from the stream and returns a value
281 * @param code A string containing code that will be executed by Lua
282 * @tparam TType The type that the executing code should return
284 template<typename TType>
285 auto executeCode(const char* code)
288 auto toCall = load(mState, code);
289 return call<TType>(mState, std::move(toCall));
293 * Executes lua code from the stream
294 * @param code A stream that Lua will read its code from
296 void executeCode(const ThreadID& thread, std::istream& code)
298 auto toCall = load(thread.state, code);
299 call<std::tuple<>>(thread.state, std::move(toCall));
303 * Executes lua code from the stream and returns a value
304 * @param code A stream that Lua will read its code from
305 * @tparam TType The type that the executing code should return
307 template<typename TType>
308 auto executeCode(const ThreadID& thread, std::istream& code)
311 auto toCall = load(thread.state, code);
312 return call<TType>(thread.state, std::move(toCall));
316 * Executes lua code given as parameter
317 * @param code A string containing code that will be executed by Lua
319 void executeCode(const ThreadID& thread, const std::string& code)
321 executeCode(thread, code.c_str());
325 * Executes Lua code from the stream and returns a value
326 * @param code A string containing code that will be executed by Lua
327 * @tparam TType The type that the executing code should return
329 template<typename TType>
330 auto executeCode(const ThreadID& thread, const std::string& code)
333 return executeCode<TType>(thread, code.c_str());
338 * @param code A string containing code that will be executed by Lua
340 void executeCode(const ThreadID& thread, const char* code)
342 auto toCall = load(thread.state, code);
343 call<std::tuple<>>(thread.state, std::move(toCall));
347 * Executes Lua code from the stream and returns a value
348 * @param code A string containing code that will be executed by Lua
349 * @tparam TType The type that the executing code should return
351 template<typename TType>
352 auto executeCode(const ThreadID& thread, const char* code)
355 auto toCall = load(thread.state, code);
356 return call<TType>(thread.state, std::move(toCall));
360 * Tells that Lua will be allowed to access an object's function
361 * This is the version "registerFunction(name, &Foo::function)"
363 template<typename TPointerToMemberFunction>
364 auto registerFunction(const std::string& name, TPointerToMemberFunction pointer)
365 -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
367 registerFunctionImpl(name, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
371 * Tells that Lua will be allowed to access an object's function
372 * This is the version with an explicit template parameter: "registerFunction<void (Foo::*)()>(name, [](Foo&) { })"
373 * @param fn Function object which takes as first parameter a reference to the object
374 * @tparam TFunctionType Pointer-to-member function type
376 template<typename TFunctionType, typename TType>
377 void registerFunction(const std::string& functionName, TType fn)
379 static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
380 registerFunctionImpl(functionName, std::move(fn), tag<TFunctionType>{});
384 * Tells that Lua will be allowed to access an object's function
385 * This is the alternative version with an explicit template parameter: "registerFunction<Foo, void (*)()>(name, [](Foo&) { })"
386 * @param fn Function object which takes as first parameter a reference to the object
387 * @tparam TObject Object to register this function to
388 * @tparam TFunctionType Function type
390 template<typename TObject, typename TFunctionType, typename TType>
391 void registerFunction(const std::string& functionName, TType fn)
393 static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
394 registerFunctionImpl(functionName, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
398 * Inverse operation of registerFunction
399 * @tparam TType Type whose function belongs to
401 template<typename TType>
402 void unregisterFunction(const std::string& functionName)
404 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType)));
406 lua_settable(mState, LUA_REGISTRYINDEX);
407 checkTypeRegistration(mState, &typeid(TType));
409 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType*)));
411 lua_settable(mState, LUA_REGISTRYINDEX);
412 checkTypeRegistration(mState, &typeid(TType*));
414 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(std::shared_ptr<TType>)));
416 lua_settable(mState, LUA_REGISTRYINDEX);
417 checkTypeRegistration(mState, &typeid(std::shared_ptr<TType>));
421 * Registers a member variable
422 * This is the version "registerMember(name, &Foo::member)"
424 template<typename TObject, typename TVarType>
425 void registerMember(const std::string& name, TVarType TObject::*member)
427 // implementation simply calls the custom member with getter and setter
428 const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; };
429 const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; };
430 registerMember<TVarType (TObject::*)>(name, getter, setter);
434 * Registers a member variable
435 * This is the version "registerMember<Foo, int>(name, getter, setter)"
436 * @tparam TObject Type to register the member to
437 * @tparam TVarType Type of the member
438 * @param name Name of the member to register
439 * @param readFunction Function of type "TVarType (const TObject&)"
440 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
442 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
443 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
445 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
449 * Registers a member variable
450 * This is the version "registerMember<int (Foo::*)>(name, getter, setter)"
451 * @tparam TMemberType Pointer to member object representing the type
452 * @param name Name of the member to register
453 * @param readFunction Function of type "TVarType (const TObject&)"
454 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
456 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
457 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
459 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
460 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction_));
464 * Registers a non-modifiable member variable
465 * This is the version "registerMember<Foo, int>(name, getter)"
466 * @tparam TObject Type to register the member to
467 * @tparam TVarType Type of the member
468 * @param name Name of the member to register
469 * @param readFunction Function of type "TVarType (const TObject&)"
471 template<typename TObject, typename TVarType, typename TReadFunction>
472 void registerMember(const std::string& name, TReadFunction readFunction)
474 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction));
478 * Registers a non-modifiable member variable
479 * This is the version "registerMember<int (Foo::*)>(name, getter)"
480 * @tparam TMemberType Pointer to member object representing the type
481 * @param name Name of the member to register
482 * @param readFunction Function of type "TVarType (const TObject&)"
484 template<typename TMemberType, typename TReadFunction>
485 void registerMember(const std::string& name, TReadFunction readFunction)
487 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
488 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction));
492 * Registers a dynamic member variable
493 * This is the version "registerMember<Foo, int>(getter, setter)"
494 * @tparam TObject Type to register the member to
495 * @tparam TVarType Type of the member
496 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
497 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
499 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
500 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
502 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
506 * Registers a dynamic member variable
507 * This is the version "registerMember<int (Foo::*)>(getter, setter)"
508 * @tparam TMemberType Pointer to member object representing the type
509 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
510 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
512 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
513 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
515 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
516 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction), std::move(writeFunction_));
520 * Registers a dynamic non-modifiable member variable
521 * This is the version "registerMember<Foo, int>(getter)"
522 * @tparam TObject Type to register the member to
523 * @tparam TVarType Type of the member
524 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
526 template<typename TObject, typename TVarType, typename TReadFunction>
527 void registerMember(TReadFunction readFunction)
529 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
533 * Registers a dynamic non-modifiable member variable
534 * This is the version "registerMember<int (Foo::*)>(getter)"
535 * @tparam TMemberType Pointer to member object representing the type
536 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
538 template<typename TMemberType, typename TReadFunction>
539 void registerMember(TReadFunction readFunction)
541 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
542 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction));
546 * Creates a new thread
547 * A Lua thread is not really a thread, but rather an "execution stack".
548 * You can destroy the thread by calling destroyThread
556 result.state = lua_newthread(mState);
557 result.threadInRegistry = std::unique_ptr<ValueInRegistry>(new ValueInRegistry(mState));
560 return std::move(result);
564 * Destroys a thread created with createThread
567 void destroyThread(ThreadID& id)
569 id.threadInRegistry.reset();
573 * Reads the content of a Lua variable
575 * @tparam TType Type requested for the read
576 * @throw WrongTypeException When the variable is not convertible to the requested type
579 * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers
580 * Additionally supported:
581 * - LuaFunctionCaller<FunctionType>, which is an alternative to std::function
582 * - references to custom objects, in which case it will return the object in-place
584 * After the variable name, you can add other parameters.
585 * If the variable is an array, it will instead get the element of that array whose offset is the second parameter.
586 * Same applies for third, fourth, etc. parameters.
588 template<typename TType, typename... TTypes>
589 TType readVariable(const std::string& name, TTypes&&... elements) const
591 lua_getglobal(mState, name.c_str());
592 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
593 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
599 template<typename TType, typename... TTypes>
600 TType readVariable(const char* name, TTypes&&... elements) const
602 lua_getglobal(mState, name);
603 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
604 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
610 template<typename TType, typename... TTypes>
611 TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const
613 lua_getglobal(thread.state, name.c_str());
614 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
615 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
621 template<typename TType, typename... TTypes>
622 TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const
624 lua_getglobal(thread.state, name);
625 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
626 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
630 * Changes the content of a Lua variable
632 * Accepted values are:
633 * - all base types (char, short, int, float, double, bool)
637 * - std::vector<std::pair<>>, std::map<> and std::unordered_map<> (the key and value must also be accepted values)
638 * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple)
639 * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>)
640 * - nullptr (writes nil)
643 * All objects are passed by copy and destroyed by the garbage collector if necessary.
645 template<typename... TData>
646 void writeVariable(TData&&... data) noexcept {
647 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable");
648 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
650 static_assert(!std::is_same<typename Tupleizer<RealDataType>::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
652 setTable<RealDataType>(mState, Globals, std::forward<TData>(data)...);
656 * Equivalent to writeVariable(varName, ..., std::function<TFunctionType>(data));
657 * This version is more efficient than writeVariable if you want to write functions
659 template<typename TFunctionType, typename... TData>
660 void writeFunction(TData&&... data) noexcept {
661 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
663 setTable<TFunctionType>(mState, Globals, std::forward<TData>(data)...);
667 * Same as the other writeFunction, except that the template parameter is automatically detected
668 * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas)
670 template<typename... TData>
671 void writeFunction(TData&&... data) noexcept {
672 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
673 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
675 typedef typename FunctionTypeDetector<RealDataType>::type
676 DetectedFunctionType;
678 return writeFunction<DetectedFunctionType>(std::forward<TData>(data)...);
683 // the state is the most important variable in the class since it is our interface with Lua
684 // - registered members and functions are stored in tables at offset &typeid(type) of the registry
685 // each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2
686 // offset 3 is unused, setter members at offset 4, default setter at offset 5
690 /**************************************************/
692 /**************************************************/
693 struct PushedObject {
694 PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {}
695 ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); }
697 PushedObject& operator=(const PushedObject&) = delete;
698 PushedObject(const PushedObject&) = delete;
699 PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; }
700 PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; }
702 PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return std::move(obj); }
703 void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; }
705 auto getState() const -> lua_State* { return state; }
706 auto getNum() const -> int { return num; }
708 int release() { const auto n = num; num = 0; return n; }
709 void pop() { if (num >= 1) lua_pop(state, num); num = 0; }
710 void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; }
718 /**************************************************/
720 /**************************************************/
721 // type used as a tag
725 // tag for "the registry"
726 enum RegistryTag { Registry };
728 // this function takes a value representing the offset to look into
729 // it will look into the top element of the stack and replace the element by its content at the given index
730 template<typename OffsetType1, typename... OffsetTypeOthers>
731 static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) {
732 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");
733 auto p1 = Pusher<typename std::decay<OffsetType1>::type>::push(state, offset1);
734 lua_gettable(state, -2);
735 lua_remove(state, -2);
738 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
741 template<typename... OffsetTypeOthers>
742 static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) {
743 lua_getmetatable(state, -1);
744 lua_remove(state, -2);
746 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
749 static void lookIntoStackTop(lua_State*) {
752 // 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
753 // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array
754 // the dataPusher MUST push only one thing on the stack
755 // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack
756 template<typename TDataType, typename TIndex, typename TData>
757 static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept
759 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");
760 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");
762 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
763 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
765 lua_settable(state, -3);
770 template<typename TDataType, typename TData>
771 static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept
773 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");
775 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
776 lua_setfield(state, -2, index.c_str());
780 template<typename TDataType, typename TData>
781 static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept
783 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");
785 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
786 lua_setfield(state, -2, index);
790 template<typename TDataType, typename TData>
791 static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept
793 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");
795 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
796 lua_setmetatable(state, -2);
800 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
801 static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
802 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
804 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");
806 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
807 lua_gettable(state, -2);
809 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
812 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
813 static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
814 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
816 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");
818 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + std::move(pushedTable);
819 lua_gettable(state, -2);
821 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
824 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
825 static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
827 if (lua_getmetatable(state, -1) == 0)
830 PushedObject p1{state, 1};
832 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
834 lua_setmetatable(state, -2);
839 setTable<TDataType>(state, pushedObject, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
843 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
844 static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
846 if (lua_getmetatable(state, -1) == 0)
849 PushedObject p1{state, 1};
851 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
853 lua_setmetatable(state, -2);
858 setTable<TDataType>(state, std::move(pushedObject), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
862 template<typename TDataType, typename TIndex, typename TData>
863 static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept
865 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");
866 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");
868 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
869 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
871 lua_settable(state, LUA_REGISTRYINDEX);
876 template<typename TDataType, typename TData>
877 static void setTable(lua_State* state, RegistryTag, const std::string& index, TData&& data) noexcept
879 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");
881 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
882 lua_setfield(state, LUA_REGISTRYINDEX, index.c_str());
886 template<typename TDataType, typename TData>
887 static void setTable(lua_State* state, RegistryTag, const char* index, TData&& data) noexcept
889 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");
891 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
892 lua_setfield(state, LUA_REGISTRYINDEX, index);
896 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
897 static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
899 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");
901 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
902 lua_gettable(state, LUA_REGISTRYINDEX);
904 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
907 template<typename TDataType, typename TIndex, typename TData>
908 static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept
910 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");
911 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");
914 # if LUA_VERSION_NUM >= 502
916 lua_pushglobaltable(state);
917 PushedObject p3{state, 1};
918 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
919 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
920 lua_settable(state, -3);
924 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
925 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
926 lua_settable(state, LUA_GLOBALSINDEX);
934 template<typename TDataType, typename TData>
935 static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept
937 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");
939 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
940 lua_setglobal(state, index.c_str());
944 template<typename TDataType, typename TData>
945 static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept
947 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");
949 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
950 lua_setglobal(state, index);
954 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
955 static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
957 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");
959 # if LUA_VERSION_NUM >= 502
961 lua_pushglobaltable(state);
962 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + PushedObject{state, 1};
963 lua_gettable(state, -2);
967 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
968 lua_gettable(state, LUA_GLOBALSINDEX);
972 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
975 // TODO: g++ reports "ambiguous overload"
976 /*template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
977 static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
979 lua_getglobal(state, index);
980 PushedObject p1{state, 1};
982 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
985 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
986 static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
988 lua_getglobal(state, index.c_str());
989 PushedObject p1{state, 1};
991 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
994 // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value
995 // warning: first parameter is the number of parameters, not the parameter index
996 // if read generates an exception, stack is poped anyway
997 template<typename TReturnType>
998 static auto readTopAndPop(lua_State* state, PushedObject object)
1001 auto val = Reader<typename std::decay<TReturnType>::type>::read(state, -object.getNum());
1002 if (!val.is_initialized())
1003 throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)};
1007 // checks that the offsets for a type's registrations are set in the registry
1008 static void checkTypeRegistration(lua_State* state, const std::type_info* type)
1010 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1011 lua_gettable(state, LUA_REGISTRYINDEX);
1012 if (!lua_isnil(state, -1)) {
1018 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1019 lua_newtable(state);
1021 lua_pushinteger(state, 0);
1022 lua_newtable(state);
1023 lua_settable(state, -3);
1025 lua_pushinteger(state, 1);
1026 lua_newtable(state);
1027 lua_settable(state, -3);
1029 lua_pushinteger(state, 3);
1030 lua_newtable(state);
1031 lua_settable(state, -3);
1033 lua_pushinteger(state, 4);
1034 lua_newtable(state);
1035 lua_settable(state, -3);
1037 lua_settable(state, LUA_REGISTRYINDEX);
1042 __declspec(noreturn)
1046 static void luaError(lua_State* state)
1050 std::terminate(); // removes compilation warning
1054 /**************************************************/
1055 /* FUNCTIONS REGISTRATION */
1056 /**************************************************/
1057 // the "registerFunction" public functions call this one
1058 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1059 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TObject>, tag<TRetValue (TOtherParams...)>)
1061 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");
1063 checkTypeRegistration(mState, &typeid(TObject));
1064 setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, std::move(function));
1066 checkTypeRegistration(mState, &typeid(TObject*));
1067 setTable<TRetValue(TObject*, TOtherParams...)>(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1069 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1070 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)...); });
1073 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1074 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<const TObject>, tag<TRetValue (TOtherParams...)> fTypeTag)
1076 registerFunctionImpl(functionName, function, tag<TObject>{}, fTypeTag);
1078 checkTypeRegistration(mState, &typeid(TObject const*));
1079 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)...); });
1081 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1082 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)...); });
1085 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1086 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...)>)
1088 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1091 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1092 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const>)
1094 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1097 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1098 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) volatile>)
1100 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1103 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1104 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const volatile>)
1106 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1109 // the "registerMember" public functions call this one
1110 template<typename TObject, typename TVarType, typename TReadFunction>
1111 void registerMemberImpl(const std::string& name, TReadFunction readFunction)
1113 static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value, "registerMember can only be called on a class or a pointer");
1115 checkTypeRegistration(mState, &typeid(TObject));
1116 setTable<TVarType (TObject&)>(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) {
1117 return readFunction(object);
1120 checkTypeRegistration(mState, &typeid(TObject*));
1121 setTable<TVarType (TObject*)>(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) {
1123 return readFunction(*object);
1126 checkTypeRegistration(mState, &typeid(TObject const*));
1127 setTable<TVarType (TObject const*)>(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) {
1129 return readFunction(*object);
1132 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1133 setTable<TVarType (std::shared_ptr<TObject>)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 1, name, [readFunction](const std::shared_ptr<TObject>& object) {
1135 return readFunction(*object);
1138 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1139 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) {
1141 return readFunction(*object);
1145 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1146 void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1148 registerMemberImpl<TObject,TVarType>(name, readFunction);
1150 setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) {
1151 writeFunction_(object, value);
1154 setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) {
1156 writeFunction_(*object, value);
1159 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) {
1161 writeFunction_(*object, value);
1165 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1166 void registerMemberImpl(tag<TVarType (TObject::*)>, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1168 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
1171 template<typename TObject, typename TVarType, typename TReadFunction>
1172 void registerMemberImpl(tag<TVarType(TObject::*)>, const std::string& name, TReadFunction readFunction)
1174 registerMemberImpl<TObject, TVarType>(name, std::move(readFunction));
1177 // the "registerMember" public functions call this one
1178 template<typename TObject, typename TVarType, typename TReadFunction>
1179 void registerMemberImpl(TReadFunction readFunction)
1181 checkTypeRegistration(mState, &typeid(TObject));
1182 setTable<TVarType (TObject const&, std::string)>(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) {
1183 return readFunction(object, name);
1186 checkTypeRegistration(mState, &typeid(TObject*));
1187 setTable<TVarType (TObject*, std::string)>(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) {
1189 return readFunction(*object, name);
1192 checkTypeRegistration(mState, &typeid(TObject const*));
1193 setTable<TVarType (TObject const*, std::string)>(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) {
1195 return readFunction(*object, name);
1198 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1199 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) {
1201 return readFunction(*object, name);
1204 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1205 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) {
1207 return readFunction(*object, name);
1211 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1212 void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction_)
1214 registerMemberImpl<TObject,TVarType>(readFunction);
1216 setTable<void (TObject&, std::string, TVarType)>(mState, Registry, &typeid(TObject), 5, [writeFunction_](TObject& object, const std::string& name, const TVarType& value) {
1217 writeFunction_(object, name, value);
1220 setTable<void (TObject*, std::string, TVarType)>(mState, Registry, &typeid(TObject*), 2, [writeFunction_](TObject* object, const std::string& name, const TVarType& value) {
1222 writeFunction_(*object, name, value);
1225 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) {
1227 writeFunction_(*object, name, value);
1231 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1232 void registerMemberImpl(tag<TVarType (TObject::*)>, TReadFunction readFunction, TWriteFunction writeFunction_)
1234 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
1237 template<typename TObject, typename TVarType, typename TReadFunction>
1238 void registerMemberImpl(tag<TVarType(TObject::*)>, TReadFunction readFunction)
1240 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
1244 /**************************************************/
1245 /* LOADING AND CALLING */
1246 /**************************************************/
1247 // this function loads data from the stream and pushes a function at the top of the stack
1248 // throws in case of syntax error
1249 static PushedObject load(lua_State* state, std::istream& code) {
1250 // since the lua_load function requires a static function, we use this structure
1251 // the Reader structure is at the same time an object storing an istream and a buffer,
1252 // and a static function provider
1254 Reader(std::istream& str) : stream(str) {}
1255 std::istream& stream;
1256 std::array<char,512> buffer;
1258 // read function ; "data" must be an instance of Reader
1259 static const char* read(lua_State* l, void* data, size_t* size) {
1260 assert(size != nullptr);
1261 assert(data != nullptr);
1262 Reader& me = *static_cast<Reader*>(data);
1263 if (me.stream.eof()) { *size = 0; return nullptr; }
1265 me.stream.read(me.buffer.data(), me.buffer.size());
1266 *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
1267 return me.buffer.data();
1271 // we create an instance of Reader, and we call lua_load
1272 Reader reader{code};
1273 const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk"
1274 # if LUA_VERSION_NUM >= 502
1279 // now we have to check return value
1280 if (loadReturnValue != 0) {
1281 // there was an error during loading, an error message was pushed on the stack
1282 const std::string errorMsg = lua_tostring(state, -1);
1284 if (loadReturnValue == LUA_ERRMEM)
1285 throw std::bad_alloc();
1286 else if (loadReturnValue == LUA_ERRSYNTAX)
1287 throw SyntaxErrorException{errorMsg};
1288 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1291 return PushedObject{state, 1};
1294 // this function loads data and pushes a function at the top of the stack
1295 // throws in case of syntax error
1296 static PushedObject load(lua_State* state, const char* code) {
1297 auto loadReturnValue = luaL_loadstring(state, code);
1299 // now we have to check return value
1300 if (loadReturnValue != 0) {
1301 // there was an error during loading, an error message was pushed on the stack
1302 const std::string errorMsg = lua_tostring(state, -1);
1304 if (loadReturnValue == LUA_ERRMEM)
1305 throw std::bad_alloc();
1306 else if (loadReturnValue == LUA_ERRSYNTAX)
1307 throw SyntaxErrorException{errorMsg};
1308 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1311 return PushedObject{state, 1};
1314 // this function calls what is on the top of the stack and removes it (just like lua_call)
1315 // if an exception is triggered, the top of the stack will be removed anyway
1316 template<typename TReturnType, typename... TParameters>
1317 static auto call(lua_State* state, PushedObject toCall, TParameters&&... input)
1320 typedef typename Tupleizer<TReturnType>::type
1323 // we push the parameters on the stack
1324 auto inArguments = Pusher<std::tuple<TParameters&&...>>::push(state, std::forward_as_tuple(std::forward<TParameters>(input)...));
1327 const int outArgumentsCount = std::tuple_size<RealReturnType>::value;
1328 auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount);
1330 // pcall succeeded, we pop the returned values and return them
1331 return readTopAndPop<TReturnType>(state, std::move(outArguments));
1334 // this function just calls lua_pcall and checks for errors
1335 static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments)
1337 // calling pcall automatically pops the parameters and pushes output
1338 const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, 0);
1339 functionAndArguments.release();
1341 // if pcall failed, analyzing the problem and throwing
1342 if (pcallReturnValue != 0) {
1343 PushedObject errorCode{state, 1};
1345 // an error occured during execution, either an error message or a std::exception_ptr was pushed on the stack
1346 if (pcallReturnValue == LUA_ERRMEM) {
1347 throw std::bad_alloc{};
1349 } else if (pcallReturnValue == LUA_ERRRUN) {
1350 if (lua_isstring(state, 1)) {
1351 // the error is a string
1352 const auto str = readTopAndPop<std::string>(state, std::move(errorCode));
1353 throw ExecutionErrorException{str};
1356 // an exception_ptr was pushed on the stack
1357 // rethrowing it with an additional ExecutionErrorException
1359 if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
1360 std::rethrow_exception(exp);
1363 std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua"});
1365 throw ExecutionErrorException{"Unknown Lua error"};
1370 return PushedObject{state, outArguments};
1374 /**************************************************/
1375 /* PUSH FUNCTIONS */
1376 /**************************************************/
1377 template<typename T>
1378 static PushedObject push(lua_State* state, T&& value)
1380 return Pusher<typename std::decay<T>::type>::push(state, std::forward<T>(value));
1383 // the Pusher structures allow you to push a value on the stack
1384 // - static const int minSize : minimum size on the stack that the value can have
1385 // - static const int maxSize : maximum size on the stack that the value can have
1386 // - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack
1388 // implementation for custom objects
1389 template<typename TType, typename = void>
1391 static const int minSize = 1;
1392 static const int maxSize = 1;
1394 template<typename TType2>
1395 static PushedObject push(lua_State* state, TType2&& value) noexcept {
1396 // this function is called when lua's garbage collector wants to destroy our object
1397 // we simply call its destructor
1398 const auto garbageCallbackFunction = [](lua_State* lua) -> int {
1399 assert(lua_gettop(lua) == 1);
1400 TType* ptr = static_cast<TType*>(lua_touserdata(lua, 1));
1406 // this function will be stored in __index in the metatable
1407 const auto indexFunction = [](lua_State* lua) -> int {
1409 assert(lua_gettop(lua) == 2);
1410 assert(lua_isuserdata(lua, 1));
1412 // searching for a handler
1413 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1414 lua_gettable(lua, LUA_REGISTRYINDEX);
1415 assert(!lua_isnil(lua, -1));
1417 // looking into getter functions
1418 lua_pushinteger(lua, 0);
1419 lua_gettable(lua, -2);
1420 lua_pushvalue(lua, 2);
1421 lua_gettable(lua, -2);
1422 if (!lua_isnil(lua, -1))
1426 // looking into getter members
1427 lua_pushinteger(lua, 1);
1428 lua_gettable(lua, -2);
1429 lua_pushvalue(lua, 2);
1430 lua_gettable(lua, -2);
1431 if (!lua_isnil(lua, -1)) {
1432 lua_pushvalue(lua, 1);
1433 return callRaw(lua, PushedObject{lua, 2}, 1).release();
1437 // looking into default getter
1438 lua_pushinteger(lua, 2);
1439 lua_gettable(lua, -2);
1440 if (lua_isnil(lua, -1))
1442 lua_pushvalue(lua, 1);
1443 lua_pushvalue(lua, 2);
1444 return callRaw(lua, PushedObject{lua, 3}, 1).release();
1447 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1452 // this function will be stored in __newindex in the metatable
1453 const auto newIndexFunction = [](lua_State* lua) -> int {
1455 assert(lua_gettop(lua) == 3);
1456 assert(lua_isuserdata(lua, 1));
1458 // searching for a handler
1459 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1460 lua_rawget(lua, LUA_REGISTRYINDEX);
1461 assert(!lua_isnil(lua, -1));
1463 // looking into setter members
1464 lua_pushinteger(lua, 4);
1465 lua_rawget(lua, -2);
1466 lua_pushvalue(lua, 2);
1467 lua_rawget(lua, -2);
1468 if (!lua_isnil(lua, -1)) {
1469 lua_pushvalue(lua, 1);
1470 lua_pushvalue(lua, 3);
1471 callRaw(lua, PushedObject{lua, 3}, 0);
1477 // looking into default setter
1478 lua_pushinteger(lua, 5);
1479 lua_rawget(lua, -2);
1480 if (lua_isnil(lua, -1))
1483 lua_pushstring(lua, "No setter found");
1486 lua_pushvalue(lua, 1);
1487 lua_pushvalue(lua, 2);
1488 lua_pushvalue(lua, 3);
1489 callRaw(lua, PushedObject{lua, 4}, 0);
1494 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1499 // writing structure for this type into the registry
1500 checkTypeRegistration(state, &typeid(TType));
1502 // creating the object
1503 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
1504 // and that's what we do with placement-new
1505 const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
1506 new (pointerLocation) TType(std::forward<TType2>(value));
1507 PushedObject obj{state, 1};
1509 // creating the metatable (over the object on the stack)
1510 // lua_settable pops the key and value we just pushed, so stack management is easy
1511 // all that remains on the stack after these function calls is the metatable
1512 lua_newtable(state);
1513 PushedObject pushedTable{state, 1};
1515 // using the garbage collecting function we created above
1516 if (!boost::has_trivial_destructor<TType>::value)
1518 lua_pushstring(state, "__gc");
1519 lua_pushcfunction(state, garbageCallbackFunction);
1520 lua_settable(state, -3);
1523 // the _typeid index of the metatable will store the type_info*
1524 lua_pushstring(state, "_typeid");
1525 lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
1526 lua_settable(state, -3);
1528 // using the index function we created above
1529 lua_pushstring(state, "__index");
1530 lua_pushcfunction(state, indexFunction);
1531 lua_settable(state, -3);
1533 // using the newindex function we created above
1534 lua_pushstring(state, "__newindex");
1535 lua_pushcfunction(state, newIndexFunction);
1536 lua_settable(state, -3);
1538 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1539 // lua_setmetatable will bind the two together and pop the metatable
1540 // our custom type remains on the stack (and that's what we want since this is a push function)
1541 lua_setmetatable(state, -2);
1542 pushedTable.release();
1544 return std::move(obj);
1548 // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1549 template<typename... TTypes>
1550 struct PusherTotalMinSize;
1552 // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1553 template<typename... TTypes>
1554 struct PusherTotalMaxSize;
1556 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1557 template<typename... TTypes>
1558 struct PusherMinSize;
1560 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1561 template<typename... TTypes>
1562 struct PusherMaxSize;
1565 /**************************************************/
1566 /* READ FUNCTIONS */
1567 /**************************************************/
1568 // the "Reader" structures allow to read data from the stack
1569 // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1570 // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1572 template<typename TType, typename = void>
1574 typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1577 static auto read(lua_State* state, int index)
1578 -> boost::optional<ReturnType>
1580 if (!test(state, index))
1582 return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
1586 static bool test(lua_State* state, int index)
1588 if (!lua_isuserdata(state, index))
1590 if (!lua_getmetatable(state, index))
1593 // now we have our metatable on the top of the stack
1594 // retrieving its _typeid member
1595 lua_pushstring(state, "_typeid");
1596 lua_gettable(state, -2);
1597 const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
1598 const auto typeIDToCompare = &typeid(TType);
1600 // if wrong typeid, returning false
1602 if (storedTypeID != typeIDToCompare)
1610 * This functions reads multiple values starting at "index" and passes them to the callback
1612 template<typename TRetValue, typename TCallback>
1613 static auto readIntoFunction(lua_State* state, tag<TRetValue>, TCallback&& callback, int index)
1618 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1619 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1620 -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1623 Binder<TCallback, const TFirstType&> binder{ callback, {} };
1624 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1627 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1629 throw WrongTypeException(lua_typename(state, index), typeid(TFirstType));
1631 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1632 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1634 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1635 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1636 -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1639 throw std::logic_error("Wrong number of parameters");
1641 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1643 throw WrongTypeException(lua_typename(state, index), typeid(TFirstType));
1645 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1646 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1650 /**************************************************/
1652 /**************************************************/
1653 // structure that will ensure that a certain value is stored somewhere in the registry
1654 struct ValueInRegistry {
1655 // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
1656 ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_}
1658 lua_pushlightuserdata(lua, this);
1659 lua_pushvalue(lua, -1 + index);
1660 lua_settable(lua, LUA_REGISTRYINDEX);
1663 // removing the function from the registry
1666 lua_pushlightuserdata(lua, this);
1668 lua_settable(lua, LUA_REGISTRYINDEX);
1671 // loads the value and puts it at the top of the stack
1674 lua_pushlightuserdata(lua, this);
1675 lua_gettable(lua, LUA_REGISTRYINDEX);
1676 return PushedObject{lua, 1};
1679 ValueInRegistry(const ValueInRegistry&) = delete;
1680 ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1686 // binds the first parameter of a function object
1687 template<typename TFunctionObject, typename TFirstParamType>
1689 TFunctionObject function;
1690 TFirstParamType param;
1692 template<typename... TParams>
1693 auto operator()(TParams&&... params)
1694 -> decltype(function(param, std::forward<TParams>(params)...))
1696 return function(param, std::forward<TParams>(params)...);
1700 // turns a type into a tuple
1701 // void is turned into std::tuple<>
1702 // existing tuples are untouched
1703 template<typename T>
1706 // this structure takes a pointer to a member function type and returns the base function type
1707 template<typename TType>
1708 struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug
1710 // this structure takes any object and detects its function type
1711 template<typename TObjectType>
1712 struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1714 // 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
1715 // the only case where "min != max" is with boost::optional at the end of the list
1716 template<typename... TArgumentsList>
1717 struct FunctionArgumentsCounter {};
1719 // true is the template parameter is a boost::optional
1720 template<typename T>
1721 struct IsOptional : public std::false_type {};
1725 static LuaContext::EmptyArray_t ATTR_UNUSED
1728 static LuaContext::Metatable_t ATTR_UNUSED
1731 /**************************************************/
1732 /* PARTIAL IMPLEMENTATIONS */
1733 /**************************************************/
1735 inline auto LuaContext::readTopAndPop<void>(lua_State* state, PushedObject obj)
1740 // this structure takes a template parameter T
1741 // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1742 // we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1743 template<typename T>
1744 struct LuaContext::Tupleizer { typedef std::tuple<T> type; };
1745 template<typename... Args>
1746 struct LuaContext::Tupleizer<std::tuple<Args...>> { typedef std::tuple<Args...> type; };
1748 struct LuaContext::Tupleizer<void> { typedef std::tuple<> type; };
1750 // this structure takes any object and detects its function type
1751 template<typename TRetValue, typename... TParameters>
1752 struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)> { typedef TRetValue type(TParameters...); };
1753 template<typename TObjectType>
1754 struct LuaContext::FunctionTypeDetector<TObjectType*> { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1756 // this structure takes a pointer to a member function type and returns the base function type
1757 template<typename TType, typename TRetValue, typename... TParameters>
1758 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)> { typedef TRetValue type(TParameters...); };
1759 template<typename TType, typename TRetValue, typename... TParameters>
1760 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const> { typedef TRetValue type(TParameters...); };
1761 template<typename TType, typename TRetValue, typename... TParameters>
1762 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile> { typedef TRetValue type(TParameters...); };
1763 template<typename TType, typename TRetValue, typename... TParameters>
1764 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile> { typedef TRetValue type(TParameters...); };
1766 // implementation of PusherTotalMinSize
1767 template<typename TFirst, typename... TTypes>
1768 struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1770 struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1772 // implementation of PusherTotalMaxSize
1773 template<typename TFirst, typename... TTypes>
1774 struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1776 struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1778 // implementation of PusherMinSize
1779 template<typename TFirst, typename TSecond, typename... TTypes>
1780 struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...>
1782 static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize
1784 PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size
1786 PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1789 template<typename TFirst>
1790 struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
1792 // implementation of PusherMaxSize
1793 template<typename TFirst, typename... TTypes>
1794 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; };
1796 struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1798 // implementation of FunctionArgumentsCounter
1799 template<typename TFirst, typename... TParams>
1800 struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1801 typedef FunctionArgumentsCounter<TParams...>
1803 static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1804 static const int max = 1 + SubType::max;
1807 struct LuaContext::FunctionArgumentsCounter<> {
1808 static const int min = 0;
1809 static const int max = 0;
1812 // implementation of IsOptional
1813 template<typename T>
1814 struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1816 // implementation of LuaFunctionCaller
1817 template<typename TFunctionType>
1818 class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1819 template<typename TRetValue, typename... TParams>
1820 class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1823 TRetValue operator()(TParams&&... params) const
1825 auto obj = valueHolder->pop();
1826 return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
1830 std::shared_ptr<ValueInRegistry> valueHolder;
1835 explicit LuaFunctionCaller(lua_State* state_, int index) :
1836 valueHolder(std::make_shared<ValueInRegistry>(state_, index)),
1842 /**************************************************/
1843 /* PUSH FUNCTIONS */
1844 /**************************************************/
1845 // specializations of the Pusher structure
1847 // opaque Lua references
1849 struct LuaContext::Pusher<LuaContext::LuaObject> {
1850 static const int minSize = 1;
1851 static const int maxSize = 1;
1853 static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept {
1854 if (value.objectInRegistry.get()) {
1855 PushedObject obj = value.objectInRegistry->pop();
1859 return PushedObject{state, 1};
1866 struct LuaContext::Pusher<bool> {
1867 static const int minSize = 1;
1868 static const int maxSize = 1;
1870 static PushedObject push(lua_State* state, bool value) noexcept {
1871 lua_pushboolean(state, value);
1872 return PushedObject{state, 1};
1878 struct LuaContext::Pusher<std::string> {
1879 static const int minSize = 1;
1880 static const int maxSize = 1;
1882 static PushedObject push(lua_State* state, const std::string& value) noexcept {
1883 lua_pushlstring(state, value.c_str(), value.length());
1884 return PushedObject{state, 1};
1890 struct LuaContext::Pusher<const char*> {
1891 static const int minSize = 1;
1892 static const int maxSize = 1;
1894 static PushedObject push(lua_State* state, const char* value) noexcept {
1895 lua_pushstring(state, value);
1896 return PushedObject{state, 1};
1902 struct LuaContext::Pusher<const char[N]> {
1903 static const int minSize = 1;
1904 static const int maxSize = 1;
1906 static PushedObject push(lua_State* state, const char* value) noexcept {
1907 lua_pushstring(state, value);
1908 return PushedObject{state, 1};
1913 template<typename T>
1914 struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
1915 static const int minSize = 1;
1916 static const int maxSize = 1;
1918 static PushedObject push(lua_State* state, T value) noexcept {
1919 lua_pushnumber(state, value);
1920 return PushedObject{state, 1};
1925 template<typename T>
1926 struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
1927 static const int minSize = 1;
1928 static const int maxSize = 1;
1930 static PushedObject push(lua_State* state, T value) noexcept {
1931 lua_pushinteger(state, value);
1932 return PushedObject{state, 1};
1938 struct LuaContext::Pusher<std::nullptr_t> {
1939 static const int minSize = 1;
1940 static const int maxSize = 1;
1942 static PushedObject push(lua_State* state, std::nullptr_t value) noexcept {
1943 assert(value == nullptr);
1945 return PushedObject{state, 1};
1951 struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
1952 static const int minSize = 1;
1953 static const int maxSize = 1;
1955 static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
1956 lua_newtable(state);
1957 return PushedObject{state, 1};
1961 // std::type_info* is a lightuserdata
1963 struct LuaContext::Pusher<const std::type_info*> {
1964 static const int minSize = 1;
1965 static const int maxSize = 1;
1967 static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
1968 lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
1969 return PushedObject{state, 1};
1975 struct LuaContext::Pusher<LuaContext::ThreadID> {
1976 static const int minSize = 1;
1977 static const int maxSize = 1;
1979 static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
1980 lua_pushthread(value.state);
1981 return PushedObject{state, 1};
1986 template<typename TKey, typename TValue>
1987 struct LuaContext::Pusher<std::map<TKey,TValue>> {
1988 static const int minSize = 1;
1989 static const int maxSize = 1;
1991 static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
1992 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");
1993 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");
1995 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
1997 for (auto i = value.begin(), e = value.end(); i != e; ++i)
1998 setTable<TValue>(state, obj, i->first, i->second);
2000 return std::move(obj);
2005 template<typename TKey, typename TValue>
2006 struct LuaContext::Pusher<std::unordered_map<TKey,TValue>> {
2007 static const int minSize = 1;
2008 static const int maxSize = 1;
2010 static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue>& value) noexcept {
2011 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");
2012 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");
2014 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2016 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2017 setTable<TValue>(state, obj, i->first, i->second);
2019 return std::move(obj);
2024 template<typename TType1, typename TType2>
2025 struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
2026 static const int minSize = 1;
2027 static const int maxSize = 1;
2029 static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
2030 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");
2031 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");
2033 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2035 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2036 setTable<TType2>(state, obj, i->first, i->second);
2038 return std::move(obj);
2043 template<typename TType>
2044 struct LuaContext::Pusher<std::vector<TType>> {
2045 static const int minSize = 1;
2046 static const int maxSize = 1;
2048 static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
2049 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");
2051 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2053 for (unsigned int i = 0; i < value.size(); ++i)
2054 setTable<TType>(state, obj, i + 1, value[i]);
2056 return std::move(obj);
2061 template<typename TType>
2062 struct LuaContext::Pusher<std::unique_ptr<TType>> {
2063 static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2064 static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2066 static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2067 return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2072 template<typename TEnum>
2073 struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2074 #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2075 typedef typename std::underlying_type<TEnum>::type
2078 // implementation when std::underlying_type is not supported
2079 typedef unsigned long
2083 static const int minSize = Pusher<RealType>::minSize;
2084 static const int maxSize = Pusher<RealType>::maxSize;
2086 static PushedObject push(lua_State* state, TEnum value) noexcept {
2087 return Pusher<RealType>::push(state, static_cast<RealType>(value));
2092 // this specialization is not directly called, but is called by other specializations
2093 template<typename TReturnType, typename... TParameters>
2094 struct LuaContext::Pusher<TReturnType (TParameters...)>
2096 static const int minSize = 1;
2097 static const int maxSize = 1;
2099 // counts the number of arguments
2100 typedef FunctionArgumentsCounter<TParameters...>
2101 LocalFunctionArgumentsCounter;
2103 // this is the version of "push" for non-trivially destructible function objects
2104 template<typename TFunctionObject>
2105 static auto push(lua_State* state, TFunctionObject fn) noexcept
2106 -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2108 // TODO: is_move_constructible not supported by some compilers
2109 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2111 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2112 // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2113 // so we use userdata instead
2115 // this function is called when the lua script tries to call our custom data type
2116 // we transfer execution to the "callback" function below
2117 const auto callCallback = [](lua_State* lua) -> int {
2118 assert(lua_gettop(lua) >= 1);
2119 assert(lua_isuserdata(lua, 1));
2120 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2123 return callback(lua, function, lua_gettop(lua) - 1).release();
2126 // this one is called when lua's garbage collector no longer needs our custom data type
2127 // we call the function object's destructor
2128 const auto garbageCallback = [](lua_State* lua) -> int {
2129 assert(lua_gettop(lua) == 1);
2130 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2132 function->~TFunctionObject();
2136 // creating the object
2137 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2138 // and that's what we do with placement-new
2139 const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2140 new (functionLocation) TFunctionObject(std::move(fn));
2142 // creating the metatable (over the object on the stack)
2143 // lua_settable pops the key and value we just pushed, so stack management is easy
2144 // all that remains on the stack after these function calls is the metatable
2145 lua_newtable(state);
2146 lua_pushstring(state, "__call");
2147 lua_pushcfunction(state, callCallback);
2148 lua_settable(state, -3);
2150 lua_pushstring(state, "__gc");
2151 lua_pushcfunction(state, garbageCallback);
2152 lua_settable(state, -3);
2154 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2155 // lua_setmetatable will bind the two together and pop the metatable
2156 // our custom function remains on the stack (and that's what we want)
2157 lua_setmetatable(state, -2);
2159 return PushedObject{state, 1};
2162 // this is the version of "push" for trivially destructible objects
2163 template<typename TFunctionObject>
2164 static auto push(lua_State* state, TFunctionObject fn) noexcept
2165 -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2167 // TODO: is_move_constructible not supported by some compilers
2168 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2170 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2171 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2173 // this is the cfunction that is the callback
2174 const auto function = [](lua_State* state_) -> int
2176 // the function object is an upvalue
2177 const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state_, lua_upvalueindex(1)));
2178 return callback(state_, toCall, lua_gettop(state_)).release();
2181 // we copy the function object onto the stack
2182 const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2183 new (functionObjectLocation) TFunctionObject(std::move(fn));
2185 // pushing the function with the function object as upvalue
2186 lua_pushcclosure(state, function, 1);
2187 return PushedObject{state, 1};
2190 // this is the version of "push" for pointer to functions
2191 static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2194 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2195 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2197 // this is the cfunction that is the callback
2198 const auto function = [](lua_State* state_) -> int
2200 // the function object is an upvalue
2201 const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state_, lua_upvalueindex(1)));
2202 return callback(state_, toCall, lua_gettop(state_)).release();
2205 // we copy the function object onto the stack
2206 lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
2208 // pushing the function with the function object as upvalue
2209 lua_pushcclosure(state, function, 1);
2210 return PushedObject{state, 1};
2213 // this is the version of "push" for references to functions
2214 static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2217 return push(state, &fn);
2221 // callback that calls the function object
2222 // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2223 template<typename TFunctionObject>
2224 static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2227 // checking if number of parameters is correct
2228 if (argumentsCount < LocalFunctionArgumentsCounter::min) {
2229 // if not, using lua_error to return an error
2230 luaL_where(state, 1);
2231 lua_pushstring(state, "This function requires at least ");
2232 lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
2233 lua_pushstring(state, " parameter(s)");
2234 lua_concat(state, 4);
2237 } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
2238 // if not, using lua_error to return an error
2239 luaL_where(state, 1);
2240 lua_pushstring(state, "This function requires at most ");
2241 lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
2242 lua_pushstring(state, " parameter(s)");
2243 lua_concat(state, 4);
2247 // calling the function
2249 return callback2(state, *toCall, argumentsCount);
2251 } catch (const WrongTypeException& ex) {
2252 // wrong parameter type, using lua_error to return an error
2253 luaL_where(state, 1);
2254 lua_pushstring(state, "Unable to convert parameter from ");
2255 lua_pushstring(state, ex.luaType.c_str());
2256 lua_pushstring(state, " to ");
2257 lua_pushstring(state, ex.destination.name());
2258 lua_concat(state, 4);
2262 Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
2267 template<typename TFunctionObject>
2268 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2269 -> typename std::enable_if<!std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2271 // pushing the result on the stack and returning number of pushed elements
2272 typedef Pusher<typename std::decay<TReturnType>::type>
2274 return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
2277 template<typename TFunctionObject>
2278 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2279 -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2281 readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
2282 return PushedObject{state, 0};
2286 // C function pointers
2287 template<typename TReturnType, typename... TParameters>
2288 struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2290 // using the function-pushing implementation
2291 typedef Pusher<TReturnType (TParameters...)>
2293 static const int minSize = SubPusher::minSize;
2294 static const int maxSize = SubPusher::maxSize;
2296 template<typename TType>
2297 static PushedObject push(lua_State* state, TType value) noexcept {
2298 return SubPusher::push(state, value);
2302 // C function references
2303 template<typename TReturnType, typename... TParameters>
2304 struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2306 // using the function-pushing implementation
2307 typedef Pusher<TReturnType(TParameters...)>
2309 static const int minSize = SubPusher::minSize;
2310 static const int maxSize = SubPusher::maxSize;
2312 template<typename TType>
2313 static PushedObject push(lua_State* state, TType value) noexcept {
2314 return SubPusher::push(state, value);
2319 template<typename TReturnType, typename... TParameters>
2320 struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2322 // using the function-pushing implementation
2323 typedef Pusher<TReturnType (TParameters...)>
2325 static const int minSize = SubPusher::minSize;
2326 static const int maxSize = SubPusher::maxSize;
2328 static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2329 return SubPusher::push(state, value);
2334 template<typename... TTypes>
2335 struct LuaContext::Pusher<boost::variant<TTypes...>>
2337 static const int minSize = PusherMinSize<TTypes...>::size;
2338 static const int maxSize = PusherMaxSize<TTypes...>::size;
2340 static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
2341 PushedObject obj{state, 0};
2342 VariantWriter writer{state, obj};
2343 value.apply_visitor(writer);
2344 return std::move(obj);
2348 struct VariantWriter : public boost::static_visitor<> {
2349 template<typename TType>
2350 void operator()(TType value) noexcept
2352 obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
2355 VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {}
2362 template<typename TType>
2363 struct LuaContext::Pusher<boost::optional<TType>> {
2364 typedef Pusher<typename std::decay<TType>::type>
2367 static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2368 static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2370 static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
2372 return UnderlyingPusher::push(state, value.get());
2375 return PushedObject{state, 1};
2381 template<typename... TTypes>
2382 struct LuaContext::Pusher<std::tuple<TTypes...>> {
2383 // TODO: NOT EXCEPTION SAFE /!\ //
2384 static const int minSize = PusherTotalMinSize<TTypes...>::size;
2385 static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2387 static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2388 return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2391 static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
2392 return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
2397 static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2398 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2400 return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2401 push2(state, value, std::integral_constant<int,N+1>{});
2405 static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
2406 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2408 return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
2409 push2(state, std::move(value), std::integral_constant<int,N+1>{});
2412 static int push2(lua_State* state, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2416 static int push2(lua_State* state, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2421 /**************************************************/
2422 /* READ FUNCTIONS */
2423 /**************************************************/
2424 // specializations of the Reader structures
2426 // opaque Lua references
2428 struct LuaContext::Reader<LuaContext::LuaObject>
2430 static auto read(lua_State* state, int index)
2431 -> boost::optional<LuaContext::LuaObject>
2433 LuaContext::LuaObject obj(state, index);
2440 struct LuaContext::Reader<std::nullptr_t>
2442 static auto read(lua_State* state, int index)
2443 -> boost::optional<std::nullptr_t>
2445 if (!lua_isnil(state, index))
2452 template<typename TType>
2453 struct LuaContext::Reader<
2455 typename std::enable_if<std::is_integral<TType>::value>::type
2458 static auto read(lua_State* state, int index)
2459 -> boost::optional<TType>
2461 # if LUA_VERSION_NUM >= 502
2464 auto value = lua_tointegerx(state, index, &success);
2467 return static_cast<TType>(value);
2471 if (!lua_isnumber(state, index))
2473 return static_cast<TType>(lua_tointeger(state, index));
2480 template<typename TType>
2481 struct LuaContext::Reader<
2483 typename std::enable_if<std::is_floating_point<TType>::value>::type
2486 static auto read(lua_State* state, int index)
2487 -> boost::optional<TType>
2489 # if LUA_VERSION_NUM >= 502
2492 auto value = lua_tonumberx(state, index, &success);
2495 return static_cast<TType>(value);
2499 if (!lua_isnumber(state, index))
2501 return static_cast<TType>(lua_tonumber(state, index));
2509 struct LuaContext::Reader<bool>
2511 static auto read(lua_State* state, int index)
2512 -> boost::optional<bool>
2514 if (!lua_isboolean(state, index))
2516 return lua_toboolean(state, index) != 0;
2521 // lua_tostring returns a temporary pointer, but that's not a problem since we copy
2522 // the data into a std::string
2524 struct LuaContext::Reader<std::string>
2526 static auto read(lua_State* state, int index)
2527 -> boost::optional<std::string>
2530 const auto val = lua_tolstring(state, index, &len);
2533 return std::string(val, len);
2538 template<typename TType>
2539 struct LuaContext::Reader<
2541 typename std::enable_if<std::is_enum<TType>::value>::type
2544 static auto read(lua_State* state, int index)
2545 -> boost::optional<TType>
2547 if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
2549 return static_cast<TType>(lua_tointeger(state, index));
2553 // LuaFunctionCaller
2554 template<typename TRetValue, typename... TParameters>
2555 struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2557 typedef LuaFunctionCaller<TRetValue (TParameters...)>
2560 static auto read(lua_State* state, int index)
2561 -> boost::optional<ReturnType>
2563 if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
2565 return ReturnType(state, index);
2570 template<typename TRetValue, typename... TParameters>
2571 struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2573 static auto read(lua_State* state, int index)
2574 -> boost::optional<std::function<TRetValue (TParameters...)>>
2576 if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
2578 std::function<TRetValue (TParameters...)> f{*val};
2579 return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
2587 template<typename TType1, typename TType2>
2588 struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2590 static auto read(lua_State* state, int index)
2591 -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2593 if (!lua_istable(state, index))
2596 std::vector<std::pair<TType1, TType2>> result;
2598 // we traverse the table at the top of the stack
2599 lua_pushnil(state); // first key
2600 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2601 // now a key and its value are pushed on the stack
2603 auto val1 = Reader<TType1>::read(state, -2);
2604 auto val2 = Reader<TType2>::read(state, -1);
2606 if (!val1.is_initialized() || !val2.is_initialized()) {
2607 lua_pop(state, 2); // we remove the value and the key
2611 result.push_back({ std::move(val1.get()), std::move(val2.get()) });
2612 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2615 lua_pop(state, 2); // we remove the value and the key
2620 return { std::move(result) };
2625 template<typename TKey, typename TValue>
2626 struct LuaContext::Reader<std::map<TKey,TValue>>
2628 static auto read(lua_State* state, int index)
2629 -> boost::optional<std::map<TKey,TValue>>
2631 if (!lua_istable(state, index))
2634 std::map<TKey,TValue> result;
2636 // we traverse the table at the top of the stack
2637 lua_pushnil(state); // first key
2638 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2639 // now a key and its value are pushed on the stack
2641 auto key = Reader<TKey>::read(state, -2);
2642 auto value = Reader<TValue>::read(state, -1);
2644 if (!key.is_initialized() || !value.is_initialized()) {
2645 lua_pop(state, 2); // we remove the value and the key
2649 result.insert({ std::move(key.get()), std::move(value.get()) });
2650 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2653 lua_pop(state, 2); // we remove the value and the key
2658 return { std::move(result) };
2663 template<typename TKey, typename TValue>
2664 struct LuaContext::Reader<std::unordered_map<TKey,TValue>>
2666 static auto read(lua_State* state, int index)
2667 -> boost::optional<std::unordered_map<TKey,TValue>>
2669 if (!lua_istable(state, index))
2672 std::unordered_map<TKey,TValue> result;
2674 // we traverse the table at the top of the stack
2675 lua_pushnil(state); // first key
2676 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2677 // now a key and its value are pushed on the stack
2679 auto key = Reader<TKey>::read(state, -2);
2680 auto value = Reader<TValue>::read(state, -1);
2682 if (!key.is_initialized() || !value.is_initialized()) {
2683 lua_pop(state, 2); // we remove the value and the key
2687 result.insert({ std::move(key.get()), std::move(value.get()) });
2688 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2691 lua_pop(state, 2); // we remove the value and the key
2696 return { std::move(result) };
2701 // IMPORTANT: optional means "either nil or the value of the right type"
2702 // * if the value is nil, then an optional containing an empty optional is returned
2703 // * if the value is of the right type, then an optional containing an optional containing the value is returned
2704 // * if the value is of the wrong type, then an empty optional is returned
2705 template<typename TType>
2706 struct LuaContext::Reader<boost::optional<TType>>
2708 static auto read(lua_State* state, int index)
2709 -> boost::optional<boost::optional<TType>>
2711 if (lua_isnil(state, index))
2712 return boost::optional<TType>{boost::none};
2713 if (auto&& other = Reader<TType>::read(state, index))
2714 return std::move(other);
2720 template<typename... TTypes>
2721 struct LuaContext::Reader<boost::variant<TTypes...>>
2723 typedef boost::variant<TTypes...>
2727 // class doing operations for a range of types from TIterBegin to TIterEnd
2728 template<typename TIterBegin, typename TIterEnd, typename = void>
2729 struct VariantReader
2731 using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2733 static auto read(lua_State* state, int index)
2734 -> boost::optional<ReturnType>
2736 // note: using SubReader::read triggers a compilation error when used with a reference
2737 if (const auto val = SubReader::read(state, index))
2738 return boost::variant<TTypes...>{*val};
2739 return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
2743 // specialization of class above being called when list of remaining types is empty
2744 template<typename TIterBegin, typename TIterEnd>
2745 struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2747 static auto read(lua_State* state, int index)
2748 -> boost::optional<ReturnType>
2754 // this is the main type
2755 typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2759 static auto read(lua_State* state, int index)
2760 -> boost::optional<ReturnType>
2762 return MainVariantReader::read(state, index);
2767 // tuple have an additional argument for their functions, that is the maximum size to read
2768 // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2770 struct LuaContext::Reader<std::tuple<>>
2772 static auto read(lua_State* state, int index, int maxSize = 0)
2773 -> boost::optional<std::tuple<>>
2775 return std::tuple<>{};
2779 template<typename TFirst, typename... TOthers>
2780 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2781 typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2784 // this is the "TFirst is NOT default constructible" version
2786 typedef std::tuple<TFirst, TOthers...>
2789 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2790 -> boost::optional<ReturnType>
2795 auto firstVal = Reader<TFirst>::read(state, index);
2796 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2798 if (!firstVal || !othersVal)
2801 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2805 template<typename TFirst, typename... TOthers>
2806 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2807 typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2810 // this is the "TFirst is default-constructible" version
2812 typedef std::tuple<TFirst, TOthers...>
2815 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2816 -> boost::optional<ReturnType>
2818 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2823 return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2825 auto firstVal = Reader<TFirst>::read(state, index);
2829 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));