// https://github.com/vinniefalco/LuaBridge
 
// Copyright 2019, Dmitry Tarakanov
 
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
 
// SPDX-License-Identifier: MIT
 
 
 
#pragma once
 
 
 
#include <LuaBridge/detail/ClassInfo.h>
 
#include <LuaBridge/detail/TypeTraits.h>
 
 
 
#include <cassert>
 
#include <stdexcept>
 
 
 
namespace luabridge {
 
 
 
namespace detail {
 
 
 
//==============================================================================
 
/**
 
  Return the identity pointer for our lightuserdata tokens.
 
 
 
  Because of Lua's dynamic typing and our improvised system of imposing C++
 
  class structure, there is the possibility that executing scripts may
 
  knowingly or unknowingly cause invalid data to get passed to the C functions
 
  created by LuaBridge. In particular, our security model addresses the
 
  following:
 
    1. Scripts cannot create a userdata (ignoring the debug lib).
 
    2. Scripts cannot create a lightuserdata (ignoring the debug lib).
 
    3. Scripts cannot set the metatable on a userdata.
 
*/
 
 
 
/**
 
  Interface to a class pointer retrievable from a userdata.
 
*/
 
class Userdata
 
{
 
protected:
 
    void* m_p; // subclasses must set this
 
 
 
    Userdata() : m_p(0) {}
 
 
 
    //--------------------------------------------------------------------------
 
    /**
 
      Get an untyped pointer to the contained class.
 
    */
 
    void* getPointer() { return m_p; }
 
 
 
private:
 
    //--------------------------------------------------------------------------
 
    /**
 
      Validate and retrieve a Userdata on the stack.
 
 
 
      The Userdata must exactly match the corresponding class table or
 
      const table, or else a Lua error is raised. This is used for the
 
      __gc metamethod.
 
    */
 
    static Userdata* getExactClass(lua_State* L, int index, void const* /*classKey*/)
 
    {
 
        return static_cast<Userdata*>(lua_touserdata(L, lua_absindex(L, index)));
 
    }
 
 
 
    //--------------------------------------------------------------------------
 
    /**
 
      Validate and retrieve a Userdata on the stack.
 
 
 
      The Userdata must be derived from or the same as the given base class,
 
      identified by the key. If canBeConst is false, generates an error if
 
      the resulting Userdata represents to a const object. We do the type check
 
      first so that the error message is informative.
 
    */
 
    static Userdata* getClass(lua_State* L,
 
                              int index,
 
                              void const* registryConstKey,
 
                              void const* registryClassKey,
 
                              bool canBeConst)
 
    {
 
        index = lua_absindex(L, index);
 
 
 
        lua_getmetatable(L, index); // Stack: object metatable (ot) | nil
 
        if (!lua_istable(L, -1))
 
        {
 
            lua_rawgetp(
 
                L, LUA_REGISTRYINDEX, registryClassKey); // Stack: registry metatable (rt) | nil
 
            return throwBadArg(L, index);
 
        }
 
 
 
        lua_rawgetp(L, -1, getConstKey()); // Stack: ot | nil, const table (co) | nil
 
        assert(lua_istable(L, -1) || lua_isnil(L, -1));
 
 
 
        // If const table is NOT present, object is const. Use non-const registry table
 
        // if object cannot be const, so constness validation is done automatically.
 
        // E.g. nonConstFn (constObj)
 
        // -> canBeConst = false, isConst = true
 
        // -> 'Class' registry table, 'const Class' object table
 
        // -> 'expected Class, got const Class'
 
        bool isConst = lua_isnil(L, -1); // Stack: ot | nil, nil, rt
 
        if (isConst && canBeConst)
 
        {
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, registryConstKey); // Stack: ot, nil, rt
 
        }
 
        else
 
        {
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, co, rt
 
        }
 
 
 
        lua_insert(L, -3); // Stack: rt, ot, co | nil
 
        lua_pop(L, 1); // Stack: rt, ot
 
 
 
        for (;;)
 
