vlambda博客
学习文章列表

第十六章 C++与Lua的相互绑定之ELuna(1)


Lua 作为一门脚本语言它的优势在于小快,当然更重要的是能够和C/C++做到连接



MDXUI支持Lua这个我们好像前面说过,好吧到底有没有说我好像也不是记得那么清楚,毕竟好久没有更新和整理啦,不过这不是今天我们要说的重点,不管有没有说过,今天我们还是要来重新说一说的。



在MDXUI早些的版本中对Lua的支持不是太多,在Lua中操作MDXUI对象和C++中操作起来感觉像是两个不同的东西,所以后面为了将两者统一所以就重新对Lua的接口进行封装——其实这是一件很烦也很枯燥的事情,之所以说他烦和枯燥是因为这东西不难,就是有大量的工作量需要去做。



为了重新封装MDXUI的Lua接口我将LuaPlus换成了ELuna,和LuaPlus比起,ELuna也太过迷你啦些,但这并不影响它的实用,而且从某些角度来说可以更好的实用,尤其是它仅仅提供了一个头文件就很值得称赞,所以我们在使用ELuna的时候就只需要将这个头文件放到我们的工程中就可以了。





ELuna 将一个C++的类映射到Lua中

我们来看一个C++的类: 


class BaseClase
{

public:
BaseClase();
virtual ~BaseClase();

public:

void funA();
void funB();
int funC() const;
virtual int funD() const;
// .......

};


假设这是一个我们需要导入到Lua的C++类,那么我们只需要做如下的包装就可以在Lua中使用该类啦,如下: 


ELuna::registerClass<BaseClase>(L, "BaseClase", ELuna::constructor<BaseClase>);
ELuna::registerMethod<BaseClase>(L, "funA", &BaseClase::funA);
ELuna::registerMethod<BaseClase>(L, "funB", &BaseClase::funB);


于是我们就可以在Lua中如下使用:


obj = BaseClase()
obj:funA()
obj:funB()


是不是很方便呢?嗯……确实很方便,但是我们可能发现了一个问题,funC 和 funD 为什么没有封装到Lua中呢?这就是ELuna的缺陷,此处 funC 和 funD 是 const 的版本,而 ELuna 却不支持const的成员函数导出到Lua中。



完善ELuna,让其支持const的成员函数导出

我们来看看ELuna是如何将类的成员函数导入到Lua中的: 


template<typename T, typename RL>
inline void registerMethod(lua_State* L, const char* name, RL (T::*func)()) {
luaL_getmetatable(L, ClassName<T>::getName());
if (lua_istable(L, -1)) {
lua_pushstring(L, name);
new (lua_newuserdata(L, sizeof(MethodClass0<RL, T>))) MethodClass0<RL, T>(name, func);
lua_pushcclosure(L, &proxyMethodCall, 1);
lua_rawset(L, -3);
} else {
printf("please register class %s\n", ClassName<T>::getName());
}
lua_pop(L, 1);
}


我们看到这里使用了一个闭包,但关键在 MethodClass0,这是什么?我们继续往下看: 


#define ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(N) \
template<ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClassConst##N<void, ELUNA_METHODCLASSES_SP_PARAM_LIST_##N> : GenericMethod {\
typedef void (T::* TFUNC)(ELUNA_PARAM_LIST_##N) const;\
TFUNC m_func;\
MethodClassConst##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
~MethodClassConst##N() {};\
inline virtual int call(lua_State *L) {\
T* obj = read2cpp<T*>(L, 1);\
(obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N);\
return 0;\
};\
};

ELUNA_MAKE_METHODCLASSX(0)
ELUNA_MAKE_METHODCLASSX(1)
ELUNA_MAKE_METHODCLASSX(2)
ELUNA_MAKE_METHODCLASSX(3)
ELUNA_MAKE_METHODCLASSX(4)
ELUNA_MAKE_METHODCLASSX(5)
ELUNA_MAKE_METHODCLASSX(6)
ELUNA_MAKE_METHODCLASSX(7)
ELUNA_MAKE_METHODCLASSX(8)
ELUNA_MAKE_METHODCLASSX(9)


