Created
July 15, 2015 13:08
-
-
Save SammyJames/b71e91de5bd089e2f2b0 to your computer and use it in GitHub Desktop.
Some Luna improvements!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015 Sammy James | |
#pragma once | |
#include <lua.hpp> | |
#include <utility> | |
#include <functional> | |
#include <list> | |
template< class T > | |
class TLuaClass | |
{ | |
public: | |
struct PropertyType | |
{ | |
const char* Name; | |
int32 ( T::*Getter )( lua_State* ); | |
int32 ( T::*Setter )( lua_State* ); | |
}; | |
struct FunctionType | |
{ | |
const char* Name; | |
int32 Call( lua_State* L, void* Object ) const | |
{ | |
return InternalCall( L, Object ); | |
} | |
FunctionType( const char* InName ) | |
: Name( InName ) | |
{ | |
} | |
private: | |
virtual int32 InternalCall( lua_State* L, void* Object ) const { return 0; } | |
}; | |
template< typename > | |
struct TFunctionType : public FunctionType | |
{ | |
}; | |
template< typename Result, typename C, typename... Args > | |
struct TFunctionType< Result( C::* )( Args... ) > : public FunctionType | |
{ | |
typedef Result( C::*Type )( Args... ); | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{ | |
} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
TLuaFunction< Type >::ArgType Arguments = TLuaFunction< Type >::Unwind( L ); | |
Result RetVal = Apply( Object, Arguments, std::make_index_sequence< std::tuple_size< TLuaFunction< Type >::ArgType >::value >{} ); | |
TLuaPush< Result >::Push( L, RetVal ); | |
return 1; | |
} | |
template< typename Tup, size_t... Index > | |
Result Apply( void* Object, Tup&& Tuple, std::index_sequence< Index... > ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
return ( ClassObject->*(m_Method) )( std::get< Index >( Tuple )... ); | |
} | |
}; | |
template< typename C, typename... Args > | |
struct TFunctionType< void( C::* )( Args... ) > : public FunctionType | |
{ | |
typedef void( C::*Type )( Args... ); | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{ | |
} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
TLuaFunction< Type >::ArgType Arguments = TLuaFunction< Type >::Unwind( L ); | |
Apply( Object, Arguments, std::make_index_sequence< std::tuple_size< TLuaFunction< Type >::ArgType >::value >{} ); | |
return 0; | |
} | |
template< typename Tup, size_t... Index > | |
void Apply( void* Object, Tup&& Tuple, std::index_sequence< Index... > ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
( ClassObject->*( m_Method ) )( std::get< Index >( Tuple )... ); | |
} | |
}; | |
template< typename Result, typename C > | |
struct TFunctionType< Result( C::* )( void ) > : public FunctionType | |
{ | |
typedef Result( C::*Type )( void ); | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{ | |
} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
Result RetVal = Apply( Object ); | |
TLuaPush< Result >::Push( L, RetVal ); | |
return 1; | |
} | |
Result Apply( void* Object ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
return ( ClassObject->*( m_Method ) )(); | |
} | |
}; | |
template< typename C > | |
struct TFunctionType< void( C::* )( void ) > : public FunctionType | |
{ | |
typedef void( C::*Type )( void ); | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
Apply( Object ); | |
return 0; | |
} | |
void Apply( void* Object ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
( ClassObject->*( m_Method ) )( ); | |
} | |
}; | |
template< typename Result, typename C, typename... Args > | |
struct TFunctionType< Result( C::* )( Args... ) const > : public FunctionType | |
{ | |
typedef Result( C::*Type )( Args... ) const; | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{ | |
} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
TLuaFunction< Type >::ArgType Arguments = TLuaFunction< Type >::Unwind( L ); | |
Result RetVal = Apply( Object, Arguments, std::make_index_sequence< std::tuple_size< TLuaFunction< Type >::ArgType >::value >{} ); | |
TLuaPush< Result >::Push( L, RetVal ); | |
return 1; | |
} | |
template< typename Tup, size_t... Index > | |
Result Apply( void* Object, Tup&& Tuple, std::index_sequence< Index... > ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
return ( ClassObject->*( m_Method ) )( std::get< Index >( Tuple )... ); | |
} | |
}; | |
template< typename C, typename... Args > | |
struct TFunctionType< void( C::* )( Args... ) const > : public FunctionType | |
{ | |
typedef void( C::*Type )( Args... ) const; | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
TLuaFunction< Type >::ArgType Arguments = TLuaFunction< Type >::Unwind( L ); | |
Apply( Object, Arguments, std::make_index_sequence< std::tuple_size< TLuaFunction< Type >::ArgType >::value >{} ); | |
return 0; | |
} | |
template< typename Tup, size_t... Index > | |
void Apply( void* Object, Tup&& Tuple, std::index_sequence< Index... > ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
( ClassObject->*( m_Method ) )( std::get< Index >( Tuple )... ); | |
} | |
}; | |
template< typename Result, typename C > | |
struct TFunctionType< Result( C::* )( void ) const > : public FunctionType | |
{ | |
typedef Result( C::*Type )( void ) const; | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
Result RetVal = Apply( Object ); | |
TLuaPush< Result >::Push( L, RetVal ); | |
return 1; | |
} | |
Result Apply( void* Object ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
return ( ClassObject->*( m_Method ) )( ); | |
} | |
}; | |
template< typename C > | |
struct TFunctionType< void( C::* )( void ) const > : public FunctionType | |
{ | |
typedef void( C::*Type )( void ) const; | |
TFunctionType( const char* Name, Type Method ) | |
: FunctionType( Name ) | |
, m_Method( Method ) | |
{} | |
private: | |
Type m_Method; | |
virtual int32 InternalCall( lua_State* L, void* Object ) const override | |
{ | |
Apply( Object ); | |
return 0; | |
} | |
void Apply( void* Object ) const | |
{ | |
C* ClassObject = static_cast<C*>( Object ); | |
( ClassObject->*( m_Method ) )( ); | |
} | |
}; | |
static void Register( lua_State* L, const char* Namespace = nullptr ) | |
{ | |
if ( Namespace && strlen( Namespace ) ) | |
{ | |
lua_getglobal( L, Namespace ); | |
if ( lua_isnil( L, -1 ) ) | |
{ | |
lua_newtable( L ); | |
lua_pushvalue( L, -1 ); | |
lua_setglobal( L, Namespace ); | |
} | |
lua_pushcfunction( L, &TLuaClass< T >::New ); | |
lua_setfield( L, -2, T::GetClassName() ); | |
lua_pop( L, 1 ); | |
} | |
else | |
{ | |
lua_pushcfunction( L, &TLuaClass< T >::New ); | |
lua_setglobal( L, T::GetClassName() ); | |
} | |
luaL_newmetatable( L, T::GetClassName() ); | |
const int32 Metatable = lua_gettop( L ); | |
lua_pushstring( L, "__gc" ); | |
lua_pushcfunction( L, &TLuaClass< T >::Collect ); | |
lua_settable( L, Metatable ); | |
lua_pushstring( L, "__tostring" ); | |
lua_pushcfunction( L, &TLuaClass< T >::ToString ); | |
lua_settable( L, Metatable ); | |
lua_pushstring( L, "__eq" ); | |
lua_pushcfunction( L, &TLuaClass< T >::Equals ); | |
lua_settable( L, Metatable ); | |
lua_pushstring( L, "__index" ); | |
lua_pushcfunction( L, &TLuaClass< T >::Get ); | |
lua_settable( L, Metatable ); | |
lua_pushstring( L, "__newindex" ); | |
lua_pushcfunction( L, &TLuaClass< T >::Set ); | |
lua_settable( L, Metatable ); | |
const PropertyType* Properties = T::GetProperties(); | |
for ( int32 i = 0; Properties[ i ].Name; ++i ) | |
{ | |
lua_pushstring( L, Properties[ i ].Name ); | |
lua_pushinteger( L, i ); | |
lua_settable( L, Metatable ); | |
} | |
const FunctionType** Functions = T::GetFunctions(); | |
for ( int32 i = 0; Functions[ i ]->Name; ++i ) | |
{ | |
lua_pushstring( L, Functions[ i ]->Name ); | |
lua_pushinteger( L, i | ( 1 << 8 ) ); | |
lua_settable( L, Metatable ); | |
} | |
} | |
static int32 New( lua_State* L ) | |
{ | |
T* Result = new T(); | |
T** Light = static_cast<T**>( lua_newuserdata( L, sizeof( T* ) ) ); | |
*Light = Result; | |
luaL_getmetatable( L, T::GetClassName() ); | |
lua_setmetatable( L, -2 ); | |
return 1; | |
} | |
static int32 Get( lua_State* L ) | |
{ | |
lua_getmetatable( L, 1 ); | |
lua_pushvalue( L, 2 ); | |
lua_rawget( L, -2 ); | |
if ( lua_isinteger( L, -1 ) ) | |
{ | |
const int32 Index = (int32)lua_tointeger( L, -1 ); | |
T** Object = static_cast<T**>( lua_touserdata( L, 1 ) ); | |
lua_pushvalue( L, 3 ); | |
if ( Index & ( 1 << 8 ) ) // function | |
{ | |
lua_pushinteger( L, Index ^ ( 1 << 8 ) ); | |
lua_pushlightuserdata( L, Object ); | |
lua_pushcclosure( L, &TLuaClass< T >::Apply, 2 ); | |
return 1; | |
} | |
lua_pop( L, 2 ); | |
lua_remove( L, 1 ); | |
lua_remove( L, 1 ); | |
return ( ( *Object )->*( T::GetProperties()[ Index ].Getter ) )( L ); | |
} | |
return 1; | |
} | |
static int32 Set( lua_State* L ) | |
{ | |
lua_getmetatable( L, 1 ); | |
lua_pushvalue( L, 2 ); | |
lua_rawget( L, -2 ); | |
if ( lua_isinteger( L, -1 ) ) | |
{ | |
const int32 Index = (int32)lua_tointeger( L, -1 ); | |
T** Object = static_cast<T**>( lua_touserdata( L, 1 ) ); | |
if ( !Object || !*Object ) | |
{ | |
luaL_error( L, "Error: No object provided to setter." ); | |
return 0; | |
} | |
if ( Index >> 8 ) // function | |
{ | |
char buffer[ 128 ]; | |
sprintf_s( buffer, "Error: Trying to set the method [%s] of class [%s].", ( *Object )->T::GetFunctions()[ Index ^ ( 1 << 8 ) ]->Name, T::GetClassName() ); | |
luaL_error( L, buffer ); | |
return 0; | |
} | |
lua_pop( L, 2 ); | |
lua_remove( L, 1 ); | |
lua_remove( L, 1 ); | |
return ( ( *Object )->*( T::GetProperties()[ Index ].Setter ) )( L ); | |
} | |
return 0; | |
} | |
static int32 Apply( lua_State* L ) | |
{ | |
const int32 Index = (int32)lua_tointeger( L, lua_upvalueindex( 1 ) ); | |
T** Object = static_cast<T**>( lua_touserdata( L, lua_upvalueindex( 2 ) ) ); | |
const TLuaClass< T >::FunctionType** Functions = T::GetFunctions(); | |
const TLuaClass< T >::FunctionType* Func = Functions[ Index ]; | |
return Func->Call( L, *Object ); | |
} | |
static int32 Collect( lua_State* L ) | |
{ | |
T** Object = static_cast<T**>( lua_touserdata( L, -1 ) ); | |
if ( Object && *Object ) | |
{ | |
delete *Object; | |
} | |
return 0; | |
} | |
static int32 ToString( lua_State* L ) | |
{ | |
T** Object = static_cast<T**>( lua_touserdata( L, -1 ) ); | |
if ( Object ) | |
{ | |
lua_pushfstring( L, "%s (%p)", T::GetClassName(), (void*)*Object ); | |
} | |
else | |
{ | |
lua_pushstring( L, "Empty" ); | |
} | |
return 1; | |
} | |
static int32 Equals( lua_State* L ) | |
{ | |
T** Left = static_cast<T**>( lua_touserdata( L, -1 ) ); | |
T** Right = static_cast<T**>( lua_touserdata( L, 1 ) ); | |
lua_pushboolean( L, *Left == *Right ); | |
return 1; | |
} | |
}; | |
template< typename T > | |
struct TLuaFunction | |
{ | |
public: | |
typedef typename TFunctionArgs< T >::Type ArgType; | |
typedef typename TFunctionResult< T >::Type ResultType; | |
static ArgType Unwind( lua_State* L ) | |
{ | |
return Helper( L, std::make_index_sequence< std::tuple_size< ArgType >::value >{} ); | |
} | |
private: | |
template< typename U > | |
static std::tuple< U > Work( int32& Index, lua_State* L ) | |
{ | |
return std::make_tuple( TLuaRead< U >::Read( L, Index++ ) ); | |
} | |
template< typename U1, typename U2, typename... US > | |
static std::tuple< U1, U2, US... > Work( int32& Index, lua_State* L ) | |
{ | |
std::tuple< U1 > Start = Work< U1 >( Index, L ); | |
return std::tuple_cat( Start, Work< U2, US... >( Index, L ) ); | |
} | |
template< size_t... Index > | |
static ArgType Helper( lua_State* L, std::index_sequence< Index... > ) | |
{ | |
int32 IndexHelper = 1; | |
return Work< std::tuple_element_t< Index, ArgType >... >( IndexHelper, L ); | |
} | |
}; | |
#define DECLARE_LUA_CLASS( ClassType ) \ | |
private: \ | |
using TProperty = TLuaClass< class ClassType >::PropertyType; \ | |
using TFunction = TLuaClass< class ClassType >::FunctionType; \ | |
static const TProperty Properties[]; \ | |
static const TFunction* Functions[]; \ | |
public: \ | |
static const char* GetClassName() { return #ClassType; } \ | |
static const TProperty* GetProperties() { return Properties; } \ | |
static const TFunction** GetFunctions() { return Functions; } \ | |
private: | |
#define INITIALIZE_LUA_PROPERTIES( ClassType ) const ClassType::TProperty ClassType::Properties[] = { { nullptr } }; | |
#define INITIALIZE_LUA_FUNCTIONS( ClassType ) const ClassType::TFunction* ClassType::Functions[] = { | |
#define FINALIZE_LUA_FUNCTIONS( ClassType ) new ClassType::TFunction{ nullptr } }; | |
#define DECLARE_LUA_FUNCTION( ClassType, Function ) new TLuaClass< ClassType >::TFunctionType< decltype( &ClassType::Function ) >{ #Function, &ClassType::Function } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment