mirror of
https://gitlab.com/niansa/SomeBot.git
synced 2025-03-06 20:48:26 +01:00
1956 lines
74 KiB
C++
1956 lines
74 KiB
C++
// https://github.com/kunitoki/LuaBridge3
|
|
// Copyright 2020, Lucio Asnaghi
|
|
// Copyright 2019, Dmitry Tarakanov
|
|
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
|
|
// Copyright 2007, Nathan Reed
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include "Config.h"
|
|
#include "ClassInfo.h"
|
|
#include "LuaHelpers.h"
|
|
#include "LuaException.h"
|
|
#include "Security.h"
|
|
#include "TypeTraits.h"
|
|
|
|
#include <stdexcept>
|
|
#include <string_view>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace luabridge {
|
|
namespace detail {
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief Base for class and namespace registration.
|
|
*
|
|
* Maintains Lua stack in the proper state. Once beginNamespace, beginClass or deriveClass is called the parent object upon its destruction
|
|
* may no longer clear the Lua stack.
|
|
*
|
|
* Then endNamespace or endClass is called, a new parent is created and the child transfers the responsibility for clearing stack to it.
|
|
*
|
|
* So there can be maximum one "active" registrar object.
|
|
*/
|
|
class Registrar
|
|
{
|
|
protected:
|
|
Registrar(lua_State* L)
|
|
: L(L)
|
|
, m_stackSize(0)
|
|
{
|
|
}
|
|
|
|
Registrar(lua_State* L, int skipStackPops)
|
|
: L(L)
|
|
, m_stackSize(0)
|
|
, m_skipStackPops(skipStackPops)
|
|
{
|
|
}
|
|
|
|
Registrar(const Registrar& rhs)
|
|
: L(rhs.L)
|
|
, m_stackSize(std::exchange(rhs.m_stackSize, 0))
|
|
, m_skipStackPops(std::exchange(rhs.m_skipStackPops, 0))
|
|
{
|
|
}
|
|
|
|
Registrar& operator=(const Registrar& rhs)
|
|
{
|
|
m_stackSize = rhs.m_stackSize;
|
|
m_skipStackPops = rhs.m_skipStackPops;
|
|
|
|
return *this;
|
|
}
|
|
|
|
~Registrar()
|
|
{
|
|
const int popsCount = m_stackSize - m_skipStackPops;
|
|
if (popsCount > 0)
|
|
{
|
|
assert(popsCount <= lua_gettop(L));
|
|
|
|
lua_pop(L, popsCount);
|
|
}
|
|
}
|
|
|
|
void assertIsActive() const
|
|
{
|
|
if (m_stackSize == 0)
|
|
{
|
|
throw_or_assert<std::logic_error>("Unable to continue registration");
|
|
}
|
|
}
|
|
|
|
lua_State* const L = nullptr;
|
|
int mutable m_stackSize = 0;
|
|
int mutable m_skipStackPops = 0;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief Provides C++ to Lua registration capabilities.
|
|
*
|
|
* This class is not instantiated directly, call `getGlobalNamespace` to start the registration process.
|
|
*/
|
|
class Namespace : public detail::Registrar
|
|
{
|
|
//=============================================================================================
|
|
#if 0
|
|
/**
|
|
* @brief Error reporting.
|
|
*
|
|
* This function looks handy, why aren't we using it?
|
|
*/
|
|
static int luaError(lua_State* L, std::string message)
|
|
{
|
|
assert(lua_isstring(L, lua_upvalueindex(1)));
|
|
std::string s;
|
|
|
|
// Get information on the caller's caller to format the message,
|
|
// so the error appears to originate from the Lua source.
|
|
lua_Debug ar;
|
|
|
|
int result = lua_getstack(L, 2, &ar);
|
|
if (result != 0)
|
|
{
|
|
lua_getinfo(L, "Sl", &ar);
|
|
s = ar.short_src;
|
|
if (ar.currentline != -1)
|
|
{
|
|
// poor mans int to string to avoid <strstrream>.
|
|
lua_pushnumber(L, ar.currentline);
|
|
s = s + ":" + lua_tostring(L, -1) + ": ";
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
s = s + message;
|
|
|
|
luaL_error(L, "%s", s.c_str());
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Factored base to reduce template instantiations.
|
|
*/
|
|
class ClassBase : public detail::Registrar
|
|
{
|
|
public:
|
|
explicit ClassBase(Namespace& parent)
|
|
: Registrar(parent)
|
|
{
|
|
}
|
|
|
|
using Registrar::operator=;
|
|
|
|
protected:
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Create the const table.
|
|
*/
|
|
void createConstTable(const char* name, bool trueConst = true)
|
|
{
|
|
assert(name != nullptr);
|
|
|
|
std::string type_name = std::string(trueConst ? "const " : "") + name;
|
|
|
|
// Stack: namespace table (ns)
|
|
lua_newtable(L); // Stack: ns, const table (co)
|
|
lua_pushvalue(L, -1); // Stack: ns, co, co
|
|
lua_setmetatable(L, -2); // co.__metatable = co. Stack: ns, co
|
|
|
|
lua_pushstring(L, type_name.c_str());
|
|
lua_rawsetp(L, -2, detail::getTypeKey()); // co [typeKey] = name. Stack: ns, co
|
|
|
|
lua_pushcfunction_x(L, &detail::index_metamethod);
|
|
rawsetfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction_x(L, &detail::newindex_object_metamethod);
|
|
rawsetfield(L, -2, "__newindex");
|
|
|
|
lua_newtable(L);
|
|
lua_rawsetp(L, -2, detail::getPropgetKey());
|
|
|
|
if (Security::hideMetatables())
|
|
{
|
|
lua_pushnil(L);
|
|
rawsetfield(L, -2, "__metatable");
|
|
}
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Create the class table.
|
|
*
|
|
* The Lua stack should have the const table on top.
|
|
*/
|
|
void createClassTable(const char* name)
|
|
{
|
|
assert(name != nullptr);
|
|
|
|
// Stack: namespace table (ns), const table (co)
|
|
|
|
// Class table is the same as const table except the propset table
|
|
createConstTable(name, false); // Stack: ns, co, cl
|
|
|
|
lua_newtable(L); // Stack: ns, co, cl, propset table (ps)
|
|
lua_rawsetp(L, -2, detail::getPropsetKey()); // cl [propsetKey] = ps. Stack: ns, co, cl
|
|
|
|
lua_pushvalue(L, -2); // Stack: ns, co, cl, co
|
|
lua_rawsetp(L, -2, detail::getConstKey()); // cl [constKey] = co. Stack: ns, co, cl
|
|
|
|
lua_pushvalue(L, -1); // Stack: ns, co, cl, cl
|
|
lua_rawsetp(L, -3, detail::getClassKey()); // co [classKey] = cl. Stack: ns, co, cl
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Create the static table.
|
|
*/
|
|
void createStaticTable(const char* name)
|
|
{
|
|
assert(name != nullptr);
|
|
|
|
// Stack: namespace table (ns), const table (co), class table (cl)
|
|
lua_newtable(L); // Stack: ns, co, cl, visible static table (vst)
|
|
lua_newtable(L); // Stack: ns, co, cl, st, static metatable (st)
|
|
lua_pushvalue(L, -1); // Stack: ns, co, cl, vst, st, st
|
|
lua_setmetatable(L, -3); // st.__metatable = mt. Stack: ns, co, cl, vst, st
|
|
lua_insert(L, -2); // Stack: ns, co, cl, st, vst
|
|
rawsetfield(L, -5, name); // ns [name] = vst. Stack: ns, co, cl, st
|
|
|
|
#if 0
|
|
lua_pushlightuserdata(L, this);
|
|
lua_pushcclosure_x(L, &tostringMetaMethod, 1);
|
|
rawsetfield(L, -2, "__tostring");
|
|
#endif
|
|
|
|
lua_pushcfunction_x(L, &detail::index_metamethod);
|
|
rawsetfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
|
|
rawsetfield(L, -2, "__newindex");
|
|
|
|
lua_newtable(L); // Stack: ns, co, cl, st, proget table (pg)
|
|
lua_rawsetp(L, -2, detail::getPropgetKey()); // st [propgetKey] = pg. Stack: ns, co, cl, st
|
|
|
|
lua_newtable(L); // Stack: ns, co, cl, st, propset table (ps)
|
|
lua_rawsetp(L, -2, detail::getPropsetKey()); // st [propsetKey] = pg. Stack: ns, co, cl, st
|
|
|
|
lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl
|
|
lua_rawsetp(L, -2, detail::getClassKey()); // st [classKey] = cl. Stack: ns, co, cl, st
|
|
|
|
if (Security::hideMetatables())
|
|
{
|
|
lua_pushnil(L);
|
|
rawsetfield(L, -2, "__metatable");
|
|
}
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Asserts on stack state.
|
|
*/
|
|
void assertStackState() const
|
|
{
|
|
// Stack: const table (co), class table (cl), static table (st)
|
|
assert(lua_istable(L, -3));
|
|
assert(lua_istable(L, -2));
|
|
assert(lua_istable(L, -1));
|
|
}
|
|
};
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Provides a class registration in a lua_State.
|
|
*
|
|
* After construction the Lua stack holds these objects:
|
|
* -1 static table
|
|
* -2 class table
|
|
* -3 const table
|
|
* -4 enclosing namespace table
|
|
*/
|
|
template <class T>
|
|
class Class : public ClassBase
|
|
{
|
|
public:
|
|
//=========================================================================================
|
|
|
|
/**
|
|
* @brief Register a new class or add to an existing class registration.
|
|
*
|
|
* @param name The new class name.
|
|
* @param parent A parent namespace object.
|
|
*/
|
|
Class(const char* name, Namespace& parent)
|
|
: ClassBase(parent)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
rawgetfield(L, -1, name); // Stack: ns, static table (st) | nil
|
|
|
|
if (lua_isnil(L, -1)) // Stack: ns, nil
|
|
{
|
|
lua_pop(L, 1); // Stack: ns
|
|
|
|
createConstTable(name); // Stack: ns, const table (co)
|
|
#if !defined(LUABRIDGE_ON_LUAU)
|
|
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, function
|
|
rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co
|
|
#endif
|
|
++m_stackSize;
|
|
|
|
createClassTable(name); // Stack: ns, co, class table (cl)
|
|
#if !defined(LUABRIDGE_ON_LUAU)
|
|
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, cl, function
|
|
rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl
|
|
#endif
|
|
++m_stackSize;
|
|
|
|
createStaticTable(name); // Stack: ns, co, cl, st
|
|
++m_stackSize;
|
|
|
|
// Map T back to its tables.
|
|
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
}
|
|
else
|
|
{
|
|
assert(lua_istable(L, -1)); // Stack: ns, st
|
|
++m_stackSize;
|
|
|
|
// Map T back from its stored tables
|
|
|
|
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>()); // Stack: ns, st, co
|
|
lua_insert(L, -2); // Stack: ns, co, st
|
|
++m_stackSize;
|
|
|
|
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>()); // Stack: ns, co, st, cl
|
|
lua_insert(L, -2); // Stack: ns, co, cl, st
|
|
++m_stackSize;
|
|
}
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Derive a new class.
|
|
*
|
|
* @param name The class name.
|
|
* @param parent A parent namespace object.
|
|
* @param staticKey Key where the class is stored.
|
|
*/
|
|
Class(const char* name, Namespace& parent, void const* const staticKey)
|
|
: ClassBase(parent)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
createConstTable(name); // Stack: ns, const table (co)
|
|
#if !defined(LUABRIDGE_ON_LUAU)
|
|
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, function
|
|
rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co
|
|
#endif
|
|
++m_stackSize;
|
|
|
|
createClassTable(name); // Stack: ns, co, class table (cl)
|
|
#if !defined(LUABRIDGE_ON_LUAU)
|
|
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, cl, function
|
|
rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl
|
|
#endif
|
|
++m_stackSize;
|
|
|
|
createStaticTable(name); // Stack: ns, co, cl, st
|
|
++m_stackSize;
|
|
|
|
lua_rawgetp(L, LUA_REGISTRYINDEX, staticKey); // Stack: ns, co, cl, st, parent st (pst) | nil
|
|
if (lua_isnil(L, -1)) // Stack: ns, co, cl, st, nil
|
|
{
|
|
lua_pop(L, 1);
|
|
|
|
throw_or_assert<std::logic_error>("Base class is not registered");
|
|
return;
|
|
}
|
|
|
|
assert(lua_istable(L, -1)); // Stack: ns, co, cl, st, pst
|
|
|
|
lua_rawgetp(L, -1, detail::getClassKey()); // Stack: ns, co, cl, st, pst, parent cl (pcl)
|
|
assert(lua_istable(L, -1));
|
|
|
|
lua_rawgetp(L, -1, detail::getConstKey()); // Stack: ns, co, cl, st, pst, pcl, parent co (pco)
|
|
assert(lua_istable(L, -1));
|
|
|
|
lua_rawsetp(L, -6, detail::getParentKey()); // co [parentKey] = pco. Stack: ns, co, cl, st, pst, pcl
|
|
lua_rawsetp(L, -4, detail::getParentKey()); // cl [parentKey] = pcl. Stack: ns, co, cl, st, pst
|
|
lua_rawsetp(L, -2, detail::getParentKey()); // st [parentKey] = pst. Stack: ns, co, cl, st
|
|
|
|
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co
|
|
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>()); // Stack: ns, co, cl, st
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Continue registration in the enclosing namespace.
|
|
*
|
|
* @returns A parent registration object.
|
|
*/
|
|
Namespace endClass()
|
|
{
|
|
assert(m_stackSize > 3);
|
|
|
|
m_stackSize -= 3;
|
|
lua_pop(L, 3);
|
|
return Namespace(*this);
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a static property.
|
|
*
|
|
* @tparam U The type of the property.
|
|
*
|
|
* @param name The property name.
|
|
* @param value A property value pointer.
|
|
* @param isWritable True for a read-write, false for read-only property.
|
|
*
|
|
* @returns This class registration object.
|
|
*/
|
|
template <class U, class = std::enable_if_t<!std::is_invocable_v<U>>>
|
|
Class<T>& addStaticProperty(const char* name, const U* value)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, const_cast<U*>(value)); // Stack: co, cl, st, pointer
|
|
lua_pushcclosure_x(L, &detail::property_getter<U>::call, 1); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
lua_pushstring(L, name); // Stack: co, cl, st, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: co, cl, st, function
|
|
|
|
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @brief Add or replace a static property.
|
|
*
|
|
* @tparam U The type of the property.
|
|
*
|
|
* @param name The property name.
|
|
* @param value A property value pointer.
|
|
* @param isWritable True for a read-write, false for read-only property.
|
|
*
|
|
* @returns This class registration object.
|
|
*/
|
|
template <class U, class = std::enable_if_t<!std::is_invocable_v<U>>>
|
|
Class<T>& addStaticProperty(const char* name, U* value, bool isWritable = true)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, value); // Stack: co, cl, st, pointer
|
|
lua_pushcclosure_x(L, &detail::property_getter<U>::call, 1); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
if (isWritable)
|
|
{
|
|
lua_pushlightuserdata(L, value); // Stack: co, cl, st, ps, pointer
|
|
lua_pushcclosure_x(L, &detail::property_setter<U>::call, 1); // Stack: co, cl, st, ps, setter
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name); // Stack: co, cl, st, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: co, cl, st, function
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a static property member.
|
|
*
|
|
* @tparam U The type of the property.
|
|
*
|
|
* @param name The property name.
|
|
* @param get A property getter function pointer.
|
|
* @param set A property setter function pointer, optional, nullable. Omit or pass nullptr for a read-only property.
|
|
*
|
|
* @returns This class registration object.
|
|
*/
|
|
template <class U>
|
|
Class<T>& addStaticProperty(const char* name, U (*get)(), void (*set)(U) = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<U (*)()>, 1); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(set)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(U)>, 1); // Stack: co, cl, st, setter
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name); // Stack: co, cl, st, ps, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: co, cl, st, function
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class U>
|
|
Class<T>& addStaticProperty(const char* name, U (*get)() noexcept, void (*set)(U) noexcept = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<U (*)() noexcept>, 1); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(set)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(U) noexcept>, 1); // Stack: co, cl, st, setter
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name); // Stack: co, cl, st, ps, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: co, cl, st, function
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a static property, by constructible by std::function.
|
|
*/
|
|
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
|
|
Class<T>& addStaticProperty(const char* name, Getter get)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
using GetType = decltype(get);
|
|
|
|
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: co, cl, st, function
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
|
|
Class<T>& addStaticProperty(const char* name, Getter get, Setter set)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
using GetType = decltype(get);
|
|
using SetType = decltype(set);
|
|
|
|
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: co, cl, st, function
|
|
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
|
|
|
|
lua_newuserdata_aligned<SetType>(L, std::move(set)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1); // Stack: co, cl, st, function
|
|
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a single static function or multiple overloaded functions.
|
|
*
|
|
* @param name The overload name.
|
|
* @param functions A single or set of static functions that will be invoked.
|
|
*
|
|
* @returns This class registration object.
|
|
*/
|
|
template <class... Functions>
|
|
auto addStaticFunction(const char* name, Functions... functions)
|
|
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
detail::push_function(L, std::move(functions));
|
|
|
|
} (), ...);
|
|
}
|
|
else
|
|
{
|
|
// create new closure of try_overloads with new table
|
|
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0); // reserve space for N overloads
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
|
|
lua_pushinteger(L, -1);
|
|
else
|
|
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
detail::push_function(L, std::move(functions));
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<false>, 1);
|
|
}
|
|
|
|
rawsetfield(L, -2, name);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member.
|
|
*/
|
|
template <class U, class V>
|
|
Class<T>& addProperty(const char* name, U V::*mp, bool isWritable = true)
|
|
{
|
|
static_assert(std::is_base_of_v<V, T>);
|
|
|
|
using MemberPtrType = decltype(mp);
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<MemberPtrType>(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr
|
|
lua_pushcclosure_x(L, &detail::property_getter<U, T>::call, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (isWritable)
|
|
{
|
|
new (lua_newuserdata_x<MemberPtrType>(L, sizeof(MemberPtrType))) MemberPtrType(mp); // Stack: co, cl, st, field ptr
|
|
lua_pushcclosure_x(L, &detail::property_setter<U, T>::call, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member.
|
|
*/
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (T::*get)() const, void (T::*set)(TS) = nullptr)
|
|
{
|
|
using GetType = TG (T::*)() const;
|
|
using SetType = void (T::*)(TS);
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (T::*get)() const noexcept, void (T::*set)(TS) noexcept = nullptr)
|
|
{
|
|
using GetType = TG (T::*)() const noexcept;
|
|
using SetType = void (T::*)(TS) noexcept;
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member.
|
|
*/
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (T::*get)(lua_State*) const, void (T::*set)(TS, lua_State*) = nullptr)
|
|
{
|
|
using GetType = TG (T::*)(lua_State*) const;
|
|
using SetType = void (T::*)(TS, lua_State*);
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (T::*get)(lua_State*) const noexcept, void (T::*set)(TS, lua_State*) noexcept = nullptr)
|
|
{
|
|
using GetType = TG (T::*)(lua_State*) const noexcept;
|
|
using SetType = void (T::*)(TS, lua_State*) noexcept;
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get); // Stack: co, cl, st, funcion ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member, by proxy.
|
|
*
|
|
* When a class is closed for modification and does not provide (or cannot provide) the function signatures necessary to implement
|
|
* get or set for a property, this will allow non-member functions act as proxies.
|
|
*
|
|
* Both the get and the set functions require a T const* and T* in the first argument respectively.
|
|
*/
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (*get)(const T*), void (*set)(T*, TS) = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)(const T*)>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st,, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata( L, reinterpret_cast<void*>(set)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(T*, TS)>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class TG, class TS = TG>
|
|
Class<T>& addProperty(const char* name, TG (*get)(const T*) noexcept, void (*set)(T*, TS) noexcept = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)(const T*) noexcept>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st,, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata( L, reinterpret_cast<void*>(set)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(T*, TS) noexcept>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member, by proxy C-function.
|
|
*
|
|
* When a class is closed for modification and does not provide (or cannot provide) the function signatures necessary to implement
|
|
* get or set for a property, this will allow non-member functions act as proxies.
|
|
*
|
|
* The object userdata ('this') value is at the index 1.
|
|
* The new value for set function is at the index 2.
|
|
*/
|
|
Class<T>& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushcfunction_x(L, get);
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st,, getter, getter
|
|
detail::add_property_getter(L, name, -5); // Stack: co, cl, st,, getter
|
|
detail::add_property_getter(L, name, -3); // Stack: co, cl, st,
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushcfunction_x(L, set);
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st,
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a property member, by constructible by std::function.
|
|
*/
|
|
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
|
|
Class<T>& addProperty(const char* name, Getter get)
|
|
{
|
|
using FirstArg = detail::function_argument_t<0, Getter>;
|
|
static_assert(std::is_same_v<std::decay_t<std::remove_pointer_t<FirstArg>>, T>);
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
using GetType = decltype(get);
|
|
|
|
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: co, cl, st, getter
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, getter, getter
|
|
detail::add_property_getter(L, name, -4); // Stack: co, cl, st, getter
|
|
detail::add_property_getter(L, name, -4); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
|
|
Class<T>& addProperty(const char* name, Getter get, Setter set)
|
|
{
|
|
addProperty<Getter>(name, std::move(get));
|
|
|
|
using FirstArg = detail::function_argument_t<0, Setter>;
|
|
static_assert(std::is_same_v<std::decay_t<std::remove_pointer_t<FirstArg>>, T>);
|
|
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
using SetType = decltype(set);
|
|
|
|
lua_newuserdata_aligned<SetType>(L, std::move(set)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1); // Stack: co, cl, st, setter
|
|
detail::add_property_setter(L, name, -3); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a function that can operate on the class.
|
|
*
|
|
* @param name The function or overloaded functions name.
|
|
* @param functions A single or set of functions that will be invoked.
|
|
*
|
|
* @returns This class registration object.
|
|
*/
|
|
template <class... Functions>
|
|
auto addFunction(const char* name, Functions... functions)
|
|
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
|
|
{
|
|
assert(name != nullptr);
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
if (name == std::string_view("__gc"))
|
|
{
|
|
throw_or_assert<std::logic_error>("__gc metamethod registration is forbidden");
|
|
return *this;
|
|
}
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
detail::push_member_function<T>(L, std::move(functions));
|
|
|
|
} (), ...);
|
|
|
|
if constexpr (detail::const_functions_count<T, Functions...> == 1)
|
|
{
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, function, function
|
|
rawsetfield(L, -4, name); // Stack: co, cl, st, function
|
|
rawsetfield(L, -4, name); // Stack: co, cl, st
|
|
}
|
|
else
|
|
{
|
|
rawsetfield(L, -3, name); // Stack: co, cl, st
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// create new closure of const try_overload_functions with new table
|
|
if constexpr (detail::const_functions_count<T, Functions...> > 0)
|
|
{
|
|
lua_createtable(L, static_cast<int>(detail::const_functions_count<T, Functions...>), 0);
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
if (!detail::is_const_function<T, Functions>)
|
|
return;
|
|
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
|
|
lua_pushinteger(L, -1);
|
|
else
|
|
lua_pushinteger(L, static_cast<int>(detail::member_function_arity_excluding_v<T, Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
detail::push_member_function<T>(L, std::move(functions));
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
assert(idx > 1);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
|
|
lua_pushvalue(L, -1); // Stack: co, cl, st, function, function
|
|
rawsetfield(L, -4, name); // Stack: co, cl, st, function
|
|
rawsetfield(L, -4, name); // Stack: co, cl, st
|
|
}
|
|
|
|
// create new closure of non const try_overload_functions with new table
|
|
if constexpr (detail::non_const_functions_count<T, Functions...> > 0)
|
|
{
|
|
lua_createtable(L, static_cast<int>(detail::non_const_functions_count<T, Functions...>), 0);
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
if (detail::is_const_function<T, Functions>)
|
|
return;
|
|
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
|
|
lua_pushinteger(L, -1);
|
|
else
|
|
lua_pushinteger(L, static_cast<int>(detail::member_function_arity_excluding_v<T, Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
detail::push_member_function<T>(L, std::move(functions));
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
assert(idx > 1);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
|
|
rawsetfield(L, -3, name); // Stack: co, cl, st
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a primary Constructor.
|
|
*
|
|
* The primary Constructor is invoked when calling the class type table like a function.
|
|
*
|
|
* The template parameter should be a function pointer type that matches the desired Constructor (since you can't take the
|
|
* address of a Constructor and pass it as an argument).
|
|
*/
|
|
template <class... Functions>
|
|
auto addConstructor()
|
|
-> std::enable_if_t<(sizeof...(Functions) > 0), Class<T>&>
|
|
{
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
lua_pushcclosure_x(L, &detail::constructor_placement_proxy<T, detail::function_arguments_t<Functions>>, 0);
|
|
|
|
} (), ...);
|
|
}
|
|
else
|
|
{
|
|
// create new closure of try_overloads with new table
|
|
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0); // reserve space for N overloads
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
lua_pushcclosure_x(L, &detail::constructor_placement_proxy<T, detail::function_arguments_t<Functions>>, 0);
|
|
lua_settable(L, -3);
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
|
|
}
|
|
|
|
rawsetfield(L, -2, "__call");
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a primary Constructor when the type is used from an intrusive container C.
|
|
*/
|
|
template <class C, class... Functions>
|
|
auto addConstructorFrom()
|
|
-> std::enable_if_t<(sizeof...(Functions) > 0), Class<T>&>
|
|
{
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
lua_pushcclosure_x(L, &detail::constructor_container_proxy<C, detail::function_arguments_t<Functions>>, 0);
|
|
|
|
} (), ...);
|
|
}
|
|
else
|
|
{
|
|
// create new closure of try_overloads with new table
|
|
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0); // reserve space for N overloads
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
lua_pushcclosure_x(L, &detail::constructor_container_proxy<C, detail::function_arguments_t<Functions>>, 0);
|
|
lua_settable(L, -3);
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
|
|
}
|
|
|
|
rawsetfield(L, -2, "__call");
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a placement constructor.
|
|
*
|
|
* The primary placement constructor is invoked when calling the class type table like a function.
|
|
*
|
|
* The provider of the Function argument is responsible of doing placement new of the type T over the void* pointer provided to
|
|
* the method as first argument.
|
|
*/
|
|
template <class... Functions>
|
|
auto addConstructor(Functions... functions)
|
|
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
|
|
{
|
|
static_assert(((detail::function_arity_excluding_v<Functions, lua_State*> >= 1) && ...));
|
|
static_assert(((std::is_same_v<detail::function_argument_t<0, Functions>, void*>) && ...));
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
detail::push_function(L, detail::constructor_forwarder<T, Functions>(std::move(functions))); // Stack: co, cl, st, function
|
|
|
|
} (), ...);
|
|
}
|
|
else
|
|
{
|
|
// create new closure of try_overloads with new table
|
|
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0); // reserve space for N overloads
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
|
|
lua_pushinteger(L, -1);
|
|
else
|
|
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>) - 1); // 1: for void* ptr
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
detail::push_function(L, detail::constructor_forwarder<T, Functions>(std::move(functions)));
|
|
lua_settable(L, -3);
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
|
|
}
|
|
|
|
rawsetfield(L, -2, "__call"); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add or replace a factory.
|
|
*
|
|
* The primary Constructor is invoked when calling the class type table like a function.
|
|
*
|
|
* The template parameter should be a function pointer type that matches the desired Constructor (since you can't take the
|
|
* address of a Constructor and pass it as an argument).
|
|
*/
|
|
template <class Allocator, class Deallocator>
|
|
Class<T>& addFactory(Allocator allocator, Deallocator deallocator)
|
|
{
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
detail::push_function(L, detail::factory_forwarder<T, Allocator, Deallocator>(std::move(allocator), std::move(deallocator)));
|
|
rawsetfield(L, -2, "__call"); // Stack: co, cl, st
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add an index metamethod function fallback that is triggered when no result is found in functions, properties or any other members.
|
|
*
|
|
* Let the user define a fallback index (__index) metamethod at its level.
|
|
*/
|
|
template <class Function>
|
|
auto addIndexMetaMethod(Function function)
|
|
-> std::enable_if_t<!std::is_pointer_v<Function>
|
|
&& std::is_invocable_v<Function, T&, const LuaRef&, lua_State*>, Class<T>&>
|
|
{
|
|
using FnType = decltype(function);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_newuserdata_aligned<FnType>(L, std::move(function)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1); // Stack: co, cl, st, function
|
|
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
|
|
Class<T>& addIndexMetaMethod(LuaRef (*idxf)(T&, const LuaRef&, lua_State*))
|
|
{
|
|
using FnType = decltype(idxf);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, 1); // Stack: co, cl, st, function
|
|
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
|
|
Class<T>& addIndexMetaMethod(LuaRef (T::* idxf)(const LuaRef&, lua_State*))
|
|
{
|
|
using MemFnPtr = decltype(idxf);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<MemFnPtr>(L, sizeof(MemFnPtr))) MemFnPtr(idxf);
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<MemFnPtr, T>, 1);
|
|
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=========================================================================================
|
|
/**
|
|
* @brief Add an insert index metamethod function fallback that is triggered when no result is found in functions, properties or any other members.
|
|
*
|
|
* Let the user define a fallback insert index (___newindex) metamethod at its level.
|
|
*/
|
|
template <class Function>
|
|
auto addNewIndexMetaMethod(Function function)
|
|
-> std::enable_if_t<!std::is_pointer_v<Function>
|
|
&& std::is_invocable_v<Function, T&, const LuaRef&, const LuaRef&, lua_State*>, Class<T>&>
|
|
{
|
|
using FnType = decltype(function);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_newuserdata_aligned<FnType>(L, std::move(function)); // Stack: co, cl, st, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1); // Stack: co, cl, st, function
|
|
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
|
|
Class<T>& addNewIndexMetaMethod(LuaRef (*idxf)(T&, const LuaRef&, const LuaRef&, lua_State*))
|
|
{
|
|
using FnType = decltype(idxf);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf)); // Stack: co, cl, st, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, 1); // Stack: co, cl, st, function
|
|
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
|
|
Class<T>& addNewIndexMetaMethod(LuaRef (T::* idxf)(const LuaRef&, const LuaRef&, lua_State*))
|
|
{
|
|
using MemFnPtr = decltype(idxf);
|
|
|
|
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
|
|
|
|
new (lua_newuserdata_x<MemFnPtr>(L, sizeof(MemFnPtr))) MemFnPtr(idxf);
|
|
lua_pushcclosure_x(L, &detail::invoke_member_function<MemFnPtr, T>, 1);
|
|
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class Table : public detail::Registrar
|
|
{
|
|
public:
|
|
explicit Table(const char* name, Namespace& parent)
|
|
: Registrar(parent)
|
|
{
|
|
lua_newtable(L); // Stack: ns, table (tb)
|
|
lua_pushvalue(L, -1); // Stack: ns, tb, tb
|
|
rawsetfield(L, -3, name);
|
|
++m_stackSize;
|
|
|
|
lua_newtable(L); // Stack: ns, table (tb)
|
|
lua_pushvalue(L, -1); // Stack: ns, tb, tb
|
|
lua_setmetatable(L, -3); // tb.__metatable = tb. Stack: ns, tb
|
|
++m_stackSize;
|
|
}
|
|
|
|
using Registrar::operator=;
|
|
|
|
template <class Function>
|
|
Table& addFunction(const char* name, Function function)
|
|
{
|
|
using FnType = decltype(function);
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_newuserdata_aligned<FnType>(L, std::move(function)); // Stack: ns, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1); // Stack: ns, function
|
|
rawsetfield(L, -3, name); // Stack: ns
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Function>
|
|
Table& addMetaFunction(const char* name, Function function)
|
|
{
|
|
using FnType = decltype(function);
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_newuserdata_aligned<FnType>(L, std::move(function)); // Stack: ns, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1); // Stack: ns, function
|
|
rawsetfield(L, -2, name); // Stack: ns
|
|
|
|
return *this;
|
|
}
|
|
|
|
Namespace endTable()
|
|
{
|
|
assert(m_stackSize > 2);
|
|
|
|
m_stackSize -= 2;
|
|
lua_pop(L, 2);
|
|
return Namespace(*this);
|
|
}
|
|
};
|
|
|
|
private:
|
|
struct FromStack {};
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Open the global namespace for registrations.
|
|
*
|
|
* @param L A Lua state.
|
|
*/
|
|
explicit Namespace(lua_State* L)
|
|
: Registrar(L)
|
|
{
|
|
lua_getglobal(L, "_G");
|
|
|
|
++m_stackSize;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Open the a namespace for registrations from a table on top of the stack.
|
|
*
|
|
* @param L A Lua state.
|
|
*/
|
|
Namespace(lua_State* L, FromStack)
|
|
: Registrar(L, 1)
|
|
{
|
|
assert(lua_istable(L, -1));
|
|
|
|
{
|
|
lua_pushvalue(L, -1); // Stack: ns, mt
|
|
|
|
// ns.__metatable = ns
|
|
lua_setmetatable(L, -2); // Stack: ns, mt
|
|
|
|
// ns.__index = index_metamethod
|
|
lua_pushcfunction_x(L, &detail::index_metamethod);
|
|
rawsetfield(L, -2, "__index"); // Stack: ns
|
|
|
|
lua_newtable(L); // Stack: ns, mt, propget table (pg)
|
|
lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: ns
|
|
|
|
lua_newtable(L); // Stack: ns, mt, propset table (ps)
|
|
lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: ns
|
|
}
|
|
|
|
++m_stackSize;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Open a namespace for registrations.
|
|
*
|
|
* The namespace is created if it doesn't already exist.
|
|
*
|
|
* @param name The namespace name.
|
|
* @param parent The parent namespace object.
|
|
*
|
|
* @pre The parent namespace is at the top of the Lua stack.
|
|
*/
|
|
Namespace(const char* name, Namespace& parent)
|
|
: Registrar(parent)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: parent namespace (pns)
|
|
|
|
rawgetfield(L, -1, name); // Stack: pns, namespace (ns) | nil
|
|
|
|
if (lua_isnil(L, -1)) // Stack: pns, nil
|
|
{
|
|
lua_pop(L, 1); // Stack: pns
|
|
|
|
lua_newtable(L); // Stack: pns, ns
|
|
lua_pushvalue(L, -1); // Stack: pns, ns, mt
|
|
|
|
// ns.__metatable = ns
|
|
lua_setmetatable(L, -2); // Stack: pns, ns
|
|
|
|
// ns.__index = index_metamethod
|
|
lua_pushcfunction_x(L, &detail::index_metamethod);
|
|
rawsetfield(L, -2, "__index"); // Stack: pns, ns
|
|
|
|
// ns.__newindex = newindex_static_metamethod
|
|
lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
|
|
rawsetfield(L, -2, "__newindex"); // Stack: pns, ns
|
|
|
|
lua_newtable(L); // Stack: pns, ns, propget table (pg)
|
|
lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: pns, ns
|
|
|
|
lua_newtable(L); // Stack: pns, ns, propset table (ps)
|
|
lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: pns, ns
|
|
|
|
// pns [name] = ns
|
|
lua_pushvalue(L, -1); // Stack: pns, ns, ns
|
|
rawsetfield(L, -3, name); // Stack: pns, ns
|
|
}
|
|
|
|
++m_stackSize;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Close the class and continue the namespace registrations.
|
|
*
|
|
* @param child A child class registration object.
|
|
*/
|
|
explicit Namespace(ClassBase& child)
|
|
: Registrar(child)
|
|
{
|
|
}
|
|
|
|
explicit Namespace(Table& child)
|
|
: Registrar(child)
|
|
{
|
|
}
|
|
|
|
using Registrar::operator=;
|
|
|
|
public:
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Retrieve the global namespace.
|
|
*
|
|
* It is recommended to put your namespace inside the global namespace, and then add your classes and functions to it, rather than
|
|
* adding many classes and functions directly to the global namespace.
|
|
*
|
|
* @param L A Lua state.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
static Namespace getGlobalNamespace(lua_State* L)
|
|
{
|
|
return Namespace(L);
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve the namespace on top of the stack.
|
|
*
|
|
* You should have a table on top of the stack before calling this function. It will then use the table there as destination for registrations.
|
|
*
|
|
* @param L A Lua state.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
static Namespace getNamespaceFromStack(lua_State* L)
|
|
{
|
|
return Namespace(L, FromStack{});
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Open a new or existing namespace for registrations.
|
|
*
|
|
* @param name The namespace name.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
Namespace beginNamespace(const char* name)
|
|
{
|
|
assertIsActive();
|
|
return Namespace(name, *this);
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Continue namespace registration in the parent.
|
|
*
|
|
* Do not use this on the global namespace.
|
|
*
|
|
* @returns A parent namespace registration object.
|
|
*/
|
|
Namespace endNamespace()
|
|
{
|
|
if (m_stackSize == 1)
|
|
{
|
|
throw_or_assert<std::logic_error>("endNamespace() called on global namespace");
|
|
|
|
return Namespace(*this);
|
|
}
|
|
|
|
assert(m_stackSize > 1);
|
|
--m_stackSize;
|
|
lua_pop(L, 1);
|
|
return Namespace(*this);
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a variable, a variable will be added in the namespace by copy of the passed value.
|
|
*
|
|
* @param name The property name.
|
|
* @param value A value object.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class T>
|
|
Namespace& addVariable(const char* name, const T& value)
|
|
{
|
|
if (m_stackSize == 1)
|
|
{
|
|
throw_or_assert<std::logic_error>("addVariable() called on global namespace");
|
|
|
|
return *this;
|
|
}
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
if constexpr (std::is_enum_v<T>)
|
|
{
|
|
using U = std::underlying_type_t<T>;
|
|
|
|
auto result = Stack<U>::push(L, static_cast<U>(value));
|
|
if (! result)
|
|
luaL_error(L, "%s", result.message().c_str());
|
|
}
|
|
else
|
|
{
|
|
auto result = Stack<T>::push(L, value);
|
|
if (! result)
|
|
luaL_error(L, "%s", result.message().c_str());
|
|
}
|
|
|
|
rawsetfield(L, -2, name); // Stack: ns
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a property.
|
|
*
|
|
* @param name The property name.
|
|
* @param value A value pointer.
|
|
* @param isWritable True for a read-write, false for read-only property.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class T>
|
|
Namespace& addProperty(const char* name, T* value, bool isWritable = true)
|
|
{
|
|
if (m_stackSize == 1)
|
|
{
|
|
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
|
|
|
|
return *this;
|
|
}
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_pushlightuserdata(L, value); // Stack: ns, pointer
|
|
lua_pushcclosure_x(L, &detail::property_getter<T>::call, 1); // Stack: ns, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: ns
|
|
|
|
if (isWritable)
|
|
{
|
|
lua_pushlightuserdata(L, value); // Stack: ns, pointer
|
|
lua_pushcclosure_x(L, &detail::property_setter<T>::call, 1); // Stack: ns, setter
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name); // Stack: ns, ps, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: ns, function
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2); // Stack: ns
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a property.
|
|
*
|
|
* If the set function is omitted or null, the property is read-only.
|
|
*
|
|
* @param name The property name.
|
|
* @param get A pointer to a property getter function.
|
|
* @param set A pointer to a property setter function, optional.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class TG, class TS = TG>
|
|
Namespace& addProperty(const char* name, TG (*get)(), void (*set)(TS) = nullptr)
|
|
{
|
|
if (m_stackSize == 1)
|
|
{
|
|
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
|
|
|
|
return *this;
|
|
}
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: ns, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)()>, 1); // Stack: ns, getter
|
|
detail::add_property_getter(L, name, -2);
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(set)); // Stack: ns, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(TS)>, 1);
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name);
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1);
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2);
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class TG, class TS = TG>
|
|
Namespace& addProperty(const char* name, TG (*get)() noexcept, void (*set)(TS) noexcept = nullptr)
|
|
{
|
|
if (m_stackSize == 1)
|
|
{
|
|
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
|
|
|
|
return *this;
|
|
}
|
|
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(get)); // Stack: ns, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)() noexcept>, 1); // Stack: ns, getter
|
|
detail::add_property_getter(L, name, -2);
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushlightuserdata(L, reinterpret_cast<void*>(set)); // Stack: ns, function ptr
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(TS) noexcept>, 1);
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name);
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1);
|
|
}
|
|
|
|
detail::add_property_setter(L, name, -2);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a readonly property.
|
|
*
|
|
* @param name The property name.
|
|
* @param get A pointer to a property getter function.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
|
|
Namespace& addProperty(const char* name, Getter get)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
if constexpr (std::is_enum_v<Getter>)
|
|
{
|
|
using U = std::underlying_type_t<Getter>;
|
|
|
|
auto enumGet = [get = std::move(get)] { return static_cast<U>(get); };
|
|
|
|
using GetType = decltype(enumGet);
|
|
lua_newuserdata_aligned<GetType>(L, std::move(enumGet)); // Stack: ns, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: ns, ud, getter
|
|
}
|
|
else
|
|
{
|
|
using GetType = decltype(get);
|
|
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: ns, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: ns, ud, getter
|
|
}
|
|
|
|
detail::add_property_getter(L, name, -2); // Stack: ns, ud, getter
|
|
|
|
lua_pushstring(L, name); // Stack: ns, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: ns, name, function
|
|
detail::add_property_setter(L, name, -2); // Stack: ns
|
|
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @brief Add or replace a mutable property.
|
|
*
|
|
* @param name The property name.
|
|
* @param get A pointer to a property getter function.
|
|
* @param set A pointer to a property setter function.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
|
|
Namespace& addProperty(const char* name, Getter get, Setter set)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
addProperty<Getter>(name, std::move(get));
|
|
|
|
using SetType = decltype(set);
|
|
|
|
lua_newuserdata_aligned<SetType>(L, std::move(set)); // Stack: ns, function userdata (ud)
|
|
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1); // Stack: ns, ud, getter
|
|
detail::add_property_setter(L, name, -2); // Stack: ns, ud, getter
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a property.
|
|
*
|
|
* If the set function is omitted or null, the property is read-only.
|
|
*
|
|
* @param name The property name.
|
|
* @param get A pointer to a property getter function.
|
|
* @param set A pointer to a property setter function, optional.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
Namespace& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr)
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
lua_pushcfunction_x(L, get); // Stack: ns, getter
|
|
detail::add_property_getter(L, name, -2); // Stack: ns
|
|
|
|
if (set != nullptr)
|
|
{
|
|
lua_pushcfunction_x(L, set); // Stack: ns, setter
|
|
detail::add_property_setter(L, name, -2); // Stack: ns
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, name); // Stack: ns, name
|
|
lua_pushcclosure_x(L, &detail::read_only_error, 1); // Stack: ns, name, function
|
|
detail::add_property_setter(L, name, -2); // Stack: ns
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add or replace a single function or multiple overloaded functions.
|
|
*
|
|
* @param name The overload name.
|
|
* @param functions A single or set of functions that will be invoked.
|
|
*
|
|
* @returns This namespace registration object.
|
|
*/
|
|
template <class... Functions>
|
|
auto addFunction(const char* name, Functions... functions)
|
|
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Namespace&>
|
|
{
|
|
assert(name != nullptr);
|
|
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
|
|
|
|
if constexpr (sizeof...(Functions) == 1)
|
|
{
|
|
([&]
|
|
{
|
|
detail::push_function(L, std::move(functions));
|
|
|
|
} (), ...);
|
|
}
|
|
else
|
|
{
|
|
// create new closure of try_overloads with new table
|
|
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0); // reserve space for N overloads
|
|
|
|
int idx = 1;
|
|
|
|
([&]
|
|
{
|
|
lua_createtable(L, 2, 0); // reserve space for: function, arity
|
|
lua_pushinteger(L, 1);
|
|
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
|
|
lua_pushinteger(L, -1);
|
|
else
|
|
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
|
|
lua_settable(L, -3);
|
|
lua_pushinteger(L, 2);
|
|
detail::push_function(L, std::move(functions));
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, idx);
|
|
++idx;
|
|
|
|
} (), ...);
|
|
|
|
lua_pushcclosure_x(L, &detail::try_overload_functions<false>, 1);
|
|
}
|
|
|
|
rawsetfield(L, -2, name);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================================
|
|
Table beginTable(const char* name)
|
|
{
|
|
assertIsActive();
|
|
return Table(name, *this);
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Open a new or existing class for registrations.
|
|
*
|
|
* @param name The class name.
|
|
*
|
|
* @returns A class registration object.
|
|
*/
|
|
template <class T>
|
|
Class<T> beginClass(const char* name)
|
|
{
|
|
assertIsActive();
|
|
return Class<T>(name, *this);
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Derive a new class for registrations.
|
|
*
|
|
* Call deriveClass() only once. To continue registrations for the class later, use beginClass().
|
|
*
|
|
* @param name The class name.
|
|
*
|
|
* @returns A class registration object.
|
|
*/
|
|
template <class Derived, class Base>
|
|
Class<Derived> deriveClass(const char* name)
|
|
{
|
|
assertIsActive();
|
|
return Class<Derived>(name, *this, detail::getStaticRegistryKey<Base>());
|
|
}
|
|
};
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief Retrieve the global namespace.
|
|
*
|
|
* It is recommended to put your namespace inside the global namespace, and then add your classes and functions to it, rather than adding
|
|
* many classes and functions directly to the global namespace.
|
|
*
|
|
* @param L A Lua state.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
inline Namespace getGlobalNamespace(lua_State* L)
|
|
{
|
|
return Namespace::getGlobalNamespace(L);
|
|
}
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief Retrieve the namespace on top of the stack.
|
|
*
|
|
* You should have a table on top of the stack before calling this function. It will then use the table there as destination for registrations.
|
|
*
|
|
* @param L A Lua state.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
inline Namespace getNamespaceFromStack(lua_State* L)
|
|
{
|
|
return Namespace::getNamespaceFromStack(L);
|
|
}
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief Registers main thread.
|
|
*
|
|
* This is a backward compatibility mitigation for lua 5.1 not supporting LUA_RIDX_MAINTHREAD.
|
|
*
|
|
* @param L The main Lua state that will be registered as main thread.
|
|
*
|
|
* @returns A namespace registration object.
|
|
*/
|
|
inline void registerMainThread(lua_State* L)
|
|
{
|
|
register_main_thread(L);
|
|
}
|
|
|
|
} // namespace luabridge
|