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