一、互调原理
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代码,定义了一个globalFunclua_getglobal(state, "globalFunc"); // 将全局变量globalFunc的值压栈lua_pushstring(state, "motherfucker"); // C++这边将一个char*字符串压栈int errCode = lua_call(state, 1, 0); // 执行lua代码;globalFunc('motherfucker')}// 在lua的全局环境中定义一个全局函数globalFuncLUA_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传来的第一个intint param2 = static_cast<int>(lua_tointeger(state, -1)); // 栈顶参数,lua出来的第二个intlua_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 = 5int 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类,封装一下Vecstruct 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();// 方法二:通过functionluabridge::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();// 方法二:通过functionluabridge::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 nilLuaRef v1( L, 1 ); // A LUA_TNUMBERLuaRef v2( L, 1.1 ); // Also a LUA_TNUMBERLuaRef v3( L, true ); // A LUA_TBOOLEANLuaRef v4( L, "string" ); // A LUA_TSTRINGLuaRef v1 = newTable( L ); // Create a new tableLuaRef v2 = getGlobal( L, "print" ); // Reference to _G ["print"]......; // A注册给了LuaLuaRef v( L, new A ); // A LuaBridge userdata holding a pointer to Av = newTable( L ); // An empty tablev = "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 unchangedv1 = newTable( L ); // An empty tablev2 = v1; // v2 references the same table as v1v1 = 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 intpassBool( v ); // implicit conversion to boolpassString( v ); // implicit conversion to stringpassObject( 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 valuev[1] = 200; // integer key, integer valuev[2] = newTable( L ); // integer key, LuaRef valuev[3] = v[1]; // assign 200 to integer index 3v[1] = 100; // v[1] is 100, v[3] is still 200v[3] = v[2]; // v[2] and v[3] reference the same tablev[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" )// endLuaRef 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// endLuaRef same = getGlobal( L, "same" );// These all evaluate to truesame( 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++ lifetimelua_setglobal( L, "a" );push( L, ( A const * ) &a ); // pointer to 'a const', C++ lifetimelua_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 memorylua_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 继承自 Avoid 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 );
