• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.5
/ext/luawrapper/include/LuaContext.hpp
1
/*
2
Copyright (c) 2013, Pierre KRIEGER
3
All rights reserved.
4

5
Redistribution and use in source and binary forms, with or without
6
modification, are permitted provided that the following conditions are met:
7
    * Redistributions of source code must retain the above copyright
8
      notice, this list of conditions and the following disclaimer.
9
    * Redistributions in binary form must reproduce the above copyright
10
      notice, this list of conditions and the following disclaimer in the
11
      documentation and/or other materials provided with the distribution.
12
    * Neither the name of the <organization> nor the
13
      names of its contributors may be used to endorse or promote products
14
      derived from this software without specific prior written permission.
15

16
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27

28
#ifndef INCLUDE_LUACONTEXT_HPP
29
#define INCLUDE_LUACONTEXT_HPP
30

31
#include <algorithm>
32
#include <array>
33
#include <cassert>
34
#include <cmath>
35
#include <cstring>
36
#include <functional>
37
#include <limits>
38
#include <list>
39
#include <map>
40
#include <memory>
41
#include <random>
42
#include <set>
43
#include <stdexcept>
44
#include <string>
45
#include <sstream>
46
#include <tuple>
47
#include <type_traits>
48
#include <unordered_map>
49
#include <boost/any.hpp>
50
#include <boost/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"
661✔
70
#define TOSTRING_FUNCTION_NAME "__tostring"
663✔
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
    {
3,342✔
98
        // luaL_newstate can return null if allocation failed
99
        mState = luaL_newstate();
3,342✔
100
        if (mState == nullptr)
3,342!
101
            throw std::bad_alloc();
×
102

103
        // setting the panic function
104
        lua_atpanic(mState, [](lua_State* state) -> int {
3,342✔
105
            const std::string str = lua_tostring(state, -1);
×
106
            lua_pop(state, 1);
×
107
            assert(false && "lua_atpanic triggered");
×
108
            exit(0);
×
109
        });
×
110

111
        // opening default library if required to do so
112
        if (openDefaultLibs)
3,342!
113
            luaL_openlibs(mState);
3,342✔
114

115
         writeGlobalEq();
3,342✔
116
    }
3,342✔
117

118
    void writeGlobalEq() {
3,342✔
119
      const auto eqFunction = [](lua_State* lua) -> int {
3,342✔
120
        try {
1✔
121
          lua_pushstring(lua, "__eq");
1✔
122
          lua_gettable(lua, -2);
1✔
123
          /* if not found, return false */
124
          if (lua_isnil(lua, -1)) {
1!
125
            lua_pop(lua, -2);
×
126
            lua_pushboolean(lua, false);
×
127
            return 1;
×
128
          }
×
129
          lua_insert(lua, lua_gettop(lua)-2);
1✔
130
          return callRaw(lua, PushedObject{lua, 3}, 1).release();
1✔
131
        } catch(...) {
1✔
132
          Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
×
133
          luaError(lua);
×
134
        }
×
135
      };
1✔
136
      lua_pushcfunction(mState, eqFunction);
3,342✔
137
      lua_setglobal(mState, LUACONTEXT_GLOBAL_EQ);
3,342✔
138
    };
3,342✔
139

140
    /**
141
     * Move constructor
142
     */
143
    LuaContext(LuaContext&& s) :
144
        mState(s.mState)
145
    {
651✔
146
        s.mState = luaL_newstate();
651✔
147
    }
651✔
148
    
149
    /**
150
     * Move operator
151
     */
152
    LuaContext& operator=(LuaContext&& s) noexcept
153
    {
4✔
154
        std::swap(mState, s.mState);
4✔
155
        return *this;
4✔
156
    }
4✔
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
    {
3,941✔
173
        assert(mState);
3,941✔
174
        lua_close(mState);
×
175
    }
3,941✔
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
        {
76✔
186
        }
76✔
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
        {
203✔
198
        }
203✔
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
        {
×
212
        }
×
213
        
214
        std::string luaType;
215
        const std::type_info& destination;
216
    };
217

218
    /**
219
     * Function object that can call a function stored by Lua
220
     * This type is copiable and movable, but not constructible. It can only be created through readVariable.
221
     * @tparam TFunctionType    Function type (eg. "int (int, bool)")
222
     */
223
    template<typename TFunctionType>
224
    class LuaFunctionCaller;
225

226
    /**
227
     * Opaque type that identifies a Lua object
228
     */
229
    struct LuaObject {
230
        LuaObject() = default;
231
        LuaObject(lua_State* state, int index=-1) {
×
232
            this->objectInRegistry = std::make_shared<LuaContext::ValueInRegistry>(state, index);
×
233
        }
×
234
        std::shared_ptr<LuaContext::ValueInRegistry> objectInRegistry;
235
    };
236

237
    /**
238
     * Opaque type that identifies a Lua thread
239
     */
240
    struct ThreadID {
241
        ThreadID() = default;
242
        ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { }
×
243
        ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; }
×
244
    public:
245
        friend LuaContext;
246
        lua_State* state;
247
        std::unique_ptr<ValueInRegistry> threadInRegistry;
248
    };
249

250
    /**
251
     * Type that is considered as an empty array
252
     */
253
    enum EmptyArray_t { EmptyArray };
254

255
    /**
256
     * Type for a metatable
257
     */
258
    enum Metatable_t { Metatable };
259

260
    /**
261
     * Executes lua code from the stream
262
     * @param code      A stream that Lua will read its code from
263
     */
264
    void executeCode(std::istream& code)
265
    {
651✔
266
        auto toCall = load(mState, code);
651✔
267
        call<std::tuple<>>(mState, std::move(toCall));
651✔
268
    }
651✔
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
    {
3✔
289
        executeCode(code.c_str());
3✔
290
    }
3✔
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
    {
290✔
301
        return executeCode<TType>(code.c_str());
290✔
302
    }
290✔
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
    {
11,737✔
310
        auto toCall = load(mState, code);
11,737✔
311
        call<std::tuple<>>(mState, std::move(toCall));
11,737✔
312
    }
11,737✔
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
    {
290✔
323
        auto toCall = load(mState, code);
290✔
324
        return call<TType>(mState, std::move(toCall));
290✔
325
    }
290✔
326

327
    /**
328
     * Executes lua code from the stream
329
     * @param code      A stream that Lua will read its code from
330
     */
331
    void executeCode(const ThreadID& thread, std::istream& code)
332
    {
×
333
        auto toCall = load(thread.state, code);
×
334
        call<std::tuple<>>(thread.state, std::move(toCall));
×
335
    }
×
336

337
    /**
338
     * Executes lua code from the stream and returns a value
339
     * @param code      A stream that Lua will read its code from
340
     * @tparam TType    The type that the executing code should return
341
     */
342
    template<typename TType>
343
    auto executeCode(const ThreadID& thread, std::istream& code)
344
        -> TType
345
    {
346
        auto toCall = load(thread.state, code);
347
        return call<TType>(thread.state, std::move(toCall));
348
    }
349

350
    /**
351
     * Executes lua code given as parameter
352
     * @param code      A string containing code that will be executed by Lua
353
     */
354
    void executeCode(const ThreadID& thread, const std::string& code)
355
    {
×
356
        executeCode(thread, code.c_str());
×
357
    }
×
358
    
359
    /*
360
     * Executes Lua code from the stream and returns a value
361
     * @param code      A string containing code that will be executed by Lua
362
     * @tparam TType    The type that the executing code should return
363
     */
364
    template<typename TType>
365
    auto executeCode(const ThreadID& thread, const std::string& code)
366
        -> TType
367
    {
368
        return executeCode<TType>(thread, code.c_str());
369
    }
370

371
    /**
372
     * Executes Lua code
373
     * @param code      A string containing code that will be executed by Lua
374
     */
375
    void executeCode(const ThreadID& thread, const char* code)
376
    {
×
377
        auto toCall = load(thread.state, code);
×
378
        call<std::tuple<>>(thread.state, std::move(toCall));
×
379
    }
×
380

381
    /*
382
     * Executes Lua code from the stream and returns a value
383
     * @param code      A string containing code that will be executed by Lua
384
     * @tparam TType    The type that the executing code should return
385
     */
386
    template<typename TType>
387
    auto executeCode(const ThreadID& thread, const char* code)
388
        -> TType
389
    {
390
        auto toCall = load(thread.state, code);
391
        return call<TType>(thread.state, std::move(toCall));
392
    }
393
    
394
    /**
395
     * Tells that Lua will be allowed to access an object's function
396
     * This is the version "registerFunction(name, &Foo::function)"
397
     */
398
    template<typename TPointerToMemberFunction>
399
    auto registerFunction(const std::string& name, TPointerToMemberFunction pointer)
400
        -> typename std::enable_if<std::is_member_function_pointer<TPointerToMemberFunction>::value>::type
401
    {
23,509✔
402
        registerFunctionImpl(name, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
23,509✔
403
    }
23,509✔
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
    {
159,709✔
414
        static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
159,709✔
415
        registerFunctionImpl(functionName, std::move(fn), tag<TFunctionType>{});
159,709✔
416
    }
159,709✔
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
    {
58✔
428
        static_assert(std::is_function<TFunctionType>::value, "registerFunction must take a function type as template parameter");
58✔
429
        registerFunctionImpl(functionName, std::move(fn), tag<TObject>{}, tag<TFunctionType>{});
58✔
430
    }
58✔
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
    {
659✔
440
        registerFunctionImpl(EQ_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
659✔
441
    }
659✔
442

443
    template<typename TFunctionType, typename TType>
444
    void registerEqFunction(TType fn)
445
    {
2✔
446
        static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
2✔
447
        registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
2✔
448
    }
2✔
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
    {
657✔
465
        registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::mem_fn(pointer), tag<TPointerToMemberFunction>{});
657✔
466
    }
657✔
467

468
    template<typename TFunctionType, typename TType>
469
    void registerToStringFunction(TType fn)
470
    {
6✔
471
        static_assert(std::is_member_function_pointer<TFunctionType>::value, "registerFunction must take a member function pointer type as template parameter");
6✔
472
        registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag<TFunctionType>{});
6✔
473
    }
6✔
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
    {
13,669✔
512
        // implementation simply calls the custom member with getter and setter
513
        const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; };
13,669✔
514
        const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; };
13,669✔
515
        registerMember<TVarType (TObject::*)>(name, getter, setter);
13,669✔
516
    }
13,669✔
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
    {
1,308✔
530
        registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
1,308✔
531
    }
1,308✔
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
    {
43,014✔
544
        static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
43,014✔
545
        registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction), std::move(writeFunction_));
43,014✔
546
    }
43,014✔
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
    {
15,576✔
572
        static_assert(std::is_member_object_pointer<TMemberType>::value, "registerMember must take a member object pointer type as template parameter");
15,576✔
573
        registerMemberImpl(tag<TMemberType>{}, name, std::move(readFunction));
15,576✔
574
    }
15,576✔
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
    {
×
639
        ThreadID result;
×
640

×
641
        result.state = lua_newthread(mState);
×
642
        result.threadInRegistry = std::unique_ptr<ValueInRegistry>(new ValueInRegistry(mState));
×
643
        lua_pop(mState, 1);
×
644

×
645
        return result;
×
646
    }
×
647

648
    /**
649
     * Destroys a thread created with createThread
650
     * @sa createThread
651
     */
652
    void destroyThread(ThreadID& id)
653
    {
×
654
        id.threadInRegistry.reset();
×
655
    }
×
656
    
