lua是一个内嵌式的语言,很多初学者对于lua中使用c++类中的函数,或者用c++直接创建一个自己的自定义数据类型比较头疼,因为这部分确实比较乱,牵扯到内存释放分配等问题,但是如果把其中关系理清,还是很简单的,下面这段代码是一个老外写的,我做了一些修改。首先看代码。
#ifndef LUNA_H #define LUNA_H 1 /**************************************************************************** * This program is free software. It comes without any warranty, to * * the extent permitted by applicable law. You can redistribute it * * and/or modify it under the terms of the Do What The Fuck You Want * * To Public License, Version 2, as published by Sam Hocevar. See * * http://sam.zoy.org/wtfpl/COPYING for more details. * ****************************************************************************/ /****************************************************************************** * 这是一个自由传播修改的文件,如果你够厚道就保留下我的签名吧 * * Edit by fox * * 2010-4-13 * * http://blog.csdn.net/limiteee * ******************************************************************************/ // convenience macros //注册一个自定义类型,并用该类名创建一个元表 #define luna_register(L, klass) (Luna::Register((L))) //注册一个自定义数据类型,并用该类名创建一个元表,该类必须是一个Ogre::Singleton的派生类,一般用于lua访问c环境中全局唯一实例的类 #define luna_register_singleton(L, klass) (Luna::Register_singleton((L))) #define luna_registermetatable(L, klass) (Luna::RegisterMetatable((L))) #define luna_inject(L, klass, t) (Luna::inject((L), (t))) #define luna_flag_meta_func 0 #define luna_flag_func 1 /* ---------------------------------------------------------------------------------------------------------- LUNA_CLASS_D_DEFAULT_VALS 需要写在类声明中 LUNA_CLASS_D_INIT_VALS_CLASSNAME 写在类实现中 LUNA_CLASS_D_INIT_VALS_FUNCNAME_START 开始进行lua声明,必须以这个宏开始 LUNA_CLASS_D_INIT_VALS_FUNCNAME_END lua声明结束,必须以这个宏结束 LUNA_CLASS_D_INIT_VALS_FUNCNAME_USER 进行用户自定义函数的lua声明 LUNA_CLASS_D_INIT_VALS_META_FUNCNAME_USER 定义一个元表的方法 LUNA_CLASS_D_INIT_VALS_META_FUNCNAME_ALIAS_USER 定义一个元表的方法,并且使用指定的别名 BUILD_LUACLASS 在lua中创建一个指定类的自定义数据,new一个该类指针赋予这个自定义数据 BUILD_LUACLASS_LP 在lua中创建一个指定类的自定义数据,使用一个已经存在的该类指针赋予这个自定义数据 cLinkName 可以是任何类,没有规定,这个类只是给lua类进行访问的。 cName 是lua类,这个类将可以在lua中访问 ---------------------------------------------------------------------------------------------------------- */ #define LUNA_CLASS_D_DEFAULT_VALS(cName,cLinkName) / public: / static cLinkName *m_pLink; / static const char className[]; / static const Luna::RegType Register[]; #define LUNA_CLASS_D_INIT_VALS_CLASSNAME(cName,cLinkName) / cLinkName *cName::m_pLink = 0; / const char cName::className[] = #cName; #define LUNA_CLASS_D_INIT_VALS_CLASSNAME_ALIAS(cName,cLinkName,Alias) / cLinkName *cName::m_pLink = 0; / const char cName::className[] = Alias; #define LUNA_CLASS_D_INIT_VALS_FUNCNAME_START(cName) / const Luna::RegType cName::Register[] = { #define LUNA_CLASS_D_INIT_VALS_FUNCNAME_USER(fName,cName) / {#fName,&cName::fName}, #define LUNA_CLASS_D_INIT_VALS_FUNCNAME_ALIAS_USER(fName,cName,Alias) / {Alias,&cName::fName}, #define LUNA_CLASS_D_INIT_VALS_FUNCNAME_END { 0 }}; #define LUNA___INDEX_FUNCTION -1 #define BUILD_LUACLASS(ClassName, L) Luna::constructor( L ) #define BUILD_LUACLASS_LP(ClassName, L, ClassPtr) Luna::constructor_lightptr( L, ClassPtr ); // //extern "C" { //#include "lua.h" //#include "lualib.h" //#include "lauxlib.h" //} #include "lua.hpp" template class Luna { public: //注册唯一实例的对象------------------------------------------------------------------------------------------------------------------------------------------------ static void Register_singleton(lua_State *L) { const char* cn = T::className; lua_pushcfunction(L, &Luna::constructor_singleton); lua_setglobal(L, T::className); // T() in lua will make a new instance. } //使用一个c环境中已存在的指针创建一个自定义数据,该指针需要c来释放,这个方法没有注册gc函数 static int constructor_lightptr(lua_State *L, T* obj) { return inject_singleton(L, obj); } //使用一个Ogre::Singleton的派生类的指针创建一个自定义数据,该指针需要c来释放,这个方法没有注册gc函数 static int constructor_singleton(lua_State *L) { return inject_singleton(L, static_cast(T::getSingletonPtr())); } static int inject_singleton(lua_State *L, T* obj) { //创建一个自定义数据,返回一个指向指针的指针,这里不使用light udata是因为我们需要lua为我们管理内存回收 T** a = static_cast(lua_newuserdata(L, sizeof(T*))); *a = obj; // 指针的指针赋值 const char *cn = T::className; int rt = luaL_newmetatable(L, T::className); // get (or create) the unique metatable if ( rt ) { //设置一个默认的__index函数 lua_pushstring(L, "__index"); lua_pushnumber(L, LUNA___INDEX_FUNCTION); lua_pushcclosure(L, &Luna::thunk, 1); lua_settable(L, -3); //----------------------------------------------------------------------- //为元表添加方法 for (int i = 0; T::Register[i].name; i++) { // register the functions lua_pushstring(L, T::Register[i].name); lua_pushnumber(L, i); // let the thunk know which method we mean lua_pushcclosure(L, &Luna::thunk, 1); lua_settable(L, -3); // self["function"] = thunk("function") } } //----------------------------------------------------------------------- lua_setmetatable(L, -2); //将自定义数据的元表设置为刚刚创建的表 return 1; } //这里注册可自动回收的对象--------------------------------------------------------------------------------------------------------------------- static void Register(lua_State *L) { const char* cn = T::className; lua_pushcfunction(L, &Luna::constructor); lua_setglobal(L, T::className); // T() in lua will make a new instance. } static int constructor(lua_State *L) { return inject(L, new T(L)); } static int inject(lua_State *L, T* obj) { //创建一个自定义数据,返回一个指向指针的指针,这里不使用light udata是因为我们需要lua为我们管理内存回收 T** a = static_cast(lua_newuserdata(L, sizeof(T*))); *a = obj; // 指针的指针赋值 const char *cn = T::className; int rt = luaL_newmetatable(L, T::className); // get (or create) the unique metatable if ( rt ) { //设置一个默认的__index函数 lua_pushstring(L, "__index"); lua_pushnumber(L, LUNA___INDEX_FUNCTION); lua_pushcclosure(L, &Luna::thunk, 1); lua_settable(L, -3); //设置自动销毁函数 lua_pushstring(L, "__gc"); lua_pushcfunction(L, &Luna::gc_obj); lua_settable(L, -3); // metatable["__gc"] = Luna::gc_obj //----------------------------------------------------------------------- //为元表添加方法 for (int i = 0; T::Register[i].name; i++) { // register the functions lua_pushstring(L, T::Register[i].name); lua_pushnumber(L, i); // let the thunk know which method we mean lua_pushcclosure(L, &Luna::thunk, 1); lua_settable(L, -3); // self["function"] = thunk("function") } } //----------------------------------------------------------------------- lua_setmetatable(L, -2); //将自定义数据的元表设置为刚刚创建的表 return 1; } static int thunk(lua_State *L) { // redirect method call to the real thing int d = lua_gettop(L); int i = (int)lua_tonumber(L, lua_upvalueindex(1)); // which function? T** obj = static_cast(luaL_checkudata(L, 1, T::className)); //第一个参数就是他自己 //察看第二个参数是什么 int iType = lua_type(L, 2); if (iType == LUA_TSTRING) { //如果第二个参数是字符型,那么它可能是一个call调用 int rt = call_obj(L); //如果调用发现同名函数那么执行它,并且返回 if ( rt ) { return rt; } } //每次调用使用的是目标对象来调用,所以函数访问的内部变量就是他自己的 return ((*obj)->*(T::Register[i].mfunc))(L); // execute the thunk } static int call_obj(lua_State *L) { int d = lua_gettop(L); T** obj = static_cast(luaL_checkudata(L, 1, T::className)); //第一个参数就是他自己 const char* funcname = lua_tostring(L, 2); for (int i = 0; T::Register[i].name; i++) { if( strcmp( funcname, T::Register[i].name ) == 0 ) { //找到这个函数,并且返回它 lua_pushnumber(L, i); // let the thunk know which method we mean lua_pushcclosure(L, &Luna::thunk, 1); return 1; } } return 0; } static int gc_obj(lua_State *L) { // clean up //printf("GC called: %s/n", T::className); T** obj = static_cast(luaL_checkudata(L, -1, T::className)); delete (*obj); return 0; } struct RegType { const char *name; int(T::*mfunc)(lua_State*); }; }; #endif /* LUNA_H */
以上是一个头文件,包含到代码中,直接就可以使用。
这段代码的用处是将一个c++类包装成lua中的自定义数据类型。
在类声明中要添加如 LUNA_CLASS_D_DEFAULT_VALS宏:
class aa { int luaprint(lua_State* l) { std::string subitemString = luaL_checkstring( l, 2 ); printf( subitemString.c_str() ); printf( "/n" ); return 0; } LUNA_CLASS_D_DEFAULT_VALS(aa,void) }
这个类实现了一个luaprint函数,他只会输出一个字符串。
在类声明的最后,要把这个宏加上,宏的第一个参数是类名,第二个参数是一个任意类型的指针,目前没用。
我们现在看看LUNA_CLASS_D_DEFAULT_VALS是什么
#define LUNA_CLASS_D_DEFAULT_VALS(cName,cLinkName) / public: / static cLinkName *m_pLink; / static const char className[]; / static const Luna::RegType Register[];
这个宏在类中添加了3个变量,其中m_pLink目前没用,className是一个字符串数组,用来保存类名,而Register是一个RegType类型的数组。
RegType是一个结构如下:
struct RegType {
const char *name;
int(T::*mfunc)(lua_State*);
};
这个结构保存一个c++类中的函数。他将在下面的部分进行初始化。
下面将要在类声明外进行刚刚那些变量的初始化工作,他们是由下面这些宏组成
LUNA_CLASS_D_INIT_VALS_CLASSNAME(aa,void)
LUNA_CLASS_D_INIT_VALS_FUNCNAME_START(aa)
LUNA_CLASS_D_INIT_VALS_FUNCNAME_USER(luaprint,aa)
LUNA_CLASS_D_INIT_VALS_FUNCNAME_END
首先LUNA_CLASS_D_INIT_VALS_CLASSNAME宏初始化m_pLink和className变量,而下面三个宏初始化RegType变量。
如果类中还有其他函数,并且他符合int(T::*mfunc)(lua_State*);的样式,那么就可以用LUNA_CLASS_D_INIT_VALS_FUNCNAME_USER添加进来。
那么,这个类就已经包装好,等待放到lua中。之所以说等待,是因为我们需要将这个类注册到lua中,好让lua知道,这样我们在lua中才能调用。
注册方法是,在程序启动的时候调用luna_register( ls, aa )宏。
其中ls是lua_state,aa是刚刚包装的类
至此,在lua中之需要下面这样,就可以轻松调用c函数了
local test = aa()
test:luaprint("123")
以上是这个luna模板最基本的应用
其他还有
LUNA_CLASS_D_INIT_VALS_FUNCNAME_ALIAS_USER用于将一个函数声明为其他名称,比如想实现lua中元表的__index函数:
LUNA_CLASS_D_INIT_VALS_FUNCNAME_ALIAS_USER(luaprint,aa,"__index")
这样,当任何对自定义函数的取值操作,都将调用luaprint函数,当然,如果不修改luaprint函数,结果肯定是错误的,__index函数必须返回一个值。
luna_register_singleton 宏用于注册一个Ogre::Singleton的派生类,一般用于lua访问c环境中全局唯一实例的类
BUILD_LUACLASS宏用于创建一个已经注册的类的新实例,并且放到当前堆栈中。
BUILD_LUACLASS_LP宏用于创建一个已经注册的类,其指针是c++提供,并且由c++释放。