        {
 
            if (lua_rawequal(L, -1, -2)) // Stack: rt, ot
 
            {
 
                lua_pop(L, 2); // Stack: -
 
                return static_cast<Userdata*>(lua_touserdata(L, index));
 
            }
 
 
 
            // Replace current metatable with it's base class.
 
            lua_rawgetp(L, -1, getParentKey()); // Stack: rt, ot, parent ot (pot) | nil
 
 
 
            if (lua_isnil(L, -1)) // Stack: rt, ot, nil
 
            {
 
                // Drop the object metatable because it may be some parent metatable
 
                lua_pop(L, 2); // Stack: rt
 
                return throwBadArg(L, index);
 
            }
 
 
 
            lua_remove(L, -2); // Stack: rt, pot
 
        }
 
 
 
        // no return
 
    }
 
 
 
    static bool isInstance(lua_State* L, int index, void const* registryClassKey)
 
    {
 
        index = lua_absindex(L, index);
 
 
 
        int result = lua_getmetatable(L, index); // Stack: object metatable (ot) | nothing
 
        if (result == 0)
 
        {
 
            return false; // Nothing was pushed on the stack
 
        }
 
        if (!lua_istable(L, -1))
 
        {
 
            lua_pop(L, 1); // Stack: -
 
            return false;
 
        }
 
 
 
        lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, rt
 
        lua_insert(L, -2); // Stack: rt, ot
 
 
 
        for (;;)
 
        {
 
            if (lua_rawequal(L, -1, -2)) // Stack: rt, ot
 
            {
 
                lua_pop(L, 2); // Stack: -
 
                return true;
 
            }
 
 
 
            // Replace current metatable with it's base class.
 
            lua_rawgetp(L, -1, getParentKey()); // Stack: rt, ot, parent ot (pot) | nil
 
 
 
            if (lua_isnil(L, -1)) // Stack: rt, ot, nil
 
            {
 
                lua_pop(L, 3); // Stack: -
 
                return false;
 
            }
 
 
 
            lua_remove(L, -2); // Stack: rt, pot
 
        }
 
    }
 
 
 
    static Userdata* throwBadArg(lua_State* L, int index)
 
    {
 
        assert(lua_istable(L, -1) || lua_isnil(L, -1)); // Stack: rt | nil
 
 
 
        const char* expected = 0;
 
        if (lua_isnil(L, -1)) // Stack: nil
 
        {
 
            expected = "unregistered class";
 
        }
 
        else
 
        {
 
            lua_rawgetp(L, -1, getTypeKey()); // Stack: rt, registry type
 
            expected = lua_tostring(L, -1);
 
        }
 
 
 
        const char* got = 0;
 
        if (lua_isuserdata(L, index))
 
        {
 
            lua_getmetatable(L, index); // Stack: ..., ot | nil
 
            if (lua_istable(L, -1)) // Stack: ..., ot
 
            {
 
                lua_rawgetp(L, -1, getTypeKey()); // Stack: ..., ot, object type | nil
 
                if (lua_isstring(L, -1))
 
                {
 
                    got = lua_tostring(L, -1);
 
                }
 
            }
 
        }
 
        if (!got)
 
        {
 
            got = lua_typename(L, lua_type(L, index));
 
        }
 
 
 
        luaL_argerror(L, index, lua_pushfstring(L, "%s expected, got %s", expected, got));
 
        return 0;
 
    }
 
 
 
public:
 
    virtual ~Userdata() {}
 
 
 
    //--------------------------------------------------------------------------
 
    /**
 
      Returns the Userdata* if the class on the Lua stack matches.
 
      If the class does not match, a Lua error is raised.
 
 
 
      @tparam T     A registered user class.
 
      @param  L     A Lua state.
 
      @param  index The index of an item on the Lua stack.
 
      @returns A userdata pointer if the class matches.
 
    */
 
    template<class T>
 
    static Userdata* getExact(lua_State* L, int index)
 
    {
 
        return getExactClass(L, index, detail::getClassRegistryKey<T>());
 
    }
 
 
 
    //--------------------------------------------------------------------------
 