657
    /**
658
     * Reads the content of a Lua variable
659
     * 
660
     * @tparam TType                Type requested for the read
661
     * @throw WrongTypeException    When the variable is not convertible to the requested type
662
     * @sa writeVariable
663
     *
664
     * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers
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
    {
690✔
687
        lua_getglobal(mState, name);
690✔
688
        lookIntoStackTop(mState, std::forward<TTypes>(elements)...);
690✔
689
        return readTopAndPop<TType>(mState, PushedObject{mState, 1});
690✔
690
    }
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 {
9,883✔
732
        static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable");
9,883✔
733
        typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
9,883✔
734
            RealDataType;
9,883✔
735
        static_assert(!std::is_same<typename Tupleizer<RealDataType>::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
9,883✔
736
        
737
        setTable<RealDataType>(mState, Globals, std::forward<TData>(data)...);
9,883✔
738
    }
9,883✔
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 {
268,245✔
746
        static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
268,245✔
747
        
748
        setTable<TFunctionType>(mState, Globals, std::forward<TData>(data)...);
268,245✔
749
    }
268,245✔
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 {
267,596✔
757
        static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction");
267,596✔
758
        typedef typename std::decay<typename std::tuple_element<sizeof...(TData) - 1,std::tuple<TData...>>::type>::type
267,596✔
759
            RealDataType;
267,596✔
760
        typedef typename FunctionTypeDetector<RealDataType>::type
267,596✔
761
            DetectedFunctionType;
267,596✔
762
        
763
        return writeFunction<DetectedFunctionType>(std::forward<TData>(data)...);
267,596✔
764
    }
267,596✔
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
        PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {}
102,986,976✔
780
        ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); }
103,264,146✔
781
        
782
        PushedObject& operator=(const PushedObject&) = delete;
783
        PushedObject(const PushedObject&) = delete;
784
        PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; }
158✔
785
        PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; }
261,087✔
786

787
        PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return obj; }
22,297,151✔
788
        void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; }
×
789
        
790
        auto getState() const -> lua_State* { return state; }
×
791
        auto getNum() const -> int { return num; }
414,163✔
792

793
        int release() { const auto n = num; num = 0; return n; }
32,556,387✔
794
        void pop() { if (num >= 1) lua_pop(state, num); num = 0; }
×
795
        void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; }
×
796

797
    private:
798
        lua_State* state;
799
        int num = 0;
800
    };
801
    
802

803
    /**************************************************/
804
    /*                     MISC                       */
805
    /**************************************************/
806
    // type used as a tag
807
    template<typename T>
808
    struct tag {};
809

810
    // tag for "the registry"
811
    enum RegistryTag { Registry };
812
    
813
    // this function takes a value representing the offset to look into
814
    // it will look into the top element of the stack and replace the element by its content at the given index
815
    template<typename OffsetType1, typename... OffsetTypeOthers>
816
    static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) {
817
        static_assert(Pusher<typename std::decay<OffsetType1>::type>::minSize == 1 && Pusher<typename std::decay<OffsetType1>::type>::maxSize == 1, "Impossible to have a multiple-values index");
818
        auto p1 = Pusher<typename std::decay<OffsetType1>::type>::push(state, offset1);
819
        lua_gettable(state, -2);
820
        lua_remove(state, -2);
821
        p1.release();
822

823
        lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
824
    }
825

826
    template<typename... OffsetTypeOthers>
827
    static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) {
828
        lua_getmetatable(state, -1);
829
        lua_remove(state, -2);
830

831
        lookIntoStackTop(state, std::forward<OffsetTypeOthers>(offsetOthers)...);
832
    }
833
    
834
    static void lookIntoStackTop(lua_State*) {
1,371✔
835
    }
1,371✔
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
    {
30,237✔
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");
30,237✔
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");
30,237✔
846
        
847
        auto p1 = Pusher<typename std::decay<TIndex>::type>::push(state, index);
30,237✔
848
        auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
30,237✔
849

850
        lua_settable(state, -3);
30,237✔
851
        p1.release();
30,237✔
852
        p2.release();
30,237✔
853
    }
30,237✔
854

855
    template<typename TDataType, typename TData>
856
    static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept
857
    {
1,257,148✔
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");
1,257,148✔
859

860
        auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
1,257,148✔
861
        lua_setfield(state, -2, index.c_str());
1,257,148✔
862
        p1.release();
1,257,148✔
863
    }
1,257,148✔
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
    {
1,167,570✔
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");
1,167,570✔
902
        
903
        auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1)) + std::move(pushedTable);
1,167,570✔
904
        lua_gettable(state, -2);
1,167,570✔
905

906
        setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1,167,570✔
907
    }
1,167,570✔
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
    {
1,167,570✔
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");
1,167,570✔
985
        
986
        auto p1 = Pusher<typename std::decay<TIndex1>::type>::push(state, std::forward<TIndex1>(index1));
1,167,570✔
987
        lua_gettable(state, LUA_REGISTRYINDEX);
1,167,570✔
988

989
        setTable<TDataType>(state, std::move(p1), std::forward<TIndex2>(index2), std::forward<TIndex3>(index3), std::forward<TIndices>(indices)...);
1,167,570✔
990
    }
1,167,570✔
991

992
    template<typename TDataType, typename TIndex, typename TData>
993
    static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept
994
    {
44,156✔
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");
44,156✔
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");
44,156✔
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);
44,156✔
1010
            auto p2 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
44,156✔
1011
            lua_settable(state, LUA_GLOBALSINDEX);
44,156✔
1012

1013
#       endif
44,156✔
1014

1015
        p1.release();
44,156✔
1016
        p2.release();
44,156✔
1017
    }
44,156✔
1018

1019
    template<typename TDataType, typename TData>
1020
    static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept
1021
    {
36,993✔
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");
36,993✔
1023

1024
        auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
36,993✔
1025
        lua_setglobal(state, index.c_str());
36,993✔
1026
        p1.release();
36,993✔
1027
    }
36,993✔
1028

1029
    template<typename TDataType, typename TData>
1030
    static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept
1031
    {
196,979✔
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");
196,979✔
1033

1034
        auto p1 = Pusher<typename std::decay<TDataType>::type>::push(state, std::forward<TData>(data));
196,979✔
1035
        lua_setglobal(state, index);
196,979✔
1036
        p1.release();
196,979✔
1037
    }
196,979✔
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
    {
18,606✔
1086
        auto val = Reader<typename std::decay<TReturnType>::type>::read(state, -object.getNum());
18,606✔
1087
        if (!val.is_initialized())
18,606!
1088
            throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)};
×
1089
        return val.get();
18,606✔
1090
    }
18,606✔
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
    {
17,818,680✔
1095
        lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
17,818,680✔
1096
        lua_gettable(state, LUA_REGISTRYINDEX);
17,818,680✔
1097
        if (!lua_isnil(state, -1)) {
17,818,680✔
1098
            lua_pop(state, 1);
15,074,485✔
1099
            return;
15,074,485✔
1100
        }
15,074,485✔
1101
        lua_pop(state, 1);
2,744,195✔
1102

1103
        lua_pushlightuserdata(state, const_cast<std::type_info*>(type));
2,744,195✔
1104
        lua_newtable(state);
2,744,195✔
1105

1106
        lua_pushinteger(state, 0);
2,744,195✔
1107
        lua_newtable(state);
2,744,195✔
1108
        lua_settable(state, -3);
2,744,195✔
1109

1110
        lua_pushinteger(state, 1);
2,744,195✔
1111
        lua_newtable(state);
2,744,195✔
1112
        lua_settable(state, -3);
2,744,195✔
1113

1114
        lua_pushinteger(state, 3);
2,744,195✔
1115
        lua_newtable(state);
2,744,195✔
1116
        lua_settable(state, -3);
2,744,195✔
1117

1118
        lua_pushinteger(state, 4);
2,744,195✔
1119
        lua_newtable(state);
2,744,195✔
1120
        lua_settable(state, -3);
2,744,195✔
1121

1122
        lua_settable(state, LUA_REGISTRYINDEX);
2,744,195✔
1123
    }
2,744,195✔
1124

1125
    // 
1126
#   ifdef _MSC_VER
1127
        __declspec(noreturn)
1128
#   else
1129
        [[noreturn]]
1130
#   endif
1131
    static void luaError(lua_State* state)
1132
    {
51✔
1133
        lua_error(state);
51✔
1134
        assert(false);
51✔
1135
        std::terminate();   // removes compilation warning
×
1136
    }
51✔
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
    {
184,600✔
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");
184,600✔
1147

1148
        checkTypeRegistration(mState, &typeid(TObject));
184,600✔
1149
        setTable<TRetValue(TObject&, TOtherParams...)>(mState, Registry, &typeid(TObject), 0, functionName, function);
184,600✔
1150
        
1151
        checkTypeRegistration(mState, &typeid(TObject*));
184,600✔
1152
        setTable<TRetValue(TObject*, TOtherParams...)>(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward<TOtherParams>(rest)...); });
184,600✔
1153
        
1154
        checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
184,600✔
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)...); });
184,600✔
1156
    }
184,600✔
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
    {
90,657✔
1161
        registerFunctionImpl(functionName, function, tag<TObject>{}, fTypeTag);
90,657✔
1162

1163
        checkTypeRegistration(mState, &typeid(TObject const*));
90,657✔
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)...); });
90,657✔
1165
        
1166
        checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
90,657✔
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)...); });
90,657✔
1168
    }
90,657✔
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
    {
93,885✔
1173
        registerFunctionImpl(functionName, std::move(function), tag<TObject>{}, tag<TRetValue (TOtherParams...)>{});
93,885✔
1174
    }
93,885✔
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
    {
90,657✔
1179
        registerFunctionImpl(functionName, std::move(function), tag<const TObject>{}, tag<TRetValue (TOtherParams...)>{});
90,657✔
1180
    }
90,657✔
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
    {
59,898✔
1198
        static_assert(std::is_class<TObject>::value || std::is_pointer<TObject>::value, "registerMember can only be called on a class or a pointer");
59,898✔
1199
        
1200
        checkTypeRegistration(mState, &typeid(TObject));
59,898✔
1201
        setTable<TVarType (TObject&)>(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) {
59,898✔
1202
            return readFunction(object);
119✔
1203
        });
119✔
1204
        
1205
        checkTypeRegistration(mState, &typeid(TObject*));
59,898✔
1206
        setTable<TVarType (TObject*)>(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) {
59,898✔
1207
            assert(object);
338✔
1208
            return readFunction(*object);
1209
        });
338✔
1210
        
1211
        checkTypeRegistration(mState, &typeid(TObject const*));
59,898✔
1212
        setTable<TVarType (TObject const*)>(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) {
59,898✔
1213
            assert(object);
9✔
1214
            return readFunction(*object);
1215
        });
9✔
1216
        
1217
        checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject>));
59,898✔
1218
        setTable<TVarType (std::shared_ptr<TObject>)>(mState, Registry, &typeid(std::shared_ptr<TObject>), 1, name, [readFunction](const std::shared_ptr<TObject>& object) {
59,898✔
1219
            assert(object);
1220
            return readFunction(*object);
1221
        });
1222
        
1223
        checkTypeRegistration(mState, &typeid(std::shared_ptr<TObject const>));
59,898✔
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) {
59,898✔
1225
            assert(object);
1226
            return readFunction(*object);
1227
        });
1228
    }
59,898✔
1229

1230
    template<typename TObject, typename TVarType, typename TReadFunction, typename TWriteFunction>
1231
    void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_)
1232
    {
44,322✔
1233
        registerMemberImpl<TObject,TVarType>(name, readFunction);
44,322✔
1234

1235
        setTable<void (TObject&, TVarType)>(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) {
44,322✔
1236
            writeFunction_(object, value);
1237
        });
1238
        
1239
        setTable<void (TObject*, TVarType)>(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) {
44,322✔
1240
            assert(object);
35✔
1241
            writeFunction_(*object, value);
1242
        });
35✔
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) {
44,322✔
1245
            assert(object);
1246
            writeFunction_(*object, value);
1247
        });
1248
    }
44,322✔
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
    {
43,014✔
1253
        registerMemberImpl<TObject,TVarType>(name, std::move(readFunction), std::move(writeFunction_));
43,014✔
1254
    }
43,014✔
1255

1256
    template<typename TObject, typename TVarType, typename TReadFunction>
1257
    void registerMemberImpl(tag<TVarType(TObject::*)>, const std::string& name, TReadFunction readFunction)
