Files
itgmania212121/src/LuaBinding.h
T
Arthur Eubanks ecfcb11a00 Remove implicit conversion operator from RString to const char*
This is required for the RString to std::string migration.

Mostly automated from https://github.com/aeubanks/rewriter/blob/main/c_str.cc, with some manual intervention required for fixing up `a + b.c_str()` to `(a + b).c_str()`.

Added some overloads for some common global functions like sm_crash to reduce the number of changes required here.
2025-05-15 21:14:54 -07:00

260 lines
8.4 KiB
C++

/* LuaBinding - helpers to expose Lua bindings for C++ classes. */
#ifndef LuaBinding_H
#define LuaBinding_H
#include "LuaManager.h"
#include <vector>
class LuaReference;
class LuaBinding
{
public:
LuaBinding();
virtual ~LuaBinding();
void Register( lua_State *L );
static void RegisterTypes( lua_State *L );
bool IsDerivedClass() const { return GetClassName() != GetBaseClassName(); }
virtual const RString &GetClassName() const = 0;
virtual const RString &GetBaseClassName() const = 0;
static void ApplyDerivedType( Lua *L, const RString &sClassname, void *pSelf );
static bool CheckLuaObjectType( lua_State *L, int narg, const char *szType );
protected:
virtual void Register( Lua *L, int iMethods, int iMetatable ) = 0;
static void CreateMethodsTable( lua_State *L, const RString &szName );
static void *GetPointerFromStack( Lua *L, const RString &sType, int iArg );
static bool Equal( lua_State *L );
static int PushEqual( lua_State *L );
};
/** @brief Allow the binding of Lua to various classes. */
template <typename Type>
class Luna: public LuaBinding
{
protected:
typedef Type T;
typedef int (binding_t)(T *p, lua_State *L);
struct RegType
{
const char *szName;
binding_t *mfunc;
};
void Register( Lua *L, int iMethods, int iMetatable )
{
lua_pushcfunction( L, tostring_T );
lua_setfield( L, iMetatable, "__tostring" );
// fill method table with methods from class T
for( unsigned i=0; i < m_aMethods.size(); i++ )
{
const RegType *l = &m_aMethods[i];
lua_pushlightuserdata( L, (void*) l->mfunc );
lua_pushcclosure( L, thunk, 1 );
lua_setfield( L, iMethods, l->szName );
}
}
public:
virtual const RString &GetClassName() const { return m_sClassName; }
virtual const RString &GetBaseClassName() const { return m_sBaseClassName; }
static RString m_sClassName;
static RString m_sBaseClassName;
// Get userdata from the Lua stack and return a pointer to T object.
static T *check( lua_State *L, int narg, bool bIsSelf = false )
{
if( !LuaBinding::CheckLuaObjectType(L, narg, m_sClassName.c_str()) )
{
if( bIsSelf )
luaL_typerror( L, narg, m_sClassName.c_str() );
else
LuaHelpers::TypeError( L, narg, m_sClassName.c_str() );
}
return get( L, narg );
}
static T *get( lua_State *L, int narg )
{
return (T *) GetPointerFromStack( L, m_sClassName, narg );
}
/* Push a table or userdata for the given object. This is called on the
* base class, so we pick up the instance of the base class, if any. */
static void PushObject( Lua *L, const RString &sDerivedClassName, T* p );
protected:
void AddMethod( const char *szName, int (*pFunc)(T *p, lua_State *L) )
{
RegType r = { szName, pFunc };
m_aMethods.push_back(r);
}
private:
// member function dispatcher
static int thunk( Lua *L )
{
// stack has userdata, followed by method args
T *obj = check( L, 1, true ); // get self
lua_remove(L, 1); // remove self so member function args start at index 1
// get member function from upvalue
binding_t *pFunc = (binding_t *) lua_touserdata( L, lua_upvalueindex(1) );
return pFunc( obj, L ); // call member function
}
std::vector<RegType> m_aMethods;
static int tostring_T( lua_State *L )
{
char buff[32];
const void *pData = check( L, 1 );
snprintf( buff, sizeof(buff), "%p", pData );
lua_pushfstring( L, "%s (%s)", m_sClassName.c_str(), buff );
return 1;
}
};
/*
* Instanced classes have an associated table, which is used as "self"
* instead of a raw userdata. This should be as lightweight as possible.
*/
#include "LuaReference.h"
class LuaClass: public LuaTable
{
public:
LuaClass() { }
LuaClass( const LuaClass &cpy );
virtual ~LuaClass();
LuaClass &operator=( const LuaClass &cpy );
};
/* Only a base class has to indicate that it's instanced (has a per-object
* Lua table). Derived classes simply call the base class's Push function,
* specifying a different class name, so they don't need to know about it. */
#define LUA_REGISTER_INSTANCED_BASE_CLASS( T ) \
template<> void Luna<T>::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { p->m_pLuaInstance->PushSelf( L ); LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \
LUA_REGISTER_CLASS_BASIC( T, T )
#define LUA_REGISTER_CLASS( T ) \
template<> void Luna<T>::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { void **pData = (void **) lua_newuserdata( L, sizeof(void *) ); *pData = p; LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \
LUA_REGISTER_CLASS_BASIC( T, T )
#define LUA_REGISTER_DERIVED_CLASS( T, B ) \
template<> void Luna<T>::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { Luna<B>::PushObject( L, sDerivedClassName, p ); } \
LUA_REGISTER_CLASS_BASIC( T, B )
#define LUA_REGISTER_CLASS_BASIC( T, B ) \
template<> RString Luna<T>::m_sClassName = #T; \
template<> RString Luna<T>::m_sBaseClassName = #B; \
void T::PushSelf( lua_State *L ) { Luna<B>::PushObject( L, Luna<T>::m_sClassName, this ); } \
static Luna##T registera##T; \
/* Call PushSelf, so we always call the derived Luna<T>::Push. */ \
namespace LuaHelpers { template<> void Push<T*>( lua_State *L, T *const &pObject ) { if( pObject == nullptr ) lua_pushnil(L); else pObject->PushSelf( L ); } }
#define DEFINE_METHOD( method_name, expr ) \
static int method_name( T* p, lua_State *L ) { LuaHelpers::Push( L, p->expr ); return 1; }
#define COMMON_RETURN_SELF p->PushSelf(L); return 1;
#define GET_SET_BOOL_METHOD(method_name, bool_name) \
static int get_##method_name(T* p, lua_State* L) \
{ \
lua_pushboolean(L, p->bool_name); \
return 1; \
} \
static int set_##method_name(T* p, lua_State* L) \
{ \
p->bool_name= lua_toboolean(L, 1); \
COMMON_RETURN_SELF; \
}
#define GETTER_SETTER_BOOL_METHOD(bool_name) \
static int get_##bool_name(T* p, lua_State* L) \
{ \
lua_pushboolean(L, p->get_##bool_name()); \
return 1; \
} \
static int set_##bool_name(T* p, lua_State* L) \
{ \
p->set_##bool_name(lua_toboolean(L, 1)); \
COMMON_RETURN_SELF; \
}
#define GET_SET_FLOAT_METHOD(method_name, float_name) \
static int get_##method_name(T* p, lua_State* L) \
{ \
lua_pushnumber(L, p->float_name); \
return 1; \
} \
static int set_##method_name(T* p, lua_State* L) \
{ \
p->float_name= FArg(1); \
COMMON_RETURN_SELF; \
}
#define GETTER_SETTER_FLOAT_METHOD(float_name) \
static int get_##float_name(T* p, lua_State* L) \
{ \
lua_pushnumber(L, p->get_##float_name()); \
return 1; \
} \
static int set_##float_name(T* p, lua_State* L) \
{ \
p->set_##float_name(FArg(1)); \
COMMON_RETURN_SELF; \
}
#define ADD_METHOD( method_name ) \
AddMethod( #method_name, method_name )
#define ADD_GET_SET_METHODS(method_name) \
ADD_METHOD(get_##method_name); ADD_METHOD(set_##method_name);
#define LUA_REGISTER_NAMESPACE( T ) \
static void Register##T( lua_State *L ) { luaL_register( L, #T, T##Table ); lua_pop( L, 1 ); } \
REGISTER_WITH_LUA_FUNCTION( Register##T )
#define LIST_METHOD( method_name ) \
{ #method_name, method_name }
// Explicitly separates the stack into args and return values.
// This way, the stack can safely be used to store the previous values.
void DefaultNilArgs(lua_State* L, int n);
float FArgGTEZero(lua_State* L, int index);
#endif
/*
* (c) 2001-2005 Peter Shook, Chris Danford, Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/