好吧,这是由宏来生成的一系列辅助类,针对参数个数不同而使用不同的辅助类,目前最多支持九个参数的成员函数,这也是一个局限性,其实我们可以有更好的解决方案让其支持多更多参数同时让代码更加简洁——至于简洁与否其实是一个仁者见仁的事,不过这事这里先不讨论,后面有机会我们再来将其进行优化让其支持更多的参数。 
回到正题,既然我们已经看到了它是如何支持普通成员函数的导出,那么根据现有的东西增加一个支持const版本的就好: 



#define ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(N)\
template<typename RL, ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClassConst##N<RL&, ELUNA_METHODCLASSES_SP_PARAM_LIST_##N> : GenericMethod {\
typedef RL& (T::* TFUNC)(ELUNA_PARAM_LIST_##N) const;\
TFUNC m_func;\
MethodClassConst##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
~MethodClassConst##N() {};\
inline virtual int call(lua_State *L) {\
T* obj = read2cpp<T*>(L, 1);\
push2lua<RL&>(L, (obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N));\
return 1;\
};\
};

#define ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(N) \
template<ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClassConst##N<void, ELUNA_METHODCLASSES_SP_PARAM_LIST_##N> : GenericMethod {\
typedef void (T::* TFUNC)(ELUNA_PARAM_LIST_##N) const;\
TFUNC m_func;\
MethodClassConst##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
~MethodClassConst##N() {};\
inline virtual int call(lua_State *L) {\
T* obj = read2cpp<T*>(L, 1);\
(obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N);\
return 0;\
};\
};

ELUNA_MAKE_METHODCLASSX(0)
ELUNA_MAKE_METHODCLASSX(1)
ELUNA_MAKE_METHODCLASSX(2)
ELUNA_MAKE_METHODCLASSX(3)
ELUNA_MAKE_METHODCLASSX(4)
ELUNA_MAKE_METHODCLASSX(5)
ELUNA_MAKE_METHODCLASSX(6)
ELUNA_MAKE_METHODCLASSX(7)
ELUNA_MAKE_METHODCLASSX(8)
ELUNA_MAKE_METHODCLASSX(9)

ELUNA_MAKE_REF_RL_METHODCLASSX(0)
ELUNA_MAKE_REF_RL_METHODCLASSX(1)
ELUNA_MAKE_REF_RL_METHODCLASSX(2)
ELUNA_MAKE_REF_RL_METHODCLASSX(3)
ELUNA_MAKE_REF_RL_METHODCLASSX(4)
ELUNA_MAKE_REF_RL_METHODCLASSX(5)
ELUNA_MAKE_REF_RL_METHODCLASSX(6)
ELUNA_MAKE_REF_RL_METHODCLASSX(7)
ELUNA_MAKE_REF_RL_METHODCLASSX(8)
ELUNA_MAKE_REF_RL_METHODCLASSX(9)

ELUNA_MAKE_VOID_RL_METHODCLASSX(0)
ELUNA_MAKE_VOID_RL_METHODCLASSX(1)
ELUNA_MAKE_VOID_RL_METHODCLASSX(2)
ELUNA_MAKE_VOID_RL_METHODCLASSX(3)
ELUNA_MAKE_VOID_RL_METHODCLASSX(4)
ELUNA_MAKE_VOID_RL_METHODCLASSX(5)
ELUNA_MAKE_VOID_RL_METHODCLASSX(6)
ELUNA_MAKE_VOID_RL_METHODCLASSX(7)
ELUNA_MAKE_VOID_RL_METHODCLASSX(8)
ELUNA_MAKE_VOID_RL_METHODCLASSX(9)

