2 Copyright (c) 2013, Pierre KRIEGER
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the <organization> nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #ifndef INCLUDE_LUACONTEXT_HPP
29 #define INCLUDE_LUACONTEXT_HPP
47 #include <type_traits>
48 #include <unordered_map>
49 #include <boost/any.hpp>
50 #include <boost/mpl/distance.hpp>
51 #include <boost/mpl/transform.hpp>
52 #include <boost/optional.hpp>
53 #include <boost/variant.hpp>
54 #include <boost/type_traits.hpp>
58 # include "misc/exception.hpp"
62 # define ATTR_UNUSED __attribute__((unused))
68 * Defines a Lua context
69 * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions),
70 * we only provide few functions like readVariable and writeVariable.
72 * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert
73 * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types
74 * it wants. These arguments may only be of basic types (int, float, etc.) or std::string.
77 struct ValueInRegistry;
78 template<typename TFunctionObject, typename TFirstParamType> struct Binder;
79 template<typename T> struct IsOptional;
80 enum Globals_t { Globals }; // tag for "global variables"
83 * @param openDefaultLibs True if luaL_openlibs should be called
85 explicit LuaContext(bool openDefaultLibs = true)
87 // luaL_newstate can return null if allocation failed
88 mState = luaL_newstate();
89 if (mState == nullptr)
90 throw std::bad_alloc();
92 // setting the panic function
93 lua_atpanic(mState, [](lua_State* state) -> int {
94 const std::string str = lua_tostring(state, -1);
96 assert(false && "lua_atpanic triggered");
100 // opening default library if required to do so
102 luaL_openlibs(mState);
108 LuaContext(LuaContext&& s) :
111 s.mState = luaL_newstate();
117 LuaContext& operator=(LuaContext&& s) noexcept
119 std::swap(mState, s.mState);
126 LuaContext(const LuaContext&) = delete;
131 LuaContext& operator=(const LuaContext&) = delete;
136 ~LuaContext() noexcept
143 * Thrown when an error happens during execution of lua code (like not enough parameters for a function)
145 class ExecutionErrorException : public std::runtime_error
148 ExecutionErrorException(const std::string& msg) :
149 std::runtime_error(msg)
155 * Thrown when a syntax error happens in a lua script
157 class SyntaxErrorException : public std::runtime_error
160 SyntaxErrorException(const std::string& msg) :
161 std::runtime_error(msg)
167 * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string
169 class WrongTypeException : public std::runtime_error
172 WrongTypeException(std::string luaType, const std::type_info& destination) :
173 std::runtime_error("Trying to cast a lua variable from \"" + luaType + "\" to \"" + destination.name() + "\""),
175 destination(destination)
180 const std::type_info& destination;
184 * Function object that can call a function stored by Lua
185 * This type is copiable and movable, but not constructible. It can only be created through readVariable.
186 * @tparam TFunctionType Function type (eg. "int (int, bool)")
188 template<typename TFunctionType>
189 class LuaFunctionCaller;
192 * Opaque type that identifies a Lua thread
195 ThreadID() = default;
196 ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { }
197 ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; }
201 std::unique_ptr<ValueInRegistry> threadInRegistry;
205 * Type that is considered as an empty array
207 enum EmptyArray_t { EmptyArray };
210 * Type for a metatable
212 enum Metatable_t { Metatable };
215 * Executes lua code from the stream
216 * @param code A stream that Lua will read its code from
218 void executeCode(std::istream& code)
220 auto toCall = load(mState, code);
221 call<std::tuple<>>(mState, std::move(toCall));
225 * Executes lua code from the stream and returns a value
226 * @param code A stream that Lua will read its code from
227 * @tparam TType The type that the executing code should return
229 template<typename TType>
230 auto executeCode(std::istream& code)
233 auto toCall = load(mState, code);
234 return call<TType>(mState, std::move(toCall));
238 * Executes lua code given as parameter
239 * @param code A string containing code that will be executed by Lua
241 void executeCode(const std::string& code)
243 executeCode(code.c_str());
247 * Executes Lua code from the stream and returns a value
248 * @param code A string containing code that will be executed by Lua
249 * @tparam TType The type that the executing code should return
251 template<typename TType>
252 auto executeCode(const std::string& code)
255 return executeCode<TType>(code.c_str());
260 * @param code A string containing code that will be executed by Lua
262 void executeCode(const char* code)
264 auto toCall = load(mState, code);
265 call<std::tuple<>>(mState, std::move(toCall));
269 * Executes Lua code from the stream and returns a value
270 * @param code A string containing code that will be executed by Lua
271 * @tparam TType The type that the executing code should return
273 template<typename TType>
274 auto executeCode(const char* code)
277 auto toCall = load(mState, code);
278 return call<TType>(mState, std::move(toCall));
282 * Executes lua code from the stream
283 * @param code A stream that Lua will read its code from
285 void executeCode(const ThreadID& thread, std::istream& code)
287 auto toCall = load(thread.state, code);
288 call<std::tuple<>>(thread.state, std::move(toCall));
292 * Executes lua code from the stream and returns a value
293 * @param code A stream that Lua will read its code from
294 * @tparam TType The type that the executing code should return
296 template<typename TType>
297 auto executeCode(const ThreadID& thread, std::istream& code)
300 auto toCall = load(thread.state, code);
301 return call<TType>(thread.state, std::move(toCall));
305 * Executes lua code given as parameter
306 * @param code A string containing code that will be executed by Lua
308 void executeCode(const ThreadID& thread, const std::string& code)
310 executeCode(thread, code.c_str());
314 * Executes Lua code from the stream and returns a value
315 * @param code A string containing code that will be executed by Lua
316 * @tparam TType The type that the executing code should return
318 template<typename TType>
319 auto executeCode(const ThreadID& thread, const std::string& code)
322 return executeCode<TType>(thread, code.c_str());
327 * @param code A string containing code that will be executed by Lua
329 void executeCode(const ThreadID& thread, const char* code)
331 auto toCall = load(thread.state, code);
332 call<std::tuple<>>(thread.state, std::move(toCall));
336 * Executes Lua code from the stream and returns a value
337 * @param code A string containing code that will be executed by Lua
338 * @tparam TType The type that the executing code should return
340 template<typename TType>
341 auto executeCode(const ThreadID& thread, const char* code)
344 auto toCall = load(thread.state, code);
345 return call<TType>(thread.state, std::move(toCall));
349 * Tells that Lua will be allowed to access an object's function
350 * This is the version "registerFunction(name, &Foo::function)"
352 template<typename TPointerToMemberFunction>
353 auto registerFunction(const std::string& name, TPointerToMemberFunction pointer)
354 -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
356 registerFunctionImpl(name, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
360 * Tells that Lua will be allowed to access an object's function
361 * This is the version with an explicit template parameter: "registerFunction<void (Foo::*)()>(name, [](Foo&) { })"
362 * @param fn Function object which takes as first parameter a reference to the object
363 * @tparam TFunctionType Pointer-to-member function type
365 template<typename TFunctionType, typename TType>
366 void registerFunction(const std::string& functionName, TType fn)
368 static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
369 registerFunctionImpl(functionName, std::move(fn), tag<TFunctionType>{});
373 * Tells that Lua will be allowed to access an object's function
374 * This is the alternative version with an explicit template parameter: "registerFunction<Foo, void (*)()>(name, [](Foo&) { })"
375 * @param fn Function object which takes as first parameter a reference to the object
376 * @tparam TObject Object to register this function to
377 * @tparam TFunctionType Function type
379 template<typename TObject, typename TFunctionType, typename TType>
380 void registerFunction(const std::string& functionName, TType fn)
382 static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
383 registerFunctionImpl(functionName, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
387 * Inverse operation of registerFunction
388 * @tparam TType Type whose function belongs to
390 template<typename TType>
391 void unregisterFunction(const std::string& functionName)
393 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType)));
395 lua_settable(mState, LUA_REGISTRYINDEX);
396 checkTypeRegistration(mState, &typeid(TType));
398 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(TType*)));
400 lua_settable(mState, LUA_REGISTRYINDEX);
401 checkTypeRegistration(mState, &typeid(TType*));
403 lua_pushlightuserdata(mState, const_cast<std::type_info*>(&typeid(std::shared_ptr<TType>)));
405 lua_settable(mState, LUA_REGISTRYINDEX);
406 checkTypeRegistration(mState, &typeid(std::shared_ptr<TType>));
410 * Registers a member variable
411 * This is the version "registerMember(name, &Foo::member)"
413 template<typename TObject, typename TVarType>
414 void registerMember(const std::string& name, TVarType TObject::*member)
416 // implementation simply calls the custom member with getter and setter
417 const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; };
418 const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; };
419 registerMember<TVarType (TObject::*)>(name, getter, setter);
423 * Registers a member variable
424 * This is the version "registerMember<Foo, int>(name, getter, setter)"
425 * @tparam TObject Type to register the member to
426 * @tparam TVarType Type of the member
427 * @param name Name of the member to register
428 * @param readFunction Function of type "TVarType (const TObject&)"
429 * @param writeFunction Function of type "void (TObject&, const TVarType&)"
431 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
432 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction)
434 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction));
438 * Registers a member variable
439 * This is the version "registerMember<int (Foo::*)>(name, getter, setter)"
440 * @tparam TMemberType Pointer to member object representing the type
441 * @param name Name of the member to register
442 * @param readFunction Function of type "TVarType (const TObject&)"
443 * @param writeFunction Function of type "void (TObject&, const TVarType&)"
445 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
446 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction)
448 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
449 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction));
453 * Registers a non-modifiable member variable
454 * This is the version "registerMember<Foo, int>(name, getter)"
455 * @tparam TObject Type to register the member to
456 * @tparam TVarType Type of the member
457 * @param name Name of the member to register
458 * @param readFunction Function of type "TVarType (const TObject&)"
460 template<typename TObject, typename TVarType, typename TReadFunction>
461 void registerMember(const std::string& name, TReadFunction readFunction)
463 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction));
467 * Registers a non-modifiable member variable
468 * This is the version "registerMember<int (Foo::*)>(name, getter)"
469 * @tparam TMemberType Pointer to member object representing the type
470 * @param name Name of the member to register
471 * @param readFunction Function of type "TVarType (const TObject&)"
473 template<typename TMemberType, typename TReadFunction>
474 void registerMember(const std::string& name, TReadFunction readFunction)
476 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
477 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction));
481 * Registers a dynamic member variable
482 * This is the version "registerMember<Foo, int>(getter, setter)"
483 * @tparam TObject Type to register the member to
484 * @tparam TVarType Type of the member
485 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
486 * @param writeFunction Function of type "void (TObject&, const std::string&, const TVarType&)"
488 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
489 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction)
491 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction));
495 * Registers a dynamic member variable
496 * This is the version "registerMember<int (Foo::*)>(getter, setter)"
497 * @tparam TMemberType Pointer to member object representing the type
498 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
499 * @param writeFunction Function of type "void (TObject&, const std::string&, const TVarType&)"
501 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
502 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction)
504 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
505 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction), std::move(writeFunction));
509 * Registers a dynamic non-modifiable member variable
510 * This is the version "registerMember<Foo, int>(getter)"
511 * @tparam TObject Type to register the member to
512 * @tparam TVarType Type of the member
513 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
515 template<typename TObject, typename TVarType, typename TReadFunction>
516 void registerMember(TReadFunction readFunction)
518 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
522 * Registers a dynamic non-modifiable member variable
523 * This is the version "registerMember<int (Foo::*)>(getter)"
524 * @tparam TMemberType Pointer to member object representing the type
525 * @param readFunction Function of type "TVarType (const TObject&, const std::string&)"
527 template<typename TMemberType, typename TReadFunction>
528 void registerMember(TReadFunction readFunction)
530 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
531 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction));
535 * Creates a new thread
536 * A Lua thread is not really a thread, but rather an "execution stack".
537 * You can destroy the thread by calling destroyThread
545 result.state = lua_newthread(mState);
546 result.threadInRegistry = std::unique_ptr<ValueInRegistry>(new ValueInRegistry(mState));
549 return std::move(result);
553 * Destroys a thread created with createThread
556 void destroyThread(ThreadID& id)
558 id.threadInRegistry.reset();
562 * Reads the content of a Lua variable
564 * @tparam TType Type requested for the read
565 * @throw WrongTypeException When the variable is not convertible to the requested type
568 * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers
569 * Additionally supported:
570 * - LuaFunctionCaller<FunctionType>, which is an alternative to std::function
571 * - references to custom objects, in which case it will return the object in-place
573 * After the variable name, you can add other parameters.
574 * If the variable is an array, it will instead get the element of that array whose offset is the second parameter.
575 * Same applies for third, fourth, etc. parameters.
577 template<typename TType, typename... TTypes>
578 TType readVariable(const std::string& name, TTypes&&... elements) const
580 lua_getglobal(mState, name.c_str());
581 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
582 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
588 template<typename TType, typename... TTypes>
589 TType readVariable(const char* name, TTypes&&... elements) const
591 lua_getglobal(mState, name);
592 lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
593 return readTopAndPop<TType>(mState, PushedObject{mState, 1});
599 template<typename TType, typename... TTypes>
600 TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const
602 lua_getglobal(thread.state, name.c_str());
603 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
604 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
610 template<typename TType, typename... TTypes>
611 TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const
613 lua_getglobal(thread.state, name);
614 lookIntoStackTop(thread.state, std::forward<TTypes>(elements)...);
615 return readTopAndPop<TType>(thread.state, PushedObject{thread.state, 1});
619 * Changes the content of a Lua variable
621 * Accepted values are:
622 * - all base types (char, short, int, float, double, bool)
626 * - std::vector<std::pair<>>, std::map<> and std::unordered_map<> (the key and value must also be accepted values)
627 * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple)
628 * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>)
629 * - nullptr (writes nil)
632 * All objects are passed by copy and destroyed by the garbage collector if necessary.
634 template<typename... TData>
635 void writeVariable(TData&&... data) noexcept {
636 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable");
637 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
639 static_assert(!std::is_same<typename Tupleizer<RealDataType>::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
641 setTable<RealDataType>(mState, Globals, std::forward<TData>(data)...);
645 * Equivalent to writeVariable(varName, ..., std::function<TFunctionType>(data));
646 * This version is more efficient than writeVariable if you want to write functions
648 template<typename TFunctionType, typename... TData>
649 void writeFunction(TData&&... data) noexcept {
650 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
652 setTable<TFunctionType>(mState, Globals, std::forward<TData>(data)...);
656 * Same as the other writeFunction, except that the template parameter is automatically detected
657 * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas)
659 template<typename... TData>
660 void writeFunction(TData&&... data) noexcept {
661 static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
662 typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
664 typedef typename FunctionTypeDetector<RealDataType>::type
665 DetectedFunctionType;
667 return writeFunction<DetectedFunctionType>(std::forward<TData>(data)...);
672 // the state is the most important variable in the class since it is our interface with Lua
673 // - registered members and functions are stored in tables at offset &typeid(type) of the registry
674 // each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2
675 // offset 3 is unused, setter members at offset 4, default setter at offset 5
679 /**************************************************/
681 /**************************************************/
682 struct PushedObject {
683 PushedObject(lua_State* state, int num = 1) : state(state), num(num) {}
684 ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); }
686 PushedObject& operator=(const PushedObject&) = delete;
687 PushedObject(const PushedObject&) = delete;
688 PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; }
689 PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; }
691 PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return std::move(obj); }
692 void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; }
694 auto getState() const -> lua_State* { return state; }
695 auto getNum() const -> int { return num; }
697 int release() { const auto n = num; num = 0; return n; }
698 void pop() { if (num >= 1) lua_pop(state, num); num = 0; }
699 void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; }
707 /**************************************************/
709 /**************************************************/
710 // type used as a tag
714 // tag for "the registry"
715 enum RegistryTag { Registry };
717 // this function takes a value representing the offset to look into
718 // it will look into the top element of the stack and replace the element by its content at the given index
719 template<typename OffsetType1, typename... OffsetTypeOthers>
720 static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) {
721 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");
722 auto p1 = Pusher<typename std::decay<OffsetType1>::type>::push(state, offset1);
723 lua_gettable(state, -2);
724 lua_remove(state, -2);
727 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
730 template<typename... OffsetTypeOthers>
731 static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) {
732 lua_getmetatable(state, -1);
733 lua_remove(state, -2);
735 lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
738 static void lookIntoStackTop(lua_State*) {
741 // 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
742 // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array
743 // the dataPusher MUST push only one thing on the stack
744 // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack
745 template<typename TDataType, typename TIndex, typename TData>
746 static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept
748 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");
749 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");
751 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
752 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
754 lua_settable(state, -3);
759 template<typename TDataType, typename TData>
760 static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept
762 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");
764 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
765 lua_setfield(state, -2, index.c_str());
769 template<typename TDataType, typename TData>
770 static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept
772 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");
774 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
775 lua_setfield(state, -2, index);
779 template<typename TDataType, typename TData>
780 static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept
782 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");
784 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
785 lua_setmetatable(state, -2);
789 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
790 static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
791 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
793 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");
795 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
796 lua_gettable(state, -2);
798 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
801 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
802 static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
803 -> typename std::enable_if<!std::is_same<typename std::decay<TIndex1>::type, Metatable_t>::value>::type
805 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");
807 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + std::move(pushedTable);
808 lua_gettable(state, -2);
810 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
813 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
814 static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
816 if (lua_getmetatable(state, -1) == 0)
819 PushedObject p1{state, 1};
821 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
823 lua_setmetatable(state, -2);
828 setTable<TDataType>(state, pushedObject, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
832 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
833 static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
835 if (lua_getmetatable(state, -1) == 0)
838 PushedObject p1{state, 1};
840 setTable<TDataType>(state, p1, std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
842 lua_setmetatable(state, -2);
847 setTable<TDataType>(state, std::move(pushedObject), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
851 template<typename TDataType, typename TIndex, typename TData>
852 static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept
854 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");
855 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");
857 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
858 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
860 lua_settable(state, LUA_REGISTRYINDEX);
865 template<typename TDataType, typename TData>
866 static void setTable(lua_State* state, RegistryTag, const std::string& 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, LUA_REGISTRYINDEX, index.c_str());
875 template<typename TDataType, typename TData>
876 static void setTable(lua_State* state, RegistryTag, const char* index, 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_setfield(state, LUA_REGISTRYINDEX, index);
885 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
886 static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
888 static_assert(Pusher<typename std::decay<TIndex1>::type>::minSize == 1 && Pusher<typename std::decay<TIndex1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
890 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
891 lua_gettable(state, LUA_REGISTRYINDEX);
893 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
896 template<typename TDataType, typename TIndex, typename TData>
897 static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept
899 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");
900 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");
903 # if LUA_VERSION_NUM >= 502
905 lua_pushglobaltable(state);
906 PushedObject p3{state, 1};
907 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
908 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
909 lua_settable(state, -3);
913 auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
914 auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
915 lua_settable(state, LUA_GLOBALSINDEX);
923 template<typename TDataType, typename TData>
924 static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept
926 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");
928 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
929 lua_setglobal(state, index.c_str());
933 template<typename TDataType, typename TData>
934 static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept
936 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");
938 auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
939 lua_setglobal(state, index);
943 template<typename TDataType, typename TIndex1, typename TIndex2, typename TIndex3, typename... TIndices>
944 static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
946 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");
948 # if LUA_VERSION_NUM >= 502
950 lua_pushglobaltable(state);
951 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + PushedObject{state, 1};
952 lua_gettable(state, -2);
956 auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
957 lua_gettable(state, LUA_GLOBALSINDEX);
961 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
964 // TODO: g++ reports "ambiguous overload"
965 /*template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
966 static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
968 lua_getglobal(state, index);
969 PushedObject p1{state, 1};
971 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
974 template<typename TDataType, typename TIndex2, typename TIndex3, typename... TIndices>
975 static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept
977 lua_getglobal(state, index.c_str());
978 PushedObject p1{state, 1};
980 setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
983 // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value
984 // warning: first parameter is the number of parameters, not the parameter index
985 // if read generates an exception, stack is poped anyway
986 template<typename TReturnType>
987 static auto readTopAndPop(lua_State* state, PushedObject object)
990 auto val = Reader<typename std::decay<TReturnType>::type>::read(state, -object.getNum());
991 if (!val.is_initialized())
992 throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)};
996 // checks that the offsets for a type's registrations are set in the registry
997 static void checkTypeRegistration(lua_State* state, const std::type_info* type)
999 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1000 lua_gettable(state, LUA_REGISTRYINDEX);
1001 if (!lua_isnil(state, -1)) {
1007 lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
1008 lua_newtable(state);
1010 lua_pushinteger(state, 0);
1011 lua_newtable(state);
1012 lua_settable(state, -3);
1014 lua_pushinteger(state, 1);
1015 lua_newtable(state);
1016 lua_settable(state, -3);
1018 lua_pushinteger(state, 3);
1019 lua_newtable(state);
1020 lua_settable(state, -3);
1022 lua_pushinteger(state, 4);
1023 lua_newtable(state);
1024 lua_settable(state, -3);
1026 lua_settable(state, LUA_REGISTRYINDEX);
1031 __declspec(noreturn)
1035 static void luaError(lua_State* state)
1039 std::terminate(); // removes compilation warning
1043 /**************************************************/
1044 /* FUNCTIONS REGISTRATION */
1045 /**************************************************/
1046 // the "registerFunction" public functions call this one
1047 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1048 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TObject>, tag<TRetValue (TOtherParams...)>)
1050 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");
1052 checkTypeRegistration(mState, &typeid(TObject));
1053 setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, std::move(function));
1055 checkTypeRegistration(mState, &typeid(TObject*));
1056 setTable<TRetValue(TObject*, TOtherParams...)>(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
1058 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1059 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)...); });
1062 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1063 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<const TObject>, tag<TRetValue (TOtherParams...)> fTypeTag)
1065 registerFunctionImpl(functionName, function, tag<TObject>{}, fTypeTag);
1067 checkTypeRegistration(mState, &typeid(TObject const*));
1068 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)...); });
1070 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1071 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)...); });
1074 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1075 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...)>)
1077 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1080 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1081 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const>)
1083 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1086 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1087 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) volatile>)
1089 registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
1092 template<typename TFunctionType, typename TRetValue, typename TObject, typename... TOtherParams>
1093 void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag<TRetValue (TObject::*)(TOtherParams...) const volatile>)
1095 registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
1098 // the "registerMember" public functions call this one
1099 template<typename TObject, typename TVarType, typename TReadFunction>
1100 void registerMemberImpl(const std::string& name, TReadFunction readFunction)
1102 static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value, "registerMember can only be called on a class or a pointer");
1104 checkTypeRegistration(mState, &typeid(TObject));
1105 setTable<TVarType (TObject&)>(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) {
1106 return readFunction(object);
1109 checkTypeRegistration(mState, &typeid(TObject*));
1110 setTable<TVarType (TObject*)>(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) {
1112 return readFunction(*object);
1115 checkTypeRegistration(mState, &typeid(TObject const*));
1116 setTable<TVarType (TObject const*)>(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) {
1118 return readFunction(*object);
1121 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1122 setTable<TVarType (std::shared_ptr<TObject>)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 1, name, [readFunction](const std::shared_ptr<TObject>& object) {
1124 return readFunction(*object);
1127 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1128 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) {
1130 return readFunction(*object);
1134 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1135 void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction)
1137 registerMemberImpl<TObject,TVarType>(name, readFunction);
1139 setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction](TObject& object, const TVarType& value) {
1140 writeFunction(object, value);
1143 setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction](TObject* object, const TVarType& value) {
1145 writeFunction(*object, value);
1148 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) {
1150 writeFunction(*object, value);
1154 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1155 void registerMemberImpl(tag<TVarType (TObject::*)>, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction)
1157 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction));
1160 template<typename TObject, typename TVarType, typename TReadFunction>
1161 void registerMemberImpl(tag<TVarType(TObject::*)>, const std::string& name, TReadFunction readFunction)
1163 registerMemberImpl<TObject, TVarType>(name, std::move(readFunction));
1166 // the "registerMember" public functions call this one
1167 template<typename TObject, typename TVarType, typename TReadFunction>
1168 void registerMemberImpl(TReadFunction readFunction)
1170 checkTypeRegistration(mState, &typeid(TObject));
1171 setTable<TVarType (TObject const&, std::string)>(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) {
1172 return readFunction(object, name);
1175 checkTypeRegistration(mState, &typeid(TObject*));
1176 setTable<TVarType (TObject*, std::string)>(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) {
1178 return readFunction(*object, name);
1181 checkTypeRegistration(mState, &typeid(TObject const*));
1182 setTable<TVarType (TObject const*, std::string)>(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) {
1184 return readFunction(*object, name);
1187 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
1188 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) {
1190 return readFunction(*object, name);
1193 checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
1194 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) {
1196 return readFunction(*object, name);
1200 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1201 void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction)
1203 registerMemberImpl<TObject,TVarType>(readFunction);
1205 setTable<void (TObject&, std::string, TVarType)>(mState, Registry, &typeid(TObject), 5, [writeFunction](TObject& object, const std::string& name, const TVarType& value) {
1206 writeFunction(object, name, value);
1209 setTable<void (TObject*, std::string, TVarType)>(mState, Registry, &typeid(TObject*), 2, [writeFunction](TObject* object, const std::string& name, const TVarType& value) {
1211 writeFunction(*object, name, value);
1214 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) {
1216 writeFunction(*object, name, value);
1220 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1221 void registerMemberImpl(tag<TVarType (TObject::*)>, TReadFunction readFunction, TWriteFunction writeFunction)
1223 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction));
1226 template<typename TObject, typename TVarType, typename TReadFunction>
1227 void registerMemberImpl(tag<TVarType(TObject::*)>, TReadFunction readFunction)
1229 registerMemberImpl<TObject, TVarType>(std::move(readFunction));
1233 /**************************************************/
1234 /* LOADING AND CALLING */
1235 /**************************************************/
1236 // this function loads data from the stream and pushes a function at the top of the stack
1237 // throws in case of syntax error
1238 static PushedObject load(lua_State* state, std::istream& code) {
1239 // since the lua_load function requires a static function, we use this structure
1240 // the Reader structure is at the same time an object storing an istream and a buffer,
1241 // and a static function provider
1243 Reader(std::istream& str) : stream(str) {}
1244 std::istream& stream;
1245 std::array<char,512> buffer;
1247 // read function ; "data" must be an instance of Reader
1248 static const char* read(lua_State* l, void* data, size_t* size) {
1249 assert(size != nullptr);
1250 assert(data != nullptr);
1251 Reader& me = *static_cast<Reader*>(data);
1252 if (me.stream.eof()) { *size = 0; return nullptr; }
1254 me.stream.read(me.buffer.data(), me.buffer.size());
1255 *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
1256 return me.buffer.data();
1260 // we create an instance of Reader, and we call lua_load
1261 Reader reader{code};
1262 const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk"
1263 # if LUA_VERSION_NUM >= 502
1268 // now we have to check return value
1269 if (loadReturnValue != 0) {
1270 // there was an error during loading, an error message was pushed on the stack
1271 const std::string errorMsg = lua_tostring(state, -1);
1273 if (loadReturnValue == LUA_ERRMEM)
1274 throw std::bad_alloc();
1275 else if (loadReturnValue == LUA_ERRSYNTAX)
1276 throw SyntaxErrorException{errorMsg};
1277 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1280 return PushedObject{state, 1};
1283 // this function loads data and pushes a function at the top of the stack
1284 // throws in case of syntax error
1285 static PushedObject load(lua_State* state, const char* code) {
1286 auto loadReturnValue = luaL_loadstring(state, code);
1288 // now we have to check return value
1289 if (loadReturnValue != 0) {
1290 // there was an error during loading, an error message was pushed on the stack
1291 const std::string errorMsg = lua_tostring(state, -1);
1293 if (loadReturnValue == LUA_ERRMEM)
1294 throw std::bad_alloc();
1295 else if (loadReturnValue == LUA_ERRSYNTAX)
1296 throw SyntaxErrorException{errorMsg};
1297 throw std::runtime_error("Error while calling lua_load: " + errorMsg);
1300 return PushedObject{state, 1};
1303 // this function calls what is on the top of the stack and removes it (just like lua_call)
1304 // if an exception is triggered, the top of the stack will be removed anyway
1305 template<typename TReturnType, typename... TParameters>
1306 static auto call(lua_State* state, PushedObject toCall, TParameters&&... input)
1309 typedef typename Tupleizer<TReturnType>::type
1312 // we push the parameters on the stack
1313 auto inArguments = Pusher<std::tuple<TParameters...>>::push(state, std::forward_as_tuple((input)...));
1316 const int outArgumentsCount = std::tuple_size<RealReturnType>::value;
1317 auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount);
1319 // pcall succeeded, we pop the returned values and return them
1320 return readTopAndPop<TReturnType>(state, std::move(outArguments));
1323 // this function just calls lua_pcall and checks for errors
1324 static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments)
1326 // calling pcall automatically pops the parameters and pushes output
1327 const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, 0);
1328 functionAndArguments.release();
1330 // if pcall failed, analyzing the problem and throwing
1331 if (pcallReturnValue != 0) {
1332 PushedObject errorCode{state, 1};
1334 // an error occured during execution, either an error message or a std::exception_ptr was pushed on the stack
1335 if (pcallReturnValue == LUA_ERRMEM) {
1336 throw std::bad_alloc{};
1338 } else if (pcallReturnValue == LUA_ERRRUN) {
1339 if (lua_isstring(state, 1)) {
1340 // the error is a string
1341 const auto str = readTopAndPop<std::string>(state, std::move(errorCode));
1342 throw ExecutionErrorException{str};
1345 // an exception_ptr was pushed on the stack
1346 // rethrowing it with an additional ExecutionErrorException
1348 if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
1349 std::rethrow_exception(exp);
1352 std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua"});
1354 throw ExecutionErrorException{"Unknown Lua error"};
1359 return PushedObject{state, outArguments};
1363 /**************************************************/
1364 /* PUSH FUNCTIONS */
1365 /**************************************************/
1366 template<typename T>
1367 static PushedObject push(lua_State* state, T&& value)
1369 return Pusher<typename std::decay<T>::type>::push(state, std::forward<T>(value));
1372 // the Pusher structures allow you to push a value on the stack
1373 // - static const int minSize : minimum size on the stack that the value can have
1374 // - static const int maxSize : maximum size on the stack that the value can have
1375 // - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack
1377 // implementation for custom objects
1378 template<typename TType, typename = void>
1380 static const int minSize = 1;
1381 static const int maxSize = 1;
1383 template<typename TType2>
1384 static PushedObject push(lua_State* state, TType2&& value) noexcept {
1385 // this function is called when lua's garbage collector wants to destroy our object
1386 // we simply call its destructor
1387 const auto garbageCallbackFunction = [](lua_State* lua) -> int {
1388 assert(lua_gettop(lua) == 1);
1389 TType* ptr = static_cast<TType*>(lua_touserdata(lua, 1));
1395 // this function will be stored in __index in the metatable
1396 const auto indexFunction = [](lua_State* lua) -> int {
1398 assert(lua_gettop(lua) == 2);
1399 assert(lua_isuserdata(lua, 1));
1401 // searching for a handler
1402 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1403 lua_gettable(lua, LUA_REGISTRYINDEX);
1404 assert(!lua_isnil(lua, -1));
1406 // looking into getter functions
1407 lua_pushinteger(lua, 0);
1408 lua_gettable(lua, -2);
1409 lua_pushvalue(lua, 2);
1410 lua_gettable(lua, -2);
1411 if (!lua_isnil(lua, -1))
1415 // looking into getter members
1416 lua_pushinteger(lua, 1);
1417 lua_gettable(lua, -2);
1418 lua_pushvalue(lua, 2);
1419 lua_gettable(lua, -2);
1420 if (!lua_isnil(lua, -1)) {
1421 lua_pushvalue(lua, 1);
1422 return callRaw(lua, PushedObject{lua, 2}, 1).release();
1426 // looking into default getter
1427 lua_pushinteger(lua, 2);
1428 lua_gettable(lua, -2);
1429 if (lua_isnil(lua, -1))
1431 lua_pushvalue(lua, 1);
1432 lua_pushvalue(lua, 2);
1433 return callRaw(lua, PushedObject{lua, 3}, 1).release();
1436 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1441 // this function will be stored in __newindex in the metatable
1442 const auto newIndexFunction = [](lua_State* lua) -> int {
1444 assert(lua_gettop(lua) == 3);
1445 assert(lua_isuserdata(lua, 1));
1447 // searching for a handler
1448 lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
1449 lua_rawget(lua, LUA_REGISTRYINDEX);
1450 assert(!lua_isnil(lua, -1));
1452 // looking into setter members
1453 lua_pushinteger(lua, 4);
1454 lua_rawget(lua, -2);
1455 lua_pushvalue(lua, 2);
1456 lua_rawget(lua, -2);
1457 if (!lua_isnil(lua, -1)) {
1458 lua_pushvalue(lua, 1);
1459 lua_pushvalue(lua, 3);
1460 callRaw(lua, PushedObject{lua, 3}, 0);
1466 // looking into default setter
1467 lua_pushinteger(lua, 5);
1468 lua_rawget(lua, -2);
1469 if (lua_isnil(lua, -1))
1472 lua_pushstring(lua, "No setter found");
1475 lua_pushvalue(lua, 1);
1476 lua_pushvalue(lua, 2);
1477 lua_pushvalue(lua, 3);
1478 callRaw(lua, PushedObject{lua, 4}, 0);
1483 Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
1488 // writing structure for this type into the registry
1489 checkTypeRegistration(state, &typeid(TType));
1491 // creating the object
1492 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
1493 // and that's what we do with placement-new
1494 const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
1495 new (pointerLocation) TType(std::forward<TType2>(value));
1496 PushedObject obj{state, 1};
1498 // creating the metatable (over the object on the stack)
1499 // lua_settable pops the key and value we just pushed, so stack management is easy
1500 // all that remains on the stack after these function calls is the metatable
1501 lua_newtable(state);
1502 PushedObject pushedTable{state, 1};
1504 // using the garbage collecting function we created above
1505 if (!boost::has_trivial_destructor<TType>::value)
1507 lua_pushstring(state, "__gc");
1508 lua_pushcfunction(state, garbageCallbackFunction);
1509 lua_settable(state, -3);
1512 // the _typeid index of the metatable will store the type_info*
1513 lua_pushstring(state, "_typeid");
1514 lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
1515 lua_settable(state, -3);
1517 // using the index function we created above
1518 lua_pushstring(state, "__index");
1519 lua_pushcfunction(state, indexFunction);
1520 lua_settable(state, -3);
1522 // using the newindex function we created above
1523 lua_pushstring(state, "__newindex");
1524 lua_pushcfunction(state, newIndexFunction);
1525 lua_settable(state, -3);
1527 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1528 // lua_setmetatable will bind the two together and pop the metatable
1529 // our custom type remains on the stack (and that's what we want since this is a push function)
1530 lua_setmetatable(state, -2);
1531 pushedTable.release();
1533 return std::move(obj);
1537 // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1538 template<typename... TTypes>
1539 struct PusherTotalMinSize;
1541 // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1542 template<typename... TTypes>
1543 struct PusherTotalMaxSize;
1545 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1546 template<typename... TTypes>
1547 struct PusherMinSize;
1549 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1550 template<typename... TTypes>
1551 struct PusherMaxSize;
1554 /**************************************************/
1555 /* READ FUNCTIONS */
1556 /**************************************************/
1557 // the "Reader" structures allow to read data from the stack
1558 // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1559 // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1561 template<typename TType, typename = void>
1563 typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1566 static auto read(lua_State* state, int index)
1567 -> boost::optional<ReturnType>
1569 if (!test(state, index))
1571 return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
1575 static bool test(lua_State* state, int index)
1577 if (!lua_isuserdata(state, index))
1579 if (!lua_getmetatable(state, index))
1582 // now we have our metatable on the top of the stack
1583 // retrieving its _typeid member
1584 lua_pushstring(state, "_typeid");
1585 lua_gettable(state, -2);
1586 const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
1587 const auto typeIDToCompare = &typeid(TType);
1589 // if wrong typeid, returning false
1591 if (storedTypeID != typeIDToCompare)
1599 * This functions reads multiple values starting at "index" and passes them to the callback
1601 template<typename TRetValue, typename TCallback>
1602 static auto readIntoFunction(lua_State* state, tag<TRetValue>, TCallback&& callback, int index)
1607 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1608 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1609 -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1612 Binder<TCallback, const TFirstType&> binder{ callback, {} };
1613 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1616 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1618 throw WrongTypeException(lua_typename(state, index), typeid(TFirstType));
1620 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1621 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1623 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1624 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1625 -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1628 throw std::logic_error("Wrong number of parameters");
1630 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1632 throw WrongTypeException(lua_typename(state, index), typeid(TFirstType));
1634 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1635 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1639 /**************************************************/
1641 /**************************************************/
1642 // structure that will ensure that a certain value is stored somewhere in the registry
1643 struct ValueInRegistry {
1644 // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
1645 ValueInRegistry(lua_State* lua, int index=-1) : lua{lua}
1647 lua_pushlightuserdata(lua, this);
1648 lua_pushvalue(lua, -1 + index);
1649 lua_settable(lua, LUA_REGISTRYINDEX);
1652 // removing the function from the registry
1655 lua_pushlightuserdata(lua, this);
1657 lua_settable(lua, LUA_REGISTRYINDEX);
1660 // loads the value and puts it at the top of the stack
1663 lua_pushlightuserdata(lua, this);
1664 lua_gettable(lua, LUA_REGISTRYINDEX);
1665 return PushedObject{lua, 1};
1668 ValueInRegistry(const ValueInRegistry&) = delete;
1669 ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1675 // binds the first parameter of a function object
1676 template<typename TFunctionObject, typename TFirstParamType>
1678 TFunctionObject function;
1679 TFirstParamType param;
1681 template<typename... TParams>
1682 auto operator()(TParams&&... params)
1683 -> decltype(function(param, std::forward<TParams>(params)...))
1685 return function(param, std::forward<TParams>(params)...);
1689 // turns a type into a tuple
1690 // void is turned into std::tuple<>
1691 // existing tuples are untouched
1692 template<typename T>
1695 // this structure takes a pointer to a member function type and returns the base function type
1696 template<typename TType>
1697 struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug
1699 // this structure takes any object and detects its function type
1700 template<typename TObjectType>
1701 struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1703 // 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
1704 // the only case where "min != max" is with boost::optional at the end of the list
1705 template<typename... TArgumentsList>
1706 struct FunctionArgumentsCounter {};
1708 // true is the template parameter is a boost::optional
1709 template<typename T>
1710 struct IsOptional : public std::false_type {};
1714 static LuaContext::EmptyArray_t ATTR_UNUSED
1717 static LuaContext::Metatable_t ATTR_UNUSED
1720 /**************************************************/
1721 /* PARTIAL IMPLEMENTATIONS */
1722 /**************************************************/
1724 inline auto LuaContext::readTopAndPop<void>(lua_State* state, PushedObject obj)
1729 // this structure takes a template parameter T
1730 // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1731 // we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1732 template<typename T>
1733 struct LuaContext::Tupleizer { typedef std::tuple<T> type; };
1734 template<typename... Args>
1735 struct LuaContext::Tupleizer<std::tuple<Args...>> { typedef std::tuple<Args...> type; };
1737 struct LuaContext::Tupleizer<void> { typedef std::tuple<> type; };
1739 // this structure takes any object and detects its function type
1740 template<typename TRetValue, typename... TParameters>
1741 struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)> { typedef TRetValue type(TParameters...); };
1742 template<typename TObjectType>
1743 struct LuaContext::FunctionTypeDetector<TObjectType*> { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1745 // this structure takes a pointer to a member function type and returns the base function type
1746 template<typename TType, typename TRetValue, typename... TParameters>
1747 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)> { typedef TRetValue type(TParameters...); };
1748 template<typename TType, typename TRetValue, typename... TParameters>
1749 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const> { typedef TRetValue type(TParameters...); };
1750 template<typename TType, typename TRetValue, typename... TParameters>
1751 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile> { typedef TRetValue type(TParameters...); };
1752 template<typename TType, typename TRetValue, typename... TParameters>
1753 struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile> { typedef TRetValue type(TParameters...); };
1755 // implementation of PusherTotalMinSize
1756 template<typename TFirst, typename... TTypes>
1757 struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1759 struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1761 // implementation of PusherTotalMaxSize
1762 template<typename TFirst, typename... TTypes>
1763 struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1765 struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1767 // implementation of PusherMinSize
1768 template<typename TFirst, typename TSecond, typename... TTypes>
1769 struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...>
1771 static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize
1773 PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size
1775 PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1778 template<typename TFirst>
1779 struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
1781 // implementation of PusherMaxSize
1782 template<typename TFirst, typename... TTypes>
1783 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; };
1785 struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1787 // implementation of FunctionArgumentsCounter
1788 template<typename TFirst, typename... TParams>
1789 struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1790 typedef FunctionArgumentsCounter<TParams...>
1792 static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1793 static const int max = 1 + SubType::max;
1796 struct LuaContext::FunctionArgumentsCounter<> {
1797 static const int min = 0;
1798 static const int max = 0;
1801 // implementation of IsOptional
1802 template<typename T>
1803 struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1805 // implementation of LuaFunctionCaller
1806 template<typename TFunctionType>
1807 class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1808 template<typename TRetValue, typename... TParams>
1809 class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1812 TRetValue operator()(TParams&&... params) const
1814 auto obj = valueHolder->pop();
1815 return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
1819 std::shared_ptr<ValueInRegistry> valueHolder;
1824 explicit LuaFunctionCaller(lua_State* state, int index) :
1825 valueHolder(std::make_shared<ValueInRegistry>(state, index)),
1831 /**************************************************/
1832 /* PUSH FUNCTIONS */
1833 /**************************************************/
1834 // specializations of the Pusher structure
1838 struct LuaContext::Pusher<bool> {
1839 static const int minSize = 1;
1840 static const int maxSize = 1;
1842 static PushedObject push(lua_State* state, bool value) noexcept {
1843 lua_pushboolean(state, value);
1844 return PushedObject{state, 1};
1850 struct LuaContext::Pusher<std::string> {
1851 static const int minSize = 1;
1852 static const int maxSize = 1;
1854 static PushedObject push(lua_State* state, const std::string& value) noexcept {
1855 lua_pushlstring(state, value.c_str(), value.length());
1856 return PushedObject{state, 1};
1862 struct LuaContext::Pusher<const char*> {
1863 static const int minSize = 1;
1864 static const int maxSize = 1;
1866 static PushedObject push(lua_State* state, const char* value) noexcept {
1867 lua_pushstring(state, value);
1868 return PushedObject{state, 1};
1874 struct LuaContext::Pusher<const char[N]> {
1875 static const int minSize = 1;
1876 static const int maxSize = 1;
1878 static PushedObject push(lua_State* state, const char* value) noexcept {
1879 lua_pushstring(state, value);
1880 return PushedObject{state, 1};
1885 template<typename T>
1886 struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
1887 static const int minSize = 1;
1888 static const int maxSize = 1;
1890 static PushedObject push(lua_State* state, T value) noexcept {
1891 lua_pushnumber(state, value);
1892 return PushedObject{state, 1};
1897 template<typename T>
1898 struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
1899 static const int minSize = 1;
1900 static const int maxSize = 1;
1902 static PushedObject push(lua_State* state, T value) noexcept {
1903 lua_pushinteger(state, value);
1904 return PushedObject{state, 1};
1910 struct LuaContext::Pusher<std::nullptr_t> {
1911 static const int minSize = 1;
1912 static const int maxSize = 1;
1914 static PushedObject push(lua_State* state, std::nullptr_t value) noexcept {
1915 assert(value == nullptr);
1917 return PushedObject{state, 1};
1923 struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
1924 static const int minSize = 1;
1925 static const int maxSize = 1;
1927 static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
1928 lua_newtable(state);
1929 return PushedObject{state, 1};
1933 // std::type_info* is a lightuserdata
1935 struct LuaContext::Pusher<const std::type_info*> {
1936 static const int minSize = 1;
1937 static const int maxSize = 1;
1939 static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
1940 lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
1941 return PushedObject{state, 1};
1947 struct LuaContext::Pusher<LuaContext::ThreadID> {
1948 static const int minSize = 1;
1949 static const int maxSize = 1;
1951 static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
1952 lua_pushthread(value.state);
1953 return PushedObject{state, 1};
1958 template<typename TKey, typename TValue>
1959 struct LuaContext::Pusher<std::map<TKey,TValue>> {
1960 static const int minSize = 1;
1961 static const int maxSize = 1;
1963 static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
1964 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");
1965 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");
1967 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
1969 for (auto i = value.begin(), e = value.end(); i != e; ++i)
1970 setTable<TValue>(state, obj, i->first, i->second);
1972 return std::move(obj);
1977 template<typename TKey, typename TValue>
1978 struct LuaContext::Pusher<std::unordered_map<TKey,TValue>> {
1979 static const int minSize = 1;
1980 static const int maxSize = 1;
1982 static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue>& value) noexcept {
1983 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");
1984 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");
1986 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
1988 for (auto i = value.begin(), e = value.end(); i != e; ++i)
1989 setTable<TValue>(state, obj, i->first, i->second);
1991 return std::move(obj);
1996 template<typename TType1, typename TType2>
1997 struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
1998 static const int minSize = 1;
1999 static const int maxSize = 1;
2001 static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
2002 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");
2003 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");
2005 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2007 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2008 setTable<TType2>(state, obj, i->first, i->second);
2010 return std::move(obj);
2015 template<typename TType>
2016 struct LuaContext::Pusher<std::vector<TType>> {
2017 static const int minSize = 1;
2018 static const int maxSize = 1;
2020 static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
2021 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");
2023 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2025 for (unsigned int i = 0; i < value.size(); ++i)
2026 setTable<TType>(state, obj, i + 1, value[i]);
2028 return std::move(obj);
2033 template<typename TType>
2034 struct LuaContext::Pusher<std::unique_ptr<TType>> {
2035 static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2036 static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2038 static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2039 return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2044 template<typename TEnum>
2045 struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2046 #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2047 typedef typename std::underlying_type<TEnum>::type
2050 // implementation when std::underlying_type is not supported
2051 typedef unsigned long
2055 static const int minSize = Pusher<RealType>::minSize;
2056 static const int maxSize = Pusher<RealType>::maxSize;
2058 static PushedObject push(lua_State* state, TEnum value) noexcept {
2059 return Pusher<RealType>::push(state, static_cast<RealType>(value));
2064 // this specialization is not directly called, but is called by other specializations
2065 template<typename TReturnType, typename... TParameters>
2066 struct LuaContext::Pusher<TReturnType (TParameters...)>
2068 static const int minSize = 1;
2069 static const int maxSize = 1;
2071 // counts the number of arguments
2072 typedef FunctionArgumentsCounter<TParameters...>
2073 LocalFunctionArgumentsCounter;
2075 // this is the version of "push" for non-trivially destructible function objects
2076 template<typename TFunctionObject>
2077 static auto push(lua_State* state, TFunctionObject fn) noexcept
2078 -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2080 // TODO: is_move_constructible not supported by some compilers
2081 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2083 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2084 // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2085 // so we use userdata instead
2087 // this function is called when the lua script tries to call our custom data type
2088 // we transfer execution to the "callback" function below
2089 const auto callCallback = [](lua_State* lua) -> int {
2090 assert(lua_gettop(lua) >= 1);
2091 assert(lua_isuserdata(lua, 1));
2092 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2095 return callback(lua, function, lua_gettop(lua) - 1).release();
2098 // this one is called when lua's garbage collector no longer needs our custom data type
2099 // we call the function object's destructor
2100 const auto garbageCallback = [](lua_State* lua) -> int {
2101 assert(lua_gettop(lua) == 1);
2102 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2104 function->~TFunctionObject();
2108 // creating the object
2109 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2110 // and that's what we do with placement-new
2111 const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2112 new (functionLocation) TFunctionObject(std::move(fn));
2114 // creating the metatable (over the object on the stack)
2115 // lua_settable pops the key and value we just pushed, so stack management is easy
2116 // all that remains on the stack after these function calls is the metatable
2117 lua_newtable(state);
2118 lua_pushstring(state, "__call");
2119 lua_pushcfunction(state, callCallback);
2120 lua_settable(state, -3);
2122 lua_pushstring(state, "__gc");
2123 lua_pushcfunction(state, garbageCallback);
2124 lua_settable(state, -3);
2126 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2127 // lua_setmetatable will bind the two together and pop the metatable
2128 // our custom function remains on the stack (and that's what we want)
2129 lua_setmetatable(state, -2);
2131 return PushedObject{state, 1};
2134 // this is the version of "push" for trivially destructible objects
2135 template<typename TFunctionObject>
2136 static auto push(lua_State* state, TFunctionObject fn) noexcept
2137 -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2139 // TODO: is_move_constructible not supported by some compilers
2140 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2142 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2143 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2145 // this is the cfunction that is the callback
2146 const auto function = [](lua_State* state) -> int
2148 // the function object is an upvalue
2149 const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state, lua_upvalueindex(1)));
2150 return callback(state, toCall, lua_gettop(state)).release();
2153 // we copy the function object onto the stack
2154 const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2155 new (functionObjectLocation) TFunctionObject(std::move(fn));
2157 // pushing the function with the function object as upvalue
2158 lua_pushcclosure(state, function, 1);
2159 return PushedObject{state, 1};
2162 // this is the version of "push" for pointer to functions
2163 static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2166 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2167 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2169 // this is the cfunction that is the callback
2170 const auto function = [](lua_State* state) -> int
2172 // the function object is an upvalue
2173 const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state, lua_upvalueindex(1)));
2174 return callback(state, toCall, lua_gettop(state)).release();
2177 // we copy the function object onto the stack
2178 lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
2180 // pushing the function with the function object as upvalue
2181 lua_pushcclosure(state, function, 1);
2182 return PushedObject{state, 1};
2185 // this is the version of "push" for references to functions
2186 static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2189 return push(state, &fn);
2193 // callback that calls the function object
2194 // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2195 template<typename TFunctionObject>
2196 static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2199 // checking if number of parameters is correct
2200 if (argumentsCount < LocalFunctionArgumentsCounter::min) {
2201 // if not, using lua_error to return an error
2202 luaL_where(state, 1);
2203 lua_pushstring(state, "This function requires at least ");
2204 lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
2205 lua_pushstring(state, " parameter(s)");
2206 lua_concat(state, 4);
2209 } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
2210 // if not, using lua_error to return an error
2211 luaL_where(state, 1);
2212 lua_pushstring(state, "This function requires at most ");
2213 lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
2214 lua_pushstring(state, " parameter(s)");
2215 lua_concat(state, 4);
2219 // calling the function
2221 return callback2(state, *toCall, argumentsCount);
2223 } catch (const WrongTypeException& ex) {
2224 // wrong parameter type, using lua_error to return an error
2225 luaL_where(state, 1);
2226 lua_pushstring(state, "Unable to convert parameter from ");
2227 lua_pushstring(state, ex.luaType.c_str());
2228 lua_pushstring(state, " to ");
2229 lua_pushstring(state, ex.destination.name());
2230 lua_concat(state, 4);
2234 Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
2239 template<typename TFunctionObject>
2240 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2241 -> typename std::enable_if<!std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2243 // pushing the result on the stack and returning number of pushed elements
2244 typedef Pusher<typename std::decay<TReturnType>::type>
2246 return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
2249 template<typename TFunctionObject>
2250 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2251 -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2253 readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
2254 return PushedObject{state, 0};
2258 // C function pointers
2259 template<typename TReturnType, typename... TParameters>
2260 struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2262 // using the function-pushing implementation
2263 typedef Pusher<TReturnType (TParameters...)>
2265 static const int minSize = SubPusher::minSize;
2266 static const int maxSize = SubPusher::maxSize;
2268 template<typename TType>
2269 static PushedObject push(lua_State* state, TType value) noexcept {
2270 return SubPusher::push(state, value);
2274 // C function references
2275 template<typename TReturnType, typename... TParameters>
2276 struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2278 // using the function-pushing implementation
2279 typedef Pusher<TReturnType(TParameters...)>
2281 static const int minSize = SubPusher::minSize;
2282 static const int maxSize = SubPusher::maxSize;
2284 template<typename TType>
2285 static PushedObject push(lua_State* state, TType value) noexcept {
2286 return SubPusher::push(state, value);
2291 template<typename TReturnType, typename... TParameters>
2292 struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2294 // using the function-pushing implementation
2295 typedef Pusher<TReturnType (TParameters...)>
2297 static const int minSize = SubPusher::minSize;
2298 static const int maxSize = SubPusher::maxSize;
2300 static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2301 return SubPusher::push(state, value);
2306 template<typename... TTypes>
2307 struct LuaContext::Pusher<boost::variant<TTypes...>>
2309 static const int minSize = PusherMinSize<TTypes...>::size;
2310 static const int maxSize = PusherMaxSize<TTypes...>::size;
2312 static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
2313 PushedObject obj{state, 0};
2314 VariantWriter writer{state, obj};
2315 value.apply_visitor(writer);
2316 return std::move(obj);
2320 struct VariantWriter : public boost::static_visitor<> {
2321 template<typename TType>
2322 void operator()(TType value) noexcept
2324 obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
2327 VariantWriter(lua_State* state, PushedObject& obj) : state(state), obj(obj) {}
2334 template<typename TType>
2335 struct LuaContext::Pusher<boost::optional<TType>> {
2336 typedef Pusher<typename std::decay<TType>::type>
2339 static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2340 static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2342 static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
2344 return UnderlyingPusher::push(state, value.get());
2347 return PushedObject{state, 1};
2353 template<typename... TTypes>
2354 struct LuaContext::Pusher<std::tuple<TTypes...>> {
2355 // TODO: NOT EXCEPTION SAFE /!\ //
2356 static const int minSize = PusherTotalMinSize<TTypes...>::size;
2357 static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2359 static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2360 return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2363 static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
2364 return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
2369 static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2370 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2372 return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2373 push2(state, value, std::integral_constant<int,N+1>{});
2377 static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
2378 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2380 return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
2381 push2(state, std::move(value), std::integral_constant<int,N+1>{});
2384 static int push2(lua_State* state, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2388 static int push2(lua_State* state, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2393 /**************************************************/
2394 /* READ FUNCTIONS */
2395 /**************************************************/
2396 // specializations of the Reader structures
2400 struct LuaContext::Reader<std::nullptr_t>
2402 static auto read(lua_State* state, int index)
2403 -> boost::optional<std::nullptr_t>
2405 if (!lua_isnil(state, index))
2412 template<typename TType>
2413 struct LuaContext::Reader<
2415 typename std::enable_if<std::is_integral<TType>::value>::type
2418 static auto read(lua_State* state, int index)
2419 -> boost::optional<TType>
2421 # if LUA_VERSION_NUM >= 502
2424 auto value = lua_tointegerx(state, index, &success);
2427 return static_cast<TType>(value);
2431 if (!lua_isnumber(state, index))
2433 return static_cast<TType>(lua_tointeger(state, index));
2440 template<typename TType>
2441 struct LuaContext::Reader<
2443 typename std::enable_if<std::is_floating_point<TType>::value>::type
2446 static auto read(lua_State* state, int index)
2447 -> boost::optional<TType>
2449 # if LUA_VERSION_NUM >= 502
2452 auto value = lua_tonumberx(state, index, &success);
2455 return static_cast<TType>(value);
2459 if (!lua_isnumber(state, index))
2461 return static_cast<TType>(lua_tonumber(state, index));
2469 struct LuaContext::Reader<bool>
2471 static auto read(lua_State* state, int index)
2472 -> boost::optional<bool>
2474 if (!lua_isboolean(state, index))
2476 return lua_toboolean(state, index) != 0;
2481 // lua_tostring returns a temporary pointer, but that's not a problem since we copy
2482 // the data into a std::string
2484 struct LuaContext::Reader<std::string>
2486 static auto read(lua_State* state, int index)
2487 -> boost::optional<std::string>
2489 const auto val = lua_tostring(state, index);
2492 return std::string(val);
2497 template<typename TType>
2498 struct LuaContext::Reader<
2500 typename std::enable_if<std::is_enum<TType>::value>::type
2503 static auto read(lua_State* state, int index)
2504 -> boost::optional<TType>
2506 if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
2508 return static_cast<TType>(lua_tointeger(state, index));
2512 // LuaFunctionCaller
2513 template<typename TRetValue, typename... TParameters>
2514 struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2516 typedef LuaFunctionCaller<TRetValue (TParameters...)>
2519 static auto read(lua_State* state, int index)
2520 -> boost::optional<ReturnType>
2522 if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
2524 return ReturnType(state, index);
2529 template<typename TRetValue, typename... TParameters>
2530 struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2532 static auto read(lua_State* state, int index)
2533 -> boost::optional<std::function<TRetValue (TParameters...)>>
2535 if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
2537 std::function<TRetValue (TParameters...)> f{*val};
2538 return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
2546 template<typename TType1, typename TType2>
2547 struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2549 static auto read(lua_State* state, int index)
2550 -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2552 if (!lua_istable(state, index))
2555 std::vector<std::pair<TType1, TType2>> result;
2557 // we traverse the table at the top of the stack
2558 lua_pushnil(state); // first key
2559 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2560 // now a key and its value are pushed on the stack
2562 auto val1 = Reader<TType1>::read(state, -2);
2563 auto val2 = Reader<TType2>::read(state, -1);
2565 if (!val1.is_initialized() || !val2.is_initialized()) {
2566 lua_pop(state, 2); // we remove the value and the key
2570 result.push_back({ std::move(val1.get()), std::move(val2.get()) });
2571 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2574 lua_pop(state, 2); // we remove the value and the key
2579 return { std::move(result) };
2584 template<typename TKey, typename TValue>
2585 struct LuaContext::Reader<std::map<TKey,TValue>>
2587 static auto read(lua_State* state, int index)
2588 -> boost::optional<std::map<TKey,TValue>>
2590 if (!lua_istable(state, index))
2593 std::map<TKey,TValue> result;
2595 // we traverse the table at the top of the stack
2596 lua_pushnil(state); // first key
2597 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2598 // now a key and its value are pushed on the stack
2600 auto key = Reader<TKey>::read(state, -2);
2601 auto value = Reader<TValue>::read(state, -1);
2603 if (!key.is_initialized() || !value.is_initialized()) {
2604 lua_pop(state, 2); // we remove the value and the key
2608 result.insert({ std::move(key.get()), std::move(value.get()) });
2609 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2612 lua_pop(state, 2); // we remove the value and the key
2617 return { std::move(result) };
2622 template<typename TKey, typename TValue>
2623 struct LuaContext::Reader<std::unordered_map<TKey,TValue>>
2625 static auto read(lua_State* state, int index)
2626 -> boost::optional<std::unordered_map<TKey,TValue>>
2628 if (!lua_istable(state, index))
2631 std::unordered_map<TKey,TValue> result;
2633 // we traverse the table at the top of the stack
2634 lua_pushnil(state); // first key
2635 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2636 // now a key and its value are pushed on the stack
2638 auto key = Reader<TKey>::read(state, -2);
2639 auto value = Reader<TValue>::read(state, -1);
2641 if (!key.is_initialized() || !value.is_initialized()) {
2642 lua_pop(state, 2); // we remove the value and the key
2646 result.insert({ std::move(key.get()), std::move(value.get()) });
2647 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2650 lua_pop(state, 2); // we remove the value and the key
2655 return { std::move(result) };
2660 // IMPORTANT: optional means "either nil or the value of the right type"
2661 // * if the value is nil, then an optional containing an empty optional is returned
2662 // * if the value is of the right type, then an optional containing an optional containing the value is returned
2663 // * if the value is of the wrong type, then an empty optional is returned
2664 template<typename TType>
2665 struct LuaContext::Reader<boost::optional<TType>>
2667 static auto read(lua_State* state, int index)
2668 -> boost::optional<boost::optional<TType>>
2670 if (lua_isnil(state, index))
2671 return boost::optional<TType>{boost::none};
2672 if (auto&& other = Reader<TType>::read(state, index))
2673 return std::move(other);
2679 template<typename... TTypes>
2680 struct LuaContext::Reader<boost::variant<TTypes...>>
2682 typedef boost::variant<TTypes...>
2686 // class doing operations for a range of types from TIterBegin to TIterEnd
2687 template<typename TIterBegin, typename TIterEnd, typename = void>
2688 struct VariantReader
2690 using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2692 static auto read(lua_State* state, int index)
2693 -> boost::optional<ReturnType>
2695 // note: using SubReader::read triggers a compilation error when used with a reference
2696 if (const auto val = SubReader::read(state, index))
2697 return boost::variant<TTypes...>{*val};
2698 return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
2702 // specialization of class above being called when list of remaining types is empty
2703 template<typename TIterBegin, typename TIterEnd>
2704 struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2706 static auto read(lua_State* state, int index)
2707 -> boost::optional<ReturnType>
2713 // this is the main type
2714 typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2718 static auto read(lua_State* state, int index)
2719 -> boost::optional<ReturnType>
2721 return MainVariantReader::read(state, index);
2726 // tuple have an additional argument for their functions, that is the maximum size to read
2727 // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2729 struct LuaContext::Reader<std::tuple<>>
2731 static auto read(lua_State* state, int index, int maxSize = 0)
2732 -> boost::optional<std::tuple<>>
2734 return std::tuple<>{};
2738 template<typename TFirst, typename... TOthers>
2739 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2740 typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2743 // this is the "TFirst is NOT default constructible" version
2745 typedef std::tuple<TFirst, TOthers...>
2748 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2749 -> boost::optional<ReturnType>
2754 auto firstVal = Reader<TFirst>::read(state, index);
2755 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2757 if (!firstVal || !othersVal)
2760 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2764 template<typename TFirst, typename... TOthers>
2765 struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2766 typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2769 // this is the "TFirst is default-constructible" version
2771 typedef std::tuple<TFirst, TOthers...>
2774 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2775 -> boost::optional<ReturnType>
2777 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2782 return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2784 auto firstVal = Reader<TFirst>::read(state, index);
2788 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));