1258
    {
15,576✔
1259
        registerMemberImpl<TObject, TVarType>(name, std::move(readFunction));
15,576✔
1260
    }
15,576✔
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
    static PushedObject load(lua_State* state, std::istream& code) {
651✔
1335
        // since the lua_load function requires a static function, we use this structure
1336
        // the Reader structure is at the same time an object storing an istream and a buffer,
1337
        //   and a static function provider
1338
        struct Reader {
651✔
1339
            Reader(std::istream& str) : stream(str) {}
651✔
1340
            std::istream&           stream;
651✔
1341
            std::array<char,512>    buffer;
651✔
1342

1343
            // read function ; "data" must be an instance of Reader
1344
            static const char* read(lua_State* /*l*/, void* data, size_t* size) {
1,783✔
1345
                assert(size != nullptr);
1,783✔
1346
                assert(data != nullptr);
×
1347
                Reader& me = *static_cast<Reader*>(data);
×
1348
                if (me.stream.eof())    { *size = 0; return nullptr; }
1,783✔
1349

1350
                me.stream.read(me.buffer.data(), me.buffer.size());
1,132✔
1351
                *size = static_cast<size_t>(me.stream.gcount());    // gcount could return a value larger than a size_t, but its maximum is me.buffer.size() so there's no problem
1,132✔
1352
                return me.buffer.data();
1,132✔
1353
            }
1,783✔
1354
        };
651✔
1355

1356
        // we create an instance of Reader, and we call lua_load
1357
        Reader reader{code};
651✔
1358
        const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk"
651✔
1359
#           if LUA_VERSION_NUM >= 502
1360
                , nullptr
1361
#           endif
1362
        );
651✔
1363

1364
        // now we have to check return value
1365
        if (loadReturnValue != 0) {
651✔
1366
            // there was an error during loading, an error message was pushed on the stack
1367
            const std::string errorMsg = lua_tostring(state, -1);
1✔
1368
            lua_pop(state, 1);
1✔
1369
            if (loadReturnValue == LUA_ERRMEM)
1!
1370
                throw std::bad_alloc();
×
1371
            else if (loadReturnValue == LUA_ERRSYNTAX)
1!
1372
                throw SyntaxErrorException{errorMsg};
1✔
1373
            throw std::runtime_error("Error while calling lua_load: " + errorMsg);
×
1374
        }
1✔
1375

1376
        return PushedObject{state, 1};
650✔
1377
    }
651✔
1378
    
1379
    // this function loads data and pushes a function at the top of the stack
1380
    // throws in case of syntax error
1381
    static PushedObject load(lua_State* state, const char* code) {
29,403✔
1382
        auto loadReturnValue = luaL_loadstring(state, code);
29,403✔
1383

1384
        // now we have to check return value
1385
        if (loadReturnValue != 0) {
29,403✔
1386
            // there was an error during loading, an error message was pushed on the stack
1387
            const std::string errorMsg = lua_tostring(state, -1);
196✔
1388
            lua_pop(state, 1);
196✔
1389
            if (loadReturnValue == LUA_ERRMEM)
196!
1390
                throw std::bad_alloc();
×
1391
            else if (loadReturnValue == LUA_ERRSYNTAX)
196!
1392
                throw SyntaxErrorException{errorMsg};
196✔
1393
            throw std::runtime_error("Error while calling lua_load: " + errorMsg);
×
1394
        }
196✔
1395

1396
        return PushedObject{state, 1};
29,207✔
1397
    }
29,403✔
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
    {
18,753✔
1405
        typedef typename Tupleizer<TReturnType>::type
18,753✔
1406
            RealReturnType;
18,753✔
1407
        
1408
        // we push the parameters on the stack
1409
        auto inArguments = Pusher<std::tuple<TParameters&&...>>::push(state, std::forward_as_tuple(std::forward<TParameters>(input)...));
18,753✔
1410

1411
        // 
1412
        const int outArgumentsCount = std::tuple_size<RealReturnType>::value;
18,753✔
1413
        auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount);
18,753✔
1414

1415
        // pcall succeeded, we pop the returned values and return them
1416
        return readTopAndPop<TReturnType>(state, std::move(outArguments));
18,753✔
1417
    }
18,753✔
1418
    
1419
    static int gettraceback(lua_State* L) {
76✔
1420
        lua_getglobal(L, "debug"); // stack: error, debug library
76✔
1421
        lua_getfield(L, -1, "traceback"); // stack: error, debug library, debug.traceback function
76✔
1422
        lua_remove(L, -2); // stack: error, debug.traceback function
76✔
1423
        lua_pushstring(L, ""); // stack: error, debug.traceback, ""
76✔
1424
        lua_pushinteger(L, 2); // stack: error, debug.traceback, "", 2
76✔
1425
        lua_call(L, 2, 1); // stack: error, traceback
76✔
1426
        lua_createtable(L, 2, 0); // stack: error, traceback, {}
76✔
1427
        lua_insert(L, 1); // stack: {}, error, traceback
76✔
1428
        lua_rawseti(L, 1, 2); // stack: {[2]=traceback}, error
76✔
1429
        lua_rawseti(L, 1, 1); // stack: {[1]=error,[2]=traceback}
76✔
1430
        return 1; // return the table
76✔
1431
    }
76✔
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
    {
141,862✔
1436
        // provide traceback handler
1437
        int tbindex = lua_gettop(state) - (functionAndArguments.getNum() - 1);
141,862✔
1438
        lua_pushcfunction(state, gettraceback);
141,862✔
1439

1440
        // move it back up, before our function and arguments
1441
        lua_insert(state, tbindex);
141,862✔
1442

1443
        // calling pcall automatically pops the parameters and pushes output
1444
        const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, tbindex);
141,862✔
1445
        functionAndArguments.release();
141,862✔
1446

1447
        lua_remove(state, tbindex); // remove traceback function
141,862✔
1448

1449

1450
        // if pcall failed, analyzing the problem and throwing
1451
        if (pcallReturnValue != 0) {
141,862✔
1452

1453
            // stack top: {error, traceback}
1454
            lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error
76✔
1455
            lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback
76✔
1456
            lua_remove(state, -3); // stack top: error, traceback
76✔
1457

1458
            PushedObject traceBackRef{state, 1};
76✔
1459
            const auto traceBack = readTopAndPop<std::string>(state, std::move(traceBackRef)); // stack top: error
76✔
1460
            PushedObject errorCode{state, 1};
76✔
1461

1462
            // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack
1463
            if (pcallReturnValue == LUA_ERRMEM) {
76!
1464
                throw std::bad_alloc{};
×
1465

1466
            } else if (pcallReturnValue == LUA_ERRRUN) {
76!
1467
                if (lua_isstring(state, 1)) {
76✔
1468
                    // the error is a string
1469
                    const auto str = readTopAndPop<std::string>(state, std::move(errorCode));
57✔
1470
                    throw ExecutionErrorException{str+traceBack};
57✔
1471

1472
                } else {
57✔
1473
                    // an exception_ptr was pushed on the stack
1474
                    // rethrowing it with an additional ExecutionErrorException
1475
                    try {
19✔
1476
                        if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
19!
1477
                            std::rethrow_exception(exp);
19✔
1478
                        }
19✔
1479
                    } catch(const std::exception& e) {
19✔
1480
                        std::throw_with_nested(ExecutionErrorException{std::string{"Exception thrown by a callback function: "} + e.what()});
×
1481
                    } catch(...) {
19✔
1482
                        std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua. "+traceBack});
19✔
1483
                    }
19✔
1484
                    throw ExecutionErrorException{"Unknown Lua error"};
×
1485
                }
19✔
1486
            }
76✔
1487
        }
76✔
1488

1489
        return PushedObject{state, outArguments};
141,786✔
1490
    }
141,862✔
1491

1492
    
1493
    /**************************************************/
1494
    /*                PUSH FUNCTIONS                  */
1495
    /**************************************************/
1496
    template<typename T>
1497
    static PushedObject push(lua_State* state, T&& value)
1498
    {
1499
        return Pusher<typename std::decay<T>::type>::push(state, std::forward<T>(value));
1500
    }
1501

1502
    // the Pusher structures allow you to push a value on the stack
1503
    //  - static const int minSize : minimum size on the stack that the value can have
1504
    //  - static const int maxSize : maximum size on the stack that the value can have
1505
    //  - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack
1506

1507
    // implementation for custom objects
1508
    template<typename TType, typename = void>
