Skip to content

Instantly share code, notes, and snippets.

@SammyJames
Created July 15, 2015 13:08
Show Gist options
  • Save SammyJames/b71e91de5bd089e2f2b0 to your computer and use it in GitHub Desktop.
Save SammyJames/b71e91de5bd089e2f2b0 to your computer and use it in GitHub Desktop.
Some Luna improvements!
// 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