    /**
 
      Get a pointer to the class from the Lua stack.
 
      If the object is not the class or a subclass, or it violates the
 
      const-ness, a Lua error is raised.
 
 
 
      @tparam T          A registered user class.
 
      @param  L          A Lua state.
 
      @param  index      The index of an item on the Lua stack.
 
      @param  canBeConst TBD
 
      @returns A pointer if the class and constness match.
 
    */
 
    template<class T>
 
    static T* get(lua_State* L, int index, bool canBeConst)
 
    {
 
        if (lua_isnil(L, index))
 
            return 0;
 
 
 
        return static_cast<T*>(getClass(L,
 
                                        index,
 
                                        detail::getConstRegistryKey<T>(),
 
                                        detail::getClassRegistryKey<T>(),
 
                                        canBeConst)
 
                                   ->getPointer());
 
    }
 
 
 
    template<class T>
 
    static bool isInstance(lua_State* L, int index)
 
    {
 
        return isInstance(L, index, detail::getClassRegistryKey<T>());
 
    }
 
};
 
 
 
//----------------------------------------------------------------------------
 
/**
 
  Wraps a class object stored in a Lua userdata.
 
 
 
  The lifetime of the object is managed by Lua. The object is constructed
 
  inside the userdata using placement new.
 
*/
 
template<class T>
 
class UserdataValue : public Userdata
 
{
 
private:
 
    UserdataValue(UserdataValue<T> const&);
 
    UserdataValue<T> operator=(UserdataValue<T> const&);
 
 
 
    char m_storage[sizeof(T)];
 
 
 
private:
 
    /**
 
      Used for placement construction.
 
    */
 
    UserdataValue() { m_p = 0; }
 
 
 
    ~UserdataValue()
 
    {
 
        if (getPointer() != 0)
 
        {
 
            getObject()->~T();
 
        }
 
    }
 
 
 
public:
 
    /**
 
      Push a T via placement new.
 
 
 
      The caller is responsible for calling placement new using the
 
      returned uninitialized storage.
 
 
 
      @param L A Lua state.
 
      @returns An object referring to the newly created userdata value.
 
    */
 
    static UserdataValue<T>* place(lua_State* const L)
 
    {
 
        UserdataValue<T>* const ud =
 
            new (lua_newuserdata(L, sizeof(UserdataValue<T>))) UserdataValue<T>();
 
        lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
 
        if (!lua_istable(L, -1))
 
        {
 
            throw std::logic_error("The class is not registered in LuaBridge");
 
        }
 
        lua_setmetatable(L, -2);
 
        return ud;
 
    }
 
 
 
    /**
 
      Push T via copy construction from U.
 
 
 
      @tparam U A container type.
 
      @param  L A Lua state.
 
      @param  u A container object reference.
 
    */
 
    template<class U>
 
    static inline void push(lua_State* const L, U const& u)
 
    {
 
        UserdataValue<T>* ud = place(L);
 
        new (ud->getObject()) U(u);
 
        ud->commit();
 
    }
 
 
 
    /**
 
      Confirm object construction.
 
    */
 
    void commit() { m_p = getObject(); }
 
 
 
    T* getObject()
 
    {
 
        // If this fails to compile it means you forgot to provide
 
        // a Container specialization for your container!
 
        //
 
        return reinterpret_cast<T*>(&m_storage[0]);
 
    }
 
};
 
 
 
//----------------------------------------------------------------------------
 
/**
 
  Wraps a pointer to a class object inside a Lua userdata.
 
 
 
  The lifetime of the object is managed by C++.
 
*/
 
class UserdataPtr : public Userdata
 
{
 
private:
 
    UserdataPtr(UserdataPtr const&);
 
    UserdataPtr operator=(UserdataPtr const&);
 
 
 
private:
 
    /** Push a pointer to object using metatable key.
 
     */
 
    static void push(lua_State* L, const void* p, void const* const key)
 
    {
 
        new (lua_newuserdata(L, sizeof(UserdataPtr))) UserdataPtr(const_cast<void*>(p));
 
        lua_rawgetp(L, LUA_REGISTRYINDEX, key);
 
        if (!lua_istable(L, -1))
 
        {
 
            lua_pop(L, 1); // possibly: a nil
 
            throw std::logic_error("The class is not registered in LuaBridge");
 
        }
 
        lua_setmetatable(L, -2);
 
    }
 
 
 
