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/format.hpp>
51 #include <boost/mpl/distance.hpp>
52 #include <boost/mpl/transform.hpp>
53 #include <boost/optional.hpp>
54 #include <boost/variant.hpp>
55 #include <boost/type_traits.hpp>
58 #if defined(_MSC_VER) && _MSC_VER < 1900
59 # include "misc/exception.hpp"
63 # define ATTR_UNUSED __attribute__((unused))
68 #define LUACONTEXT_GLOBAL_EQ "e5ddced079fc405aa4937b386ca387d2"
69 #define EQ_FUNCTION_NAME "__eq"
70 #define TOSTRING_FUNCTION_NAME "__tostring"
73 * Defines a Lua context
74 * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions),
75 * we only provide few functions like readVariable and writeVariable.
77 * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert
78 * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types
79 * it wants. These arguments may only be of basic types (int, float, etc.) or std::string.
82 #if defined(__GNUC__) && !defined(__clang__)
83 #pragma GCC diagnostic push
84 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
88 struct ValueInRegistry;
89 template<typename TFunctionObject, typename TFirstParamType> struct Binder;
90 template<typename T> struct IsOptional;
91 enum Globals_t { Globals }; // tag for "global variables"
94 * @param openDefaultLibs True if luaL_openlibs should be called
96 explicit LuaContext(bool openDefaultLibs = true)
98 // luaL_newstate can return null if allocation failed
99 mState = luaL_newstate();
100 if (mState == nullptr)
101 throw std::bad_alloc();
103 // setting the panic function
104 lua_atpanic(mState, [](lua_State* state) -> int {
105 const std::string str = lua_tostring(state, -1);
107 assert(false && "lua_atpanic triggered");
111 // opening default library if required to do so
113 luaL_openlibs(mState);
118 void writeGlobalEq() {
119 const auto eqFunction = [](lua_State* lua) -> int {
121 lua_pushstring(lua, "__eq");
122 lua_gettable(lua, -2);
123 /* if not found, return false */
124 if (lua_isnil(lua, -1)) {
126 lua_pushboolean(lua, false);
129 lua_insert(lua, lua_gettop(lua)-2);
130 return callRaw(lua, PushedObject{lua, 3}, 1).release();
132 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
136 lua_pushcfunction(mState, eqFunction);
137 lua_setglobal(mState, LUACONTEXT_GLOBAL_EQ);
143 LuaContext(LuaContext&& s) :
146 s.mState = luaL_newstate();
152 LuaContext& operator=(LuaContext&& s) noexcept
154 std::swap(mState, s.mState);
161 LuaContext(const LuaContext&) = delete;
166 LuaContext& operator=(const LuaContext&) = delete;
171 ~LuaContext() noexcept
178 * Thrown when an error happens during execution of lua code (like not enough parameters for a function)
180 class ExecutionErrorException : public std::runtime_error
183 ExecutionErrorException(const std::string& msg) :
184 std::runtime_error(msg)
190 * Thrown when a syntax error happens in a lua script
192 class SyntaxErrorException : public std::runtime_error
195 SyntaxErrorException(const std::string& msg) :
196 std::runtime_error(msg)
202 * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string
204 class WrongTypeException : public std::runtime_error
207 WrongTypeException(std::string luaType_, const std::type_info& destination_) :
208 std::runtime_error("Trying to cast a lua variable from \"" + luaType_ + "\" to \"" + destination_.name() + "\""),
210 destination(destination_)
215 const std::type_info& destination;
219 * Function object that can call a function stored by Lua
220 * This type is copiable and movable, but not constructible. It can only be created through readVariable.
221 * @tparam TFunctionType Function type (eg. "int (int, bool)")
223 template<typename TFunctionType>
224 class LuaFunctionCaller;
227 * Opaque type that identifies a Lua object
230 LuaObject() = default;
231 LuaObject(lua_State* state, int index=-1) {
232 this->objectInRegistry = std::make_shared<LuaContext::ValueInRegistry>(state, index);
234 std::shared_ptr<LuaContext::ValueInRegistry> objectInRegistry;
238 * Opaque type that identifies a Lua thread
241 ThreadID() = default;
242 ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { }
243 ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; }
247 std::unique_ptr<ValueInRegistry> threadInRegistry;
251 * Type that is considered as an empty array
253 enum EmptyArray_t { EmptyArray };
256 * Type for a metatable
258 enum Metatable_t { Metatable };
261 * Executes lua code from the stream
262 * @param code A stream that Lua will read its code from
264 void executeCode(std::istream& code)
266 auto toCall = load(mState, code);
267 call<std::tuple<>>(mState, std::move(toCall));
271 * Executes lua code from the stream and returns a value
272 * @param code A stream that Lua will read its code from
273 * @tparam TType The type that the executing code should return
275 template<typename TType>
276 auto executeCode(std::istream& code)
279 auto toCall = load(mState, code);
280 return call<TType>(mState, std::move(toCall));
284 * Executes lua code given as parameter
285 * @param code A string containing code that will be executed by Lua
287 void executeCode(const std::string& code)
289 executeCode(code.c_str());
293 * Executes Lua code from the stream and returns a value
294 * @param code A string containing code that will be executed by Lua
295 * @tparam TType The type that the executing code should return
297 template<typename TType>
298 auto executeCode(const std::string& code)
301 return executeCode<TType>(code.c_str());
306 * @param code A string containing code that will be executed by Lua
308 void executeCode(const char* code)
310 auto toCall = load(mState, code);
311 call<std::tuple<>>(mState, std::move(toCall));
315 * Executes Lua code from the stream and returns a value
316 * @param code A string containing code that will be executed by Lua
317 * @tparam TType The type that the executing code should return
319 template<typename TType>
320 auto executeCode(const char* code)
323 auto toCall = load(mState, code);
324 return call<TType>(mState, std::move(toCall));
328 * Executes lua code from the stream
329 * @param code A stream that Lua will read its code from
331 void executeCode(const ThreadID& thread, std::istream& code)
333 auto toCall = load(thread.state, code);
334 call<std::tuple<>>(thread.state, std::move(toCall));
338 * Executes lua code from the stream and returns a value
339 * @param code A stream that Lua will read its code from
340 * @tparam TType The type that the executing code should return
342 template<typename TType>
343 auto executeCode(const ThreadID& thread, std::istream& code)
346 auto toCall = load(thread.state, code);
347 return call<TType>(thread.state, std::move(toCall));
351 * Executes lua code given as parameter
352 * @param code A string containing code that will be executed by Lua
354 void executeCode(const ThreadID& thread, const std::string& code)
356 executeCode(thread, code.c_str());
360 * Executes Lua code from the stream and returns a value
361 * @param code A string containing code that will be executed by Lua
362 * @tparam TType The type that the executing code should return
364 template<typename TType>
365 auto executeCode(const ThreadID& thread, const std::string& code)
368 return executeCode<TType>(thread, code.c_str());
373 * @param code A string containing code that will be executed by Lua
375 void executeCode(const ThreadID& thread, const char* code)
377 auto toCall = load(thread.state, code);
378 call<std::tuple<>>(thread.state, std::move(toCall));
382 * Executes Lua code from the stream and returns a value
383 * @param code A string containing code that will be executed by Lua
384 * @tparam TType The type that the executing code should return
386 template<typename TType>
387 auto executeCode(const ThreadID& thread, const char* code)
390 auto toCall = load(thread.state, code);
391 return call<TType>(thread.state, std::move(toCall));
395 * Tells that Lua will be allowed to access an object's function
396 * This is the version "registerFunction(name, &Foo::function)"
398 template<typename TPointerToMemberFunction>
399 auto registerFunction(const std::string& name, TPointerToMemberFunction pointer)
400 -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
402 registerFunctionImpl(name, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
406 * Tells that Lua will be allowed to access an object's function
407 * This is the version with an explicit template parameter: "registerFunction<void (Foo::*)()>(name, [](Foo&) { })"
408 * @param fn Function object which takes as first parameter a reference to the object
409 * @tparam TFunctionType Pointer-to-member function type
411 template<typename TFunctionType, typename TType>
412 void registerFunction(const std::string& functionName, TType fn)
414 static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
415 registerFunctionImpl(functionName, std::move(fn), tag<TFunctionType>{});
419 * Tells that Lua will be allowed to access an object's function
420 * This is the alternative version with an explicit template parameter: "registerFunction<Foo, void (*)()>(name, [](Foo&) { })"
421 * @param fn Function object which takes as first parameter a reference to the object
422 * @tparam TObject Object to register this function to
423 * @tparam TFunctionType Function type
425 template<typename TObject, typename TFunctionType, typename TType>
426 void registerFunction(const std::string& functionName, TType fn)
428 static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
429 registerFunctionImpl(functionName, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
433 * Wrappers for registering "__eq" function in case we want to change this to something else some day
436 template<typename TPointerToMemberFunction>
437 auto registerEqFunction(TPointerToMemberFunction pointer)
438 -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
440 registerFunctionImpl(EQ_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
443 template<typename TFunctionType, typename TType>
444 void registerEqFunction(TType fn)
446 static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
447 registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
450 template<typename TObject, typename TFunctionType, typename TType>
451 void registerEqFunction(TType fn)
453 static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
454 registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
458 * Wrappers for registering "__tostring" function in case we want to change this to something else some day
461 template<typename TPointerToMemberFunction>
462 auto registerToStringFunction(TPointerToMemberFunction pointer)
463 -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
465 registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
468 template<typename TFunctionType, typename TType>
469 void registerToStringFunction(TType fn)
471 static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
472 registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
475 template<typename TObject, typename TFunctionType, typename TType>
476 void registerToStringFunction(TType fn)
478 static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
479 registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
483 * Inverse operation of registerFunction
484 * @tparam TType Type whose function belongs to
486 template<typename TType>
487 void unregisterFunction(const std::string& /*functionName*/)
489 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType)));
491 lua_settable(mState, LUA_REGISTRYINDEX);
492 checkTypeRegistration(mState, &typeid(TType));
494 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType*)));
496 lua_settable(mState, LUA_REGISTRYINDEX);
497 checkTypeRegistration(mState, &typeid(TType*));
499 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(std::shared_ptr<TType>)));
501 lua_settable(mState, LUA_REGISTRYINDEX);
502 checkTypeRegistration(mState, &typeid(std::shared_ptr<TType>));
506 * Registers a member variable
507 * This is the version "registerMember(name, &Foo::member)"
509 template<typename TObject, typename TVarType>
510 void registerMember(const std::string& name, TVarType TObject::*member)
512 // implementation simply calls the custom member with getter and setter
513 const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; };
514 const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; };
515 registerMember<TVarType (TObject::*)>(name, getter, setter);
519 * Registers a member variable
520 * This is the version "registerMember<Foo, int>(name, getter, setter)"
521 * @tparam TObject Type to register the member to
522 * @tparam TVarType Type of the member
523 * @param name Name of the member to register
524 * @param readFunction Function of type "TVarType (const TObject&)"
525 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
527 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
528 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
530 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
534 * Registers a member variable
535 * This is the version "registerMember<int (Foo::*)>(name, getter, setter)"
536 * @tparam TMemberType Pointer to member object representing the type
537 * @param name Name of the member to register
538 * @param readFunction Function of type "TVarType (const TObject&)"
539 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
541 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
542 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
544 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
545 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction_));
549 * Registers a non-modifiable member variable
550 * This is the version "registerMember<Foo, int>(name, getter)"
551 * @tparam TObject Type to register the member to
552 * @tparam TVarType Type of the member
553 * @param name Name of the member to register
554 * @param readFunction Function of type "TVarType (const TObject&)"
556 template<typename TObject, typename TVarType, typename TReadFunction>
557 void registerMember(const std::string& name, TReadFunction readFunction)
559 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction));
563 * Registers a non-modifiable member variable
564 * This is the version "registerMember<int (Foo::*)>(name, getter)"
565 * @tparam TMemberType Pointer to member object representing the type
566 * @param name Name of the member to register
567 * @param readFunction Function of type "TVarType (const TObject&)"
569 template<typename TMemberType, typename TReadFunction>
570 void registerMember(const std::string& name, TReadFunction readFunction)
572 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
573 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction));
577 * Registers a dynamic member variable
578 * This is the version "registerMember<Foo, int>(getter, setter)"
579 * @tparam TObject Type to register the member to
580 * @tparam TVarType Type of the member
581 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
582 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
584 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
585 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
587 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
591 * Registers a dynamic member variable
592 * This is the version "registerMember<int (Foo::*)>(getter, setter)"
593 * @tparam TMemberType Pointer to member object representing the type
594 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
595 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
597 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
598 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
600 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
601 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction), std::move(writeFunction_));
605 * Registers a dynamic non-modifiable member variable
606 * This is the version "registerMember<Foo, int>(getter)"
607 * @tparam TObject Type to register the member to
608 * @tparam TVarType Type of the member
609 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
611 template<typename TObject, typename TVarType, typename TReadFunction>
612 void registerMember(TReadFunction readFunction)
614 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
618 * Registers a dynamic non-modifiable member variable
619 * This is the version "registerMember<int (Foo::*)>(getter)"
620 * @tparam TMemberType Pointer to member object representing the type
621 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
623 template<typename TMemberType, typename TReadFunction>
624 void registerMember(TReadFunction readFunction)
626 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
627 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction));
631 * Creates a new thread
632 * A Lua thread is not really a thread, but rather an "execution stack".
633 * You can destroy the thread by calling destroyThread
641 result.state = lua_newthread(mState);
642 result.threadInRegistry = std::unique_ptr<ValueInRegistry>(new ValueInRegistry(mState));
649 * Destroys a thread created with createThread
652 void destroyThread(ThreadID& id)
654 id.threadInRegistry.reset();
658 * Reads the content of a Lua variable
660 * @tparam TType Type requested for the read
661 * @throw WrongTypeException When the variable is not convertible to the requested type
664 * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers
665 * Additionally supported:
666 * - LuaFunctionCaller<FunctionType>, which is an alternative to std::function
667 * - references to custom objects, in which case it will return the object in-place
669 * After the variable name, you can add other parameters.
670 * If the variable is an array, it will instead get the element of that array whose offset is the second parameter.
671 * Same applies for third, fourth, etc. parameters.
673 template<typename TType, typename... TTypes>
674 TType readVariable(const std::string& name, TTypes&&... elements) const
676 lua_getglobal(mState, name.c_str());
677 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
678 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
684 template<typename TType, typename... TTypes>
685 TType readVariable(const char* name, TTypes&&... elements) const
687 lua_getglobal(mState, name);
688 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
689 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
695 template<typename TType, typename... TTypes>
696 TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const
698 lua_getglobal(thread.state, name.c_str());
699 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
700 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
706 template<typename TType, typename... TTypes>
707 TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const
709 lua_getglobal(thread.state, name);
710 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
711 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
715 * Changes the content of a Lua variable
717 * Accepted values are:
718 * - all base types (char, short, int, float, double, bool)
722 * - std::vector<std::pair<>>, std::map<> and std::unordered_map<> (the key and value must also be accepted values)
723 * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple)
724 * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>)
725 * - nullptr (writes nil)
728 * All objects are passed by copy and destroyed by the garbage collector if necessary.
730 template<typename... TData>
731 void writeVariable(TData&&... data) noexcept {
732 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable");
733 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
735 static_assert(!std::is_same<typename Tupleizer<RealDataType>::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
737 setTable<RealDataType>(mState, Globals, std::forward<TData>(data)...);
741 * Equivalent to writeVariable(varName, ..., std::function<TFunctionType>(data));
742 * This version is more efficient than writeVariable if you want to write functions
744 template<typename TFunctionType, typename... TData>
745 void writeFunction(TData&&... data) noexcept {
746 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
748 setTable<TFunctionType>(mState, Globals, std::forward<TData>(data)...);
752 * Same as the other writeFunction, except that the template parameter is automatically detected
753 * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas)
755 template<typename... TData>
756 void writeFunction(TData&&... data) noexcept {
757 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
758 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
760 typedef typename FunctionTypeDetector<RealDataType>::type
761 DetectedFunctionType;
763 return writeFunction<DetectedFunctionType>(std::forward<TData>(data)...);
768 // the state is the most important variable in the class since it is our interface with Lua
769 // - registered members and functions are stored in tables at offset &typeid(type) of the registry
770 // each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2
771 // offset 3 is unused, setter members at offset 4, default setter at offset 5
775 /**************************************************/
777 /**************************************************/
778 struct PushedObject {
779 PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {}
780 ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); }
782 PushedObject& operator=(const PushedObject&) = delete;
783 PushedObject(const PushedObject&) = delete;
784 PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; }
785 PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; }
787 PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return obj; }
788 void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; }
790 auto getState() const -> lua_State* { return state; }
791 auto getNum() const -> int { return num; }
793 int release() { const auto n = num; num = 0; return n; }
794 void pop() { if (num >= 1) lua_pop(state, num); num = 0; }
795 void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; }
803 /**************************************************/
805 /**************************************************/
806 // type used as a tag
810 // tag for "the registry"
811 enum RegistryTag { Registry };
813 // this function takes a value representing the offset to look into
814 // it will look into the top element of the stack and replace the element by its content at the given index
815 template<typename OffsetType1, typename... OffsetTypeOthers>
816 static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) {
817 static_assert(Pusher<typename std::decay<OffsetType1>::type>::minSize == 1 && Pusher<typename std::decay<OffsetType1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
818 auto p1 = Pusher<typename std::decay<OffsetType1>::type>::push(state, offset1);
819 lua_gettable(state, -2);
820 lua_remove(state, -2);
823 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
826 template<typename... OffsetTypeOthers>
827 static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) {
828 lua_getmetatable(state, -1);
829 lua_remove(state, -2);
831 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
834 static void lookIntoStackTop(lua_State*) {
837 // equivalent of lua_settable with t[k]=n, where t is the value at the index in the template parameter, k is the second parameter, n is the last parameter, and n is pushed by the function in the first parameter
838 // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array
839 // the dataPusher MUST push only one thing on the stack
840 // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack
841 template<typename TDataType, typename TIndex, typename TData>
842 static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept
844 static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
845 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
847 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
848 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
850 lua_settable(state, -3);
855 template<typename TDataType, typename TData>
856 static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept
858 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
860 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
861 lua_setfield(state, -2, index.c_str());
865 template<typename TDataType, typename TData>
866 static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept
868 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
870 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
871 lua_setfield(state, -2, index);
875 template<typename TDataType, typename TData>
876 static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept
878 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
880 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
881 lua_setmetatable(state, -2);
885 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
886 static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
887 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
889 static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
891 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
892 lua_gettable(state, -2);
894 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
897 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
898 static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
899 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
901 static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
903 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + std::move(pushedTable);
904 lua_gettable(state, -2);
906 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
909 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
910 static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
912 if (lua_getmetatable(state, -1) == 0)
915 PushedObject p1{state, 1};
917 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
919 lua_setmetatable(state, -2);
924 setTable<TDataType>(state, pushedObject, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
928 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
929 static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
931 if (lua_getmetatable(state, -1) == 0)
934 PushedObject p1{state, 1};
936 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
938 lua_setmetatable(state, -2);
943 setTable<TDataType>(state, std::move(pushedObject), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
947 template<typename TDataType, typename TIndex, typename TData>
948 static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept
950 static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
951 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
953 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
954 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
956 lua_settable(state, LUA_REGISTRYINDEX);
961 template<typename TDataType, typename TData>
962 static void setTable(lua_State* state, RegistryTag, const std::string& index, TData&& data) noexcept
964 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
966 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
967 lua_setfield(state, LUA_REGISTRYINDEX, index.c_str());
971 template<typename TDataType, typename TData>
972 static void setTable(lua_State* state, RegistryTag, const char* index, TData&& data) noexcept
974 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
976 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
977 lua_setfield(state, LUA_REGISTRYINDEX, index);
981 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
982 static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
984 static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
986 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
987 lua_gettable(state, LUA_REGISTRYINDEX);
989 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
992 template<typename TDataType, typename TIndex, typename TData>
993 static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept
995 static_assert(Pusher<typename std::decay<TIndex>::type>::minSize == 1 && Pusher<typename std::decay<TIndex>::type>::maxSize == 1, "Impossible to have a multiple-values index");
996 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
999 # if LUA_VERSION_NUM >= 502
1001 lua_pushglobaltable(state);
1002 PushedObject p3{state, 1};
1003 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
1004 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1005 lua_settable(state, -3);
1009 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
1010 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1011 lua_settable(state, LUA_GLOBALSINDEX);
1019 template<typename TDataType, typename TData>
1020 static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept
1022 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
1024 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1025 lua_setglobal(state, index.c_str());
1029 template<typename TDataType, typename TData>
1030 static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept
1032 static_assert(Pusher<typename std::decay<TDataType>::type>::minSize == 1 && Pusher<typename std::decay<TDataType>::type>::maxSize == 1, "Impossible to have a multiple-values data");
1034 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1035 lua_setglobal(state, index);
1039 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
1040 static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1042 static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
1044 # if LUA_VERSION_NUM >= 502
1046 lua_pushglobaltable(state);
1047 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + PushedObject{state, 1};
1048 lua_gettable(state, -2);
1052 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
1053 lua_gettable(state, LUA_GLOBALSINDEX);
1057 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1060 // TODO: g++ reports "ambiguous overload"
1061 /*template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
1062 static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1064 lua_getglobal(state, index);
1065 PushedObject p1{state, 1};
1067 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1070 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
1071 static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
1073 lua_getglobal(state, index.c_str());
1074 PushedObject p1{state, 1};
1076 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1079 // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value
1080 // warning: first parameter is the number of parameters, not the parameter index
1081 // if read generates an exception, stack is poped anyway
1082 template<typename TReturnType>
1083 static auto readTopAndPop(lua_State* state, PushedObject object)
1086 auto val = Reader<typename std::decay<TReturnType>::type>::read(state, -object.getNum());
1087 if (!val.is_initialized())
1088 throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)};
1092 // checks that the offsets for a type's registrations are set in the registry
1093 static void checkTypeRegistration(lua_State* state, const std::type_info* type)
1095 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1096 lua_gettable(state, LUA_REGISTRYINDEX);
1097 if (!lua_isnil(state, -1)) {
1103 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1104 lua_newtable(state);
1106 lua_pushinteger(state, 0);
1107 lua_newtable(state);
1108 lua_settable(state, -3);
1110 lua_pushinteger(state, 1);
1111 lua_newtable(state);
1112 lua_settable(state, -3);
1114 lua_pushinteger(state, 3);
1115 lua_newtable(state);
1116 lua_settable(state, -3);
1118 lua_pushinteger(state, 4);
1119 lua_newtable(state);
1120 lua_settable(state, -3);
1122 lua_settable(state, LUA_REGISTRYINDEX);
1127 __declspec(noreturn)
1131 static void luaError(lua_State* state)
1135 std::terminate(); // removes compilation warning
1139 /**************************************************/
1140 /* FUNCTIONS REGISTRATION */
1141 /**************************************************/
1142 // the "registerFunction" public functions call this one
1143 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1144 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TObject>, tag<TRetValue (TOtherParams...)>)
1146 static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value || std::is_union<TObject>::value , "registerFunction can only be used for a class a union or a pointer");
1148 checkTypeRegistration(mState, &typeid(TObject));
1149 setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, function);
1151 checkTypeRegistration(mState, &typeid(TObject*));
1152 setTable<TRetValue(TObject*, TOtherParams...)>(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1154 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1155 setTable<TRetValue(std::shared_ptr<TObject>, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 0, functionName, [=](const std::shared_ptr<TObject>& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1158 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1159 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<const TObject>, tag<TRetValue (TOtherParams...)> fTypeTag)
1161 registerFunctionImpl(functionName, function, tag<TObject>{}, fTypeTag);
1163 checkTypeRegistration(mState, &typeid(TObject const*));
1164 setTable<TRetValue(TObject const*, TOtherParams...)>(mState, Registry, &typeid(TObject const*), 0, functionName, [=](TObject const* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1166 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1167 setTable<TRetValue(std::shared_ptr<TObject const>, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 0, functionName, [=](const std::shared_ptr<TObject const>& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1170 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1171 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...)>)
1173 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1176 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1177 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const>)
1179 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1182 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1183 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) volatile>)
1185 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1188 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1189 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const volatile>)
1191 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1194 // the "registerMember" public functions call this one
1195 template<typename TObject, typename TVarType, typename TReadFunction>
1196 void registerMemberImpl(const std::string& name, TReadFunction readFunction)
1198 static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value, "registerMember can only be called on a class or a pointer");
1200 checkTypeRegistration(mState, &typeid(TObject));
1201 setTable<TVarType (TObject&)>(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) {
1202 return readFunction(object);
1205 checkTypeRegistration(mState, &typeid(TObject*));
1206 setTable<TVarType (TObject*)>(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) {
1208 return readFunction(*object);
1211 checkTypeRegistration(mState, &typeid(TObject const*));
1212 setTable<TVarType (TObject const*)>(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) {
1214 return readFunction(*object);
1217 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1218 setTable<TVarType (std::shared_ptr<TObject>)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 1, name, [readFunction](const std::shared_ptr<TObject>& object) {
1220 return readFunction(*object);
1223 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1224 setTable<TVarType (std::shared_ptr<TObject const>)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 1, name, [readFunction](const std::shared_ptr<TObject const>& object) {
1226 return readFunction(*object);
1230 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1231 void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1233 registerMemberImpl<TObject,TVarType>(name, readFunction);
1235 setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) {
1236 writeFunction_(object, value);
1239 setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) {
1241 writeFunction_(*object, value);
1244 setTable<void (std::shared_ptr<TObject>, TVarType)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 4, name, [writeFunction_](std::shared_ptr<TObject> object, const TVarType& value) {
1246 writeFunction_(*object, value);
1250 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1251 void registerMemberImpl(tag<TVarType (TObject::*)>, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1253 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
1256 template<typename TObject, typename TVarType, typename TReadFunction>
1257 void registerMemberImpl(tag<TVarType(TObject::*)>, const std::string& name, TReadFunction readFunction)
1259 registerMemberImpl<TObject, TVarType>(name, std::move(readFunction));
1262 // the "registerMember" public functions call this one
1263 template<typename TObject, typename TVarType, typename TReadFunction>
1264 void registerMemberImpl(TReadFunction readFunction)
1266 checkTypeRegistration(mState, &typeid(TObject));
1267 setTable<TVarType (TObject const&, std::string)>(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) {
1268 return readFunction(object, name);
1271 checkTypeRegistration(mState, &typeid(TObject*));
1272 setTable<TVarType (TObject*, std::string)>(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) {
1274 return readFunction(*object, name);
1277 checkTypeRegistration(mState, &typeid(TObject const*));
1278 setTable<TVarType (TObject const*, std::string)>(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) {
1280 return readFunction(*object, name);
1283 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1284 setTable<TVarType (std::shared_ptr<TObject>, std::string)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 2, [readFunction](const std::shared_ptr<TObject>& object, const std::string& name) {
1286 return readFunction(*object, name);
1289 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1290 setTable<TVarType (std::shared_ptr<TObject const>, std::string)>(mState, Registry, &typeid(std::shared_ptr<TObject const>), 2, [readFunction](const std::shared_ptr<TObject const>& object, const std::string& name) {
1292 return readFunction(*object, name);
1296 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1297 void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction_)
1299 registerMemberImpl<TObject,TVarType>(readFunction);
1301 setTable<void (TObject&, std::string, TVarType)>(mState, Registry, &typeid(TObject), 5, [writeFunction_](TObject& object, const std::string& name, const TVarType& value) {
1302 writeFunction_(object, name, value);
1305 setTable<void (TObject*, std::string, TVarType)>(mState, Registry, &typeid(TObject*), 2, [writeFunction_](TObject* object, const std::string& name, const TVarType& value) {
1307 writeFunction_(*object, name, value);
1310 setTable<void (std::shared_ptr<TObject>, std::string, TVarType)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 2, [writeFunction_](const std::shared_ptr<TObject>& object, const std::string& name, const TVarType& value) {
1312 writeFunction_(*object, name, value);
1316 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1317 void registerMemberImpl(tag<TVarType (TObject::*)>, TReadFunction readFunction, TWriteFunction writeFunction_)
1319 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
1322 template<typename TObject, typename TVarType, typename TReadFunction>
1323 void registerMemberImpl(tag<TVarType(TObject::*)>, TReadFunction readFunction)
1325 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
1329 /**************************************************/
1330 /* LOADING AND CALLING */
1331 /**************************************************/
1332 // this function loads data from the stream and pushes a function at the top of the stack
1333 // throws in case of syntax error
1334 static PushedObject load(lua_State* state, std::istream& code) {
1335 // since the lua_load function requires a static function, we use this structure
1336 // the Reader structure is at the same time an object storing an istream and a buffer,
1337 // and a static function provider
1339 Reader(std::istream& str) : stream(str) {}
1340 std::istream& stream;
1341 std::array<char,512> buffer;
1343 // read function ; "data" must be an instance of Reader
1344 static const char* read(lua_State* /*l*/, void* data, size_t* size) {
1345 assert(size != nullptr);
1346 assert(data != nullptr);
1347 Reader& me = *static_cast<Reader*>(data);
1348 if (me.stream.eof()) { *size = 0; return nullptr; }
1350 me.stream.read(me.buffer.data(), me.buffer.size());
1351 *size = static_cast<size_t>(me.stream.gcount()); // gcount could return a value larger than a size_t, but its maximum is me.buffer.size() so there's no problem
1352 return me.buffer.data();
1356 // we create an instance of Reader, and we call lua_load
1357 Reader reader{code};
1358 const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk"
1359 # if LUA_VERSION_NUM >= 502
1364 // now we have to check return value
1365 if (loadReturnValue != 0) {
1366 // there was an error during loading, an error message was pushed on the stack
1367 const std::string errorMsg = lua_tostring(state, -1);
1369 if (loadReturnValue == LUA_ERRMEM)
1370 throw std::bad_alloc();
1371 else if (loadReturnValue == LUA_ERRSYNTAX)
1372 throw SyntaxErrorException{errorMsg};
1373 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1376 return PushedObject{state, 1};
1379 // this function loads data and pushes a function at the top of the stack
1380 // throws in case of syntax error
1381 static PushedObject load(lua_State* state, const char* code) {
1382 auto loadReturnValue = luaL_loadstring(state, code);
1384 // now we have to check return value
1385 if (loadReturnValue != 0) {
1386 // there was an error during loading, an error message was pushed on the stack
1387 const std::string errorMsg = lua_tostring(state, -1);
1389 if (loadReturnValue == LUA_ERRMEM)
1390 throw std::bad_alloc();
1391 else if (loadReturnValue == LUA_ERRSYNTAX)
1392 throw SyntaxErrorException{errorMsg};
1393 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1396 return PushedObject{state, 1};
1399 // this function calls what is on the top of the stack and removes it (just like lua_call)
1400 // if an exception is triggered, the top of the stack will be removed anyway
1401 template<typename TReturnType, typename... TParameters>
1402 static auto call(lua_State* state, PushedObject toCall, TParameters&&... input)
1405 typedef typename Tupleizer<TReturnType>::type
1408 // we push the parameters on the stack
1409 auto inArguments = Pusher<std::tuple<TParameters&&...>>::push(state, std::forward_as_tuple(std::forward<TParameters>(input)...));
1412 const int outArgumentsCount = std::tuple_size<RealReturnType>::value;
1413 auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount);
1415 // pcall succeeded, we pop the returned values and return them
1416 return readTopAndPop<TReturnType>(state, std::move(outArguments));
1419 static int gettraceback(lua_State* L) {
1420 lua_getglobal(L, "debug"); // stack: error, debug library
1421 lua_getfield(L, -1, "traceback"); // stack: error, debug library, debug.traceback function
1422 lua_remove(L, -2); // stack: error, debug.traceback function
1423 lua_pushstring(L, ""); // stack: error, debug.traceback, ""
1424 lua_pushinteger(L, 2); // stack: error, debug.traceback, "", 2
1425 lua_call(L, 2, 1); // stack: error, traceback
1426 lua_createtable(L, 2, 0); // stack: error, traceback, {}
1427 lua_insert(L, 1); // stack: {}, error, traceback
1428 lua_rawseti(L, 1, 2); // stack: {[2]=traceback}, error
1429 lua_rawseti(L, 1, 1); // stack: {[1]=error,[2]=traceback}
1430 return 1; // return the table
1433 // this function just calls lua_pcall and checks for errors
1434 static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments)
1436 // provide traceback handler
1437 int tbindex = lua_gettop(state) - (functionAndArguments.getNum() - 1);
1438 lua_pushcfunction(state, gettraceback);
1440 // move it back up, before our function and arguments
1441 lua_insert(state, tbindex);
1443 // calling pcall automatically pops the parameters and pushes output
1444 const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, tbindex);
1445 functionAndArguments.release();
1447 lua_remove(state, tbindex); // remove traceback function
1450 // if pcall failed, analyzing the problem and throwing
1451 if (pcallReturnValue != 0) {
1453 // stack top: {error, traceback}
1454 lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error
1455 lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback
1456 lua_remove(state, -3); // stack top: error, traceback
1458 PushedObject traceBackRef{state, 1};
1459 const auto traceBack = readTopAndPop<std::string>(state, std::move(traceBackRef)); // stack top: error
1460 PushedObject errorCode{state, 1};
1462 // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack
1463 if (pcallReturnValue == LUA_ERRMEM) {
1464 throw std::bad_alloc{};
1466 } else if (pcallReturnValue == LUA_ERRRUN) {
1467 if (lua_isstring(state, 1)) {
1468 // the error is a string
1469 const auto str = readTopAndPop<std::string>(state, std::move(errorCode));
1470 throw ExecutionErrorException{str+traceBack};
1473 // an exception_ptr was pushed on the stack
1474 // rethrowing it with an additional ExecutionErrorException
1476 if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
1477 std::rethrow_exception(exp);
1479 } catch(const std::exception& e) {
1480 std::throw_with_nested(ExecutionErrorException{std::string{"Exception thrown by a callback function: "} + e.what()});
1482 std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua. "+traceBack});
1484 throw ExecutionErrorException{"Unknown Lua error"};
1489 return PushedObject{state, outArguments};
1493 /**************************************************/
1494 /* PUSH FUNCTIONS */
1495 /**************************************************/
1496 template<typename T>
1497 static PushedObject push(lua_State* state, T&& value)
1499 return Pusher<typename std::decay<T>::type>::push(state, std::forward<T>(value));
1502 // the Pusher structures allow you to push a value on the stack
1503 // - static const int minSize : minimum size on the stack that the value can have
1504 // - static const int maxSize : maximum size on the stack that the value can have
1505 // - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack
1507 // implementation for custom objects
1508 template<typename TType, typename = void>
1510 static const int minSize = 1;
1511 static const int maxSize = 1;
1513 template<typename TType2>
1514 static PushedObject push(lua_State* state, TType2&& value) noexcept {
1515 // this function is called when lua's garbage collector wants to destroy our object
1516 // we simply call its destructor
1517 const auto garbageCallbackFunction = [](lua_State* lua) -> int {
1518 assert(lua_gettop(lua) == 1);
1519 TType* ptr = static_cast<TType*>(lua_touserdata(lua, 1));
1525 // this function will be stored in __index in the metatable
1526 const auto indexFunction = [](lua_State* lua) -> int {
1528 assert(lua_gettop(lua) == 2);
1529 assert(lua_isuserdata(lua, 1));
1531 // searching for a handler
1532 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1533 lua_gettable(lua, LUA_REGISTRYINDEX);
1534 assert(!lua_isnil(lua, -1));
1536 // looking into getter functions
1537 lua_pushinteger(lua, 0);
1538 lua_gettable(lua, -2);
1539 lua_pushvalue(lua, 2);
1540 lua_gettable(lua, -2);
1541 if (!lua_isnil(lua, -1))
1545 // looking into getter members
1546 lua_pushinteger(lua, 1);
1547 lua_gettable(lua, -2);
1548 lua_pushvalue(lua, 2);
1549 lua_gettable(lua, -2);
1550 if (!lua_isnil(lua, -1)) {
1551 lua_pushvalue(lua, 1);
1552 return callRaw(lua, PushedObject{lua, 2}, 1).release();
1556 // looking into default getter
1557 lua_pushinteger(lua, 2);
1558 lua_gettable(lua, -2);
1559 if (lua_isnil(lua, -1))
1561 lua_pushvalue(lua, 1);
1562 lua_pushvalue(lua, 2);
1563 return callRaw(lua, PushedObject{lua, 3}, 1).release();
1566 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1571 // this function will be stored in __newindex in the metatable
1572 const auto newIndexFunction = [](lua_State* lua) -> int {
1574 assert(lua_gettop(lua) == 3);
1575 assert(lua_isuserdata(lua, 1));
1577 // searching for a handler
1578 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1579 lua_rawget(lua, LUA_REGISTRYINDEX);
1580 assert(!lua_isnil(lua, -1));
1582 // looking into setter members
1583 lua_pushinteger(lua, 4);
1584 lua_rawget(lua, -2);
1585 lua_pushvalue(lua, 2);
1586 lua_rawget(lua, -2);
1587 if (!lua_isnil(lua, -1)) {
1588 lua_pushvalue(lua, 1);
1589 lua_pushvalue(lua, 3);
1590 callRaw(lua, PushedObject{lua, 3}, 0);
1596 // looking into default setter
1597 lua_pushinteger(lua, 5);
1598 lua_rawget(lua, -2);
1599 if (lua_isnil(lua, -1))
1602 lua_pushstring(lua, "No setter found");
1605 lua_pushvalue(lua, 1);
1606 lua_pushvalue(lua, 2);
1607 lua_pushvalue(lua, 3);
1608 callRaw(lua, PushedObject{lua, 4}, 0);
1613 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1618 const auto toStringFunction = [](lua_State* lua) -> int {
1620 assert(lua_gettop(lua) == 1);
1621 assert(lua_isuserdata(lua, 1));
1622 lua_pushstring(lua, "__tostring");
1623 lua_gettable(lua, 1);
1624 if (lua_isnil(lua, -1))
1626 const void *ptr = lua_topointer(lua, -2);
1628 lua_pushstring(lua, (boost::format("userdata 0x%08x") % reinterpret_cast<intptr_t>(ptr)).str().c_str());
1631 lua_pushvalue(lua, 1);
1632 return callRaw(lua, PushedObject{lua, 2}, 1).release();
1634 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1640 // writing structure for this type into the registry
1641 checkTypeRegistration(state, &typeid(TType));
1643 // creating the object
1644 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
1645 // and that's what we do with placement-new
1646 const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
1647 new (pointerLocation) TType(std::forward<TType2>(value));
1648 PushedObject obj{state, 1};
1650 // creating the metatable (over the object on the stack)
1651 // lua_settable pops the key and value we just pushed, so stack management is easy
1652 // all that remains on the stack after these function calls is the metatable
1653 lua_newtable(state);
1654 PushedObject pushedTable{state, 1};
1656 // using the garbage collecting function we created above
1657 if (!boost::has_trivial_destructor<TType>::value)
1659 lua_pushstring(state, "__gc");
1660 lua_pushcfunction(state, garbageCallbackFunction);
1661 lua_settable(state, -3);
1664 // the _typeid index of the metatable will store the type_info*
1665 lua_pushstring(state, "_typeid");
1666 lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
1667 lua_settable(state, -3);
1669 // using the index function we created above
1670 lua_pushstring(state, "__index");
1671 lua_pushcfunction(state, indexFunction);
1672 lua_settable(state, -3);
1674 // using the newindex function we created above
1675 lua_pushstring(state, "__newindex");
1676 lua_pushcfunction(state, newIndexFunction);
1677 lua_settable(state, -3);
1679 lua_pushstring(state, "__tostring");
1680 lua_pushcfunction(state, toStringFunction);
1681 lua_settable(state, -3);
1683 lua_pushstring(state, "__eq");
1684 lua_getglobal(state, LUACONTEXT_GLOBAL_EQ);
1685 lua_settable(state, -3);
1688 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1689 // lua_setmetatable will bind the two together and pop the metatable
1690 // our custom type remains on the stack (and that's what we want since this is a push function)
1691 lua_setmetatable(state, -2);
1692 pushedTable.release();
1698 // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1699 template<typename... TTypes>
1700 struct PusherTotalMinSize;
1702 // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1703 template<typename... TTypes>
1704 struct PusherTotalMaxSize;
1706 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1707 template<typename... TTypes>
1708 struct PusherMinSize;
1710 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1711 template<typename... TTypes>
1712 struct PusherMaxSize;
1715 /**************************************************/
1716 /* READ FUNCTIONS */
1717 /**************************************************/
1718 // the "Reader" structures allow to read data from the stack
1719 // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1720 // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1722 template<typename TType, typename = void>
1724 typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1727 static auto read(lua_State* state, int index)
1728 -> boost::optional<ReturnType>
1730 if (!test(state, index))
1732 return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
1736 static bool test(lua_State* state, int index)
1738 if (!lua_isuserdata(state, index))
1740 if (!lua_getmetatable(state, index))
1743 // now we have our metatable on the top of the stack
1744 // retrieving its _typeid member
1745 lua_pushstring(state, "_typeid");
1746 lua_gettable(state, -2);
1747 const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
1748 const auto typeIDToCompare = &typeid(TType);
1750 // if wrong typeid, returning false
1752 if (storedTypeID != typeIDToCompare)
1760 * This functions reads multiple values starting at "index" and passes them to the callback
1762 template<typename TRetValue, typename TCallback>
1763 static auto readIntoFunction(lua_State* /*state*/, tag<TRetValue>, TCallback&& callback, int /*index*/)
1768 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1769 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1770 -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1773 Binder<TCallback, const TFirstType&> binder{ callback, {} };
1774 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1777 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1779 throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
1781 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1782 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1784 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1785 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1786 -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1789 throw std::logic_error("Wrong number of parameters");
1791 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1793 throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
1795 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1796 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1800 /**************************************************/
1802 /**************************************************/
1803 // structure that will ensure that a certain value is stored somewhere in the registry
1804 struct ValueInRegistry {
1805 // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
1806 ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_}
1808 lua_pushlightuserdata(lua, this);
1809 lua_pushvalue(lua, -1 + index);
1810 lua_settable(lua, LUA_REGISTRYINDEX);
1813 // removing the function from the registry
1816 lua_pushlightuserdata(lua, this);
1818 lua_settable(lua, LUA_REGISTRYINDEX);
1821 // loads the value and puts it at the top of the stack
1824 lua_pushlightuserdata(lua, this);
1825 lua_gettable(lua, LUA_REGISTRYINDEX);
1826 return PushedObject{lua, 1};
1829 ValueInRegistry(const ValueInRegistry&) = delete;
1830 ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1836 // binds the first parameter of a function object
1837 template<typename TFunctionObject, typename TFirstParamType>
1839 TFunctionObject function;
1840 TFirstParamType param;
1842 template<typename... TParams>
1843 auto operator()(TParams&&... params)
1844 -> decltype(function(param, std::forward<TParams>(params)...))
1846 return function(param, std::forward<TParams>(params)...);
1850 // turns a type into a tuple
1851 // void is turned into std::tuple<>
1852 // existing tuples are untouched
1853 template<typename T>
1856 // this structure takes a pointer to a member function type and returns the base function type
1857 template<typename TType>
1858 struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug
1860 // this structure takes any object and detects its function type
1861 template<typename TObjectType>
1862 struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1864 // this structure takes a function arguments list and has the "min" and the "max" static const member variables, whose value equal to the min and max number of parameters for the function
1865 // the only case where "min != max" is with boost::optional at the end of the list
1866 template<typename... TArgumentsList>
1867 struct FunctionArgumentsCounter {};
1869 // true is the template parameter is a boost::optional
1870 template<typename T>
1871 struct IsOptional : public std::false_type {};
1875 static LuaContext::EmptyArray_t ATTR_UNUSED
1878 static LuaContext::Metatable_t ATTR_UNUSED
1881 /**************************************************/
1882 /* PARTIAL IMPLEMENTATIONS */
1883 /**************************************************/
1885 inline auto LuaContext::readTopAndPop<void>(lua_State* /*state*/, PushedObject /*obj*/)
1890 // this structure takes a template parameter T
1891 // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1892 // we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1893 template<typename T>
1894 struct LuaContext::Tupleizer { typedef std::tuple<T> type; };
1895 template<typename... Args>
1896 struct LuaContext::Tupleizer<std::tuple<Args...>> { typedef std::tuple<Args...> type; };
1898 struct LuaContext::Tupleizer<void> { typedef std::tuple<> type; };
1900 // this structure takes any object and detects its function type
1901 template<typename TRetValue, typename... TParameters>
1902 struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)> { typedef TRetValue type(TParameters...); };
1903 template<typename TObjectType>
1904 struct LuaContext::FunctionTypeDetector<TObjectType*> { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1906 // this structure takes a pointer to a member function type and returns the base function type
1907 template<typename TType, typename TRetValue, typename... TParameters>
1908 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)> { typedef TRetValue type(TParameters...); };
1909 template<typename TType, typename TRetValue, typename... TParameters>
1910 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const> { typedef TRetValue type(TParameters...); };
1911 template<typename TType, typename TRetValue, typename... TParameters>
1912 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile> { typedef TRetValue type(TParameters...); };
1913 template<typename TType, typename TRetValue, typename... TParameters>
1914 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile> { typedef TRetValue type(TParameters...); };
1916 // implementation of PusherTotalMinSize
1917 template<typename TFirst, typename... TTypes>
1918 struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1920 struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1922 // implementation of PusherTotalMaxSize
1923 template<typename TFirst, typename... TTypes>
1924 struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1926 struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1928 // implementation of PusherMinSize
1929 template<typename TFirst, typename TSecond, typename... TTypes>
1930 struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...>
1932 static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize
1934 PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size
1936 PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1939 template<typename TFirst>
1940 struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
1942 // implementation of PusherMaxSize
1943 template<typename TFirst, typename... TTypes>
1944 struct LuaContext::PusherMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize > PusherTotalMaxSize<TTypes...>::size ? Pusher<typename std::decay<TFirst>::type>::maxSize : PusherMaxSize<TTypes...>::size; };
1946 struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1948 // implementation of FunctionArgumentsCounter
1949 template<typename TFirst, typename... TParams>
1950 struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1951 typedef FunctionArgumentsCounter<TParams...>
1953 static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1954 static const int max = 1 + SubType::max;
1957 struct LuaContext::FunctionArgumentsCounter<> {
1958 static const int min = 0;
1959 static const int max = 0;
1962 // implementation of IsOptional
1963 template<typename T>
1964 struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1966 // implementation of LuaFunctionCaller
1967 template<typename TFunctionType>
1968 class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1969 template<typename TRetValue, typename... TParams>
1970 class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1973 TRetValue operator()(TParams&&... params) const
1975 auto obj = valueHolder->pop();
1976 return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
1980 std::shared_ptr<ValueInRegistry> valueHolder;
1985 explicit LuaFunctionCaller(lua_State* state_, int index) :
1986 valueHolder(std::make_shared<ValueInRegistry>(state_, index)),
1992 /**************************************************/
1993 /* PUSH FUNCTIONS */
1994 /**************************************************/
1995 // specializations of the Pusher structure
1997 // opaque Lua references
1999 struct LuaContext::Pusher<LuaContext::LuaObject> {
2000 static const int minSize = 1;
2001 static const int maxSize = 1;
2003 static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept {
2004 if (value.objectInRegistry.get()) {
2005 PushedObject obj = value.objectInRegistry->pop();
2009 return PushedObject{state, 1};
2016 struct LuaContext::Pusher<bool> {
2017 static const int minSize = 1;
2018 static const int maxSize = 1;
2020 static PushedObject push(lua_State* state, bool value) noexcept {
2021 lua_pushboolean(state, value);
2022 return PushedObject{state, 1};
2028 struct LuaContext::Pusher<std::string> {
2029 static const int minSize = 1;
2030 static const int maxSize = 1;
2032 static PushedObject push(lua_State* state, const std::string& value) noexcept {
2033 lua_pushlstring(state, value.c_str(), value.length());
2034 return PushedObject{state, 1};
2040 struct LuaContext::Pusher<const char*> {
2041 static const int minSize = 1;
2042 static const int maxSize = 1;
2044 static PushedObject push(lua_State* state, const char* value) noexcept {
2045 lua_pushstring(state, value);
2046 return PushedObject{state, 1};
2052 struct LuaContext::Pusher<const char[N]> {
2053 static const int minSize = 1;
2054 static const int maxSize = 1;
2056 static PushedObject push(lua_State* state, const char* value) noexcept {
2057 lua_pushstring(state, value);
2058 return PushedObject{state, 1};
2063 template<typename T>
2064 struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
2065 static const int minSize = 1;
2066 static const int maxSize = 1;
2068 static PushedObject push(lua_State* state, T value) noexcept {
2069 lua_pushnumber(state, value);
2070 return PushedObject{state, 1};
2075 template<typename T>
2076 struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2077 static const int minSize = 1;
2078 static const int maxSize = 1;
2080 static PushedObject push(lua_State* state, T value) noexcept {
2081 lua_pushinteger(state, value);
2082 return PushedObject{state, 1};
2088 struct LuaContext::Pusher<std::nullptr_t> {
2089 static const int minSize = 1;
2090 static const int maxSize = 1;
2092 static PushedObject push(lua_State* state, std::nullptr_t) noexcept {
2094 return PushedObject{state, 1};
2100 struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
2101 static const int minSize = 1;
2102 static const int maxSize = 1;
2104 static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
2105 lua_newtable(state);
2106 return PushedObject{state, 1};
2110 // std::type_info* is a lightuserdata
2112 struct LuaContext::Pusher<const std::type_info*> {
2113 static const int minSize = 1;
2114 static const int maxSize = 1;
2116 static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
2117 lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
2118 return PushedObject{state, 1};
2124 struct LuaContext::Pusher<LuaContext::ThreadID> {
2125 static const int minSize = 1;
2126 static const int maxSize = 1;
2128 static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
2129 lua_pushthread(value.state);
2130 return PushedObject{state, 1};
2135 template<typename TKey, typename TValue>
2136 struct LuaContext::Pusher<std::map<TKey,TValue>> {
2137 static const int minSize = 1;
2138 static const int maxSize = 1;
2140 static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
2141 static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2142 static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2144 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2146 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2147 setTable<TValue>(state, obj, i->first, i->second);
2154 template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2155 struct LuaContext::Pusher<std::unordered_map<TKey,TValue,THash,TKeyEqual>> {
2156 static const int minSize = 1;
2157 static const int maxSize = 1;
2159 static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue,THash,TKeyEqual>& value) noexcept {
2160 static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2161 static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2163 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2165 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2166 setTable<TValue>(state, obj, i->first, i->second);
2173 template<typename TType1, typename TType2>
2174 struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
2175 static const int minSize = 1;
2176 static const int maxSize = 1;
2178 static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
2179 static_assert(Pusher<typename std::decay<TType1>::type>::minSize == 1 && Pusher<typename std::decay<TType1>::type>::maxSize == 1, "Can't push multiple elements for a table key");
2180 static_assert(Pusher<typename std::decay<TType2>::type>::minSize == 1 && Pusher<typename std::decay<TType2>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2182 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2184 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2185 setTable<TType2>(state, obj, i->first, i->second);
2192 template<typename TType>
2193 struct LuaContext::Pusher<std::vector<TType>> {
2194 static const int minSize = 1;
2195 static const int maxSize = 1;
2197 static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
2198 static_assert(Pusher<typename std::decay<TType>::type>::minSize == 1 && Pusher<typename std::decay<TType>::type>::maxSize == 1, "Can't push multiple elements for a table value");
2200 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2202 for (unsigned int i = 0; i < value.size(); ++i)
2203 setTable<TType>(state, obj, i + 1, value[i]);
2210 template<typename TType>
2211 struct LuaContext::Pusher<std::unique_ptr<TType>> {
2212 static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2213 static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2215 static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2216 return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2221 template<typename TEnum>
2222 struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2223 #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2224 typedef typename std::underlying_type<TEnum>::type
2227 // implementation when std::underlying_type is not supported
2228 typedef unsigned long
2232 static const int minSize = Pusher<RealType>::minSize;
2233 static const int maxSize = Pusher<RealType>::maxSize;
2235 static PushedObject push(lua_State* state, TEnum value) noexcept {
2236 return Pusher<RealType>::push(state, static_cast<RealType>(value));
2241 // this specialization is not directly called, but is called by other specializations
2242 template<typename TReturnType, typename... TParameters>
2243 struct LuaContext::Pusher<TReturnType (TParameters...)>
2245 static const int minSize = 1;
2246 static const int maxSize = 1;
2248 // counts the number of arguments
2249 typedef FunctionArgumentsCounter<TParameters...>
2250 LocalFunctionArgumentsCounter;
2252 // this is the version of "push" for non-trivially destructible function objects
2253 template<typename TFunctionObject>
2254 static auto push(lua_State* state, TFunctionObject fn) noexcept
2255 -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2257 // TODO: is_move_constructible not supported by some compilers
2258 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2260 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2261 // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2262 // so we use userdata instead
2264 // this function is called when the lua script tries to call our custom data type
2265 // we transfer execution to the "callback" function below
2266 const auto callCallback = [](lua_State* lua) -> int {
2267 assert(lua_gettop(lua) >= 1);
2268 assert(lua_isuserdata(lua, 1));
2269 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2272 return callback(lua, function, lua_gettop(lua) - 1).release();
2275 // this one is called when lua's garbage collector no longer needs our custom data type
2276 // we call the function object's destructor
2277 const auto garbageCallback = [](lua_State* lua) -> int {
2278 assert(lua_gettop(lua) == 1);
2279 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2281 function->~TFunctionObject();
2285 // creating the object
2286 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2287 // and that's what we do with placement-new
2288 const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2289 new (functionLocation) TFunctionObject(std::move(fn));
2291 // creating the metatable (over the object on the stack)
2292 // lua_settable pops the key and value we just pushed, so stack management is easy
2293 // all that remains on the stack after these function calls is the metatable
2294 lua_newtable(state);
2295 lua_pushstring(state, "__call");
2296 lua_pushcfunction(state, callCallback);
2297 lua_settable(state, -3);
2299 lua_pushstring(state, "__gc");
2300 lua_pushcfunction(state, garbageCallback);
2301 lua_settable(state, -3);
2303 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2304 // lua_setmetatable will bind the two together and pop the metatable
2305 // our custom function remains on the stack (and that's what we want)
2306 lua_setmetatable(state, -2);
2308 return PushedObject{state, 1};
2311 // this is the version of "push" for trivially destructible objects
2312 template<typename TFunctionObject>
2313 static auto push(lua_State* state, TFunctionObject fn) noexcept
2314 -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2316 // TODO: is_move_constructible not supported by some compilers
2317 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2319 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2320 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2322 // this is the cfunction that is the callback
2323 const auto function = [](lua_State* state_) -> int
2325 // the function object is an upvalue
2326 const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state_, lua_upvalueindex(1)));
2327 return callback(state_, toCall, lua_gettop(state_)).release();
2330 // we copy the function object onto the stack
2331 const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2332 new (functionObjectLocation) TFunctionObject(std::move(fn));
2334 // pushing the function with the function object as upvalue
2335 lua_pushcclosure(state, function, 1);
2336 return PushedObject{state, 1};
2339 // this is the version of "push" for pointer to functions
2340 static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2343 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2344 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2346 // this is the cfunction that is the callback
2347 const auto function = [](lua_State* state_) -> int
2349 // the function object is an upvalue
2350 const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state_, lua_upvalueindex(1)));
2351 return callback(state_, toCall, lua_gettop(state_)).release();
2354 // we copy the function object onto the stack
2355 lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
2357 // pushing the function with the function object as upvalue
2358 lua_pushcclosure(state, function, 1);
2359 return PushedObject{state, 1};
2362 // this is the version of "push" for references to functions
2363 static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2366 return push(state, &fn);
2370 // callback that calls the function object
2371 // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2372 template<typename TFunctionObject>
2373 static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2376 // checking if number of parameters is correct
2377 if (argumentsCount < LocalFunctionArgumentsCounter::min) {
2378 // if not, using lua_error to return an error
2379 luaL_where(state, 1);
2380 lua_pushstring(state, "This function requires at least ");
2381 lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
2382 lua_pushstring(state, " parameter(s)");
2383 lua_concat(state, 4);
2386 } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
2387 // if not, using lua_error to return an error
2388 luaL_where(state, 1);
2389 lua_pushstring(state, "This function requires at most ");
2390 lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
2391 lua_pushstring(state, " parameter(s)");
2392 lua_concat(state, 4);
2396 // calling the function
2398 return callback2(state, *toCall, argumentsCount);
2400 } catch (const WrongTypeException& ex) {
2401 // wrong parameter type, using lua_error to return an error
2402 luaL_where(state, 1);
2403 lua_pushstring(state, "Unable to convert parameter from ");
2404 lua_pushstring(state, ex.luaType.c_str());
2405 lua_pushstring(state, " to ");
2406 lua_pushstring(state, ex.destination.name());
2407 lua_concat(state, 5);
2410 } catch (const std::exception& e) {
2411 luaL_where(state, 1);
2412 lua_pushstring(state, "Caught exception: ");
2413 lua_pushstring(state, e.what());
2414 lua_concat(state, 3);
2417 Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
2422 template<typename TFunctionObject>
2423 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2424 -> typename std::enable_if<!std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2426 // pushing the result on the stack and returning number of pushed elements
2427 typedef Pusher<typename std::decay<TReturnType>::type>
2429 return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
2432 template<typename TFunctionObject>
2433 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2434 -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2436 readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
2437 return PushedObject{state, 0};
2441 // C function pointers
2442 template<typename TReturnType, typename... TParameters>
2443 struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2445 // using the function-pushing implementation
2446 typedef Pusher<TReturnType (TParameters...)>
2448 static const int minSize = SubPusher::minSize;
2449 static const int maxSize = SubPusher::maxSize;
2451 template<typename TType>
2452 static PushedObject push(lua_State* state, TType value) noexcept {
2453 return SubPusher::push(state, value);
2457 // C function references
2458 template<typename TReturnType, typename... TParameters>
2459 struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2461 // using the function-pushing implementation
2462 typedef Pusher<TReturnType(TParameters...)>
2464 static const int minSize = SubPusher::minSize;
2465 static const int maxSize = SubPusher::maxSize;
2467 template<typename TType>
2468 static PushedObject push(lua_State* state, TType value) noexcept {
2469 return SubPusher::push(state, value);
2474 template<typename TReturnType, typename... TParameters>
2475 struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2477 // using the function-pushing implementation
2478 typedef Pusher<TReturnType (TParameters...)>
2480 static const int minSize = SubPusher::minSize;
2481 static const int maxSize = SubPusher::maxSize;
2483 static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2484 return SubPusher::push(state, value);
2489 template<typename... TTypes>
2490 struct LuaContext::Pusher<boost::variant<TTypes...>>
2492 static const int minSize = PusherMinSize<TTypes...>::size;
2493 static const int maxSize = PusherMaxSize<TTypes...>::size;
2495 static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
2496 PushedObject obj{state, 0};
2497 VariantWriter writer{state, obj};
2498 value.apply_visitor(writer);
2503 struct VariantWriter : public boost::static_visitor<> {
2504 template<typename TType>
2505 void operator()(TType value) noexcept
2507 obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
2510 VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {}
2517 template<typename TType>
2518 struct LuaContext::Pusher<boost::optional<TType>> {
2519 typedef Pusher<typename std::decay<TType>::type>
2522 static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2523 static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2525 static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
2527 return UnderlyingPusher::push(state, value.get());
2530 return PushedObject{state, 1};
2536 template<typename... TTypes>
2537 struct LuaContext::Pusher<std::tuple<TTypes...>> {
2538 // TODO: NOT EXCEPTION SAFE /!\ //
2539 static const int minSize = PusherTotalMinSize<TTypes...>::size;
2540 static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2542 static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2543 return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2546 static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
2547 return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
2552 static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2553 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2555 return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2556 push2(state, value, std::integral_constant<int,N+1>{});
2560 static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
2561 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2563 return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
2564 push2(state, std::move(value), std::integral_constant<int,N+1>{});
2567 static int push2(lua_State* /*state*/, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2571 static int push2(lua_State* /*state*/, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2576 /**************************************************/
2577 /* READ FUNCTIONS */
2578 /**************************************************/
2579 // specializations of the Reader structures
2581 // opaque Lua references
2583 struct LuaContext::Reader<LuaContext::LuaObject>
2585 static auto read(lua_State* state, int index)
2586 -> boost::optional<LuaContext::LuaObject>
2588 LuaContext::LuaObject obj(state, index);
2595 struct LuaContext::Reader<std::nullptr_t>
2597 static auto read(lua_State* state, int index)
2598 -> boost::optional<std::nullptr_t>
2600 if (!lua_isnil(state, index))
2607 template<typename TType>
2608 struct LuaContext::Reader<
2610 typename std::enable_if<std::is_integral<TType>::value>::type
2613 static auto read(lua_State* state, int index)
2614 -> boost::optional<TType>
2616 # if LUA_VERSION_NUM >= 502
2619 auto value = lua_tointegerx(state, index, &success);
2622 return static_cast<TType>(value);
2626 if (!lua_isnumber(state, index))
2628 return static_cast<TType>(lua_tointeger(state, index));
2635 template<typename TType>
2636 struct LuaContext::Reader<
2638 typename std::enable_if<std::is_floating_point<TType>::value>::type
2641 static auto read(lua_State* state, int index)
2642 -> boost::optional<TType>
2644 # if LUA_VERSION_NUM >= 502
2647 auto value = lua_tonumberx(state, index, &success);
2650 return static_cast<TType>(value);
2654 if (!lua_isnumber(state, index))
2656 return static_cast<TType>(lua_tonumber(state, index));
2664 struct LuaContext::Reader<bool>
2666 static auto read(lua_State* state, int index)
2667 -> boost::optional<bool>
2669 if (!lua_isboolean(state, index))
2671 return lua_toboolean(state, index) != 0;
2676 // lua_tostring returns a temporary pointer, but that's not a problem since we copy
2677 // the data into a std::string
2679 struct LuaContext::Reader<std::string>
2681 static auto read(lua_State* state, int index)
2682 -> boost::optional<std::string>
2685 const auto val = lua_tolstring(state, index, &len);
2688 return std::string(val, len);
2693 template<typename TType>
2694 struct LuaContext::Reader<
2696 typename std::enable_if<std::is_enum<TType>::value>::type
2699 static auto read(lua_State* state, int index)
2700 -> boost::optional<TType>
2702 if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
2704 return static_cast<TType>(lua_tointeger(state, index));
2708 // LuaFunctionCaller
2709 template<typename TRetValue, typename... TParameters>
2710 struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2712 typedef LuaFunctionCaller<TRetValue (TParameters...)>
2715 static auto read(lua_State* state, int index)
2716 -> boost::optional<ReturnType>
2718 if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
2720 return ReturnType(state, index);
2725 template<typename TRetValue, typename... TParameters>
2726 struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2728 static auto read(lua_State* state, int index)
2729 -> boost::optional<std::function<TRetValue (TParameters...)>>
2731 if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
2733 std::function<TRetValue (TParameters...)> f{*val};
2734 return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
2742 template<typename TType1, typename TType2>
2743 struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2745 static auto read(lua_State* state, int index)
2746 -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2748 if (!lua_istable(state, index))
2751 std::vector<std::pair<TType1, TType2>> result;
2753 // we traverse the table at the top of the stack
2754 lua_pushnil(state); // first key
2755 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2756 // now a key and its value are pushed on the stack
2758 auto val1 = Reader<TType1>::read(state, -2);
2759 auto val2 = Reader<TType2>::read(state, -1);
2761 if (!val1.is_initialized() || !val2.is_initialized()) {
2762 lua_pop(state, 2); // we remove the value and the key
2766 result.push_back({ val1.get(), val2.get() });
2767 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2770 lua_pop(state, 2); // we remove the value and the key
2775 return { std::move(result) };
2780 template<typename TKey, typename TValue>
2781 struct LuaContext::Reader<std::map<TKey,TValue>>
2783 static auto read(lua_State* state, int index)
2784 -> boost::optional<std::map<TKey,TValue>>
2786 if (!lua_istable(state, index))
2789 std::map<TKey,TValue> result;
2791 // we traverse the table at the top of the stack
2792 lua_pushnil(state); // first key
2793 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2794 // now a key and its value are pushed on the stack
2796 auto key = Reader<TKey>::read(state, -2);
2797 auto value = Reader<TValue>::read(state, -1);
2799 if (!key.is_initialized() || !value.is_initialized()) {
2800 lua_pop(state, 2); // we remove the value and the key
2804 result.insert({ key.get(), value.get() });
2805 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2808 lua_pop(state, 2); // we remove the value and the key
2813 return { std::move(result) };
2818 template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2819 struct LuaContext::Reader<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2821 static auto read(lua_State* state, int index)
2822 -> boost::optional<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2824 if (!lua_istable(state, index))
2827 std::unordered_map<TKey,TValue,THash,TKeyEqual> result;
2829 // we traverse the table at the top of the stack
2830 lua_pushnil(state); // first key
2831 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2832 // now a key and its value are pushed on the stack
2834 auto key = Reader<TKey>::read(state, -2);
2835 auto value = Reader<TValue>::read(state, -1);
2837 if (!key.is_initialized() || !value.is_initialized()) {
2838 lua_pop(state, 2); // we remove the value and the key
2842 result.insert({ key.get(), value.get() });
2843 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2846 lua_pop(state, 2); // we remove the value and the key
2851 return { std::move(result) };
2856 // IMPORTANT: optional means "either nil or the value of the right type"
2857 // * if the value is nil, then an optional containing an empty optional is returned
2858 // * if the value is of the right type, then an optional containing an optional containing the value is returned
2859 // * if the value is of the wrong type, then an empty optional is returned
2860 template<typename TType>
2861 struct LuaContext::Reader<boost::optional<TType>>
2863 static auto read(lua_State* state, int index)
2864 -> boost::optional<boost::optional<TType>>
2866 if (lua_isnil(state, index))
2867 return boost::optional<TType>{boost::none};
2868 if (auto&& other = Reader<TType>::read(state, index))
2869 return std::move(other);
2875 template<typename... TTypes>
2876 struct LuaContext::Reader<boost::variant<TTypes...>>
2878 typedef boost::variant<TTypes...>
2882 // class doing operations for a range of types from TIterBegin to TIterEnd
2883 template<typename TIterBegin, typename TIterEnd, typename = void>
2884 struct VariantReader
2886 using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2888 static auto read(lua_State* state, int index)
2889 -> boost::optional<ReturnType>
2891 // note: using SubReader::read triggers a compilation error when used with a reference
2892 if (const auto val = SubReader::read(state, index))
2893 return boost::variant<TTypes...>{*val};
2894 return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
2898 // specialization of class above being called when list of remaining types is empty
2899 template<typename TIterBegin, typename TIterEnd>
2900 struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2902 static auto read(lua_State* /*state*/, int /*index*/)
2903 -> boost::optional<ReturnType>
2909 // this is the main type
2910 typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2914 static auto read(lua_State* state, int index)
2915 -> boost::optional<ReturnType>
2917 return MainVariantReader::read(state, index);
2922 // tuple have an additional argument for their functions, that is the maximum size to read
2923 // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2925 struct LuaContext::Reader<std::tuple<>>
2927 static auto read(lua_State* /*state*/, int /*index*/, int /*maxSize*/ = 0)
2928 -> boost::optional<std::tuple<>>
2930 return std::tuple<>{};
2934 template<typename TFirst, typename... TOthers>
2935 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2936 typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2939 // this is the "TFirst is NOT default constructible" version
2941 typedef std::tuple<TFirst, TOthers...>
2944 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2945 -> boost::optional<ReturnType>
2950 auto firstVal = Reader<TFirst>::read(state, index);
2951 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2953 if (!firstVal || !othersVal)
2956 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2960 template<typename TFirst, typename... TOthers>
2961 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2962 typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2965 // this is the "TFirst is default-constructible" version
2967 typedef std::tuple<TFirst, TOthers...>
2970 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2971 -> boost::optional<ReturnType>
2973 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2978 return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2980 auto firstVal = Reader<TFirst>::read(state, index);
2984 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2988 #if defined(__GNUC__) && !defined(__clang__)
2989 #pragma GCC diagnostic pop