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