    explicit UserdataPtr(void* const p)
 
    {
 
        m_p = p;
 
 
 
        // Can't construct with a null pointer!
 
        //
 
        assert(m_p != 0);
 
    }
 
 
 
public:
 
    /** Push non-const pointer to object.
 
 
 
      @tparam T A user registered class.
 
      @param  L A Lua state.
 
      @param  p A pointer to the user class instance.
 
    */
 
    template<class T>
 
    static void push(lua_State* const L, T* const p)
 
    {
 
        if (p)
 
            push(L, p, getClassRegistryKey<T>());
 
        else
 
            lua_pushnil(L);
 
    }
 
 
 
    /** Push const pointer to object.
 
 
 
      @tparam T A user registered class.
 
      @param  L A Lua state.
 
      @param  p A pointer to the user class instance.
 
    */
 
    template<class T>
 
    static void push(lua_State* const L, T const* const p)
 
    {
 
        if (p)
 
            push(L, p, getConstRegistryKey<T>());
 
        else
 
            lua_pushnil(L);
 
    }
 
};
 
 
 
//============================================================================
 
/**
 
  Wraps a container that references a class object.
 
 
 
  The template argument C is the container type, ContainerTraits must be
 
  specialized on C or else a compile error will result.
 
*/
 
template<class C>
 
class UserdataShared : public Userdata
 
{
 
private:
 
    UserdataShared(UserdataShared<C> const&);
 
    UserdataShared<C>& operator=(UserdataShared<C> const&);
 
 
 
    typedef typename TypeTraits::removeConst<typename ContainerTraits<C>::Type>::Type T;
 
 
 
    C m_c;
 
 
 
private:
 
    ~UserdataShared() {}
 
 
 
public:
 
    /**
 
      Construct from a container to the class or a derived class.
 
 
 
      @tparam U A container type.
 
      @param  u A container object reference.
 
    */
 
    template<class U>
 
    explicit UserdataShared(U const& u) : m_c(u)
 
    {
 
        m_p = const_cast<void*>(reinterpret_cast<void const*>((ContainerTraits<C>::get(m_c))));
 
    }
 
 
 
    /**
 
      Construct from a pointer to the class or a derived class.
 
 
 
      @tparam U A container type.
 
      @param  u A container object pointer.
 
    */
 
    template<class U>
 
    explicit UserdataShared(U* u) : m_c(u)
 
    {
 
        m_p = const_cast<void*>(reinterpret_cast<void const*>((ContainerTraits<C>::get(m_c))));
 
    }
 
};
 
 
 
//----------------------------------------------------------------------------
 
//
 
// SFINAE helpers.
 
//
 
 
 
// non-const objects
 
template<class C, bool makeObjectConst>
 
struct UserdataSharedHelper
 
{
 
    typedef typename TypeTraits::removeConst<typename ContainerTraits<C>::Type>::Type T;
 
 
 
    static void push(lua_State* L, C const& c)
 
    {
 
        if (ContainerTraits<C>::get(c) != 0)
 
        {
 
            new (lua_newuserdata(L, sizeof(UserdataShared<C>))) UserdataShared<C>(c);
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey<T>());
 
            // If this goes off it means the class T is unregistered!
 
            assert(lua_istable(L, -1));
 
            lua_setmetatable(L, -2);
 
        }
 
        else
 
        {
 
            lua_pushnil(L);
 
        }
 
    }
 
 
 
    static void push(lua_State* L, T* const t)
 
    {
 
        if (t)
 
        {
 
            new (lua_newuserdata(L, sizeof(UserdataShared<C>))) UserdataShared<C>(t);
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey<T>());
 
            // If this goes off it means the class T is unregistered!
 
            assert(lua_istable(L, -1));
 
            lua_setmetatable(L, -2);
 
        }
 
        else
 
        {
 
            lua_pushnil(L);
 
        }
 
    }
 
};
 
 
 
// const objects
 
template<class C>
 
struct UserdataSharedHelper<C, true>
 
{
 