1509
    struct Pusher {
1510
        static const int minSize = 1;
1511
        static const int maxSize = 1;
1512

1513
        template<typename TType2>
1514
        static PushedObject push(lua_State* state, TType2&& value) noexcept {
43,870✔
1515
            // this function is called when lua's garbage collector wants to destroy our object
1516
            // we simply call its destructor
1517
            const auto garbageCallbackFunction = [](lua_State* lua) -> int {
43,870✔
1518
                assert(lua_gettop(lua) == 1);
30,323✔
1519
                TType* ptr = static_cast<TType*>(lua_touserdata(lua, 1));
×
1520
                assert(ptr);
30,323✔
1521
                ptr->~TType();
×
1522
                return 0;
30,323✔
1523
            };
30,323✔
1524

1525
            // this function will be stored in __index in the metatable
1526
            const auto indexFunction = [](lua_State* lua) -> int {
43,871✔
1527
                try {
3,817✔
1528
                    assert(lua_gettop(lua) == 2);
3,817✔
1529
                    assert(lua_isuserdata(lua, 1));
×
1530

1531
                    // searching for a handler
1532
                    lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
×
1533
                    lua_gettable(lua, LUA_REGISTRYINDEX);
3,817✔
1534
                    assert(!lua_isnil(lua, -1));
3,817✔
1535
                    
1536
                    // looking into getter functions
1537
                    lua_pushinteger(lua, 0);
×
1538
                    lua_gettable(lua, -2);
3,817✔
1539
                    lua_pushvalue(lua, 2);
3,817✔
1540
                    lua_gettable(lua, -2);
3,817✔
1541
                    if (!lua_isnil(lua, -1))
3,817!
1542
                        return 1;
3,247✔
1543
                    lua_pop(lua, 2);
570✔
1544
                    
1545
                    // looking into getter members
1546
                    lua_pushinteger(lua, 1);
570✔
1547
                    lua_gettable(lua, -2);
570✔
1548
                    lua_pushvalue(lua, 2);
570✔
1549
                    lua_gettable(lua, -2);
570✔
1550
                    if (!lua_isnil(lua, -1)) {
570!
1551
                        lua_pushvalue(lua, 1);
570✔
1552
                        return callRaw(lua, PushedObject{lua, 2}, 1).release();
570✔
1553
                    }
570✔
1554
                    lua_pop(lua, 2);
×
1555

1556
                    // looking into default getter
1557
                    lua_pushinteger(lua, 2);
×
1558
                    lua_gettable(lua, -2);
×
1559
                    if (lua_isnil(lua, -1))
×
1560
                        return 1;
×
1561
                    lua_pushvalue(lua, 1);
×
1562
                    lua_pushvalue(lua, 2);
×
1563
                    return callRaw(lua, PushedObject{lua, 3}, 1).release();
×
1564

1565
                } catch (...) {
×
1566
                    Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
×
1567
                    luaError(lua);
×
1568
                }
×
1569
            };
3,817✔
1570

1571
            // this function will be stored in __newindex in the metatable
1572
            const auto newIndexFunction = [](lua_State* lua) -> int {
43,870✔
1573
                try {
35✔
1574
                    assert(lua_gettop(lua) == 3);
35✔
1575
                    assert(lua_isuserdata(lua, 1));
×
1576

1577
                    // searching for a handler
1578
                    lua_pushlightuserdata(lua, const_cast<std::type_info*>(&typeid(TType)));
×
1579
                    lua_rawget(lua, LUA_REGISTRYINDEX);
35✔
1580
                    assert(!lua_isnil(lua, -1));
35✔
1581
                    
1582
                    // looking into setter members
1583
                    lua_pushinteger(lua, 4);
×
1584
                    lua_rawget(lua, -2);
35✔
1585
                    lua_pushvalue(lua, 2);
35✔
1586
                    lua_rawget(lua, -2);
35✔
1587
                    if (!lua_isnil(lua, -1)) {
35!
1588
                        lua_pushvalue(lua, 1);
35✔
1589
                        lua_pushvalue(lua, 3);
35✔
1590
                        callRaw(lua, PushedObject{lua, 3}, 0);
35✔
1591
                        lua_pop(lua, 2);
35✔
1592
                        return 0;
35✔
1593
                    }
35✔
1594
                    lua_pop(lua, 2);
×
1595

1596
                    // looking into default setter
1597
                    lua_pushinteger(lua, 5);
×
1598
                    lua_rawget(lua, -2);
×
1599
                    if (lua_isnil(lua, -1))
×
1600
                    {
×
1601
                        lua_pop(lua, 2);
×
1602
                        lua_pushstring(lua, "No setter found");
×
1603
                        luaError(lua);
×
1604
                    }
×
1605
                    lua_pushvalue(lua, 1);
×
1606
                    lua_pushvalue(lua, 2);
×
1607
                    lua_pushvalue(lua, 3);
×
1608
                    callRaw(lua, PushedObject{lua, 4}, 0);
×
1609
                    lua_pop(lua, 1);
×
1610
                    return 0;
×
1611

1612
                } catch (...) {
35✔
1613
                    Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
×
1614
                    luaError(lua);
×
1615
                }
×
1616
            };
35✔
1617

1618
            const auto toStringFunction = [](lua_State* lua) -> int {
43,870✔
1619
               try {
195✔
1620
                    assert(lua_gettop(lua) == 1);
195✔
1621
                    assert(lua_isuserdata(lua, 1));
×
1622
                    lua_pushstring(lua, "__tostring");
×
1623
                    lua_gettable(lua, 1);
195✔
1624
                    if (lua_isnil(lua, -1))
195!
1625
                    {
×
1626
                        const void *ptr = lua_topointer(lua, -2);
×
1627
                        lua_pop(lua, 1);
×
1628
                        lua_pushstring(lua, (boost::format("userdata 0x%08x") % reinterpret_cast<intptr_t>(ptr)).str().c_str());
×
1629
                        return 1;
×
1630
                    }
×
1631
                    lua_pushvalue(lua, 1);
195✔
1632
                    return callRaw(lua, PushedObject{lua, 2}, 1).release();
195✔
1633
                } catch (...) {
195✔
1634
                    Pusher<std::exception_ptr>::push(lua, std::current_exception()).release();
×
1635
                    luaError(lua);
×
1636
                }
×
1637
            };
195✔
1638

1639

1640
            // writing structure for this type into the registry
1641
            checkTypeRegistration(state, &typeid(TType));
43,870✔
1642
            try {
43,870✔
1643
              // creating the object
1644
              // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
1645
              //   and that's what we do with placement-new
1646
              static_assert(alignof(TType) <= 8);
43,870✔
1647
              const auto pointerLocation = static_cast<TType*>(lua_newuserdata(state, sizeof(TType)));
43,870✔
1648
              new (pointerLocation) TType(std::forward<TType2>(value));
43,870✔
1649
            }
43,870✔
1650
            catch (...) {
43,870✔
1651
              Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
×
1652
              luaError(state);
×
1653
            }
×
1654

1655
            PushedObject obj{state, 1};
43,870✔
1656

1657
            // creating the metatable (over the object on the stack)
1658
            // lua_settable pops the key and value we just pushed, so stack management is easy
1659
            // all that remains on the stack after these function calls is the metatable
1660
            lua_newtable(state);
43,870✔
1661
            PushedObject pushedTable{state, 1};
43,870✔
1662

1663
            // using the garbage collecting function we created above
1664
            if (!boost::has_trivial_destructor<TType>::value)
43,870✔
1665
            {
39,671✔
1666
                lua_pushstring(state, "__gc");
39,671✔
1667
                lua_pushcfunction(state, garbageCallbackFunction);
39,671✔
1668
                lua_settable(state, -3);
39,671✔
1669
            }
39,671✔
1670

1671
            // the _typeid index of the metatable will store the type_info*
1672
            lua_pushstring(state, "_typeid");
43,870✔
1673
            lua_pushlightuserdata(state, const_cast<std::type_info*>(&typeid(TType)));
43,870✔
1674
            lua_settable(state, -3);
43,870✔
1675

1676
            // using the index function we created above
1677
            lua_pushstring(state, "__index");
43,870✔
1678
            lua_pushcfunction(state, indexFunction);
43,870✔
1679
            lua_settable(state, -3);
43,870✔
1680

1681
            // using the newindex function we created above
1682
            lua_pushstring(state, "__newindex");
43,870✔
1683
            lua_pushcfunction(state, newIndexFunction);
43,870✔
1684
            lua_settable(state, -3);
43,870✔
1685

1686
            lua_pushstring(state, "__tostring");
43,870✔
1687
            lua_pushcfunction(state, toStringFunction);
43,870✔
1688
            lua_settable(state, -3);
43,870✔
1689

1690
            lua_pushstring(state, "__eq");
43,870✔
1691
            lua_getglobal(state, LUACONTEXT_GLOBAL_EQ);
43,870✔
1692
            lua_settable(state, -3);
43,870✔
1693

1694

1695
            // at this point, the stack contains the object at offset -2 and the metatable at offset -1
1696
            // lua_setmetatable will bind the two together and pop the metatable
1697
            // our custom type remains on the stack (and that's what we want since this is a push function)
1698
            lua_setmetatable(state, -2);
43,870✔
1699
            pushedTable.release();
43,870✔
1700
            
1701
            return obj;
43,870✔
1702
        }
43,870✔
1703
    };
1704
    
1705
    // this structure has a "size" int static member which is equal to the total of the push min size of all the types
1706
    template<typename... TTypes>
1707
    struct PusherTotalMinSize;
1708

1709
    // this structure has a "size" int static member which is equal to the total of the push max size of all the types
1710
    template<typename... TTypes>
1711
    struct PusherTotalMaxSize;
1712
    
1713
    // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1714
    template<typename... TTypes>
1715
    struct PusherMinSize;
1716
    
1717
    // this structure has a "size" int static member which is equal to the maximum size of the push of all the types
1718
    template<typename... TTypes>
1719
    struct PusherMaxSize;
1720

1721
    
1722
    /**************************************************/
1723
    /*                READ FUNCTIONS                  */
1724
    /**************************************************/
1725
    // the "Reader" structures allow to read data from the stack
1726
    // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness)
1727
    // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type
1728
    
1729
    template<typename TType, typename = void>
1730
    struct Reader {
1731
        typedef typename std::conditional<std::is_pointer<TType>::value, TType, TType&>::type
1732
            ReturnType;
1733
        
1734
        static auto read(lua_State* state, int index)
1735
            -> boost::optional<ReturnType>
1736
        {
11,656✔
1737
            if (!test(state, index))
11,656!
1738
                return boost::none;
507✔
1739
            return boost::optional<ReturnType>(*static_cast<TType*>(lua_touserdata(state, index)));
11,149✔
1740
        }
11,656✔
1741

1742
    private:
1743
        static bool test(lua_State* state, int index)
1744
        {
11,656✔
1745
            if (!lua_isuserdata(state, index))
11,656!
1746
                return false;
405✔
1747
            if (!lua_getmetatable(state, index))
11,251!
1748
                return false;
×
1749

1750
            // now we have our metatable on the top of the stack
1751
            // retrieving its _typeid member
1752
            lua_pushstring(state, "_typeid");
11,251✔
1753
            lua_gettable(state, -2);
11,251✔
1754
            const auto storedTypeID = static_cast<const std::type_info*>(lua_touserdata(state, -1));
11,251✔
1755
            const auto typeIDToCompare = &typeid(TType);
11,251✔
1756

1757
            // if wrong typeid, returning false
1758
            lua_pop(state, 2);
11,251✔
1759
            if (storedTypeID != typeIDToCompare)
11,251!
1760
                return false;
102✔
1761

1762
            return true;
11,149✔
1763
        }
11,251✔
1764
    };
1765
    
1766
    /**
1767
     * This functions reads multiple values starting at "index" and passes them to the callback
1768
     */
1769
    template<typename TRetValue, typename TCallback>
1770
    static auto readIntoFunction(lua_State* /*state*/, tag<TRetValue>, TCallback&& callback, int /*index*/)
1771
        -> TRetValue
1772
    {
8,588✔
1773
        return callback();
8,588✔
1774
    }
8,588✔
1775
    template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1776
    static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1777
        -> typename std::enable_if<IsOptional<TFirstType>::value, TRetValue>::type
1778
    {
3,503✔
1779
        if (index >= 0) {
3,503!
1780
            static const TFirstType empty{};
2,395✔
1781
            Binder<TCallback, const TFirstType&> binder{ callback, empty };
2,395✔
1782
            return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
2,395✔
1783
        }
2,395✔
1784

1785
        const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
1,108✔
1786
        if (!firstElem)
1,108!
1787
            throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
×
1788

1789
        Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
1,108✔
1790
        return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
1,108✔
1791
    }
1,108✔
1792
    template<typename TRetValue, typename TCallback, typename TFirstType, typename... TTypes>
1793
    static auto readIntoFunction(lua_State* state, tag<TRetValue> retValueTag, TCallback&& callback, int index, tag<TFirstType>, tag<TTypes>... othersTags)
1794
        -> typename std::enable_if<!IsOptional<TFirstType>::value, TRetValue>::type
1795
    {
11,706✔
1796
        if (index >= 0)
11,706!
1797
            throw std::logic_error("Wrong number of parameters");
×
1798

1799
        const auto& firstElem = Reader<typename std::decay<TFirstType>::type>::read(state, index);
11,706✔
1800
        if (!firstElem)
11,706!
1801
            throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType));
×
1802

1803
        Binder<TCallback, const TFirstType&> binder{ callback, *firstElem };
11,706✔
1804
        return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...);
11,706✔
1805
    }
11,706✔
1806

1807

1808
    /**************************************************/
1809
    /*                   UTILITIES                    */
1810
    /**************************************************/
1811
    // structure that will ensure that a certain value is stored somewhere in the registry
1812
    struct ValueInRegistry {
1813
        // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry
1814
        ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_}
1815
        {
6,257✔
1816
            lua_pushlightuserdata(lua, this);
6,257✔
1817
            lua_pushvalue(lua, -1 + index);
6,257✔
1818
            lua_settable(lua, LUA_REGISTRYINDEX);
6,257✔
1819
        }
6,257✔
1820
        
1821
        // removing the function from the registry
1822
        ~ValueInRegistry()
1823
        {
3,716✔
1824
            lua_pushlightuserdata(lua, this);
3,716✔
1825
            lua_pushnil(lua);
3,716✔
1826
            lua_settable(lua, LUA_REGISTRYINDEX);
3,716✔
1827
        }
3,716✔
1828

1829
        // loads the value and puts it at the top of the stack
1830
        PushedObject pop()
1831
        {
22,624✔
1832
            lua_pushlightuserdata(lua, this);
22,624✔
1833
            lua_gettable(lua, LUA_REGISTRYINDEX);
22,624✔
1834
            return PushedObject{lua, 1};
22,624✔
1835
        }
22,624✔
1836

1837
        ValueInRegistry(const ValueInRegistry&) = delete;
1838
        ValueInRegistry& operator=(const ValueInRegistry&) = delete;
1839

1840
    private:
1841
        lua_State* lua;
1842
    };
1843
    
1844
    // binds the first parameter of a function object
1845
    template<typename TFunctionObject, typename TFirstParamType>
1846
    struct Binder {
1847
        TFunctionObject function;
1848
        TFirstParamType param;
1849

1850
        template<typename... TParams>
1851
        auto operator()(TParams&&... params)
1852
            -> decltype(function(param, std::forward<TParams>(params)...))
1853
        {
15,209✔
1854
            return function(param, std::forward<TParams>(params)...);
15,209✔
1855
        }
15,209✔
1856
    };
