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