    typedef typename TypeTraits::removeConst<typename ContainerTraits<C>::Type>::Type T;
 
 
 
    static void push(lua_State* L, C const& c)
 
    {
 
        if (ContainerTraits<C>::get(c) != 0)
 
        {
 
            new (lua_newuserdata(L, sizeof(UserdataShared<C>))) UserdataShared<C>(c);
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey<T>());
 
            // If this goes off it means the class T is unregistered!
 
            assert(lua_istable(L, -1));
 
            lua_setmetatable(L, -2);
 
        }
 
        else
 
        {
 
            lua_pushnil(L);
 
        }
 
    }
 
 
 
    static void push(lua_State* L, T* const t)
 
    {
 
        if (t)
 
        {
 
            new (lua_newuserdata(L, sizeof(UserdataShared<C>))) UserdataShared<C>(t);
 
            lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey<T>());
 
            // If this goes off it means the class T is unregistered!
 
            assert(lua_istable(L, -1));
 
            lua_setmetatable(L, -2);
 
        }
 
        else
 
        {
 
            lua_pushnil(L);
 
        }
 
    }
 
};
 
 
 
/**
 
  Pass by container.
 
 
 
  The container controls the object lifetime. Typically this will be a
 
  lifetime shared by C++ and Lua using a reference count. Because of type
 
  erasure, containers like std::shared_ptr will not work. Containers must
 
  either be of the intrusive variety, or in the style of the RefCountedPtr
 
  type provided by LuaBridge (that uses a global hash table).
 
*/
 
template<class C, bool byContainer>
 
struct StackHelper
 
{
 
    static void push(lua_State* L, C const& c)
 
    {
 
        UserdataSharedHelper<C, TypeTraits::isConst<typename ContainerTraits<C>::Type>::value>::
 
            push(L, c);
 
    }
 
 
 
    typedef typename TypeTraits::removeConst<typename ContainerTraits<C>::Type>::Type T;
 
 
 
    static C get(lua_State* L, int index) { return Userdata::get<T>(L, index, true); }
 
};
 
 
 
/**
 
  Pass by value.
 
 
 
  Lifetime is managed by Lua. A C++ function which accesses a pointer or
 
  reference to an object outside the activation record in which it was
 
  retrieved may result in undefined behavior if Lua garbage collected it.
 
*/
 
template<class T>
 
struct StackHelper<T, false>
 
{
 
    static inline void push(lua_State* L, T const& t) { UserdataValue<T>::push(L, t); }
 
 
 
    static inline T const& get(lua_State* L, int index)
 
    {
 
        return *Userdata::get<T>(L, index, true);
 
    }
 
};
 
 
 
//------------------------------------------------------------------------------
 
/**
 
  Lua stack conversions for pointers and references to class objects.
 
 
 
  Lifetime is managed by C++. Lua code which remembers a reference to the
 
  value may result in undefined behavior if C++ destroys the object. The
 
  handling of the const and volatile qualifiers happens in UserdataPtr.
 
*/
 
 
 
template<class C, bool byContainer>
 
struct RefStackHelper
 
{
 
    typedef C return_type;
 
 
 
    static inline void push(lua_State* L, C const& t)
 
    {
 
        UserdataSharedHelper<C, TypeTraits::isConst<typename ContainerTraits<C>::Type>::value>::
 
            push(L, t);
 
    }
 
 
 
    typedef typename TypeTraits::removeConst<typename ContainerTraits<C>::Type>::Type T;
 
 
 
    static return_type get(lua_State* L, int index) { return Userdata::get<T>(L, index, true); }
 
};
 
 
 
template<class T>
 
struct RefStackHelper<T, false>
 
{
 
    typedef T& return_type;
 
 
 
    static void push(lua_State* L, T const& t) { UserdataPtr::push(L, &t); }
 
 
 
    static return_type get(lua_State* L, int index)
 
    {
 
        T* t = Userdata::get<T>(L, index, true);
 
 
 
        if (!t)
 
            luaL_error(L, "nil passed to reference");
 
        return *t;
 
    }
 
};
 
 
 