1857
    
1858
    // turns a type into a tuple
1859
    // void is turned into std::tuple<>
1860
    // existing tuples are untouched
1861
    template<typename T>
1862
    struct Tupleizer;
1863

1864
    // this structure takes a pointer to a member function type and returns the base function type
1865
    template<typename TType>
1866
    struct RemoveMemberPointerFunction { typedef void type; };          // required because of a compiler bug
1867

1868
    // this structure takes any object and detects its function type
1869
    template<typename TObjectType>
1870
    struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction<decltype(&std::decay<TObjectType>::type::operator())>::type type; };
1871

1872
    // this structure takes a function arguments list and has the "min" and the "max" static const member variables, whose value equal to the min and max number of parameters for the function
1873
    // the only case where "min != max" is with boost::optional at the end of the list
1874
    template<typename... TArgumentsList>
1875
    struct FunctionArgumentsCounter {};
1876
    
1877
    // true is the template parameter is a boost::optional
1878
    template<typename T>
1879
    struct IsOptional : public std::false_type {};
1880
};
1881

1882
/// @deprecated
1883
static LuaContext::EmptyArray_t ATTR_UNUSED
1884
    LuaEmptyArray {};
1885
/// @deprecated
1886
static LuaContext::Metatable_t ATTR_UNUSED
1887
    LuaMetatable {};
1888
    
1889
/**************************************************/
1890
/*            PARTIAL IMPLEMENTATIONS             */
1891
/**************************************************/
1892
template<>
1893
inline auto LuaContext::readTopAndPop<void>(lua_State* /*state*/, PushedObject /*obj*/)
1894
    -> void
1895
{
5,488✔
1896
}
5,488✔
1897

1898
// this structure takes a template parameter T
1899
// if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
1900
// we have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
1901
template<typename T>
1902
struct LuaContext::Tupleizer                        { typedef std::tuple<T>         type; };
1903
template<typename... Args>
1904
struct LuaContext::Tupleizer<std::tuple<Args...>>   { typedef std::tuple<Args...>   type; };
1905
template<>
1906
struct LuaContext::Tupleizer<void>                  { typedef std::tuple<>          type; };
1907

1908
// this structure takes any object and detects its function type
1909
template<typename TRetValue, typename... TParameters>
1910
struct LuaContext::FunctionTypeDetector<TRetValue (TParameters...)>             { typedef TRetValue type(TParameters...); };
1911
template<typename TObjectType>
1912
struct LuaContext::FunctionTypeDetector<TObjectType*>                           { typedef typename FunctionTypeDetector<TObjectType>::type type; };
1913

1914
// this structure takes a pointer to a member function type and returns the base function type
1915
template<typename TType, typename TRetValue, typename... TParameters>
1916
struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...)>                    { typedef TRetValue type(TParameters...); };
1917
template<typename TType, typename TRetValue, typename... TParameters>
1918
struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const>              { typedef TRetValue type(TParameters...); };
1919
template<typename TType, typename TRetValue, typename... TParameters>
1920
struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) volatile>           { typedef TRetValue type(TParameters...); };
1921
template<typename TType, typename TRetValue, typename... TParameters>
1922
struct LuaContext::RemoveMemberPointerFunction<TRetValue (TType::*)(TParameters...) const volatile>     { typedef TRetValue type(TParameters...); };
1923

1924
// implementation of PusherTotalMinSize
1925
template<typename TFirst, typename... TTypes>
1926
struct LuaContext::PusherTotalMinSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize + PusherTotalMinSize<TTypes...>::size; };
1927
template<>
1928
struct LuaContext::PusherTotalMinSize<> { static const int size = 0; };
1929

1930
// implementation of PusherTotalMaxSize
1931
template<typename TFirst, typename... TTypes>
1932
struct LuaContext::PusherTotalMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize + PusherTotalMaxSize<TTypes...>::size; };
1933
template<>
1934
struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; };
1935

1936
// implementation of PusherMinSize
1937
template<typename TFirst, typename TSecond, typename... TTypes>
1938
struct LuaContext::PusherMinSize<TFirst, TSecond, TTypes...> 
1939
{ 
1940
    static const int size = Pusher<typename std::decay<TFirst>::type>::minSize < Pusher<typename std::decay<TSecond>::type>::minSize 
1941
                            ? 
1942
                            PusherMinSize<typename std::decay<TFirst>::type, TTypes...>::size 
1943
                            : 
1944
                            PusherMinSize<typename std::decay<TSecond>::type, TTypes...>::size;
1945
};
1946

1947
template<typename TFirst>
1948
struct LuaContext::PusherMinSize<TFirst> { static const int size = Pusher<typename std::decay<TFirst>::type>::minSize; };
1949

1950
// implementation of PusherMaxSize
1951
template<typename TFirst, typename... TTypes>
1952
struct LuaContext::PusherMaxSize<TFirst, TTypes...> { static const int size = Pusher<typename std::decay<TFirst>::type>::maxSize > PusherTotalMaxSize<TTypes...>::size ? Pusher<typename std::decay<TFirst>::type>::maxSize : PusherMaxSize<TTypes...>::size; };
1953
template<>
1954
struct LuaContext::PusherMaxSize<> { static const int size = 0; };
1955

1956
// implementation of FunctionArgumentsCounter
1957
template<typename TFirst, typename... TParams>
1958
struct LuaContext::FunctionArgumentsCounter<TFirst, TParams...> {
1959
    typedef FunctionArgumentsCounter<TParams...>
1960
        SubType;
1961
    static const int min = (IsOptional<TFirst>::value && SubType::min == 0) ? 0 : 1 + SubType::min;
1962
    static const int max = 1 + SubType::max;
1963
};
1964
template<>
1965
struct LuaContext::FunctionArgumentsCounter<> {
1966
    static const int min = 0;
1967
    static const int max = 0;
1968
};
1969

1970
// implementation of IsOptional
1971
template<typename T>
1972
struct LuaContext::IsOptional<boost::optional<T>> : public std::true_type {};
1973

1974
// implementation of LuaFunctionCaller
1975
template<typename TFunctionType>
1976
class LuaContext::LuaFunctionCaller { static_assert(std::is_function<TFunctionType>::value, "Template parameter of LuaFunctionCaller must be a function type"); };
1977
template<typename TRetValue, typename... TParams>
1978
class LuaContext::LuaFunctionCaller<TRetValue (TParams...)>
1979
{
1980
public:
1981
    TRetValue operator()(TParams&&... params) const
1982
    {
4,808✔
1983
        auto obj = valueHolder->pop();
4,808✔
1984
        return call<TRetValue>(state, std::move(obj), std::forward<TParams>(params)...);
4,808✔
1985
    }
4,808✔
1986

1987
private:
1988
    std::shared_ptr<ValueInRegistry>    valueHolder;
1989
    lua_State*                          state;
1990

1991
private:
1992
    friend LuaContext;
1993
    explicit LuaFunctionCaller(lua_State* state_, int index) :
1994
        valueHolder(std::make_shared<ValueInRegistry>(state_, index)),
1995
        state(state_)
1996
    {}
906✔
1997
};
1998

1999

2000
/**************************************************/
2001
/*                PUSH FUNCTIONS                  */
2002
/**************************************************/
2003
// specializations of the Pusher structure
2004

2005
// opaque Lua references
2006
template<>
2007
struct LuaContext::Pusher<LuaContext::LuaObject> {
2008
    static const int minSize = 1;
2009
    static const int maxSize = 1;
2010

2011
    static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept {
×
2012
        if (value.objectInRegistry.get()) {
×
2013
            PushedObject obj = value.objectInRegistry->pop();
×
2014
            return obj;
×
2015
        } else {
×
2016
            lua_pushnil(state);
×
2017
            return PushedObject{state, 1};
×
2018
        }
×
2019
    }
×
2020
};
2021

2022
// boolean
2023
template<>
2024
struct LuaContext::Pusher<bool> {
2025
    static const int minSize = 1;
2026
    static const int maxSize = 1;
2027

2028
    static PushedObject push(lua_State* state, bool value) noexcept {
7,525✔
2029
        lua_pushboolean(state, value);
7,525✔
2030
        return PushedObject{state, 1};
7,525✔
2031
    }
7,525✔
2032
};
2033

2034
// string
2035
template<>
2036
struct LuaContext::Pusher<std::string> {
2037
    static const int minSize = 1;
2038
    static const int maxSize = 1;
2039

2040
    static PushedObject push(lua_State* state, const std::string& value) noexcept {
633,463✔
2041
        lua_pushlstring(state, value.c_str(), value.length());
633,463✔
2042
        return PushedObject{state, 1};
633,463✔
2043
    }
633,463✔
2044
};
2045

2046
// const char*
2047
template<>
2048
struct LuaContext::Pusher<const char*> {
2049
    static const int minSize = 1;
2050
    static const int maxSize = 1;
2051

2052
    static PushedObject push(lua_State* state, const char* value) noexcept {
×
2053
        lua_pushstring(state, value);
×
2054
        return PushedObject{state, 1};
×
2055
    }
×
2056
};
2057

2058
// const char[N]
2059
template<int N>
2060
struct LuaContext::Pusher<const char[N]> {
2061
    static const int minSize = 1;
2062
    static const int maxSize = 1;
2063

2064
    static PushedObject push(lua_State* state, const char* value) noexcept {
2065
        lua_pushstring(state, value);
2066
        return PushedObject{state, 1};
2067
    }
2068
};
2069

2070
// floating numbers
2071
template<typename T>
2072
struct LuaContext::Pusher<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
2073
    static const int minSize = 1;
2074
    static const int maxSize = 1;
2075

2076
    static PushedObject push(lua_State* state, T value) noexcept {
60✔
2077
        lua_pushnumber(state, value);
60✔
2078
        return PushedObject{state, 1};
60✔
2079
    }
60✔
2080
};
2081

2082
// integers
2083
template<typename T>
2084
struct LuaContext::Pusher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2085
    static const int minSize = 1;
2086
    static const int maxSize = 1;
2087

2088
    static PushedObject push(lua_State* state, T value) noexcept {
18,888,335✔
2089
        lua_pushinteger(state, value);
18,888,335✔
2090
        return PushedObject{state, 1};
18,888,335✔
2091
    }
18,888,335✔
2092
};
2093

2094
// nil
2095
template<>
2096
struct LuaContext::Pusher<std::nullptr_t> {
2097
    static const int minSize = 1;
2098
    static const int maxSize = 1;
2099

2100
    static PushedObject push(lua_State* state, std::nullptr_t) noexcept {
×
2101
        lua_pushnil(state);
×
2102
        return PushedObject{state, 1};
×
2103
    }
×
2104
};
2105

2106
// empty arrays
2107
template<>
2108
struct LuaContext::Pusher<LuaContext::EmptyArray_t> {
2109
    static const int minSize = 1;
2110
    static const int maxSize = 1;
2111

2112
    static PushedObject push(lua_State* state, EmptyArray_t) noexcept {
81,528✔
2113
        lua_newtable(state);
81,528✔
2114
        return PushedObject{state, 1};
81,528✔
2115
    }
81,528✔
2116
};
2117

2118
// std::type_info* is a lightuserdata
2119
template<>
2120
struct LuaContext::Pusher<const std::type_info*> {
2121
    static const int minSize = 1;
2122
    static const int maxSize = 1;
2123

2124
    static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept {
16,333,525✔
2125
        lua_pushlightuserdata(state, const_cast<std::type_info*>(ptr));
16,333,525✔
2126
        return PushedObject{state, 1};
16,333,525✔
2127
    }
16,333,525✔
2128
};
2129

2130
// thread
2131
template<>
2132
struct LuaContext::Pusher<LuaContext::ThreadID> {
2133
    static const int minSize = 1;
2134
    static const int maxSize = 1;
2135

2136
    static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept {
×
2137
        lua_pushthread(value.state);
×
2138
        return PushedObject{state, 1};
×
2139
    }
×
2140
};
2141

