Coverage Report

Created: 2023-03-26 07:18

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