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