ELUNA_MAKE_METHODCLASSX_CONST(0)
ELUNA_MAKE_METHODCLASSX_CONST(1)
ELUNA_MAKE_METHODCLASSX_CONST(2)
ELUNA_MAKE_METHODCLASSX_CONST(3)
ELUNA_MAKE_METHODCLASSX_CONST(4)
ELUNA_MAKE_METHODCLASSX_CONST(5)
ELUNA_MAKE_METHODCLASSX_CONST(6)
ELUNA_MAKE_METHODCLASSX_CONST(7)
ELUNA_MAKE_METHODCLASSX_CONST(8)
ELUNA_MAKE_METHODCLASSX_CONST(9)

ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(0)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(1)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(2)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(3)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(4)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(5)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(6)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(7)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(8)
ELUNA_MAKE_REF_RL_METHODCLASSX_CONST(9)

ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(0)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(1)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(2)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(3)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(4)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(5)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(6)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(7)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(8)
ELUNA_MAKE_VOID_RL_METHODCLASSX_CONST(9)


template<typename T, typename RL>
inline void registerConstMethod(lua_State* L, const char* name, RL(T::*func)() const) {
luaL_getmetatable(L, ClassName<T>::getName());

if (lua_istable(L, -1)) {
lua_pushstring(L, name);
new (lua_newuserdata(L, sizeof(MethodClassConst0<RL, T>))) MethodClassConst0<RL, T>(name, func);
lua_pushcclosure(L, &proxyMethodCall, 1);
lua_rawset(L, -3);
}
else {
printf("please register class %s\n", ClassName<T>::getName());
}
lua_pop(L, 1);
}

.....


现在我们可以将 const 版本的 funC 和 funD 导出到 Lua 中,如下: 


ELuna::registerClass<BaseClase>(L, "BaseClase", ELuna::constructor<BaseClase>);
ELuna::registerMethod<BaseClase>(L, "funA", &BaseClase::funA);
ELuna::registerMethod<BaseClase>(L, "funB", &BaseClase::funB);
ELuna::registerConstMethod<BaseClase>(L, "funC", &BaseClase::funC);
ELuna::registerConstMethod<BaseClase>(L, "funD", &BaseClase::funD);



支持派生类导出

假如我们有一个 class 继承值 BaseClase,同时我们也需要导出到 Lua 中,加这个class 如下: 


class DriveClass : public BaseClase
{
public:
int funD() const;
void funE();
};


我们发现 ELuna是不支持派生类的导出的,如果一定要导出派生类,那么只能全部从头导出: 


ELuna::registerClass<DriveClass>(L, "DriveClass", ELuna::constructor<DriveClass>);
ELuna::registerMethod<DriveClass>(L, "funA", &DriveClass::funA);
ELuna::registerMethod<DriveClass>(L, "funB", &DriveClass::funB);
ELuna::registerConstMethod<DriveClass>(L, "funC", &DriveClass::funC);
ELuna::registerConstMethod<DriveClass>(L, "funD", &DriveClass::funD);
ELuna::registerConstMethod<DriveClass>(L, "funE", &DriveClass::funE);


这是不是很繁琐呢?好吧确实很繁琐,尤其是当基类已经导出了很多函数后再来导出派生类就很烦了,而且还不没有导出的对象还丢失了继承关系,尤其是GUI框架这种东西如何不能导出派生类或者类之间的继承关系丢失的话就是一件不可接受的事,所以我修改了一部分代码,让其支持派生类导出: 



template<typename T>
struct convert2CppType<T*> {
inline static T* convertType(lua_State* L, int index) {
UserData<T>* ud = static_cast<UserData<T>*>(luaL_checkudata(L, index, ClassName<T>::getName()));
return static_cast<T*>(ud->m_objPtr);
}
};




我们对上面的代码稍作修改如下: 


template<typename T>
struct convert2CppType<T*> {
inline static T* convertType(lua_State* L, int index) {
// UserData<T>* ud = static_cast<UserData<T>*>(luaL_checkudata(L, index, ClassName<T>::getName()));
UserData<T>* ud = static_cast<UserData<T>*>(lua_touserdata(L, index));// for dirive class but unsafe......
return static_cast<T*>(ud->m_objPtr);
}
};