2142
// maps
2143
template<typename TKey, typename TValue>
2144
struct LuaContext::Pusher<std::map<TKey,TValue>> {
2145
    static const int minSize = 1;
2146
    static const int maxSize = 1;
2147

2148
    static PushedObject push(lua_State* state, const std::map<TKey,TValue>& value) noexcept {
34✔
2149
        static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
34✔
2150
        static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
34✔
2151
        
2152
        auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
34✔
2153

2154
        for (auto i = value.begin(), e = value.end(); i != e; ++i)
68!
2155
            setTable<TValue>(state, obj, i->first, i->second);
34✔
2156
        
2157
        return obj;
34✔
2158
    }
34✔
2159
};
2160

2161
// unordered_maps
2162
template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2163
struct LuaContext::Pusher<std::unordered_map<TKey,TValue,THash,TKeyEqual>> {
2164
    static const int minSize = 1;
2165
    static const int maxSize = 1;
2166

2167
    static PushedObject push(lua_State* state, const std::unordered_map<TKey,TValue,THash,TKeyEqual>& value) noexcept {
6,159✔
2168
        static_assert(Pusher<typename std::decay<TKey>::type>::minSize == 1 && Pusher<typename std::decay<TKey>::type>::maxSize == 1, "Can't push multiple elements for a table key");
6,159✔
2169
        static_assert(Pusher<typename std::decay<TValue>::type>::minSize == 1 && Pusher<typename std::decay<TValue>::type>::maxSize == 1, "Can't push multiple elements for a table value");
6,159✔
2170
        
2171
        auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
6,159✔
2172

2173
        for (auto i = value.begin(), e = value.end(); i != e; ++i)
95,586!
2174
            setTable<TValue>(state, obj, i->first, i->second);
89,427✔
2175
        
2176
        return obj;
6,159✔
2177
    }
6,159✔
2178
};
2179

2180
// vectors of pairs
2181
template<typename TType1, typename TType2>
2182
struct LuaContext::Pusher<std::vector<std::pair<TType1,TType2>>> {
2183
    static const int minSize = 1;
2184
    static const int maxSize = 1;
2185

2186
    static PushedObject push(lua_State* state, const std::vector<std::pair<TType1,TType2>>& value) noexcept {
3,062✔
2187
        static_assert(Pusher<typename std::decay<TType1>::type>::minSize == 1 && Pusher<typename std::decay<TType1>::type>::maxSize == 1, "Can't push multiple elements for a table key");
3,062✔
2188
        static_assert(Pusher<typename std::decay<TType2>::type>::minSize == 1 && Pusher<typename std::decay<TType2>::type>::maxSize == 1, "Can't push multiple elements for a table value");
3,062✔
2189

2190
        auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
3,062✔
2191

2192
        for (auto i = value.begin(), e = value.end(); i != e; ++i)
33,372!
2193
            setTable<TType2>(state, obj, i->first, i->second);
30,310✔
2194
        
2195
        return obj;
3,062✔
2196
    }
3,062✔
2197
};
2198

2199
// vectors
2200
template<typename TType>
2201
struct LuaContext::Pusher<std::vector<TType>> {
2202
    static const int minSize = 1;
2203
    static const int maxSize = 1;
2204

2205
    static PushedObject push(lua_State* state, const std::vector<TType>& value) noexcept {
36✔
2206
        static_assert(Pusher<typename std::decay<TType>::type>::minSize == 1 && Pusher<typename std::decay<TType>::type>::maxSize == 1, "Can't push multiple elements for a table value");
36✔
2207
        
2208
        auto obj = Pusher<EmptyArray_t>::push(state, EmptyArray);
36✔
2209

2210
        for (unsigned int i = 0; i < value.size(); ++i)
80!
2211
            setTable<TType>(state, obj, i + 1, value[i]);
44✔
2212
        
2213
        return obj;
36✔
2214
    }
36✔
2215
};
2216

2217
// unique_ptr
2218
template<typename TType>
2219
struct LuaContext::Pusher<std::unique_ptr<TType>> {
2220
    static const int minSize = Pusher<std::shared_ptr<TType>>::minSize;
2221
    static const int maxSize = Pusher<std::shared_ptr<TType>>::maxSize;
2222

2223
    static PushedObject push(lua_State* state, std::unique_ptr<TType> value) noexcept {
2224
        return Pusher<std::shared_ptr<TType>>::push(state, std::move(value));
2225
    }
2226
};
2227

2228
// enum
2229
template<typename TEnum>
2230
struct LuaContext::Pusher<TEnum, typename std::enable_if<std::is_enum<TEnum>::value>::type> {
2231
    #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
2232
        typedef typename std::underlying_type<TEnum>::type
2233
            RealType;
2234
    #else
2235
        // implementation when std::underlying_type is not supported
2236
        typedef unsigned long
2237
            RealType;
2238
    #endif
2239

2240
    static const int minSize = Pusher<RealType>::minSize;
2241
    static const int maxSize = Pusher<RealType>::maxSize;
2242

2243
    static PushedObject push(lua_State* state, TEnum value) noexcept {
2244
        return Pusher<RealType>::push(state, static_cast<RealType>(value));
2245
    }
2246
};
2247

2248
// any function
2249
// this specialization is not directly called, but is called by other specializations
2250
template<typename TReturnType, typename... TParameters>
2251
struct LuaContext::Pusher<TReturnType (TParameters...)>
2252
{
2253
    static const int minSize = 1;
2254
    static const int maxSize = 1;
2255

2256
    // counts the number of arguments
2257
    typedef FunctionArgumentsCounter<TParameters...>
2258
        LocalFunctionArgumentsCounter;
2259

2260
    // this is the version of "push" for non-trivially destructible function objects
2261
    template<typename TFunctionObject>
2262
    static auto push(lua_State* state, TFunctionObject fn) noexcept
2263
        -> typename std::enable_if<!boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2264
    {
2265
        // TODO: is_move_constructible not supported by some compilers
2266
        //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2267

2268
        // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2269
        // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
2270
        // so we use userdata instead
2271
        
2272
        // this function is called when the lua script tries to call our custom data type
2273
        // we transfer execution to the "callback" function below
2274
        const auto callCallback = [](lua_State* lua) -> int {
2275
            assert(lua_gettop(lua) >= 1);
2276
            assert(lua_isuserdata(lua, 1));
2277
            auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2278
            assert(function);
2279

2280
            return callback(lua, function, lua_gettop(lua) - 1).release();
2281
        };
2282

2283
        // this one is called when lua's garbage collector no longer needs our custom data type
2284
        // we call the function object's destructor
2285
        const auto garbageCallback = [](lua_State* lua) -> int {
2286
            assert(lua_gettop(lua) == 1);
2287
            auto function = static_cast<TFunctionObject*>(lua_touserdata(lua, 1));
2288
            assert(function);
2289
            function->~TFunctionObject();
2290
            return 0;
2291
        };
2292

2293
        // creating the object
2294
        // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
2295
        //   and that's what we do with placement-new
2296
        // static_assert(alignof(TFunctionObject) <= 8); XXX trips on at least c++lib 17, see #13766
2297
        const auto functionLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
2298
        new (functionLocation) TFunctionObject(std::move(fn));
2299

2300
        // creating the metatable (over the object on the stack)
2301
        // lua_settable pops the key and value we just pushed, so stack management is easy
2302
        // all that remains on the stack after these function calls is the metatable
2303
        lua_newtable(state);
2304
        lua_pushstring(state, "__call");
2305
        lua_pushcfunction(state, callCallback);
2306
        lua_settable(state, -3);
2307

2308
        lua_pushstring(state, "__gc");
2309
        lua_pushcfunction(state, garbageCallback);
2310
        lua_settable(state, -3);
2311

2312
        // at this point, the stack contains the object at offset -2 and the metatable at offset -1
2313
        // lua_setmetatable will bind the two together and pop the metatable
2314
        // our custom function remains on the stack (and that's what we want)
2315
        lua_setmetatable(state, -2);
2316

2317
        return PushedObject{state, 1};
2318
    }
2319

2320
    // this is the version of "push" for trivially destructible objects
2321
    template<typename TFunctionObject>
2322
    static auto push(lua_State* state, TFunctionObject fn) noexcept
2323
        -> typename std::enable_if<boost::has_trivial_destructor<TFunctionObject>::value, PushedObject>::type
2324
    {
1,433,868✔
2325
        // TODO: is_move_constructible not supported by some compilers
2326
        //static_assert(std::is_move_constructible<TFunctionObject>::value, "The function object must be move-constructible");
2327

2328
        // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2329
        // since "fn" doesn't need to be destroyed, we simply push it on the stack
2330

2331
        // this is the cfunction that is the callback
2332
        const auto function = [](lua_State* state_) -> int
1,433,868✔
2333
        {
1,433,868✔
2334
            // the function object is an upvalue
2335
            const auto toCall = static_cast<TFunctionObject*>(lua_touserdata(state_, lua_upvalueindex(1)));
8,481✔
2336
            return callback(state_, toCall, lua_gettop(state_)).release();
8,481✔
2337
        };
8,481✔
2338

2339
        // we copy the function object onto the stack
2340
        static_assert(alignof(TFunctionObject) <= 8);
1,433,868✔
2341
        const auto functionObjectLocation = static_cast<TFunctionObject*>(lua_newuserdata(state, sizeof(TFunctionObject)));
1,433,868✔
2342
        new (functionObjectLocation) TFunctionObject(std::move(fn));
1,433,868✔
2343

2344
        // pushing the function with the function object as upvalue
2345
        lua_pushcclosure(state, function, 1);
1,433,868✔
2346
        return PushedObject{state, 1};
1,433,868✔
2347
    }
1,433,868✔
2348
    
2349
    // this is the version of "push" for pointer to functions
2350
    static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept
2351
        -> PushedObject
2352
    {
1,947✔
2353
        // when the lua script calls the thing we will push on the stack, we want "fn" to be executed
2354
        // since "fn" doesn't need to be destroyed, we simply push it on the stack
2355

2356
        // this is the cfunction that is the callback
2357
        const auto function = [](lua_State* state_) -> int
1,947✔
2358
        {
1,947✔
2359
            // the function object is an upvalue
2360
            const auto toCall = reinterpret_cast<TReturnType (*)(TParameters...)>(lua_touserdata(state_, lua_upvalueindex(1)));
107✔
2361
            return callback(state_, toCall, lua_gettop(state_)).release();
107✔
2362
        };
107✔
2363

2364
        // we copy the function object onto the stack
2365
        lua_pushlightuserdata(state, reinterpret_cast<void*>(fn));
1,947✔
2366

2367
        // pushing the function with the function object as upvalue
2368
        lua_pushcclosure(state, function, 1);
1,947✔
2369
        return PushedObject{state, 1};
1,947✔
2370
    }
1,947✔
2371
    
2372
    // this is the version of "push" for references to functions
2373
    static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept
2374
        -> PushedObject
2375
    {
2376
        return push(state, &fn);
2377
    }
2378

2379
private:
2380
    // callback that calls the function object
2381
    // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back
2382
    template<typename TFunctionObject>
2383
    static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount)
2384
        -> PushedObject