/**
 
 * Voider class template. Used to force a comiler to instantiate
 
 * an otherwise probably unused template parameter type T.
 
 * See the C++20 std::void_t <> for details.
 
 */
 
template<class T>
 
struct Void
 
{
 
    typedef void Type;
 
};
 
 
 
/**
 
 * Trait class that selects whether to return a user registered
 
 * class object by value or by reference.
 
 */
 
 
 
template<class T, class Enabler = void>
 
struct UserdataGetter
 
{
 
    typedef T* ReturnType;
 
 
 
    static ReturnType get(lua_State* L, int index) { return Userdata::get<T>(L, index, false); }
 
};
 
 
 
template<class T>
 
struct UserdataGetter<T, typename Void<T (*)()>::Type>
 
{
 
    typedef T ReturnType;
 
 
 
    static ReturnType get(lua_State* L, int index)
 
    {
 
        return StackHelper<T, TypeTraits::isContainer<T>::value>::get(L, index);
 
    }
 
};
 
 
 
} // namespace detail
 
 
 
//==============================================================================
 
 
 
/**
 
  Lua stack conversions for class objects passed by value.
 
*/
 
template<class T>
 
struct Stack
 
{
 
    typedef void IsUserdata;
 
 
 
    typedef detail::UserdataGetter<T> Getter;
 
    typedef typename Getter::ReturnType ReturnType;
 
 
 
    static void push(lua_State* L, T const& value)
 
    {
 
        using namespace detail;
 
        StackHelper<T, TypeTraits::isContainer<T>::value>::push(L, value);
 
    }
 
 
 
    static ReturnType get(lua_State* L, int index) { return Getter::get(L, index); }
 
 
 
    static bool isInstance(lua_State* L, int index)
 
    {
 
        return detail::Userdata::isInstance<T>(L, index);
 
    }
 
};
 
 
 
namespace detail {
 
 
 
/**
 
 * Trait class indicating whether the parameter type must be
 
 * a user registered class. The trait checks the existence of
 
 * member type Stack::IsUserdata specialization for detection.
 
 */
 
template<class T, class Enable = void>
 
struct IsUserdata
 
{
 
    static const bool value = false;
 
};
 
 
 
template<class T>
 
struct IsUserdata<T, typename Void<typename Stack<T>::IsUserdata>::Type>
 
{
 
    static const bool value = true;
 
};
 
 
 
/**
 
 * Trait class that selects a specific push/get implemenation.
 
 */
 
template<class T, bool isUserdata>
 
struct StackOpSelector;
 
 
 
// pointer
 
template<class T>
 
struct StackOpSelector<T*, true>
 
{
 
    typedef T* ReturnType;
 
 
 
    static void push(lua_State* L, T* value) { UserdataPtr::push(L, value); }
 
 
 
    static T* get(lua_State* L, int index) { return Userdata::get<T>(L, index, false); }
 
 
 
    static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
 
};
 
 
 
// pointer to const
 
template<class T>
 
struct StackOpSelector<const T*, true>
 
{
 
    typedef const T* ReturnType;
 
 
 
    static void push(lua_State* L, const T* value) { UserdataPtr::push(L, value); }
 
 
 
    static const T* get(lua_State* L, int index) { return Userdata::get<T>(L, index, true); }
 
 
 
    static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
 
};
 
 
 
// reference
 
template<class T>
 
struct StackOpSelector<T&, true>
 
{
 
    typedef RefStackHelper<T, TypeTraits::isContainer<T>::value> Helper;
 
    typedef typename Helper::return_type ReturnType;
 
 
 
    static void push(lua_State* L, T& value) { UserdataPtr::push(L, &value); }
 
 
 
    static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
 
 
 
    static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
 
};
 
 
 
// reference to const
 
template<class T>
 
struct StackOpSelector<const T&, true>
 
{
 
    typedef RefStackHelper<T, TypeTraits::isContainer<T>::value> Helper;
 
    typedef typename Helper::return_type ReturnType;
 
 
 
    static void push(lua_State* L, const T& value) { Helper::push(L, value); }
 
 
 
    static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
 
 
 
    static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
 
};
 
 
 
} // namespace detail
 
 
 
} // namespace luabridge