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++释放。