2385
    {
8,588✔
2386
        // checking if number of parameters is correct
2387
        if (argumentsCount < LocalFunctionArgumentsCounter::min) {
8,588!
2388
            // if not, using lua_error to return an error
2389
            luaL_where(state, 1);
×
2390
            lua_pushstring(state, "This function requires at least ");
×
2391
            lua_pushnumber(state, LocalFunctionArgumentsCounter::min);
×
2392
            lua_pushstring(state, " parameter(s)");
×
2393
            lua_concat(state, 4);
×
2394
            luaError(state);
×
2395

2396
        } else if (argumentsCount > LocalFunctionArgumentsCounter::max) {
8,588!
2397
            // if not, using lua_error to return an error
2398
            luaL_where(state, 1);
×
2399
            lua_pushstring(state, "This function requires at most ");
×
2400
            lua_pushnumber(state, LocalFunctionArgumentsCounter::max);
×
2401
            lua_pushstring(state, " parameter(s)");
×
2402
            lua_concat(state, 4);
×
2403
            luaError(state);
×
2404
        }
×
2405
                
2406
        // calling the function
2407
        try {
8,588✔
2408
            return callback2(state, *toCall, argumentsCount);
8,588✔
2409

2410
        } catch (const WrongTypeException& ex) {
8,588✔
2411
            // wrong parameter type, using lua_error to return an error
2412
            luaL_where(state, 1);
×
2413
            lua_pushstring(state, "Unable to convert parameter from ");
×
2414
            lua_pushstring(state, ex.luaType.c_str());
×
2415
            lua_pushstring(state, " to ");
×
2416
            lua_pushstring(state, ex.destination.name());
×
2417
            lua_concat(state, 5);
×
2418
            luaError(state);
×
2419

2420
        } catch (const std::exception& e) {
2✔
2421
          luaL_where(state, 1);
2✔
2422
          lua_pushstring(state, "Caught exception: ");
2✔
2423
          lua_pushstring(state, e.what());
2✔
2424
          lua_concat(state, 3);
2✔
2425
          luaError(state);
2✔
2426
        } catch (...) {
2✔
2427
            Pusher<std::exception_ptr>::push(state, std::current_exception()).release();
1✔
2428
            luaError(state);
1✔
2429
        }
1✔
2430
    }
8,588✔
2431
    
2432
    template<typename TFunctionObject>
2433
    static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2434
        -> typename std::enable_if<!std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2435
    {
5,140✔
2436
        // pushing the result on the stack and returning number of pushed elements
2437
        typedef Pusher<typename std::decay<TReturnType>::type>
5,140✔
2438
            P;
5,140✔
2439
        return P::push(state, readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...));
5,140✔
2440
    }
5,140✔
2441
    
2442
    template<typename TFunctionObject>
2443
    static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount)
2444
        -> typename std::enable_if<std::is_void<TReturnType>::value && !std::is_void<TFunctionObject>::value, PushedObject>::type
2445
    {
3,448✔
2446
        readIntoFunction(state, tag<TReturnType>{}, toCall, -argumentsCount, tag<TParameters>{}...);
3,448✔
2447
        return PushedObject{state, 0};
3,448✔
2448
    }
3,448✔
2449
};
2450

2451
// C function pointers
2452
template<typename TReturnType, typename... TParameters>
2453
struct LuaContext::Pusher<TReturnType (*)(TParameters...)>
2454
{
2455
    // using the function-pushing implementation
2456
    typedef Pusher<TReturnType (TParameters...)>
2457
        SubPusher;
2458
    static const int minSize = SubPusher::minSize;
2459
    static const int maxSize = SubPusher::maxSize;
2460

2461
    template<typename TType>
2462
    static PushedObject push(lua_State* state, TType value) noexcept {
1,435,815✔
2463
        return SubPusher::push(state, value);
1,435,815✔
2464
    }
1,435,815✔
2465
};
2466

2467
// C function references
2468
template<typename TReturnType, typename... TParameters>
2469
struct LuaContext::Pusher<TReturnType (&)(TParameters...)>
2470
{
2471
    // using the function-pushing implementation
2472
    typedef Pusher<TReturnType(TParameters...)>
2473
        SubPusher;
2474
    static const int minSize = SubPusher::minSize;
2475
    static const int maxSize = SubPusher::maxSize;
2476

2477
    template<typename TType>
2478
    static PushedObject push(lua_State* state, TType value) noexcept {
2479
        return SubPusher::push(state, value);
2480
    }
2481
};
2482

2483
// std::function
2484
template<typename TReturnType, typename... TParameters>
2485
struct LuaContext::Pusher<std::function<TReturnType (TParameters...)>>
2486
{
2487
    // using the function-pushing implementation
2488
    typedef Pusher<TReturnType (TParameters...)>
2489
        SubPusher;
2490
    static const int minSize = SubPusher::minSize;
2491
    static const int maxSize = SubPusher::maxSize;
2492

2493
    static PushedObject push(lua_State* state, const std::function<TReturnType (TParameters...)>& value) noexcept {
2494
        return SubPusher::push(state, value);
2495
    }
2496
};
2497

2498
// boost::variant
2499
template<typename... TTypes>
2500
struct LuaContext::Pusher<boost::variant<TTypes...>>
2501
{
2502
    static const int minSize = PusherMinSize<TTypes...>::size;
2503
    static const int maxSize = PusherMaxSize<TTypes...>::size;
2504

2505
    static PushedObject push(lua_State* state, const boost::variant<TTypes...>& value) noexcept {
158✔
2506
        PushedObject obj{state, 0};
158✔
2507
        VariantWriter writer{state, obj};
158✔
2508
        value.apply_visitor(writer);
158✔
2509
        return obj;
158✔
2510
    }
158✔
2511

2512
private:
2513
    struct VariantWriter : public boost::static_visitor<> {
2514
        template<typename TType>
2515
        void operator()(TType value) noexcept
2516
        {
158✔
2517
            obj = Pusher<typename std::decay<TType>::type>::push(state, std::move(value));
158✔
2518
        }
158✔
2519

2520
        VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {}
158✔
2521
        lua_State* state;
2522
        PushedObject& obj;
2523
    };
2524
};
2525

2526
// boost::optional
2527
template<typename TType>
2528
struct LuaContext::Pusher<boost::optional<TType>> {
2529
    typedef Pusher<typename std::decay<TType>::type>
2530
        UnderlyingPusher;
2531

2532
    static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1;
2533
    static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1;
2534

2535
    static PushedObject push(lua_State* state, const boost::optional<TType>& value) noexcept {
9✔
2536
        if (value) {
9!
2537
            return UnderlyingPusher::push(state, value.get());
5✔
2538
        } else {
5✔
2539
            lua_pushnil(state);
4✔
2540
            return PushedObject{state, 1};
4✔
2541
        }
4✔
2542
    }
9✔
2543
};
2544

2545
// tuple
2546
template<typename... TTypes>
2547
struct LuaContext::Pusher<std::tuple<TTypes...>> {
2548
    // TODO: NOT EXCEPTION SAFE /!\ //
2549
    static const int minSize = PusherTotalMinSize<TTypes...>::size;
2550
    static const int maxSize = PusherTotalMaxSize<TTypes...>::size;
2551

2552
    static PushedObject push(lua_State* state, const std::tuple<TTypes...>& value) noexcept {
2553
        return PushedObject{state, push2(state, value, std::integral_constant<int,0>{})};
2554
    }
2555

2556
    static PushedObject push(lua_State* state, std::tuple<TTypes...>&& value) noexcept {
50,594✔
2557
        return PushedObject{state, push2(state, std::move(value), std::integral_constant<int,0>{})};
50,594✔
2558
    }
50,594✔
2559

2560
private:
2561
    template<int N>
2562
    static int push2(lua_State* state, const std::tuple<TTypes...>& value, std::integral_constant<int,N>) noexcept {
2563
        typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
2564

2565
        return Pusher<typename std::decay<ElemType>::type>::push(state, std::get<N>(value)).release() +
2566
            push2(state, value, std::integral_constant<int,N+1>{});
2567
    }
2568

2569
    template<int N>
2570
    static int push2(lua_State* state, std::tuple<TTypes...>&& value, std::integral_constant<int,N>) noexcept {
8,161✔
2571
        typedef typename std::tuple_element<N,std::tuple<TTypes...>>::type ElemType;
8,161✔
2572

2573
        return Pusher<typename std::decay<ElemType>::type>::push(state, std::move(std::get<N>(value))).release() +
8,161✔
2574
            push2(state, std::move(value), std::integral_constant<int,N+1>{});
8,161✔
2575
    }
8,161✔
2576
    
2577
    static int push2(lua_State* /*state*/, const std::tuple<TTypes...>&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
2578
        return 0;
2579
    }
2580
    
2581
    static int push2(lua_State* /*state*/, std::tuple<TTypes...>&&, std::integral_constant<int,sizeof...(TTypes)>) noexcept {
50,594✔
2582
        return 0;
50,594✔
2583
    }
50,594✔
2584
};
2585

2586
/**************************************************/
2587
/*                READ FUNCTIONS                  */
2588
/**************************************************/
2589
// specializations of the Reader structures
2590

2591
// opaque Lua references
2592
template<>
2593
struct LuaContext::Reader<LuaContext::LuaObject>
2594
{
2595
    static auto read(lua_State* state, int index)
2596
        -> boost::optional<LuaContext::LuaObject>
2597
    {
×
2598
        LuaContext::LuaObject obj(state, index);
×
2599
        return obj;
×
2600
    }
×
2601
};
2602

2603
// reading null
2604
template<>
2605
struct LuaContext::Reader<std::nullptr_t>
2606
{
2607
    static auto read(lua_State* state, int index)
2608
        -> boost::optional<std::nullptr_t>
2609
    {
×
2610
        if (!lua_isnil(state, index))
×
2611
            return boost::none;
×
2612
        return nullptr;
×
2613
    }
×
2614
};
2615

2616
// integrals
2617
template<typename TType>
2618
struct LuaContext::Reader<
2619
            TType,
2620
            typename std::enable_if<std::is_integral<TType>::value>::type
2621
        >
2622
{
2623
    static auto read(lua_State* state, int index)
2624
        -> boost::optional<TType>
2625
    {
38,435✔
2626
#       if LUA_VERSION_NUM >= 502
2627

2628
            int success;
2629
            auto value = lua_tointegerx(state, index, &success);
2630
            if (success == 0)
2631
                return boost::none;
2632
            return static_cast<TType>(value);
2633

2634
#       else
2635

2636
            if (!lua_isnumber(state, index))
38,435!
2637
                return boost::none;
2,687✔
2638
            return static_cast<TType>(lua_tointeger(state, index));
35,748✔
2639

2640
#       endif
38,435✔
2641
    }
38,435✔
2642
};
2643

2644
// floating points
2645
template<typename TType>
2646
struct LuaContext::Reader<
2647
            TType,
2648
            typename std::enable_if<std::is_floating_point<TType>::value>::type
2649
        >
2650
{
2651
    static auto read(lua_State* state, int index)
2652
        -> boost::optional<TType>
2653
    {
112✔
2654
#       if LUA_VERSION_NUM >= 502
2655

2656
            int success;
2657
            auto value = lua_tonumberx(state, index, &success);
2658
            if (success == 0)
2659
                return boost::none;
2660
            return static_cast<TType>(value);
2661

2662
#       else
2663

2664
            if (!lua_isnumber(state, index))
112!
2665
                return boost::none;
2666
            return static_cast<TType>(lua_tonumber(state, index));
112✔
2667

2668
#       endif
112✔
2669
    }
112✔
2670
};
2671

2672
// boolean
2673
template<>
2674
struct LuaContext::Reader<bool>
2675
{
2676
    static auto read(lua_State* state, int index)
2677
        -> boost::optional<bool>
2678
    {
19,255✔
2679
        if (!lua_isboolean(state, index))
19,255✔
2680
            return boost::none;
15,075✔
2681
        return lua_toboolean(state, index) != 0;
4,180✔
2682
    }
19,255✔
2683
};
2684

2685
// string
2686
// lua_tostring returns a temporary pointer, but that's not a problem since we copy
2687
//   the data into a std::string
2688
template<>
2689
struct LuaContext::Reader<std::string>
2690
{
2691
    static auto read(lua_State* state, int index)
2692
        -> boost::optional<std::string>
2693
    {
181,026✔
2694
        std::string result;
181,026✔
2695

2696
        // lua_tolstring might convert the variable that would confuse lua_next, so we
2697
        //   make a copy of the variable.
2698
        lua_pushvalue(state, index);
181,026✔
2699

2700
        size_t len;
181,026✔
2701
        const auto val = lua_tolstring(state, -1, &len);
181,026✔
2702

2703
        if (val != nullptr)
181,026✔
2704
          result.assign(val, len);
153,172✔
2705

2706
        lua_pop(state, 1);
181,026✔
2707

2708
        return val != nullptr ? boost::optional<std::string>{ std::move(result) } : boost::none;
181,026✔
2709
    }
181,026✔
2710
};
2711

