]> git.ipfire.org Git - thirdparty/pdns.git/blame - ext/luawrapper/include/LuaContext.hpp
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / ext / luawrapper / include / LuaContext.hpp
CommitLineData
8d1a79f2 1/*
2Copyright (c) 2013, Pierre KRIEGER
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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>
23473852 50#include <boost/format.hpp>
8d1a79f2 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
2dc07b95 58#if defined(_MSC_VER) && _MSC_VER < 1900
8d1a79f2 59# include "misc/exception.hpp"
60#endif
61
6ed34973 62#ifdef __GNUC__
63# define ATTR_UNUSED __attribute__((unused))
64#else
65# define ATTR_UNUSED
66#endif
67
23473852
AT
68#define LUACONTEXT_GLOBAL_EQ "e5ddced079fc405aa4937b386ca387d2"
69#define EQ_FUNCTION_NAME "__eq"
70#define TOSTRING_FUNCTION_NAME "__tostring"
71
8d1a79f2 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 */
b08d5f4b
RG
81
82#if defined(__GNUC__) && !defined(__clang__)
83#pragma GCC diagnostic push
84#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
85#endif
86
8d1a79f2 87class 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"
92public:
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);
23473852
AT
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 };
8d1a79f2 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:
d78d76c3 207 WrongTypeException(const std::string& luaType_, const std::type_info& destination_) :
a07410d7
RG
208 std::runtime_error("Trying to cast a lua variable from \"" + luaType_ + "\" to \"" + destination_.name() + "\""),
209 luaType(luaType_),
210 destination(destination_)
8d1a79f2 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
5fd2577f
PD
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
8d1a79f2 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
23473852
AT
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
8d1a79f2 482 /**
483 * Inverse operation of registerFunction
484 * @tparam TType Type whose function belongs to
485 */
486 template<typename TType>
fc61a45c 487 void unregisterFunction(const std::string& /*functionName*/)
8d1a79f2 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&)"
a07410d7 525 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
8d1a79f2 526 */
527 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
a07410d7 528 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 529 {
a07410d7 530 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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&)"
a07410d7 539 * @param writeFunction_ Function of type "void (TObject&, const TVarType&)"
8d1a79f2 540 */
541 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
a07410d7 542 void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 543 {
544 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
a07410d7 545 registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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&)"
a07410d7 582 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
8d1a79f2 583 */
584 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
a07410d7 585 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 586 {
a07410d7 587 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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&)"
a07410d7 595 * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)"
8d1a79f2 596 */
597 template<typename TMemberType, typename TReadFunction, typename TWriteFunction>
a07410d7 598 void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 599 {
600 static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
a07410d7 601 registerMemberImpl(tag<TMemberType>{}, std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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
c735407e 645 return result;
8d1a79f2 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
6aaf937d 665 * Additionally supported:
8d1a79f2 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));
51135080 742 * This version is more efficient than writeVariable if you want to write functions
8d1a79f2 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
767private:
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 {
a07410d7 779 PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {}
8d1a79f2 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
c735407e 787 PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return obj; }
8d1a79f2 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
00d3c4e3 840 // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack
8d1a79f2 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 {
6ed34973 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");
8d1a79f2 1147
1148 checkTypeRegistration(mState, &typeid(TObject));
34abd715 1149 setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, function);
8d1a79f2 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>
a07410d7 1231 void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 1232 {
1233 registerMemberImpl<TObject,TVarType>(name, readFunction);
1234
a07410d7
RG
1235 setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) {
1236 writeFunction_(object, value);
8d1a79f2 1237 });
1238
a07410d7 1239 setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) {
8d1a79f2 1240 assert(object);
a07410d7 1241 writeFunction_(*object, value);
8d1a79f2 1242 });
1243
a07410d7 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) {
8d1a79f2 1245 assert(object);
a07410d7 1246 writeFunction_(*object, value);
8d1a79f2 1247 });
1248 }
1249
1250 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
a07410d7 1251 void registerMemberImpl(tag<TVarType (TObject::*)>, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 1252 {
a07410d7 1253 registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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>
a07410d7 1297 void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 1298 {
1299 registerMemberImpl<TObject,TVarType>(readFunction);
1300
a07410d7
RG
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);
8d1a79f2 1303 });
1304
a07410d7 1305 setTable<void (TObject*, std::string, TVarType)>(mState, Registry, &typeid(TObject*), 2, [writeFunction_](TObject* object, const std::string& name, const TVarType& value) {
8d1a79f2 1306 assert(object);
a07410d7 1307 writeFunction_(*object, name, value);
8d1a79f2 1308 });
1309
a07410d7 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) {
8d1a79f2 1311 assert(object);
a07410d7 1312 writeFunction_(*object, name, value);
8d1a79f2 1313 });
1314 }
1315
1316 template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
a07410d7 1317 void registerMemberImpl(tag<TVarType (TObject::*)>, TReadFunction readFunction, TWriteFunction writeFunction_)
8d1a79f2 1318 {
a07410d7 1319 registerMemberImpl<TObject,TVarType>(std::move(readFunction), std::move(writeFunction_));
8d1a79f2 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
fc61a45c 1344 static const char* read(lua_State* /*l*/, void* data, size_t* size) {
8d1a79f2 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
fa161082 1409 auto inArguments = Pusher<std::tuple<TParameters&&...>>::push(state, std::forward_as_tuple(std::forward<TParameters>(input)...));
8d1a79f2 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
d4ad5e4c 1419 static int gettraceback(lua_State* L) {
43eb24c5
PD
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
d4ad5e4c
PD
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
8d1a79f2 1433 // this function just calls lua_pcall and checks for errors
1434 static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments)
1435 {
d4ad5e4c
PD
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
8d1a79f2 1443 // calling pcall automatically pops the parameters and pushes output
d4ad5e4c 1444 const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, tbindex);
8d1a79f2 1445 functionAndArguments.release();
1446
d4ad5e4c
PD
1447 lua_remove(state, tbindex); // remove traceback function
1448
1449
8d1a79f2 1450 // if pcall failed, analyzing the problem and throwing
1451 if (pcallReturnValue != 0) {
d4ad5e4c
PD
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
8d1a79f2 1460 PushedObject errorCode{state, 1};
1461
ef2ea4bf 1462 // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack
8d1a79f2 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));
d4ad5e4c 1470 throw ExecutionErrorException{str+traceBack};
8d1a79f2 1471
1472 } else {
1473 // an exception_ptr was pushed on the stack
1474 // rethrowing it with an additional ExecutionErrorException
1475 try {
5f029139
RG
1476 if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
1477 std::rethrow_exception(exp);
1478 }
2186a964
CHB
1479 } catch(const std::exception& e) {
1480 std::throw_with_nested(ExecutionErrorException{std::string{"Exception thrown by a callback function: "} + e.what()});
8d1a79f2 1481 } catch(...) {
d4ad5e4c 1482 std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua. "+traceBack});
8d1a79f2 1483 }
5f029139 1484 throw ExecutionErrorException{"Unknown Lua error"};
8d1a79f2 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
23473852
AT
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
8d1a79f2 1640 // writing structure for this type into the registry
1641 checkTypeRegistration(state, &typeid(TType));
87b59268
OM
1642 try {
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
5bec8d9d 1646 static_assert(alignof(TType) <= 8);
87b59268
OM
1647 const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
1648 new (pointerLocation) TType(std::forward<TType2>(value));
1649 }
1650 catch (...) {
1651 Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
1652 luaError(state);
1653 }
8d1a79f2 1654
8d1a79f2 1655 PushedObject obj{state, 1};
1656
1657 // creating the metatable (over the object on the stack)
1658 // lua_settable pops the key and value we just pushed, so stack management is easy
1659 // all that remains on the stack after these function calls is the metatable
1660 lua_newtable(state);
1661 PushedObject pushedTable{state, 1};
1662
1663 // using the garbage collecting function we created above
1664 if (!boost::has_trivial_destructor<TType>::value)
1665 {
1666 lua_pushstring(state, "__gc");
1667 lua_pushcfunction(state, garbageCallbackFunction);
1668 lua_settable(state, -3);
1669 }
1670
1671 // the _typeid index of the metatable will store the type_info*
1672 lua_pushstring(state, "_typeid");
1673 lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
1674 lua_settable(state, -3);
1675
1676 // using the index function we created above
1677 lua_pushstring(state, "__index");
1678 lua_pushcfunction(state, indexFunction);
1679 lua_settable(state, -3);
1680
1681 // using the newindex function we created above
1682 lua_pushstring(state, "__newindex");
1683 lua_pushcfunction(state, newIndexFunction);
1684 lua_settable(state, -3);
1685
23473852
AT
1686 lua_pushstring(state, "__tostring");
1687 lua_pushcfunction(state, toStringFunction);
1688 lua_settable(state, -3);
1689
1690 lua_pushstring(state, "__eq");
1691 lua_getglobal(state, LUACONTEXT_GLOBAL_EQ);
1692 lua_settable(state, -3);
1693
1694
8d1a79f2 1695 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1696 // lua_setmetatable will bind the two together and pop the metatable
1697 // our custom type remains on the stack (and that's what we want since this is a push function)
1698 lua_setmetatable(state, -2);
1699 pushedTable.release();
1700
c735407e 1701 return obj;
8d1a79f2 1702 }
1703 };
1704
1705 // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1706 template<typename... TTypes>
1707 struct PusherTotalMinSize;
1708
1709 // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1710 template<typename... TTypes>
1711 struct PusherTotalMaxSize;
1712
1713 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1714 template<typename... TTypes>
1715 struct PusherMinSize;
1716
1717 // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1718 template<typename... TTypes>
1719 struct PusherMaxSize;
1720
1721
1722 /**************************************************/
1723 /* READ FUNCTIONS */
1724 /**************************************************/
1725 // the "Reader" structures allow to read data from the stack
1726 // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1727 // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1728
1729 template<typename TType, typename = void>
1730 struct Reader {
1731 typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1732 ReturnType;
1733
1734 static auto read(lua_State* state, int index)
1735 -> boost::optional<ReturnType>
1736 {
1737 if (!test(state, index))
1738 return boost::none;
1739 return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
1740 }
1741
1742 private:
1743 static bool test(lua_State* state, int index)
1744 {
1745 if (!lua_isuserdata(state, index))
1746 return false;
1747 if (!lua_getmetatable(state, index))
1748 return false;
1749
1750 // now we have our metatable on the top of the stack
1751 // retrieving its _typeid member
1752 lua_pushstring(state, "_typeid");
1753 lua_gettable(state, -2);
1754 const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
1755 const auto typeIDToCompare = &typeid(TType);
1756
1757 // if wrong typeid, returning false
1758 lua_pop(state, 2);
1759 if (storedTypeID != typeIDToCompare)
1760 return false;
1761
1762 return true;
1763 }
1764 };
1765
1766 /**
1767 * This functions reads multiple values starting at "index" and passes them to the callback
1768 */
1769 template<typename TRetValue, typename TCallback>
fc61a45c 1770 static auto readIntoFunction(lua_State* /*state*/, tag<TRetValue>, TCallback&& callback, int /*index*/)
8d1a79f2 1771 -> TRetValue
1772 {
1773 return callback();
1774 }
1775 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1776 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1777 -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1778 {
1779 if (index >= 0) {
9e900ac2
RG
1780 static const TFirstType empty{};
1781 Binder<TCallback, const TFirstType&> binder{ callback, empty };
8d1a79f2 1782 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1783 }
1784
1785 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1786 if (!firstElem)
8cf811bf 1787 throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
8d1a79f2 1788
1789 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1790 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1791 }
1792 template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1793 static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1794 -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1795 {
1796 if (index >= 0)
1797 throw std::logic_error("Wrong number of parameters");
1798
1799 const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1800 if (!firstElem)
8cf811bf 1801 throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
8d1a79f2 1802
1803 Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1804 return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1805 }
1806
1807
1808 /**************************************************/
1809 /* UTILITIES */
1810 /**************************************************/
28abe755 1811 // structure that will ensure that a certain value is stored somewhere in the registry
8d1a79f2 1812 struct ValueInRegistry {
28abe755 1813 // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
a07410d7 1814 ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_}
8d1a79f2 1815 {
1816 lua_pushlightuserdata(lua, this);
4ec1e174 1817 lua_pushvalue(lua, -1 + index);
8d1a79f2 1818 lua_settable(lua, LUA_REGISTRYINDEX);
1819 }
1820
1821 // removing the function from the registry
1822 ~ValueInRegistry()
1823 {
1824 lua_pushlightuserdata(lua, this);
1825 lua_pushnil(lua);
1826 lua_settable(lua, LUA_REGISTRYINDEX);
1827 }
1828
1829 // loads the value and puts it at the top of the stack
1830 PushedObject pop()
1831 {
1832 lua_pushlightuserdata(lua, this);
1833 lua_gettable(lua, LUA_REGISTRYINDEX);
1834 return PushedObject{lua, 1};
1835 }
1836
1837 ValueInRegistry(const ValueInRegistry&) = delete;
1838 ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1839
1840 private:
1841 lua_State* lua;
1842 };
1843
1844 // binds the first parameter of a function object
1845 template<typename TFunctionObject, typename TFirstParamType>
1846 struct Binder {
1847 TFunctionObject function;
1848 TFirstParamType param;
1849
1850 template<typename... TParams>
1851 auto operator()(TParams&&... params)
1852 -> decltype(function(param, std::forward<TParams>(params)...))
1853 {
1854 return function(param, std::forward<TParams>(params)...);
1855 }
1856 };
1857
1858 // turns a type into a tuple
1859 // void is turned into std::tuple<>
1860 // existing tuples are untouched
1861 template<typename T>
1862 struct Tupleizer;
1863
1864 // this structure takes a pointer to a member function type and returns the base function type
1865 template<typename TType>
1866 struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug
1867
1868 // this structure takes any object and detects its function type
1869 template<typename TObjectType>
1870 struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1871
1872 // 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
1873 // the only case where "min != max" is with boost::optional at the end of the list
1874 template<typename... TArgumentsList>
1875 struct FunctionArgumentsCounter {};
1876
1877 // true is the template parameter is a boost::optional
1878 template<typename T>
1879 struct IsOptional : public std::false_type {};
1880};
1881
1882/// @deprecated
6ed34973 1883static LuaContext::EmptyArray_t ATTR_UNUSED
1884 LuaEmptyArray {};
8d1a79f2 1885/// @deprecated
6ed34973 1886static LuaContext::Metatable_t ATTR_UNUSED
1887 LuaMetatable {};
8d1a79f2 1888
1889/**************************************************/
1890/* PARTIAL IMPLEMENTATIONS */
1891/**************************************************/
1892template<>
fc61a45c 1893inline auto LuaContext::readTopAndPop<void>(lua_State* /*state*/, PushedObject /*obj*/)
8d1a79f2 1894 -> void
1895{
1896}
1897
1898// this structure takes a template parameter T
1899// if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1900// we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1901template<typename T>
1902struct LuaContext::Tupleizer { typedef std::tuple<T> type; };
1903template<typename... Args>
1904struct LuaContext::Tupleizer<std::tuple<Args...>> { typedef std::tuple<Args...> type; };
1905template<>
1906struct LuaContext::Tupleizer<void> { typedef std::tuple<> type; };
1907
1908// this structure takes any object and detects its function type
1909template<typename TRetValue, typename... TParameters>
1910struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)> { typedef TRetValue type(TParameters...); };
1911template<typename TObjectType>
1912struct LuaContext::FunctionTypeDetector<TObjectType*> { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1913
1914// this structure takes a pointer to a member function type and returns the base function type
1915template<typename TType, typename TRetValue, typename... TParameters>
1916struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)> { typedef TRetValue type(TParameters...); };
1917template<typename TType, typename TRetValue, typename... TParameters>
1918struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const> { typedef TRetValue type(TParameters...); };
1919template<typename TType, typename TRetValue, typename... TParameters>
1920struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile> { typedef TRetValue type(TParameters...); };
1921template<typename TType, typename TRetValue, typename... TParameters>
1922struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile> { typedef TRetValue type(TParameters...); };
1923
1924// implementation of PusherTotalMinSize
1925template<typename TFirst, typename... TTypes>
1926struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1927template<>
1928struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1929
1930// implementation of PusherTotalMaxSize
1931template<typename TFirst, typename... TTypes>
1932struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1933template<>
1934struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1935
1936// implementation of PusherMinSize
6ed34973 1937template<typename TFirst, typename TSecond, typename... TTypes>
1938struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...>
1939{
1940 static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize
1941 ?
1942 PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size
1943 :
1944 PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1945};
1946
1947template<typename TFirst>
1948struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
8d1a79f2 1949
1950// implementation of PusherMaxSize
1951template<typename TFirst, typename... TTypes>
1952struct 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; };
1953template<>
1954struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1955
1956// implementation of FunctionArgumentsCounter
1957template<typename TFirst, typename... TParams>
1958struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1959 typedef FunctionArgumentsCounter<TParams...>
1960 SubType;
1961 static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1962 static const int max = 1 + SubType::max;
1963};
1964template<>
1965struct LuaContext::FunctionArgumentsCounter<> {
1966 static const int min = 0;
1967 static const int max = 0;
1968};
1969
1970// implementation of IsOptional
1971template<typename T>
1972struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1973
1974// implementation of LuaFunctionCaller
1975template<typename TFunctionType>
1976class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1977template<typename TRetValue, typename... TParams>
1978class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1979{
1980public:
1981 TRetValue operator()(TParams&&... params) const
1982 {
1983 auto obj = valueHolder->pop();
1984 return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
1985 }
1986
1987private:
1988 std::shared_ptr<ValueInRegistry> valueHolder;
1989 lua_State* state;
1990
1991private:
1992 friend LuaContext;
a07410d7
RG
1993 explicit LuaFunctionCaller(lua_State* state_, int index) :
1994 valueHolder(std::make_shared<ValueInRegistry>(state_, index)),
1995 state(state_)
8d1a79f2 1996 {}
1997};
1998
1999
2000/**************************************************/
2001/* PUSH FUNCTIONS */
2002/**************************************************/
2003// specializations of the Pusher structure
2004
5fd2577f
PD
2005// opaque Lua references
2006template<>
2007struct LuaContext::Pusher<LuaContext::LuaObject> {
2008 static const int minSize = 1;
2009 static const int maxSize = 1;
2010
2011 static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept {
2012 if (value.objectInRegistry.get()) {
2013 PushedObject obj = value.objectInRegistry->pop();
2014 return obj;
2015 } else {
2016 lua_pushnil(state);
2017 return PushedObject{state, 1};
2018 }
2019 }
2020};
2021
8d1a79f2 2022// boolean
2023template<>
2024struct LuaContext::Pusher<bool> {
2025 static const int minSize = 1;
2026 static const int maxSize = 1;
2027
2028 static PushedObject push(lua_State* state, bool value) noexcept {
2029 lua_pushboolean(state, value);
2030 return PushedObject{state, 1};
2031 }
2032};
2033
2034// string
2035template<>
2036struct LuaContext::Pusher<std::string> {
2037 static const int minSize = 1;
2038 static const int maxSize = 1;
2039
2040 static PushedObject push(lua_State* state, const std::string& value) noexcept {
e0c3971a 2041 lua_pushlstring(state, value.c_str(), value.length());
8d1a79f2 2042 return PushedObject{state, 1};
2043 }
2044};
2045
2046// const char*
2047template<>
2048struct LuaContext::Pusher<const char*> {
2049 static const int minSize = 1;
2050 static const int maxSize = 1;
2051
2052 static PushedObject push(lua_State* state, const char* value) noexcept {
2053 lua_pushstring(state, value);
2054 return PushedObject{state, 1};
2055 }
2056};
2057
2058// const char[N]
2059template<int N>
2060struct LuaContext::Pusher<const char[N]> {
2061 static const int minSize = 1;
2062 static const int maxSize = 1;
2063
2064 static PushedObject push(lua_State* state, const char* value) noexcept {
2065 lua_pushstring(state, value);
2066 return PushedObject{state, 1};
2067 }
2068};
2069
2070// floating numbers
2071template<typename T>
2072struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
2073 static const int minSize = 1;
2074 static const int maxSize = 1;
2075
2076 static PushedObject push(lua_State* state, T value) noexcept {
2077 lua_pushnumber(state, value);
2078 return PushedObject{state, 1};
2079 }
2080};
2081
2082// integers
2083template<typename T>
2084struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2085 static const int minSize = 1;
2086 static const int maxSize = 1;
2087
2088 static PushedObject push(lua_State* state, T value) noexcept {
2089 lua_pushinteger(state, value);
2090 return PushedObject{state, 1};
2091 }
2092};
2093
2094// nil
2095template<>
2096struct LuaContext::Pusher<std::nullptr_t> {
2097 static const int minSize = 1;
2098 static const int maxSize = 1;
2099
fc61a45c 2100 static PushedObject push(lua_State* state, std::nullptr_t) noexcept {
8d1a79f2 2101 lua_pushnil(state);
2102 return PushedObject{state, 1};
2103 }
2104};
2105
2106// empty arrays
2107template<>
2108struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
2109 static const int minSize = 1;
2110 static const int maxSize = 1;
2111
2112 static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
2113 lua_newtable(state);
2114 return PushedObject{state, 1};
2115 }
2116};
2117
2118// std::type_info* is a lightuserdata
2119template<>
2120struct LuaContext::Pusher<const std::type_info*> {
2121 static const int minSize = 1;
2122 static const int maxSize = 1;
2123
2124 static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
2125 lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
2126 return PushedObject{state, 1};
2127 }
2128};
2129
2130// thread
2131template<>
2132struct LuaContext::Pusher<LuaContext::ThreadID> {
2133 static const int minSize = 1;
2134 static const int maxSize = 1;
2135
2136 static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
2137 lua_pushthread(value.state);
2138 return PushedObject{state, 1};
2139 }
2140};
2141
2142// maps
2143template<typename TKey, typename TValue>
2144struct LuaContext::Pusher<std::map<TKey,TValue>> {
2145 static const int minSize = 1;
2146 static const int maxSize = 1;
2147
2148 static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
2149 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");
2150 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");
2151
2152 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2153
2154 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2155 setTable<TValue>(state, obj, i->first, i->second);
2156
c735407e 2157 return obj;
8d1a79f2 2158 }
2159};
2160
2161// unordered_maps
b59b2759
RG
2162template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2163struct LuaContext::Pusher<std::unordered_map<TKey,TValue,THash,TKeyEqual>> {
8d1a79f2 2164 static const int minSize = 1;
2165 static const int maxSize = 1;
2166
b59b2759 2167 static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue,THash,TKeyEqual>& value) noexcept {
8d1a79f2 2168 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");
2169 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");
2170
2171 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2172
2173 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2174 setTable<TValue>(state, obj, i->first, i->second);
2175
c735407e 2176 return obj;
8d1a79f2 2177 }
2178};
2179
2180// vectors of pairs
2181template<typename TType1, typename TType2>
2182struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
2183 static const int minSize = 1;
2184 static const int maxSize = 1;
2185
2186 static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
2187 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");
2188 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");
2189
2190 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2191
2192 for (auto i = value.begin(), e = value.end(); i != e; ++i)
2193 setTable<TType2>(state, obj, i->first, i->second);
2194
c735407e 2195 return obj;
8d1a79f2 2196 }
2197};
2198
2199// vectors
2200template<typename TType>
2201struct LuaContext::Pusher<std::vector<TType>> {
2202 static const int minSize = 1;
2203 static const int maxSize = 1;
2204
2205 static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
2206 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");
2207
2208 auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
2209
2210 for (unsigned int i = 0; i < value.size(); ++i)
2211 setTable<TType>(state, obj, i + 1, value[i]);
2212
c735407e 2213 return obj;
8d1a79f2 2214 }
2215};
2216
2217// unique_ptr
2218template<typename TType>
2219struct LuaContext::Pusher<std::unique_ptr<TType>> {
2220 static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2221 static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2222
2223 static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2224 return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2225 }
2226};
2227
2228// enum
2229template<typename TEnum>
2230struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2231 #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2232 typedef typename std::underlying_type<TEnum>::type
2233 RealType;
2234 #else
2235 // implementation when std::underlying_type is not supported
2236 typedef unsigned long
2237 RealType;
2238 #endif
2239
2240 static const int minSize = Pusher<RealType>::minSize;
2241 static const int maxSize = Pusher<RealType>::maxSize;
2242
2243 static PushedObject push(lua_State* state, TEnum value) noexcept {
2244 return Pusher<RealType>::push(state, static_cast<RealType>(value));
2245 }
2246};
2247
2248// any function
2249// this specialization is not directly called, but is called by other specializations
2250template<typename TReturnType, typename... TParameters>
2251struct LuaContext::Pusher<TReturnType (TParameters...)>
2252{
2253 static const int minSize = 1;
2254 static const int maxSize = 1;
2255
2256 // counts the number of arguments
2257 typedef FunctionArgumentsCounter<TParameters...>
2258 LocalFunctionArgumentsCounter;
2259
2260 // this is the version of "push" for non-trivially destructible function objects
2261 template<typename TFunctionObject>
2262 static auto push(lua_State* state, TFunctionObject fn) noexcept
2263 -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2264 {
2265 // TODO: is_move_constructible not supported by some compilers
2266 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2267
2268 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2269 // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2270 // so we use userdata instead
2271
2272 // this function is called when the lua script tries to call our custom data type
2273 // we transfer execution to the "callback" function below
2274 const auto callCallback = [](lua_State* lua) -> int {
2275 assert(lua_gettop(lua) >= 1);
2276 assert(lua_isuserdata(lua, 1));
2277 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2278 assert(function);
2279
2280 return callback(lua, function, lua_gettop(lua) - 1).release();
2281 };
2282
2283 // this one is called when lua's garbage collector no longer needs our custom data type
2284 // we call the function object's destructor
2285 const auto garbageCallback = [](lua_State* lua) -> int {
2286 assert(lua_gettop(lua) == 1);
2287 auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2288 assert(function);
2289 function->~TFunctionObject();
2290 return 0;
2291 };
2292
2293 // creating the object
2294 // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2295 // and that's what we do with placement-new
5bec8d9d 2296 // static_assert(alignof(TFunctionObject) <= 8); XXX trips on at least c++lib 17, see #13766
8d1a79f2 2297 const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2298 new (functionLocation) TFunctionObject(std::move(fn));
2299
2300 // creating the metatable (over the object on the stack)
2301 // lua_settable pops the key and value we just pushed, so stack management is easy
2302 // all that remains on the stack after these function calls is the metatable
2303 lua_newtable(state);
2304 lua_pushstring(state, "__call");
2305 lua_pushcfunction(state, callCallback);
2306 lua_settable(state, -3);
2307
2308 lua_pushstring(state, "__gc");
2309 lua_pushcfunction(state, garbageCallback);
2310 lua_settable(state, -3);
2311
2312 // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2313 // lua_setmetatable will bind the two together and pop the metatable
2314 // our custom function remains on the stack (and that's what we want)
2315 lua_setmetatable(state, -2);
2316
2317 return PushedObject{state, 1};
2318 }
2319
2320 // this is the version of "push" for trivially destructible objects
2321 template<typename TFunctionObject>
2322 static auto push(lua_State* state, TFunctionObject fn) noexcept
2323 -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2324 {
2325 // TODO: is_move_constructible not supported by some compilers
2326 //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2327
2328 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2329 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2330
2331 // this is the cfunction that is the callback
a07410d7 2332 const auto function = [](lua_State* state_) -> int
8d1a79f2 2333 {
2334 // the function object is an upvalue
a07410d7
RG
2335 const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state_, lua_upvalueindex(1)));
2336 return callback(state_, toCall, lua_gettop(state_)).release();
8d1a79f2 2337 };
2338
2339 // we copy the function object onto the stack
5bec8d9d 2340 static_assert(alignof(TFunctionObject) <= 8);
8d1a79f2 2341 const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2342 new (functionObjectLocation) TFunctionObject(std::move(fn));
2343
2344 // pushing the function with the function object as upvalue
2345 lua_pushcclosure(state, function, 1);
2346 return PushedObject{state, 1};
2347 }
2348
2349 // this is the version of "push" for pointer to functions
2350 static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2351 -> PushedObject
2352 {
2353 // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2354 // since "fn" doesn't need to be destroyed, we simply push it on the stack
2355
2356 // this is the cfunction that is the callback
a07410d7 2357 const auto function = [](lua_State* state_) -> int
8d1a79f2 2358 {
2359 // the function object is an upvalue
a07410d7
RG
2360 const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state_, lua_upvalueindex(1)));
2361 return callback(state_, toCall, lua_gettop(state_)).release();
8d1a79f2 2362 };
2363
2364 // we copy the function object onto the stack
2365 lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
2366
2367 // pushing the function with the function object as upvalue
2368 lua_pushcclosure(state, function, 1);
2369 return PushedObject{state, 1};
2370 }
2371
2372 // this is the version of "push" for references to functions
2373 static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2374 -> PushedObject
2375 {
2376 return push(state, &fn);
2377 }
2378
2379private:
2380 // callback that calls the function object
2381 // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2382 template<typename TFunctionObject>
2383 static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2384 -> PushedObject
2385 {
2386 // checking if number of parameters is correct
2387 if (argumentsCount < LocalFunctionArgumentsCounter::min) {
2388 // if not, using lua_error to return an error
2389 luaL_where(state, 1);
2390 lua_pushstring(state, "This function requires at least ");
2391 lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
2392 lua_pushstring(state, " parameter(s)");
2393 lua_concat(state, 4);
2394 luaError(state);
2395
2396 } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
2397 // if not, using lua_error to return an error
2398 luaL_where(state, 1);
2399 lua_pushstring(state, "This function requires at most ");
2400 lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
2401 lua_pushstring(state, " parameter(s)");
2402 lua_concat(state, 4);
2403 luaError(state);
2404 }
2405
2406 // calling the function
2407 try {
2408 return callback2(state, *toCall, argumentsCount);
2409
2410 } catch (const WrongTypeException& ex) {
2411 // wrong parameter type, using lua_error to return an error
2412 luaL_where(state, 1);
2413 lua_pushstring(state, "Unable to convert parameter from ");
2414 lua_pushstring(state, ex.luaType.c_str());
2415 lua_pushstring(state, " to ");
2416 lua_pushstring(state, ex.destination.name());
a36609d1 2417 lua_concat(state, 5);
8d1a79f2 2418 luaError(state);
2419
a36609d1
CHB
2420 } catch (const std::exception& e) {
2421 luaL_where(state, 1);
2422 lua_pushstring(state, "Caught exception: ");
2423 lua_pushstring(state, e.what());
2424 lua_concat(state, 3);
2425 luaError(state);
8d1a79f2 2426 } catch (...) {
2427 Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
2428 luaError(state);
2429 }
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 // pushing the result on the stack and returning number of pushed elements
2437 typedef Pusher<typename std::decay<TReturnType>::type>
2438 P;
2439 return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
2440 }
2441
2442 template<typename TFunctionObject>
2443 static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2444 -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2445 {
2446 readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
2447 return PushedObject{state, 0};
2448 }
2449};
2450
2451// C function pointers
2452template<typename TReturnType, typename... TParameters>
2453struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2454{
2455 // using the function-pushing implementation
2456 typedef Pusher<TReturnType (TParameters...)>
2457 SubPusher;
2458 static const int minSize = SubPusher::minSize;
2459 static const int maxSize = SubPusher::maxSize;
2460
2461 template<typename TType>
2462 static PushedObject push(lua_State* state, TType value) noexcept {
2463 return SubPusher::push(state, value);
2464 }
2465};
2466
2467// C function references
2468template<typename TReturnType, typename... TParameters>
2469struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2470{
2471 // using the function-pushing implementation
2472 typedef Pusher<TReturnType(TParameters...)>
2473 SubPusher;
2474 static const int minSize = SubPusher::minSize;
2475 static const int maxSize = SubPusher::maxSize;
2476
2477 template<typename TType>
2478 static PushedObject push(lua_State* state, TType value) noexcept {
2479 return SubPusher::push(state, value);
2480 }
2481};
2482
2483// std::function
2484template<typename TReturnType, typename... TParameters>
2485struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2486{
2487 // using the function-pushing implementation
2488 typedef Pusher<TReturnType (TParameters...)>
2489 SubPusher;
2490 static const int minSize = SubPusher::minSize;
2491 static const int maxSize = SubPusher::maxSize;
2492
2493 static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2494 return SubPusher::push(state, value);
2495 }
2496};
2497
2498// boost::variant
2499template<typename... TTypes>
2500struct LuaContext::Pusher<boost::variant<TTypes...>>
2501{
2502 static const int minSize = PusherMinSize<TTypes...>::size;
2503 static const int maxSize = PusherMaxSize<TTypes...>::size;
2504
2505 static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
2506 PushedObject obj{state, 0};
2507 VariantWriter writer{state, obj};
2508 value.apply_visitor(writer);
c735407e 2509 return obj;
8d1a79f2 2510 }
2511
2512private:
2513 struct VariantWriter : public boost::static_visitor<> {
2514 template<typename TType>
2515 void operator()(TType value) noexcept
2516 {
2517 obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
2518 }
2519
a07410d7 2520 VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {}
8d1a79f2 2521 lua_State* state;
2522 PushedObject& obj;
2523 };
2524};
2525
2526// boost::optional
2527template<typename TType>
2528struct LuaContext::Pusher<boost::optional<TType>> {
2529 typedef Pusher<typename std::decay<TType>::type>
2530 UnderlyingPusher;
2531
2532 static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2533 static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2534
2535 static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
2536 if (value) {
2537 return UnderlyingPusher::push(state, value.get());
2538 } else {
2539 lua_pushnil(state);
2540 return PushedObject{state, 1};
2541 }
2542 }
2543};
2544
2545// tuple
2546template<typename... TTypes>
2547struct LuaContext::Pusher<std::tuple<TTypes...>> {
2548 // TODO: NOT EXCEPTION SAFE /!\ //
2549 static const int minSize = PusherTotalMinSize<TTypes...>::size;
2550 static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2551
2552 static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2553 return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2554 }
2555
2556 static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
2557 return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
2558 }
2559
2560private:
2561 template<int N>
2562 static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2563 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2564
2565 return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2566 push2(state, value, std::integral_constant<int,N+1>{});
2567 }
2568
2569 template<int N>
2570 static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
2571 typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2572
2573 return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
2574 push2(state, std::move(value), std::integral_constant<int,N+1>{});
2575 }
2576
fc61a45c 2577 static int push2(lua_State* /*state*/, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
8d1a79f2 2578 return 0;
2579 }
2580
fc61a45c 2581 static int push2(lua_State* /*state*/, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
8d1a79f2 2582 return 0;
2583 }
2584};
2585
2586/**************************************************/
2587/* READ FUNCTIONS */
2588/**************************************************/
2589// specializations of the Reader structures
2590
5fd2577f
PD
2591// opaque Lua references
2592template<>
2593struct LuaContext::Reader<LuaContext::LuaObject>
2594{
2595 static auto read(lua_State* state, int index)
2596 -> boost::optional<LuaContext::LuaObject>
2597 {
2598 LuaContext::LuaObject obj(state, index);
2599 return obj;
2600 }
2601};
2602
8d1a79f2 2603// reading null
2604template<>
2605struct LuaContext::Reader<std::nullptr_t>
2606{
2607 static auto read(lua_State* state, int index)
2608 -> boost::optional<std::nullptr_t>
2609 {
2610 if (!lua_isnil(state, index))
2611 return boost::none;
2612 return nullptr;
2613 }
2614};
2615
2616// integrals
2617template<typename TType>
2618struct LuaContext::Reader<
2619 TType,
2620 typename std::enable_if<std::is_integral<TType>::value>::type
2621 >
2622{
2623 static auto read(lua_State* state, int index)
2624 -> boost::optional<TType>
2625 {
2626# if LUA_VERSION_NUM >= 502
2627
2628 int success;
2629 auto value = lua_tointegerx(state, index, &success);
2630 if (success == 0)
2631 return boost::none;
2632 return static_cast<TType>(value);
2633
2634# else
2635
2636 if (!lua_isnumber(state, index))
2637 return boost::none;
2638 return static_cast<TType>(lua_tointeger(state, index));
2639
2640# endif
2641 }
2642};
2643
2644// floating points
2645template<typename TType>
2646struct LuaContext::Reader<
2647 TType,
2648 typename std::enable_if<std::is_floating_point<TType>::value>::type
2649 >
2650{
2651 static auto read(lua_State* state, int index)
2652 -> boost::optional<TType>
2653 {
2654# if LUA_VERSION_NUM >= 502
2655
2656 int success;
2657 auto value = lua_tonumberx(state, index, &success);
2658 if (success == 0)
2659 return boost::none;
2660 return static_cast<TType>(value);
2661
2662# else
2663
2664 if (!lua_isnumber(state, index))
2665 return boost::none;
2666 return static_cast<TType>(lua_tonumber(state, index));
2667
2668# endif
2669 }
2670};
2671
2672// boolean
2673template<>
2674struct LuaContext::Reader<bool>
2675{
2676 static auto read(lua_State* state, int index)
2677 -> boost::optional<bool>
2678 {
2679 if (!lua_isboolean(state, index))
2680 return boost::none;
2681 return lua_toboolean(state, index) != 0;
2682 }
2683};
2684
2685// string
2686// lua_tostring returns a temporary pointer, but that's not a problem since we copy
2687// the data into a std::string
2688template<>
2689struct LuaContext::Reader<std::string>
2690{
2691 static auto read(lua_State* state, int index)
2692 -> boost::optional<std::string>
2693 {
a6fb00c9
PD
2694 std::string result;
2695
2696 // lua_tolstring might convert the variable that would confuse lua_next, so we
2697 // make a copy of the variable.
2698 lua_pushvalue(state, index);
2699
448990ab 2700 size_t len;
a6fb00c9
PD
2701 const auto val = lua_tolstring(state, -1, &len);
2702
9c4d2c74 2703 if (val != nullptr)
a6fb00c9
PD
2704 result.assign(val, len);
2705
2706 lua_pop(state, 1);
2707
9c4d2c74 2708 return val != nullptr ? boost::optional<std::string>{ std::move(result) } : boost::none;
8d1a79f2 2709 }
2710};
2711
2712// enums
2713template<typename TType>
2714struct LuaContext::Reader<
2715 TType,
2716 typename std::enable_if<std::is_enum<TType>::value>::type
2717 >
2718{
2719 static auto read(lua_State* state, int index)
2720 -> boost::optional<TType>
2721 {
1752ce2b 2722 if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
8d1a79f2 2723 return boost::none;
2724 return static_cast<TType>(lua_tointeger(state, index));
2725 }
2726};
2727
2728// LuaFunctionCaller
2729template<typename TRetValue, typename... TParameters>
2730struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2731{
2732 typedef LuaFunctionCaller<TRetValue (TParameters...)>
2733 ReturnType;
2734
2735 static auto read(lua_State* state, int index)
2736 -> boost::optional<ReturnType>
2737 {
2738 if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
2739 return boost::none;
4ec1e174 2740 return ReturnType(state, index);
8d1a79f2 2741 }
2742};
2743
2744// function
2745template<typename TRetValue, typename... TParameters>
2746struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2747{
2748 static auto read(lua_State* state, int index)
2749 -> boost::optional<std::function<TRetValue (TParameters...)>>
2750 {
2751 if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
2752 {
2753 std::function<TRetValue (TParameters...)> f{*val};
2754 return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
2755 }
2756
2757 return boost::none;
2758 }
2759};
2760
2761// vector of pairs
2762template<typename TType1, typename TType2>
2763struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2764{
2765 static auto read(lua_State* state, int index)
2766 -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2767 {
2768 if (!lua_istable(state, index))
2769 return boost::none;
2770
2771 std::vector<std::pair<TType1, TType2>> result;
2772
2773 // we traverse the table at the top of the stack
2774 lua_pushnil(state); // first key
2775 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2776 // now a key and its value are pushed on the stack
2777 try {
2778 auto val1 = Reader<TType1>::read(state, -2);
2779 auto val2 = Reader<TType2>::read(state, -1);
2780
2781 if (!val1.is_initialized() || !val2.is_initialized()) {
2782 lua_pop(state, 2); // we remove the value and the key
2783 return {};
2784 }
2785
bb6f25c8 2786 result.push_back({ val1.get(), val2.get() });
8d1a79f2 2787 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2788
2789 } catch(...) {
2790 lua_pop(state, 2); // we remove the value and the key
2791 return {};
2792 }
2793 }
2794
2795 return { std::move(result) };
2796 }
2797};
2798
2799// map
2800template<typename TKey, typename TValue>
2801struct LuaContext::Reader<std::map<TKey,TValue>>
2802{
2803 static auto read(lua_State* state, int index)
2804 -> boost::optional<std::map<TKey,TValue>>
2805 {
2806 if (!lua_istable(state, index))
2807 return boost::none;
2808
2809 std::map<TKey,TValue> result;
2810
2811 // we traverse the table at the top of the stack
2812 lua_pushnil(state); // first key
2813 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2814 // now a key and its value are pushed on the stack
2815 try {
2816 auto key = Reader<TKey>::read(state, -2);
2817 auto value = Reader<TValue>::read(state, -1);
2818
2819 if (!key.is_initialized() || !value.is_initialized()) {
2820 lua_pop(state, 2); // we remove the value and the key
2821 return {};
2822 }
2823
bb6f25c8 2824 result.insert({ key.get(), value.get() });
8d1a79f2 2825 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2826
2827 } catch(...) {
2828 lua_pop(state, 2); // we remove the value and the key
2829 return {};
2830 }
2831 }
2832
2833 return { std::move(result) };
2834 }
2835};
2836
2837// unordered_map
b59b2759
RG
2838template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2839struct LuaContext::Reader<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
8d1a79f2 2840{
2841 static auto read(lua_State* state, int index)
b59b2759 2842 -> boost::optional<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
8d1a79f2 2843 {
2844 if (!lua_istable(state, index))
2845 return boost::none;
2846
b59b2759 2847 std::unordered_map<TKey,TValue,THash,TKeyEqual> result;
8d1a79f2 2848
2849 // we traverse the table at the top of the stack
2850 lua_pushnil(state); // first key
2851 while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
2852 // now a key and its value are pushed on the stack
2853 try {
2854 auto key = Reader<TKey>::read(state, -2);
2855 auto value = Reader<TValue>::read(state, -1);
2856
2857 if (!key.is_initialized() || !value.is_initialized()) {
2858 lua_pop(state, 2); // we remove the value and the key
2859 return {};
2860 }
2861
bb6f25c8 2862 result.insert({ key.get(), value.get() });
8d1a79f2 2863 lua_pop(state, 1); // we remove the value but keep the key for the next iteration
2864
2865 } catch(...) {
2866 lua_pop(state, 2); // we remove the value and the key
2867 return {};
2868 }
2869 }
2870
2871 return { std::move(result) };
2872 }
2873};
2874
2875// optional
2876// IMPORTANT: optional means "either nil or the value of the right type"
2877// * if the value is nil, then an optional containing an empty optional is returned
2878// * if the value is of the right type, then an optional containing an optional containing the value is returned
2879// * if the value is of the wrong type, then an empty optional is returned
2880template<typename TType>
2881struct LuaContext::Reader<boost::optional<TType>>
2882{
2883 static auto read(lua_State* state, int index)
2884 -> boost::optional<boost::optional<TType>>
2885 {
2886 if (lua_isnil(state, index))
2887 return boost::optional<TType>{boost::none};
2888 if (auto&& other = Reader<TType>::read(state, index))
2889 return std::move(other);
2890 return boost::none;
2891 }
2892};
2893
2894// variant
2895template<typename... TTypes>
2896struct LuaContext::Reader<boost::variant<TTypes...>>
2897{
2898 typedef boost::variant<TTypes...>
2899 ReturnType;
2900
2901private:
2902 // class doing operations for a range of types from TIterBegin to TIterEnd
2903 template<typename TIterBegin, typename TIterEnd, typename = void>
2904 struct VariantReader
2905 {
2906 using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2907
2908 static auto read(lua_State* state, int index)
2909 -> boost::optional<ReturnType>
2910 {
2911 // note: using SubReader::read triggers a compilation error when used with a reference
2912 if (const auto val = SubReader::read(state, index))
2913 return boost::variant<TTypes...>{*val};
2914 return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
2915 }
2916 };
2917
2918 // specialization of class above being called when list of remaining types is empty
2919 template<typename TIterBegin, typename TIterEnd>
2920 struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2921 {
fc61a45c 2922 static auto read(lua_State* /*state*/, int /*index*/)
8d1a79f2 2923 -> boost::optional<ReturnType>
2924 {
2925 return boost::none;
2926 }
2927 };
2928
2929 // this is the main type
2930 typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2931 MainVariantReader;
2932
2933public:
2934 static auto read(lua_State* state, int index)
2935 -> boost::optional<ReturnType>
2936 {
2937 return MainVariantReader::read(state, index);
2938 }
2939};
2940
2941// reading a tuple
2942// tuple have an additional argument for their functions, that is the maximum size to read
2943// if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2944template<>
2945struct LuaContext::Reader<std::tuple<>>
2946{
fc61a45c 2947 static auto read(lua_State* /*state*/, int /*index*/, int /*maxSize*/ = 0)
8d1a79f2 2948 -> boost::optional<std::tuple<>>
2949 {
2950 return std::tuple<>{};
2951 }
2952};
2953
2954template<typename TFirst, typename... TOthers>
2955struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2956 typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2957 >
2958{
2959 // this is the "TFirst is NOT default constructible" version
2960
2961 typedef std::tuple<TFirst, TOthers...>
2962 ReturnType;
2963
2964 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2965 -> boost::optional<ReturnType>
2966 {
2967 if (maxSize <= 0)
2968 return boost::none;
2969
2970 auto firstVal = Reader<TFirst>::read(state, index);
2971 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2972
2973 if (!firstVal || !othersVal)
2974 return boost::none;
2975
2976 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
2977 }
2978};
2979
2980template<typename TFirst, typename... TOthers>
2981struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2982 typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler
2983 >
2984{
2985 // this is the "TFirst is default-constructible" version
2986
2987 typedef std::tuple<TFirst, TOthers...>
2988 ReturnType;
2989
2990 static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2991 -> boost::optional<ReturnType>
2992 {
2993 auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
2994 if (!othersVal)
2995 return boost::none;
2996
2997 if (maxSize <= 0)
2998 return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2999
3000 auto firstVal = Reader<TFirst>::read(state, index);
3001 if (!firstVal)
3002 return boost::none;
3003
3004 return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
3005 }
3006};
3007
b08d5f4b
RG
3008#if defined(__GNUC__) && !defined(__clang__)
3009#pragma GCC diagnostic pop
3010#endif
3011
8d1a79f2 3012#endif