简单点说,其实我们是丢失了安全性换来可以实现派生类的操作,不过话说回来,如果用户乱用无论是我修改后的版本还是原来的版本一样都是会导致程序出错的,只是原来的版本会有一个提示是因为什么出错,好吧,总之不可兼得,我们在修改这个类型转换后我们就可以增加一个支持导出派生类的接口: 


//
// 继承
//
template<typename T,typename SupperType>
inline void registerSupperMetatable(lua_State *L, const char *name) {
luaL_newmetatable(L, name); // create a metatable in the registry

lua_pushstring(L, "__index"); //push metamethods's name
lua_pushvalue(L, -2);
lua_settable(L, -3); //metatable.__index = metatable

lua_pushstring(L, "__newindex");
lua_pushvalue(L, -2);
lua_settable(L, -3); //metatable.__newindex = metatable

lua_pushstring(L, "__gc");
lua_pushcfunction(L, &gc_obj<T>);
lua_rawset(L, -3); // metatable.__gc = Luna<T>::gc_obj

lua_pop(L, 1);

luaL_getmetatable(L, name);
luaL_getmetatable(L, ClassName<SupperType>::getName());
lua_setmetatable(L, -2); //metatable.metatable = suppermetatable
lua_pop(L, 1);
}

template<typename T, typename SupperType,typename F>
inline void registerDeriveClass(lua_State *L, const char* name, F constructor) {
ClassName<T>::setName(name);
lua_pushcfunction(L, constructor);
lua_setglobal(L, name);
registerSupperMetatable<T, SupperType>(L, name);
}


现在我们再来导出派生类就很简单了: 


ELuna::registerDeriveClass<DriveClass,BaseClase>(L, "DriveClass", ELuna::constructor<DriveClass>);
ELuna::registerConstMethod<DriveClass>(L, "funE", &DriveClass::funE);


就只需要这样导出我们新增加的函数就可以,这样在Lua中就可以像在C++中一样操作: 


obj = DriveClass()
obj:funA()
obj:funB()
obj:funC()
obj:funD()
obj:funE()




支持数据成员的导出

通常这种操作会用到strut上,好吧这里不是说strut和class有如何明显的区别,只是大家都习惯于用struct表示数据,而通常情况是大家习惯于用Lua的table表示C/C++中的strut中的数据,但是这样有一个问题,那就是在Lua中和C++中无法实现同步,或者说不能很好的实现同步,为了解决这个问题,我们使用了一种这种的方案,将C/C++中的数据成员映射到Lua中,让Lua中提供读写接口来访问这个数据段,这样就可以实现同步啦: 


struct RECT{
long left;
long top;
long right;
long bottom;
};


在GUI中,RECT是一个非常常用的数据类了,所以我们会在Lua和C++中来回传递这个数据结构,所以为了解决这个问题,我们在ELuna中新增了如下接口: 


template<typename T,typename V>
struct MethodSetForProperty : public GenericMethod {
typedef V(T::* TObject);
TObject mVal = { nullptr };
MethodSetForProperty(const char* name, V(T::*obj)) :GenericMethod(name), mVal(obj) {}
virtual int call(lua_State *L) {
T* obj = read2cpp<T*>(L, 1);
obj->*mVal = read2cpp<V>(L, 2);
return 0;
}
};

template<typename T, typename V>
struct MethodGetForProperty : public GenericMethod {
typedef V(T::* TObject);
TObject mVal = { nullptr };
MethodGetForProperty(const char* name, V(T::*obj)) :GenericMethod(name), mVal(obj) {}
virtual int call(lua_State *L) {
T* obj = read2cpp<T*>(L, 1);
push2lua(L, obj->*mVal);
return 1;
}
};


