471 lines
15 KiB
C++
471 lines
15 KiB
C++
// 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/Config.h>
|
|
#include <LuaBridge/detail/FuncTraits.h>
|
|
|
|
#include <string>
|
|
|
|
namespace luabridge {
|
|
|
|
namespace detail {
|
|
|
|
// We use a structure so we can define everything in the header.
|
|
//
|
|
struct CFunc
|
|
{
|
|
static void addGetter(lua_State* L, const char* name, int tableIndex)
|
|
{
|
|
assert(lua_istable(L, tableIndex));
|
|
assert(lua_iscfunction(L, -1)); // Stack: getter
|
|
|
|
lua_rawgetp(L, tableIndex, getPropgetKey()); // Stack: getter, propget table (pg)
|
|
lua_pushvalue(L, -2); // Stack: getter, pg, getter
|
|
rawsetfield(L, -2, name); // Stack: getter, pg
|
|
lua_pop(L, 2); // Stack: -
|
|
}
|
|
|
|
static void addSetter(lua_State* L, const char* name, int tableIndex)
|
|
{
|
|
assert(lua_istable(L, tableIndex));
|
|
assert(lua_iscfunction(L, -1)); // Stack: setter
|
|
|
|
lua_rawgetp(L, tableIndex, getPropsetKey()); // Stack: setter, propset table (ps)
|
|
lua_pushvalue(L, -2); // Stack: setter, ps, setter
|
|
rawsetfield(L, -2, name); // Stack: setter, ps
|
|
lua_pop(L, 2); // Stack: -
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
__index metamethod for a namespace or class static and non-static members.
|
|
Retrieves functions from metatables and properties from propget tables.
|
|
Looks through the class hierarchy if inheritance is present.
|
|
*/
|
|
static int indexMetaMethod(lua_State* L)
|
|
{
|
|
assert(lua_istable(L, 1) ||
|
|
lua_isuserdata(L, 1)); // Stack (further not shown): table | userdata, name
|
|
|
|
lua_getmetatable(L, 1); // Stack: class/const table (mt)
|
|
assert(lua_istable(L, -1));
|
|
|
|
for (;;)
|
|
{
|
|
lua_pushvalue(L, 2); // Stack: mt, field name
|
|
lua_rawget(L, -2); // Stack: mt, field | nil
|
|
|
|
if (lua_iscfunction(L, -1)) // Stack: mt, field
|
|
{
|
|
lua_remove(L, -2); // Stack: field
|
|
return 1;
|
|
}
|
|
|
|
assert(lua_isnil(L, -1)); // Stack: mt, nil
|
|
lua_pop(L, 1); // Stack: mt
|
|
|
|
lua_rawgetp(L, -1, getPropgetKey()); // Stack: mt, propget table (pg)
|
|
assert(lua_istable(L, -1));
|
|
|
|
lua_pushvalue(L, 2); // Stack: mt, pg, field name
|
|
lua_rawget(L, -2); // Stack: mt, pg, getter | nil
|
|
lua_remove(L, -2); // Stack: mt, getter | nil
|
|
|
|
if (lua_iscfunction(L, -1)) // Stack: mt, getter
|
|
{
|
|
lua_remove(L, -2); // Stack: getter
|
|
lua_pushvalue(L, 1); // Stack: getter, table | userdata
|
|
lua_call(L, 1, 1); // Stack: value
|
|
return 1;
|
|
}
|
|
|
|
assert(lua_isnil(L, -1)); // Stack: mt, nil
|
|
lua_pop(L, 1); // Stack: mt
|
|
|
|
// It may mean that the field may be in const table and it's constness violation.
|
|
// Don't check that, just return nil
|
|
|
|
// Repeat the lookup in the parent metafield,
|
|
// or return nil if the field doesn't exist.
|
|
lua_rawgetp(L, -1, getParentKey()); // Stack: mt, parent mt | nil
|
|
|
|
if (lua_isnil(L, -1)) // Stack: mt, nil
|
|
{
|
|
lua_remove(L, -2); // Stack: nil
|
|
return 1;
|
|
}
|
|
|
|
// Removethe metatable and repeat the search in the parent one.
|
|
assert(lua_istable(L, -1)); // Stack: mt, parent mt
|
|
lua_remove(L, -2); // Stack: parent mt
|
|
}
|
|
|
|
// no return
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
__newindex metamethod for namespace or class static members.
|
|
Retrieves properties from propset tables.
|
|
*/
|
|
static int newindexStaticMetaMethod(lua_State* L) { return newindexMetaMethod(L, false); }
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
__newindex metamethod for non-static members.
|
|
Retrieves properties from propset tables.
|
|
*/
|
|
static int newindexObjectMetaMethod(lua_State* L) { return newindexMetaMethod(L, true); }
|
|
|
|
static int newindexMetaMethod(lua_State* L, bool pushSelf)
|
|
{
|
|
assert(
|
|
lua_istable(L, 1) ||
|
|
lua_isuserdata(L, 1)); // Stack (further not shown): table | userdata, name, new value
|
|
|
|
lua_getmetatable(L, 1); // Stack: metatable (mt)
|
|
assert(lua_istable(L, -1));
|
|
|
|
for (;;)
|
|
{
|
|
lua_rawgetp(L, -1, getPropsetKey()); // Stack: mt, propset table (ps) | nil
|
|
|
|
if (lua_isnil(L, -1)) // Stack: mt, nil
|
|
{
|
|
lua_pop(L, 2); // Stack: -
|
|
return luaL_error(L, "No member named '%s'", lua_tostring(L, 2));
|
|
}
|
|
|
|
assert(lua_istable(L, -1));
|
|
|
|
lua_pushvalue(L, 2); // Stack: mt, ps, field name
|
|
lua_rawget(L, -2); // Stack: mt, ps, setter | nil
|
|
lua_remove(L, -2); // Stack: mt, setter | nil
|
|
|
|
if (lua_iscfunction(L, -1)) // Stack: mt, setter
|
|
{
|
|
lua_remove(L, -2); // Stack: setter
|
|
if (pushSelf)
|
|
{
|
|
lua_pushvalue(L, 1); // Stack: setter, table | userdata
|
|
}
|
|
lua_pushvalue(L, 3); // Stack: setter, table | userdata, new value
|
|
lua_call(L, pushSelf ? 2 : 1, 0); // Stack: -
|
|
return 0;
|
|
}
|
|
|
|
assert(lua_isnil(L, -1)); // Stack: mt, nil
|
|
lua_pop(L, 1); // Stack: mt
|
|
|
|
lua_rawgetp(L, -1, getParentKey()); // Stack: mt, parent mt | nil
|
|
|
|
if (lua_isnil(L, -1)) // Stack: mt, nil
|
|
{
|
|
lua_pop(L, 1); // Stack: -
|
|
return luaL_error(L, "No writable member '%s'", lua_tostring(L, 2));
|
|
}
|
|
|
|
assert(lua_istable(L, -1)); // Stack: mt, parent mt
|
|
lua_remove(L, -2); // Stack: parent mt
|
|
// Repeat the search in the parent
|
|
}
|
|
|
|
// no return
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to report an error writing to a read-only value.
|
|
|
|
The name of the variable is in the first upvalue.
|
|
*/
|
|
static int readOnlyError(lua_State* L)
|
|
{
|
|
std::string s;
|
|
|
|
s = s + "'" + lua_tostring(L, lua_upvalueindex(1)) + "' is read-only";
|
|
|
|
return luaL_error(L, s.c_str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to get a variable.
|
|
|
|
This is used for global variables or class static data members.
|
|
|
|
The pointer to the data is in the first upvalue.
|
|
*/
|
|
template<class T>
|
|
static int getVariable(lua_State* L)
|
|
{
|
|
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
|
|
T const* ptr = static_cast<T const*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(ptr != 0);
|
|
Stack<T>::push(L, *ptr);
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to set a variable.
|
|
|
|
This is used for global variables or class static data members.
|
|
|
|
The pointer to the data is in the first upvalue.
|
|
*/
|
|
template<class T>
|
|
static int setVariable(lua_State* L)
|
|
{
|
|
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
|
|
T* ptr = static_cast<T*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(ptr != 0);
|
|
*ptr = Stack<T>::get(L, 1);
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to call a function with a return value.
|
|
|
|
This is used for global functions, global properties, class static methods,
|
|
and class static properties.
|
|
|
|
The function pointer (lightuserdata) in the first upvalue.
|
|
*/
|
|
template<class FnPtr>
|
|
struct Call
|
|
{
|
|
typedef typename FuncTraits<FnPtr>::Params Params;
|
|
typedef typename FuncTraits<FnPtr>::ReturnType ReturnType;
|
|
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
|
|
FnPtr fnptr = reinterpret_cast<FnPtr>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return Invoke<ReturnType, Params, 1>::run(L, fnptr);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to call a class member function with a return value.
|
|
|
|
The member function pointer is in the first upvalue.
|
|
The class userdata object is at the top of the Lua stack.
|
|
*/
|
|
template<class MemFnPtr>
|
|
struct CallMember
|
|
{
|
|
typedef typename FuncTraits<MemFnPtr>::ClassType T;
|
|
typedef typename FuncTraits<MemFnPtr>::Params Params;
|
|
typedef typename FuncTraits<MemFnPtr>::ReturnType ReturnType;
|
|
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, lua_upvalueindex(1)));
|
|
T* const t = Userdata::get<T>(L, 1, false);
|
|
MemFnPtr const& fnptr =
|
|
*static_cast<MemFnPtr const*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return Invoke<ReturnType, Params, 2>::run(L, t, fnptr);
|
|
}
|
|
};
|
|
|
|
template<class MemFnPtr>
|
|
struct CallConstMember
|
|
{
|
|
typedef typename FuncTraits<MemFnPtr>::ClassType T;
|
|
typedef typename FuncTraits<MemFnPtr>::Params Params;
|
|
typedef typename FuncTraits<MemFnPtr>::ReturnType ReturnType;
|
|
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, lua_upvalueindex(1)));
|
|
T const* const t = Userdata::get<T>(L, 1, true);
|
|
MemFnPtr const& fnptr =
|
|
*static_cast<MemFnPtr const*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return Invoke<ReturnType, Params, 2>::run(L, t, fnptr);
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to call a class member lua_CFunction.
|
|
|
|
The member function pointer is in the first upvalue.
|
|
The object userdata ('this') value is at top ot the Lua stack.
|
|
*/
|
|
template<class T>
|
|
struct CallMemberCFunction
|
|
{
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, lua_upvalueindex(1)));
|
|
typedef int (T::*MFP)(lua_State * L);
|
|
T* const t = Userdata::get<T>(L, 1, false);
|
|
MFP const& fnptr = *static_cast<MFP const*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return (t->*fnptr)(L);
|
|
}
|
|
};
|
|
|
|
template<class T>
|
|
struct CallConstMemberCFunction
|
|
{
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, lua_upvalueindex(1)));
|
|
typedef int (T::*MFP)(lua_State * L);
|
|
T const* const t = Userdata::get<T>(L, 1, true);
|
|
MFP const& fnptr = *static_cast<MFP const*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return (t->*fnptr)(L);
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to call on a object.
|
|
|
|
The proxy function pointer (lightuserdata) is in the first upvalue.
|
|
The class userdata object is at the top of the Lua stack.
|
|
*/
|
|
template<class FnPtr>
|
|
struct CallProxyFunction
|
|
{
|
|
using Params = typename FuncTraits<FnPtr>::Params;
|
|
using ReturnType = typename FuncTraits<FnPtr>::ReturnType;
|
|
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
|
|
auto fnptr = reinterpret_cast<FnPtr>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
assert(fnptr != 0);
|
|
return Invoke<ReturnType, Params, 1>::run(L, fnptr);
|
|
}
|
|
};
|
|
|
|
template<class Functor>
|
|
struct CallProxyFunctor
|
|
{
|
|
using Params = typename FuncTraits<Functor>::Params;
|
|
using ReturnType = typename FuncTraits<Functor>::ReturnType;
|
|
|
|
static int f(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, lua_upvalueindex(1)));
|
|
Functor& fn = *static_cast<Functor*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
return Invoke<ReturnType, Params, 1>::run(L, fn);
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
// SFINAE Helpers
|
|
|
|
template<class MemFnPtr, bool isConst>
|
|
struct CallMemberFunctionHelper
|
|
{
|
|
static void add(lua_State* L, char const* name, MemFnPtr mf)
|
|
{
|
|
new (lua_newuserdata(L, sizeof(MemFnPtr))) MemFnPtr(mf);
|
|
lua_pushcclosure(L, &CallConstMember<MemFnPtr>::f, 1);
|
|
lua_pushvalue(L, -1);
|
|
rawsetfield(L, -5, name); // const table
|
|
rawsetfield(L, -3, name); // class table
|
|
}
|
|
};
|
|
|
|
template<class MemFnPtr>
|
|
struct CallMemberFunctionHelper<MemFnPtr, false>
|
|
{
|
|
static void add(lua_State* L, char const* name, MemFnPtr mf)
|
|
{
|
|
new (lua_newuserdata(L, sizeof(MemFnPtr))) MemFnPtr(mf);
|
|
lua_pushcclosure(L, &CallMember<MemFnPtr>::f, 1);
|
|
rawsetfield(L, -3, name); // class table
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
/**
|
|
__gc metamethod for a class.
|
|
*/
|
|
template<class C>
|
|
static int gcMetaMethod(lua_State* L)
|
|
{
|
|
Userdata* const ud = Userdata::getExact<C>(L, 1);
|
|
ud->~Userdata();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
__gc metamethod for an arbitrary class.
|
|
*/
|
|
template<class T>
|
|
static int gcMetaMethodAny(lua_State* L)
|
|
{
|
|
assert(isfulluserdata(L, 1));
|
|
T* t = static_cast<T*>(lua_touserdata(L, 1));
|
|
t->~T();
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to get a class data member.
|
|
|
|
The pointer-to-member is in the first upvalue.
|
|
The class userdata object is at the top of the Lua stack.
|
|
*/
|
|
template<class C, typename T>
|
|
static int getProperty(lua_State* L)
|
|
{
|
|
C* const c = Userdata::get<C>(L, 1, true);
|
|
T C::** mp = static_cast<T C::**>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
try
|
|
{
|
|
Stack<T&>::push(L, c->**mp);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
luaL_error(L, e.what());
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/**
|
|
lua_CFunction to set a class data member.
|
|
|
|
The pointer-to-member is in the first upvalue.
|
|
The class userdata object is at the top of the Lua stack.
|
|
*/
|
|
template<class C, typename T>
|
|
static int setProperty(lua_State* L)
|
|
{
|
|
C* const c = Userdata::get<C>(L, 1, false);
|
|
T C::** mp = static_cast<T C::**>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
try
|
|
{
|
|
c->** mp = Stack<T>::get(L, 2);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
luaL_error(L, e.what());
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
} // namespace luabridge
|