一、互调原理
C++接入Lua,免不了互相调用的问题。互调的本质,其实就是数据传送。而这两种语言之间的数据类型是千差万别,而且内存管理机制也完全不同,C++有栈、堆内存,而Lua则是自动回收机制,如何保证数据传送呢?Lua虚拟机提供了一个栈结构,C++和Lua都可以往这个栈里压栈数据,栈负责数据类型的转换,同时保证压栈的数据在弹栈之前一直有效。
C调用Lua
// **********************************
// C++ 执行一个Lua(全局)函数
// **********************************
#include "lua.h"
const char* LUA_CODE = NULL;
void main()
{
lua_State* state = lua_newstate(); // lua全局环境表
luaL_dostring(state, LUA_CODE); // 执行一段lua代码,定义了一个globalFunc
lua_getglobal(state, "globalFunc"); // 将全局变量globalFunc的值压栈
lua_pushstring(state, "motherfucker"); // C++这边将一个char*字符串压栈
int errCode = lua_call(state, 1, 0); // 执行lua代码;globalFunc('motherfucker')
}
// 在lua的全局环境中定义一个全局函数globalFunc
LUA_CODE = " \
function globalFunc( str ) \
print(str) \
end \
";
Lua调用C
// ****************************************
// C++提供一个shit函数供Lua调用
// ****************************************
#include "lua.h"
// ***************************************************
// shit是提供给Lua调用的C函数,signature比如如下:
// 1、必须返回int,返回值的意义是指执行这个C函数之后的压栈结果数,可以认为是返回值个数
// Lua是要根据这个值来从栈中弹栈返回值的,如果数值错误,将导致栈状态被破坏。
// 2、参数必须是有且仅有一个luaState*,表示C与Lua通过这个获取栈。
// ***************************************************
int shit(lua_State* state)
{
// 当执行这里的时候,lua栈已经压栈好了lua传来的数据,C这边只需要弹栈即可获取参数
int param1 = static_cast<int>(lua_tointeger(state, -2)); // 栈顶-1参数,lua传来的第一个int
int param2 = static_cast<int>(lua_tointeger(state, -1)); // 栈顶参数,lua出来的第二个int
lua_pop(state, 2); // 弹栈栈顶两个参数
// 将两个值相加,然后压栈,就是给lua的返回值
lua_pushinteger(state, static_cast<LUA_INTEGER>( param1 + param2));
return 1; // 告诉lua,我执行完了之后,栈里面剩一个返回值,你可以去取。
}
void main()
{
lua_State* state = lua_newstate(); // lua全局环境表
lua_pushcfunction(state, shit);
lua_setglobal(state, "globalshit"); // lua代码:globalshit = shit(C函数)
}
// ****************************************
// Lua代码
// ****************************************
local fuck = globalshit
// 虽然lua可以像下面这样随意调用fuck,但是一定要根据C函数shit的定义逻辑看,
// 可以接受哪些参数。
int result = fuck(1) // 报错
int result = fuck(2,3) // 运行正常,result = 5
int result = fuck(3,3,4) // 报错
int result = fuck("3","3","4") // 报错
二、太麻烦了
我们可以看到,如果直接用lua本身提供的库函数,那么互调成本和门槛会非常高,写的代码很多,很容易出现压栈弹栈错误(这会破坏栈数据),同时还要对错误情况进行处理,上面的代码完全没有考虑参数不合法或者数量不对的情况。
因此我们完全有这个必要,对Lua/C/C++的互调进行封装,幸运的是,已经有前辈做了这个工作,并且一直在维护。
三、LuaBridge
LuaBridge是一个轻量级且无依赖的库,用于在C ++和Lua之间来回映射数据、函数和类。
LuaBridge支持在Lua5.1.0及以后的版本中使用,也兼容LuaJIT。
- GitHub
- 使用手册
LuaBridge源码全部都是header files,#include就可以引入项目中,没有任何其他第三方项目依赖,不需要进行配置,使用也非常简单方便,遵循MIT许可,无限制使用。
1、namespace
namespace并不是指C++的namespace,其实就是Lua的table,设计目的就是为了逻辑分组,避免全局变量名污染。
lua_State *L = stack->getLuaState();
luabridge::getGlobalNamespace( L ) // 获取_G全局表
.beginNamespace( "test" ) // 在_G打开或者创建test表
.beginNamespace( "detail" ) // 打开或者创建test.detail表
.endNamespace() // 关闭,表示不往里加元素了
.beginNamespace( "utility" ) // 打开或者创建test.utility
.endNamespace()
.beginNamespace( "utility" ) // 添加相同的
.endNamespace()
.endNamespace();
给namespace添加元素:
int globalVar;
static float staticVar;
std::string stringProperty;
std::string getString( ) { return stringProperty; }
void setString( std::string s ) { stringProperty = s; }
int foo ( ) { return 42; }
void bar ( char const * ) { }
int cFunc ( lua_State *L ) { return 0; }
void fuck( lua_State *L )
{
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" ) // test -- a namespace
// 给test表添加属性
.addProperty( "var1", &globalVar ) // test.var1 -- a lua_Number property
.addProperty( "var2", &staticVar, false ) // test.var2 -- a read - only lua_Number property
.addProperty( "prop1", getString, setString ) // test.prop1 -- a lua_String property
.addProperty( "prop2", getString ) // test.prop2 -- a read - only lua_String property
// 给test表添加C函数,这个C函数的格式不再有什么限制,随意返回值,随意的参数个数,参数类型(自定义类型必须注册给Lua)
// 如果C函数需要访问lua_State,只需要确保这个lua_State是最后一个参数就可以了,方便的很。
.addFunction( "foo", foo ) // test.foo -- a function returning a lua_Number
.addFunction( "bar", bar ) // test.bar -- a function taking a lua_String as a parameter
.addFunction( "cfunc", cFunc ) // test.cfunc -- a function with a variable argument list and multi - return
.endNamespace();
// test.var1 = 5 -- okay
// test.var2 = 6 -- error: var2 is not writable
// test.prop1 = "Hello" -- okay
// test.prop1 = 68 -- okay, Lua converts the number to a string
// test.prop2 = "bar" -- error : prop2 is not writable
// test.foo() -- calls foo and discards the return value
// test.var1 = foo() -- calls foo and stores the result in var1
// test.bar( "Employee" ) -- calls bar with a string
// test.bar( test ) -- error : bar expects a string not a table
}
2、Lua调用C++(注册C++类)
void fuck( lua_State *L )
{
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" )
// 创建或者打开一个注册给Lua的C类,"A"表示在Lua中的类名,下次还可打开来加成员
.beginClass<A>( "A" )
// 添加静态属性
.addStaticProperty( "staticData", &A::staticData )
.addStaticProperty( "staticProperty", &A::getStaticProperty, &A::setStaticProperty )
// 添加静态函数
.addStaticFunction( "staticFunc", &A::staticFunc )
.addStaticFunction( "staticCFunc", &A::staticCFunc )
// 添加成员属性
.addProperty( "data", &A::dataMember, false ) // read-only,lua中赋值会报错
.addProperty( "prop", &A::getProperty, &A::setProperty )
// 添加成员函数
.addFunction( "func1", &A::func1 )
.addFunction( "virtualFunc", &A::virtualFunc )
.addFunction( "__tostring", &A::toString ) // 元方法,可注册所有元方法(除__gc),自动为每个类注册析构函数
.addFunction( "cfunc", &A::cfunc )
.endClass()
// 创建一个注册给Lua的类B,它继承自A,注意这是创建,只能调用一次,下次要打开加成员,就用beginClass
// 父类的方法已经在父类注册,这里无需再注册子类的方法
// 如果A没有注册到Lua中,就不需要用derivedClass,用beginClass即可。
.deriveClass<B, A>( "B" )
.addProperty( "data", &B::dataMember2 )
.addFunction( "func1", &B::func1 )
.addFunction( "func2", &B::func2 )
.endClass()
.endNamespace();
// local a = A()
// a.func1() -- error: func1 expects an object of a registered class
// a.func1(a) -- okay, verbose, this how OOP works in Lua
// a:func1() -- okay, less verbose, equvalent to the previous
}
struct A
{
static int staticData;
static float staticProperty;
std::string dataMember;
char dataProperty;
static float getStaticProperty( ) { return staticProperty; }
static void setStaticProperty( float f ) { staticProperty = f; }
static void staticFunc ( ) { }
static int staticCFunc ( lua_State *L ) { return 0; }
char getProperty ( ) const{ return dataProperty; }
void setProperty ( char v ) { dataProperty = v; }
std::string toString ( ) const{ return dataMember; }
void func1 ( ) { }
virtual void virtualFunc ( ) { }
int cfunc ( lua_State *L ) { return 0; }
};
struct B : public A
{
double dataMember2;
void func1 (){}
void func2 (){}
void virtualFunc(){}
};
int A::staticData;
float A::staticProperty;
属性成员代理
对应英文单词property,就是有get/set函数那种。
有时候需要注册的类不是自定义的,而是第三方的,这个类很有可能无法满足注册到Lua的要求,比如没有get/set函数,或者get/set没有正确的函数签名,我们又很可能无法或者不方便修改这个类,这时候可以用一个proxy包装一下。
// 这是个第三方的类,不可修改
struct Vec
{
float coord[3];
};
// 设计一个Proxy类,封装一下Vec
struct VecHelper
{
// get函数,第一个参数必须是被封装的类对象的指针
template <unsigned index>
static float get( Vec const *vec ){ return vec->coord[index]; }
// set函数,第一个参数必须是被封装的类对象的指针
template <unsigned index>
static void set( Vec *vec, float value ){ vec->coord[index] = value; }
};
void fuck( lua_State *L )
{
// 方法一:
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" )
.beginClass<Vec>( "Vec" )
.addProperty( "x", &VecHelper::get<0>, &VecHelper::set<0> )
.addProperty( "y", &VecHelper::get<1>, &VecHelper::set<1> )
.addProperty( "z", &VecHelper::get<2>, &VecHelper::set<2> )
.endClass()
.endNamespace();
// 方法二:通过function
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" )
.beginClass <Vec>( "Vec" )
.addProperty( "x",
std::function <float( const Vec* )>(
[]( const Vec* vec ) {return vec->coord[0]; }
),
std::function <void( Vec*, float )>(
[]( Vec* vec, float v ) {vec->coord[0] = v; }
)
)
// ... same for "y" and "z"
.endClass()
.endNamespace();
}
函数成员代理
同上面理,只是换成了addFunction了。
// 这是个第三方的类,不可修改
struct Vec
{
float coord[3];
};
// 直接用
void scale(Vec* vec, float value )
{
vec->coord[0] *= value;
vec->coord[1] *= value;
vec->coord[2] *= value;
};
void fuck( lua_State *L )
{
// 方法一:
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" )
.beginClass<Vec>( "Vec" )
.addFunction( "scale", &scale )
.endClass()
.endNamespace();
// 方法二:通过function
luabridge::getGlobalNamespace( L )
.beginClass<Vec>( "Vec" )
.addFunction( "scaleX",
std::function <void( Vec*, float )>(
[]( Vec* vec, float v ) {vec->coord[0] *= v; }
)
)
.endClass();
}
构造函数
可以使用addConstructor为类添加单个构造函数。LuaBridge无法像函数和方法那样自动确定构造函数参数的数量和类型,因此必须提供它们。 这是通过将所需构造函数的签名指定为addConstructor的第一个模板参数来完成的。 将从中提取参数类型(忽略返回类型)。
struct A
{
A();
};
struct B
{
explicit B( char const* s, int nChars );
};
void fuck( lua_State *L )
{
// 方法一:
luabridge::getGlobalNamespace( L )
.beginNamespace( "test" )
// LuaBridge无法自动检测类构造函数的signature,因此我们需要手动传function signature
.beginClass<A>( "A" )
.addConstructor<void( *) ( void )>() // 提供给A构造函数的函数签名
.endClass()
.beginClass<B>( "B" )
.addConstructor<void( *) ( char const*, int )>() // 提供给B的构造函数的函数签名
.endClass()
.endNamespace();
// a = test.A() -- Create a new A.
// b = test.B("hello", 5) -- Create a new B.
// b = test.B() -- Error: expected string in argument 1
}
3、C++调用Lua(LuaRef)
LuaRef类
把LuaRef理解成是C++中和Lua中的变量对等就可以。
LuaRef对象可表示所有Lua数据类型以及注册过来的C类型(类、函数、数据)。
LuaRef对象中存储的值与Lua中的变量遵循相同的规则:表,函数,线程和完整的userdata值是对象。 LuaRef实际上并不包含这些值,仅包含对它们的引用。 赋值,参数传递和函数返回总是操纵对此类值的引用; 这些操作并不意味着任何形式的复制。
LuaRef v( L ); // References nil
LuaRef v1( L, 1 ); // A LUA_TNUMBER
LuaRef v2( L, 1.1 ); // Also a LUA_TNUMBER
LuaRef v3( L, true ); // A LUA_TBOOLEAN
LuaRef v4( L, "string" ); // A LUA_TSTRING
LuaRef v1 = newTable( L ); // Create a new table
LuaRef v2 = getGlobal( L, "print" ); // Reference to _G ["print"]
......; // A注册给了Lua
LuaRef v( L, new A ); // A LuaBridge userdata holding a pointer to A
v = newTable( L ); // An empty table
v = "string"; // A string. The prevous value becomes eligible for garbage collection.
LuaRef v1( L, "x" ); // assign "x"
LuaRef v2( L, "y" ); // assign "y"
v2 = v1; // v2 becomes "x"
v1 = "z"; // v1 becomes "z", v2 is unchanged
v1 = newTable( L ); // An empty table
v2 = v1; // v2 references the same table as v1
v1 = Nil(); // v1 becomes nil, table is still referenced by v2.
类型转换
void passInt( int );
void passBool( bool );
void passString( std::string );
void passObject( A * );
LuaRef v( L );
//...
passInt( v ); // implicit conversion to int
passBool( v ); // implicit conversion to bool
passString( v ); // implicit conversion to string
passObject( v ); // must hold a registered LuaBridge class or a lua_error() will be called.
// The following are all equivalent:
passString(std::string (v));
passString((std::string)v);
passString(static_cast <std::string> (v));
passString(v.cast<std::string>());
表代理(Table Proxies)
由于表是Lua中唯一的数据结构化机制,因此LuaRef类使用简单,精确的语法为访问和操作表元素提供了强大的功能。 任何可转换类型都可以用作键或值。 将数组索引运算符[]应用于LuaRef会返回一个特殊的临时对象,称为表代理,该对象支持可以在LuaRef上执行的所有操作。 此外,对表代理进行的分配会更改基础表。 由于表代理是编译器创建的临时对象,因此您不能直接使用它们。 LuaBridge表代理不应与Lua中的代理表相混淆。 LuaBridge表代理只是一个中间的C ++类对象,它在幕后工作以使表操作语法符合C ++习惯用法。
LuaRef v = newTable( L );
v["name"] = "John Doe"; // string key, string value
v[1] = 200; // integer key, integer value
v[2] = newTable( L ); // integer key, LuaRef value
v[3] = v[1]; // assign 200 to integer index 3
v[1] = 100; // v[1] is 100, v[3] is still 200
v[3] = v[2]; // v[2] and v[3] reference the same table
v[2] = Nil(); // Removes the value with key = 2. The table is still referenced by v[3].
// t = {}
// t[1] = function() print( "hello" ) end
// t[2] = function( u, v ) print( u, v ) end
// t[3] = "foo"
LuaRef t = getGlobal( L, "t" );
t[1]();
t[2]( "a", "b" );
t[2]( t[1] ); // Call t[3] with the value in t[2]
t[4] = t[3](); // Call t[3] and store the result in t[4].
t[t[5]()] = "wow"; // Store "wow" at the key returned by the call to t[5]
// function fail()
// error( "A problem occurred" )
// end
LuaRef f = getGlobal( L, "fail" );
try {
f();
}
catch( LuaException const &e ) {
std::cerr &&e.what();
}
调用Lua
表代理和LuaRef对象为在适当的引用对象上调用lua_pcall提供了一种方便的语法。 这包括具有适当的__call元方法集的C函数,Lua函数或Lua对象。 提供的实现最多支持八个参数(尽管可以通过添加新功能来支持更多参数)。 任何可转换的C ++类型都可以以其native格式作为参数传递。 函数调用的返回值以LuaRef的形式提供,可以为nil。
// function same( arg1, arg )
// return arg1 == arg2
// end
LuaRef same = getGlobal( L, "same" );
// These all evaluate to true
same( 1, 1 );
!same( 1, 2 );
same( "text", "text" );
!same( 1, "text" );
same( 1, 1, 2 ); // third param ignored
4、LuaStack栈操作
Lua和C通过Lua栈交换数据。LuaBridge利用类模板技术进行了栈操作的封装:
void fuck( lua_State *L )
{
A a("asdfasdfasdf");
Stack<bool> ::push( L, false );
Stack<char> ::push( L, 1 );
Stack<const char *>::push( L, "asdfasdf" );
Stack<std::string> ::push( L, "asdfasdfasdfasdf" );
Stack<int> ::push( L, 1 );
Stack<float> ::push( L, 1 );
Stack<double> ::push( L, 1 );
Stack<A> ::push( L, a ); // 压栈自定义类型,见下面定义
}
class A
{
public:
A( const std::string& str ) { m_strContent = str; }
std::string getContent() { return m_strContent; }
private:
std::string m_strContent;
};
template <>
struct Stack <A>
{
static void push ( lua_State *L, A a ) { lua_pushstring( L, a.getContent().c_str() ); }
static A get ( lua_State *L, int index ) { return A( luaL_checkstring( L, index ) ); }
static bool isInstance( lua_State *L, int index ) { return lua_type( L, index ) == LUA_TSTRING; }
};
5、对象生命周期控制
一个C++对象传递给Lua有以下几种情况:
- T Passed by value (a copy), with Lua lifetime.
- T const Passed by value (a copy), with Lua lifetime.
- T Passed by reference, with *C++ lifetime.
- T& Passed by reference, with C++ lifetime.
- T const Passed by const reference, with *C++ lifetime.
- T const& Passed by const reference, with C++ lifetime.
只有值传递,才由Lua控制对象的生命周期,这很好理解,值传递已经是拷贝了一份给Lua了,C++这边的对象销毁了也没什么影响了。当Lua这边对这个对象进行回收的时候,将触发析构函数,也有可能C++又对这个对象有引用,那C++这边就要注意它的生命周期了。
当Lua是通过注册的构造函数创建的对象,那么这个对象是Lua lifetime。
当是其他情况的时候,将一直由C++控制对象生命周期,Lua使用这个对象时要确保C++这边这个对象还没有被销毁,Lua这边回收这个对象的时候,其实不是回收对象本身,仅仅是对这个对象的引用,并不会触发对象本身的析构。
A a;
B b;
push( L, &a ); // pointer to 'a', C++ lifetime
lua_setglobal( L, "a" );
push( L, ( A const * ) &a ); // pointer to 'a const', C++ lifetime
lua_setglobal( L, "ac" );
push( L, const_cast<A const *>( &a ) ); // equivalent to push (L, (A const*) &a)
lua_setglobal( L, "ac2" );
push( L, new A ); // compiles, but will leak memory
lua_setglobal( L, "ap" );
push (L, b); // Copy of b passed, Lua lifetime.
lua_setglobal (L, "b");
a = nil; collectgarbage () -- 'a' still exists in C++.
b = nil; collectgarbage () -- Lua calls ~B() on the copy of b.
参数传递
// B 继承自 A
void func0( A a );
void func1( A * a );
void func2( A const * a );
void func3( A & a );
void func4( A const & a );
// func0( a ) -- Passes a copy of a, using A's copy constructor.
// func1( a ) -- Passes a pointer to a.
// func2( a ) -- Passes a pointer to a const a. 无法修改a的内容
// func3( a ) -- Passes a reference to a.
// func4( a ) -- Passes a reference to a const a. 无法修改a的内容
// func1( b ) -- 运行正常,B是A的子类,且A、B都注册给Lua了。
共同管理的对象(Shared Lifetime)
就是由C++和Lua共同控制对象的生命周期,只有当任何一方都不在引用对象,对象才会被销毁。
待完成。
四、cocos项目实践
1、C调用Lua
这个用LuaRef完美解决,使用起来非常方便。
https://www.yuque.com/tvvhealth/cs/cs7xf6#KEhG8
2、Lua调用C
- 如果要调用复杂类体系中的类,比如继承自Node的UI控件等。
- 选择LuaBinding,因为涉及到庞大的继承体系还有众多类成员,这需要进行大量的注册绑定,用LuaBridge显然不合适,而LuaBinding已经完成这部分工作。
- 相反,如果调用C中简单类体系中的类,比如没有父类,或者父类比较简单。
- 选择LuaBridge,注册绑定工作非常简单而且可以动态改变(按需注册成员)
- 如果就需要注册函数、全局变量
- 选择LuaBridge,非常简单灵活,LuaBinding过于笨重,且不好维护。
总结起来,如果是要调用cocos内置类及其子类,则用LuaBinding;如果是客户端自定义的类、函数、变量,则推荐LuaBridge:
- 注册函数、变量给Lua
- 注册自定义类给Lua
- https://www.yuque.com/tvvhealth/cs/cs7xf6#IYv4O
五、API Reference(LuaBridge)
1、Free Functions
```cpp
- https://www.yuque.com/tvvhealth/cs/cs7xf6#IYv4O
/// Gets a global Lua variable reference. LuaRef getGlobal( lua_State L, const char name );
/// Sets a global Lua variable.
template
/// Gets the global namespace registration object. Namespace getGlobalNamespace( lua_State *L );
<a name="6xU11"></a>
## 2、Namespace Registration
```cpp
/// Begins or continues class registration, returns this class object.
template<class T>
Class<T> beginClass( const char *name );
/// Begins derived class registration, returns this class object.
template<class T, class Base>
Class<T> deriveClass( const char *name );
/// Begin or continues namespace registration, returns this namespace object.
template<class T>
Namespace beginNamespace( const char *name );
/// Ends namespace registration, returns the parent namespace object.
template<class T>
Namespace endNamespace();
/// Registers a function.
template<class R, class... Params >>
Namespace addFunction( const char *name,
R( *fn )( Params... ) );
/// Registers a function.
template<class R, class... Params >>
Namespace addFunction( const char *name,
std::function<R( Params... )> fn );
/// Registers a function with an extra Lua state parameter.
template<class R, class... Params >>
Namespace addFunction( const char *name,
R( *fn )( Params..., lua_State * ) );
/// Registers a C-function.
Namespace addFunction( const char *name,
int( *fn )( lua_State * ) );
/// Registers a property with a getter and setter.
template<class V>
Namespace addProperty( const char *name,
V( *getFn )( ),
void( *setFn )( V ) );
/// Registers a property with a getter and setter.
template<class V>
Namespace addProperty( const char *name,
std::function<V()> getFn,
std::function<void( V )> setFn );
/// Registers a property with a C-function getter and setter.
Namespace addProperty( const char *name,
int( *getFn )( lua_State * ),
int( *setFn )( lua_State * ) );
/// Registers a read-only property with a getter function.
template<class V>
Namespace addProperty( const char *name,
V( *getFn )( ) );
/// Registers a read-only property with a getter function.
template<class V>
Namespace addProperty( const char *name,
std::function<V()> getFn );
/// Registers a read-only property with a C-function getter.
Namespace addProperty( const char *name,
int( *getFn )( lua_State * ) );
/// Registers a variable, writable or read-only.
template<class V>
Namespace addVariable( const char *name,
V *varPtr,
bool isWritable = true );
3、Class Registration
/// Open a new or existing class for registrations.
template<class T>
Class<T> beginClass( char const *name );
/// Derive a new class for registrations.
/// Call deriveClass() only once.
/// To continue registrations for the class later, use beginClass().
template<class Derived, class Base>
Class<Derived> deriveClass( char const *name );
/// Ends class registration, returns the parent namespace object.
template<class T>
Namespace endClass();
Member Function Registration
/// Registers a member function.
template<class R, class... Params >>
Namespace addFunction( const char *name, R( T::* fn )( Params... ) );
/// Registers a function.
template<class R, class... Params >>
Namespace addFunction( const char *name, std::function<R( Params... )> fn );
/// Registers a function with an extra Lua state parameter.
template<class R, class... Params >>
Namespace addFunction( const char *name, R( T::* fn )( Params..., lua_State * ) );
/// Registers a C-function.
Namespace addFunction( const char *name, int( *fn )( lua_State * ) );
Member Property Registration
/// Registers a property with a getter and setter.
template<class V>
Namespace addProperty( const char *name,
V( T::* getFn )( ),
void ( T::* setFn )( V ) );
/// Registers a property with a getter and setter.
template<class V>
Namespace addProperty( const char *name,
std::function<V()> getFn,
std::function<void( V )> setFn );
/// Registers a property with a C-function getter and setter.
Namespace addProperty( const char *name,
int( *getFn )( lua_State * ),
int( *setFn )( lua_State * ) );
/// Registers a read-only property with a getter member function.
template<class V>
Namespace addProperty( const char *name, V( T::* getFn )( ) );
/// Registers a read-only property with a getter function.
template<class V>
Namespace addProperty( const char *name, std::function<V()> getFn );
/// Registers a read-only property with a C-function getter.
Namespace addProperty( const char *name, int( *getFn )( lua_State * ) );
/// Registers a member variable, writable or read-only.
template<class V>
Namespace addData( const char *name, V T::* varPtr, bool isWritable = true );
Static Function Registration
/// Registers a function.
template<class R, class... Params >>
Namespace addStaticFunction( const char *name, R( *fn )( Params... ) );
/// Registers a function.
template<class R, class... Params >>
Namespace addStaticFunction( const char *name, std::function<R( Params... )> fn );
/// Registers a function with an extra Lua state parameter.
template<class R, class... Params >>
Namespace addStaticFunction( const char *name, R( *fn )( Params..., lua_State * ) )
/// Registers a C-function.
Namespace addStaticFunction( const char *name, int( *fn )( lua_State * ) );
Static Property Registration
/// Registers a property with a getter and setter.
template<class V>
Namespace addStaticProperty( const char *name, V( *getFn )( ), void( *setFn )( V ) );
/// Registers a property with a getter and setter.
template<class V>
Namespace addStaticProperty( const char *name,
std::function<V()> getFn,
std::function<void( V )> setFn );
/// Registers a property with a C-function getter and setter.
Namespace addStaticProperty( const char *name,
int( *getFn )( lua_State * ),
int( *setFn )( lua_State * ) );
/// Registers a read-only property with a getter function.
template<class V>
Namespace addStaticProperty( const char *name, V( *getFn )( ) );
/// Registers a read-only property with a getter function.
template<class V>
Namespace addStaticProperty( const char *name, std::function<V()> getFn );
/// Registers a read-only property with a C-function getter.
Namespace addStaticProperty( const char *name, int( *getFn )( lua_State * ) );
/// Registers a variable, writable or read-only.
Namespace addStaticData( const char *name, T *varPtr, bool isWritable = true );
4、Lua Variable Reference - LuaRef
/// Creates a nil reference.
LuaRef( lua_State* L );
/// Returns native Lua string representation.
std::string tostring() const;
/// Dumps reference to a stream.
void print( std::ostream& stream ) const;
/// Returns the Lua state.
lua_State* state() const;
/// Place the object onto the Lua stack.
void push( lua_State* L );
/// Return the lua_type.
int type() const;
/// Perform the explicit type conversion.
template<class T>
T cast() const;
/// Check if the Lua value is convertible to the type T.
template<class T>
bool isInstance() const;
5、Stack Traits - Stack
/// Converts the C++ value into the Lua value at the top of the Lua stack.
void put( lua_State* L, T value );
/// Converts the Lua value at the index into the C++ value of the type T.
T get( lua_State* L, int index );
/// Checks if the Lua value at the index is convertible into the C++ value of the type T.
bool isInstance( lua_State* L, int index );