template<typename T,typename V>
inline void registerProperty(lua_State* L, const char* name, V(T::*obj)) {
luaL_getmetatable(L, ClassName<T>::getName());
if (lua_istable(L, -1)) {
lua_pushstring(L, (std::string("get_") + name).c_str());
new (lua_newuserdata(L, sizeof(MethodGetForProperty<T, V>))) MethodGetForProperty<T,V>(name, obj);
lua_pushcclosure(L, &proxyMethodCall, 1);
lua_rawset(L, -3);

lua_pushstring(L, (std::string("set_") + name).c_str());
new (lua_newuserdata(L, sizeof(MethodSetForProperty<T, V>))) MethodSetForProperty<T, V>(name, obj);
lua_pushcclosure(L, &proxyMethodCall, 1);
lua_rawset(L, -3);
}
else {
printf("please register class %s\n", ClassName<T>::getName());
}
lua_pop(L, 1);
}


ELuna::registerClass<RECT>(L, "RECT", ELuna::constructor<RECT>);
ELuna::registerProperty<RECT, long>(L, "left", &RECT::left);
ELuna::registerProperty<RECT, long>(L, "top", &RECT::top);
ELuna::registerProperty<RECT, long>(L, "right", &RECT::right);
ELuna::registerProperty<RECT, long>(L, "bottom", &RECT::bottom);


于是我们就可以在Lua中如下操作: 


rc = RECT()

print(rc:get_left(),rc:get_top())

rc:set_left(100)
rc:set_top(100)

print(rc:get_left(),rc:get_top())




让Lua函数成功C/C++的回调函数

在操作GUI对象时我们经常使用到回调函数,比如我们点击一下按钮,那么按钮会发现一个点击事件,怎么处理这个事件呢?在C/C++中我们可能简单的连接一下这个事件就可以按照我们的方式进行处理,那么如果我们使用Lua来编程又怎么处理这个事件呢?当然我们希望这个点击事件能够传递到Lua中,这样我们就可以在Lua中编写我们的处理函数,为了解决这个问题,我们在ELuna中新增了如下接口: 


template<typename T, typename HelpType>
struct ExternalMethodClass : GenericMethod {
std::vector<HelpType*> mPtrs;
ExternalMethodClass(const char* name) : GenericMethod(name){};

~ExternalMethodClass() {
for (auto& __Ptr__ : mPtrs){
delete __Ptr__;
__Ptr__ = nullptr;
}
mPtrs.clear();
};

inline virtual int call(lua_State *L) {
T* obj = read2cpp<T*>(L, 1);
const char* fun = lua_tostring(L, 2);
if (fun){
mPtrs.push_back(new HelpType(L, obj, fun));
}
else{
std::cout << "arg is must function name" << std::endl;
}
return 0;
};
};

inline int proxyExternalMethodCall(lua_State *L) {
GenericMethod* pMethod = static_cast<GenericMethod*>(lua_touserdata(L, lua_upvalueindex(1)));
return pMethod->call(L); // execute method
}

template<typename T, typename __HelpType__>
inline void registerExternalMethod(lua_State* L, const char* name) {
luaL_getmetatable(L, ClassName<T>::getName());
if (lua_istable(L, -1)) {
lua_pushstring(L, name);
new (lua_newuserdata(L, sizeof(ExternalMethodClass<T, __HelpType__>))) ExternalMethodClass<T, __HelpType__>(name);
lua_pushcclosure(L, &proxyExternalMethodCall, 1);
lua_rawset(L, -3);
}
else {
printf("please register class %s\n", ClassName<T>::getName());
}
lua_pop(L, 1);
}


这个接口看起来有些不知所措,不知道该怎么用,好吧下面我们来演示一下如何将Button的Clicked事件回调到Lua中: 


class CDxFlatButton : public CDxWidget{
msignals:
//+---------------
//
// 使用 Event_Clicked 可以直接使用 += 操作符进行事件绑定
// Event_Clicked += fun
// Event_Clicked += fun
// += 返回一个事件id
// 如果想要移除事件响应函数 -= 即可
// Button 添加该事件仅作尝试
//
//+---------------
TL::MTLVoidEvent<bool, CDxWidget*> Event_Clicked;
};


