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 *
* *
******************************************************************************/
// convenience macros
//注册一个自定义类型,并用该类名创建一个元表
#define luna_register(L, klass) (Luna<klass>::Register((L)))
//注册一个自定义数据类型,并用该类名创建一个元表,该类必须是一个Ogre::Singleton的派生类,一般用于lua访问c环境中全局唯一实例的类
#define luna_register_singleton(L, klass) (Luna<klass>::Register_singleton((L)))
#define luna_registermetatable(L, klass) (Luna<klass>::RegisterMetatable((L)))
#define luna_inject(L, klass, t) (Luna<klass>::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<cName>::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<cName>::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<ClassName>::constructor( L )
#define BUILD_LUACLASS_LP(ClassName, L, ClassPtr) Luna<ClassName>::constructor_lightptr( L, ClassPtr );
//
//extern "C" {
//#include "lua.h"
//#include "lualib.h"
//#include "lauxlib.h"
//}
#include "lua.hpp"
template<class T> class Luna {
public:
//注册唯一实例的对象------------------------------------------------------------------------------------------------------------------------------------------------
static void Register_singleton(lua_State *L) {
const char* cn = T::className;
lua_pushcfunction(L, &Luna<T>::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*>(T::getSingletonPtr()));
}
static int inject_singleton(lua_State *L, T* obj) {
//创建一个自定义数据,返回一个指向指针的指针,这里不使用light udata是因为我们需要lua为我们管理内存回收
T** a = static_cast<T**>(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<T>::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<T>::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<T>::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<T**>(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<T>::thunk, 1);
lua_settable(L, -3);
//设置自动销毁函数
lua_pushstring(L, "__gc");
lua_pushcfunction(L, &Luna<T>::gc_obj);
lua_settable(L, -3); // metatable["__gc"] = Luna<T>::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<T>::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<T**>(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<T**>(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<T>::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<T**>(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<cName>::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++释放。