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