为了将 CDxFlatButton 的 Event_Clicked 事件传递到Lua中,我们需要如下操作: 


class __Event_Clicked__{
typedef ELuna::MLuaFun<void> FunType;
FunType mFun;
public:
template<class T>
__Event_Clicked__(lua_State* L, T* obj, const char* funName) :
mFun(L, funName){
CDxFlatButton* btn = object_cast<CDxFlatButton>(obj);
if (btn){
btn->Event_Clicked += TL::MSLOT(&__Event_Clicked__::OnCall, this);
}
}
void OnCall(bool isClicked, CDxWidget* sender){
if (mFun.IsValid()){
mFun(isClicked, sender);
}
}
};


ELuna::registerExternalMethod<CDxFlatButton, __Event_Clicked__>(L, "Event_Clicked");


在Lua中我们就可以这么用: 


btn = CDxFlatButton()

function clicked(isClicked,sender){
--
-- dosomething
--
}

btn:Event_Clicked("clicked")



动态添加类成员函数

这种操作在C++中是不存在的,但是我们可以在将C++ class导出 Lua 中的时候给他添加一些新接口,比如我们想给 DriveClass 新增一个接口,这个接口和数据没啥关系,在C++中我们不方便加,除非我们重写这个类,但是如果这个类已经在很多地方使用了,那么再新加接口就是一件很不好的操作,但是我们可以在导出Lua的时候就可以干这件事啦,我们在Eluna中添加如下接口: 


template<int N,class T>
struct LuaFunHelpRead;


template<class...ArgsType>
struct LuaFunHelpRead<1, mjTL::MTypeList<ArgsType...>>{

template<class...Args>
static void Apply(lua_State* L, void(*Fun)(ArgsType...), Args...args){

typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
ApplyRun(Fun, read2cpp<__type>(L, 2), args...);
}
template<class...Args>
static void ApplyRun(void(*Fun)(ArgsType...), Args...args){

(*Fun)(args...);
}
};

template<int N, class...ArgsType>
struct LuaFunHelpRead<N, mjTL::MTypeList<ArgsType...>>{

template<class...Args>
static void Apply(lua_State* L, void(*Fun)(ArgsType...), Args...args){

typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N-1>::type __type;
LuaFunHelpRead<N - 1, mjTL::MTypeList<ArgsType...>>::Apply(L, Fun, read2cpp<__type>(L, N+1), args...);
}
};


template<class...Args>
struct MLuaExtFunction :
GenericFunction{
typedef void(*TFUNC)(Args...);
TFUNC m_func;
MLuaExtFunction(const char* name, TFUNC func) : GenericFunction(name), m_func(func) {};
~MLuaExtFunction() {};
inline virtual int call(lua_State *L) {
LuaFunHelpRead<sizeof...(Args), mjTL::MTypeList<Args...>>::Apply(L, *m_func);
return 0;
};
};

//
// 将一个函数和一个类注册在一起
//
template<class T,class...ArgsType>
inline void registerExternalFunction(lua_State* L, const char* name, void(*Fun)(ArgsType...)){

luaL_getmetatable(L, ClassName<T>::getName());
if (lua_istable(L, -1)) {
lua_pushstring(L, name);
new (lua_newuserdata(L, sizeof(MLuaExtFunction<ArgsType...>))) MLuaExtFunction<ArgsType...>(name, Fun);
lua_pushcclosure(L, &proxyMethodCall, 1);
lua_rawset(L, -3);
}
else {
printf("please register class %s\n", ClassName<T>::getName());
}
lua_pop(L, 1);
}


这样我们就可以将一个自由函数和一个类绑定在一起了: 


void test(int axis, bool isenabel){
std::cout << "External Function : " << axis <<"\t"<<isenabel<< std::endl;
}

ELuna::registerExternalFunction<DriveClass>(L, "test", test);


于是我们就可以在Lua中这样使用: 


obj = DriveClass()
obj:funA()
obj:funB()
obj:funC()
obj:funD()
obj:funE()
obj:test()


还是搞不定这个排版………………