2712
// enums
2713
template<typename TType>
2714
struct LuaContext::Reader<
2715
            TType,
2716
            typename std::enable_if<std::is_enum<TType>::value>::type
2717
        >
2718
{
2719
    static auto read(lua_State* state, int index)
2720
        -> boost::optional<TType>
2721
    {
122✔
2722
        if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0)
122!
2723
            return boost::none;
2724
        return static_cast<TType>(lua_tointeger(state, index));
122✔
2725
    }
122✔
2726
};
2727

2728
// LuaFunctionCaller
2729
template<typename TRetValue, typename... TParameters>
2730
struct LuaContext::Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>
2731
{
2732
    typedef LuaFunctionCaller<TRetValue (TParameters...)>
2733
        ReturnType;
2734

2735
    static auto read(lua_State* state, int index)
2736
        -> boost::optional<ReturnType>
2737
    {
906✔
2738
        if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0)
906!
2739
            return boost::none;
×
2740
        return ReturnType(state, index);
906✔
2741
    }
906✔
2742
};
2743

2744
// function
2745
template<typename TRetValue, typename... TParameters>
2746
struct LuaContext::Reader<std::function<TRetValue (TParameters...)>>
2747
{
2748
    static auto read(lua_State* state, int index)
2749
        -> boost::optional<std::function<TRetValue (TParameters...)>>
2750
    {
906✔
2751
                if (auto val = Reader<LuaContext::LuaFunctionCaller<TRetValue (TParameters...)>>::read(state, index))
906!
2752
                {
906✔
2753
                        std::function<TRetValue (TParameters...)> f{*val};
906✔
2754
                        return boost::optional<std::function<TRetValue (TParameters...)>>{std::move(f)};
906✔
2755
                }
906✔
2756

2757
        return boost::none;
×
2758
    }
906✔
2759
};
2760

2761
// vector of pairs
2762
template<typename TType1, typename TType2>
2763
struct LuaContext::Reader<std::vector<std::pair<TType1,TType2>>>
2764
{
2765
    static auto read(lua_State* state, int index)
2766
        -> boost::optional<std::vector<std::pair<TType1, TType2>>>
2767
    {
5,452✔
2768
        if (!lua_istable(state, index))
5,452!
2769
            return boost::none;
3,292✔
2770

2771
        std::vector<std::pair<TType1, TType2>> result;
2,160✔
2772

2773
        // we traverse the table at the top of the stack
2774
        lua_pushnil(state);     // first key
2,160✔
2775
        while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
5,021!
2776
            // now a key and its value are pushed on the stack
2777
            try {
3,018✔
2778
                auto val1 = Reader<TType1>::read(state, -2);
3,018✔
2779
                auto val2 = Reader<TType2>::read(state, -1);
3,018✔
2780

2781
                if (!val1.is_initialized() || !val2.is_initialized()) {
3,018!
2782
                    lua_pop(state, 2);      // we remove the value and the key
157✔
2783
                    return {};
157✔
2784
                }
157✔
2785

2786
                result.push_back({ val1.get(), val2.get() });
2,861✔
2787
                lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2,861✔
2788

2789
            } catch(...) {
2,861✔
2790
                lua_pop(state, 2);      // we remove the value and the key
×
2791
                return {};
×
2792
            }
×
2793
        }
3,018✔
2794

2795
        return { std::move(result) };
2,003✔
2796
    }
2,160✔
2797
};
2798

2799
// map
2800
template<typename TKey, typename TValue>
2801
struct LuaContext::Reader<std::map<TKey,TValue>>
2802
{
2803
    static auto read(lua_State* state, int index)
2804
        -> boost::optional<std::map<TKey,TValue>>
2805
    {
2806
        if (!lua_istable(state, index))
×
2807
            return boost::none;
2808

2809
        std::map<TKey,TValue> result;
2810

2811
        // we traverse the table at the top of the stack
2812
        lua_pushnil(state);     // first key
2813
        while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
×
2814
            // now a key and its value are pushed on the stack
2815
            try {
2816
                auto key = Reader<TKey>::read(state, -2);
2817
                auto value = Reader<TValue>::read(state, -1);
2818

2819
                if (!key.is_initialized() || !value.is_initialized()) {
×
2820
                    lua_pop(state, 2);      // we remove the value and the key
2821
                    return {};
2822
                }
2823

2824
                result.insert({ key.get(), value.get() });
2825
                lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2826

2827
            } catch(...) {
2828
                lua_pop(state, 2);      // we remove the value and the key
2829
                return {};
2830
            }
2831
        }
2832

2833
        return { std::move(result) };
2834
    }
2835
};
2836

2837
// unordered_map
2838
template<typename TKey, typename TValue, typename THash, typename TKeyEqual>
2839
struct LuaContext::Reader<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2840
{
2841
    static auto read(lua_State* state, int index)
2842
        -> boost::optional<std::unordered_map<TKey,TValue,THash,TKeyEqual>>
2843
    {
1,746✔
2844
        if (!lua_istable(state, index))
1,746!
2845
            return boost::none;
×
2846

2847
        std::unordered_map<TKey,TValue,THash,TKeyEqual> result;
1,746✔
2848

2849
        // we traverse the table at the top of the stack
2850
        lua_pushnil(state);     // first key
1,746✔
2851
        while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) {
4,652!
2852
            // now a key and its value are pushed on the stack
2853
            try {
2,906✔
2854
                auto key = Reader<TKey>::read(state, -2);
2,906✔
2855
                auto value = Reader<TValue>::read(state, -1);
2,906✔
2856

2857
                if (!key.is_initialized() || !value.is_initialized()) {
2,906!
2858
                    lua_pop(state, 2);      // we remove the value and the key
×
2859
                    return {};
×
2860
                }
×
2861

2862
                result.insert({ key.get(), value.get() });
2,906✔
2863
                lua_pop(state, 1);      // we remove the value but keep the key for the next iteration
2,906✔
2864

2865
            } catch(...) {
2,906✔
2866
                lua_pop(state, 2);      // we remove the value and the key
×
2867
                return {};
×
2868
            }
×
2869
        }
2,906✔
2870

2871
        return { std::move(result) };
1,746✔
2872
    }
1,746✔
2873
};
2874

2875
// optional
2876
// IMPORTANT: optional means "either nil or the value of the right type"
2877
//  * if the value is nil, then an optional containing an empty optional is returned
2878
//  * if the value is of the right type, then an optional containing an optional containing the value is returned
2879
//  * if the value is of the wrong type, then an empty optional is returned
2880
template<typename TType>
2881
struct LuaContext::Reader<boost::optional<TType>>
2882
{
2883
    static auto read(lua_State* state, int index)
2884
        -> boost::optional<boost::optional<TType>>
2885
    {
5,299✔
2886
        if (lua_isnil(state, index))
5,299!
2887
            return boost::optional<TType>{boost::none};
1,920✔
2888
        if (auto&& other = Reader<TType>::read(state, index))
3,379!
2889
            return std::move(other);
3,379✔
2890
        return boost::none;
×
2891
    }
3,379✔
2892
};
2893

2894
// variant
2895
template<typename... TTypes>
2896
struct LuaContext::Reader<boost::variant<TTypes...>>
2897
{
2898
        typedef boost::variant<TTypes...>
2899
                ReturnType;
2900

2901
private:
2902
    // class doing operations for a range of types from TIterBegin to TIterEnd
2903
    template<typename TIterBegin, typename TIterEnd, typename = void>
2904
    struct VariantReader
2905
    {
2906
        using SubReader = Reader<typename std::decay<typename boost::mpl::deref<TIterBegin>::type>::type>;
2907

2908
        static auto read(lua_State* state, int index)
2909
            -> boost::optional<ReturnType>
2910
        {
13,231✔
2911
            // note: using SubReader::read triggers a compilation error when used with a reference
2912
            if (const auto val = SubReader::read(state, index))
13,231!
2913
                return boost::variant<TTypes...>{*val};
7,686✔
2914
            return VariantReader<typename boost::mpl::next<TIterBegin>::type, TIterEnd>::read(state, index);
5,545✔
2915
        }
13,231✔
2916
    };
2917

2918
    // specialization of class above being called when list of remaining types is empty
2919
    template<typename TIterBegin, typename TIterEnd>
2920
    struct VariantReader<TIterBegin, TIterEnd, typename std::enable_if<boost::mpl::distance<TIterBegin, TIterEnd>::type::value == 0>::type>
2921
    {
2922
        static auto read(lua_State* /*state*/, int /*index*/)
2923
            -> boost::optional<ReturnType> 
2924
        {
×
2925
            return boost::none;
×
2926
        }
×
2927
    };
2928

2929
    // this is the main type
2930
    typedef VariantReader<typename boost::mpl::begin<typename ReturnType::types>::type, typename boost::mpl::end<typename ReturnType::types>::type>
2931
        MainVariantReader;
2932

2933
public:
2934
    static auto read(lua_State* state, int index)
2935
        -> boost::optional<ReturnType>
2936
    {
7,686✔
2937
        return MainVariantReader::read(state, index);
7,686✔
2938
    }
7,686✔
2939
};
2940

2941
// reading a tuple
2942
// tuple have an additional argument for their functions, that is the maximum size to read
2943
// if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value
2944
template<>
2945
struct LuaContext::Reader<std::tuple<>>
2946
{
2947
    static auto read(lua_State* /*state*/, int /*index*/, int /*maxSize*/ = 0)
2948
        -> boost::optional<std::tuple<>>
2949
    {
19,947✔
2950
        return std::tuple<>{};
19,947✔
2951
    }
19,947✔
2952
};
2953

2954
template<typename TFirst, typename... TOthers>
2955
struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2956
        typename std::enable_if<!LuaContext::IsOptional<TFirst>::value>::type       // TODO: replace by std::is_default_constructible when it works on every compiler
2957
    >
2958
{
2959
    // this is the "TFirst is NOT default constructible" version
2960

2961
        typedef std::tuple<TFirst, TOthers...>
2962
                ReturnType;
2963

2964
    static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2965
        -> boost::optional<ReturnType>
2966
    {
440✔
2967
        if (maxSize <= 0)
440!
2968
            return boost::none;
2969

2970
        auto firstVal = Reader<TFirst>::read(state, index);
440✔
2971
        auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
440✔
2972
        
2973
        if (!firstVal || !othersVal)
440!
2974
            return boost::none;
2975

2976
        return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
440✔
2977
    }
440✔
2978
};
2979

2980
template<typename TFirst, typename... TOthers>
2981
struct LuaContext::Reader<std::tuple<TFirst, TOthers...>,
2982
        typename std::enable_if<LuaContext::IsOptional<TFirst>::value>::type        // TODO: replace by std::is_default_constructible when it works on every compiler
2983
    >
2984
{
2985
    // this is the "TFirst is default-constructible" version
2986
        
2987
        typedef std::tuple<TFirst, TOthers...>
2988
                ReturnType;
2989
    
2990
    static auto read(lua_State* state, int index, int maxSize = std::tuple_size<ReturnType>::value)
2991
        -> boost::optional<ReturnType>
2992
    {
431✔
2993
        auto othersVal = Reader<std::tuple<TOthers...>>::read(state, index + 1, maxSize - 1);
431✔
2994
        if (!othersVal)
431!
2995
            return boost::none;
2996
        
2997
        if (maxSize <= 0)
431!
2998
            return std::tuple_cat(std::tuple<TFirst>(), std::move(*othersVal));
2999
        
3000
        auto firstVal = Reader<TFirst>::read(state, index);
431✔
3001
        if (!firstVal)
431!
3002
            return boost::none;
3003

3004
        return std::tuple_cat(std::tuple<TFirst>(*firstVal), std::move(*othersVal));
431✔
3005
    }
431✔
3006
};
3007

3008
#if defined(__GNUC__) && !defined(__clang__)
3009
#pragma GCC diagnostic pop
3010
#endif
3011

3012
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc