Lua调用C++类
Lua调用C++主要使用自定义类型userdata。通过创建对象指针与自定义类型关联,并利用元表校验指针以及实现面向对象语法。本文使用了以前编译的Lua静态库
void *lua_newuserdata (lua_State *L, size_tsize);
int luaL_newmetatable (lua_State *L, constchar *tname);
void luaL_getmetatable (lua_State *L, constchar *tname);
void *luaL_checkudata (lua_State *L, intindex, const char *tname);
lua_newuserdata函数按照指定的大小分配一块内存,将对应的userdata放到栈内,并返回内存块的地址。你可以申请一块内存,也可以只申请指针大小的内存,再将该指针指向另一块区域。lua_newuserdata申请的内存由Lua自己释放
luaL_newmetatable函数创建一个新表(将用作metatable),将新表放到栈顶并建立表和registry中类型名的联系。
luaL_getmetatable函数获取registry中的tname对应的metatable。
luaL_checkudata检查在栈中指定位置的对象是否为带有给定名字的metatable的usertatum。如果对象不存在正确的metatable,返回NULL(或者它不是一个userdata);否则,返回userdata的地址。
2.自定义C++类
1.在vs2012中新建一个项目Studenet,并导入Lua的所有头文件
2.新建Student.h和Student.cpp文件输入:
//
student.h
/
#ifndef __MyCppGame__Student__
#define __MyCppGame__Student__
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student();
~Student();
string getName();
void setName(string name);
private:
string name;
};
#endif/*defined(__MyCppGame__Student__) */
//
student.cpp
/
extern"C"
{
#include "Lua/lua.h"
#include "Lua/lauxlib.h"
#include "Lua/lualib.h"
#include <stdio.h>
}
#include "Student.h"
#pragma comment(lib,"LuaTest.lib")
Student::Student()
{
name ="default";
}
Student::~Student()
{
printf("wwwwwww\n");
}
stringStudent::getName()
{
return name;
}
void Student::setName(string name)
{
this->name=name;
}
static int newStudent(lua_State*L)
{
Student **s = (Student**)lua_newuserdata(L,sizeof(Student*));
*s =new Student;
return 1;
}
staticintl_setName(lua_State*L)
{
Student ** s = (Student **)lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1,"invalid user data");
luaL_checktype(L,-1,LUA_TSTRING);
std::string name =lua_tostring(L,-1);
(*s)->setName(name);
return 0;
}
static int l_getName(lua_State*L)
{
Student ** s = (Student **)lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1,"invalid user data");
lua_settop(L,0);
lua_pushstring(L,(*s)->getName().c_str());
return 1;
}
static const struct luaL_Reg studentlib_f [] = {
{"create", newStudent},
{"setName",l_setName},
{"getName",l_getName},
{NULL,NULL}
};
int main()
{
lua_State *L =lua_open();
luaL_openlibs(L);
//创建student表,包含映射函数
luaL_openlib(L,"student", studentlib_f, 0);
luaL_dofile(L,"wo.lua");
lua_close(L);
printf("over----\n");
return 0;
}
/
wo.lua文件中
local s = student.create()
student.setName(s,"nihaoa")
print("haha",student.getName(s))
可以调用相应的自定义数据userdata,此处为s
会有相应的输出:
/
haha nihaoa
over----
请按任意键继续. . .
上面的例子存在两个问题:一是没有调用析构函数,存在内存泄露。应该添加注册一个student.release()函数,但Lua存在__gc域,最好让Lua自动管理。二是:上面调用不符合面向对象语法,调用不方面,s和student关系不清晰
1.使用面向对象语法,设置元表
s:setName(...)=s.setName(s,...)
若想这样表达,只要s拥有setName方法即可。s是userdata,不是表,不能拥有方法。但是userdata可以设置元表,如果元表的__index域拥有此方法即可
将以上student.cpp修改为:
extern"C"
{
#include "Lua/lua.h"
#include "Lua/lauxlib.h"
#include "Lua/lualib.h"
#include <stdio.h>
}
#include "Student.h"
#pragma comment(lib,"LuaTest.lib")
Student::Student()
{
name ="default";
}
Student::~Student()
{
printf("wwwwwww\n");
}
stringStudent::getName()
{
return name;
}
void Student::setName(stringname)
{
this->name=name;
}
static int newStudent(lua_State*L)
{
Student **s = (Student**)lua_newuserdata(L,sizeof(Student*));
*s =new Student;
//设置元表
luaL_getmetatable(L,"LuaBook.array");
lua_setmetatable(L,-2);
return 1;
}
static int l_setName(lua_State*L)
{
Student ** s = (Student **)lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1,"invalid user data");
luaL_checktype(L,-1,LUA_TSTRING);
std::string name =lua_tostring(L,-1);
(*s)->setName(name);
return 0;
}
static int l_getName(lua_State*L)
{
Student ** s = (Student **)lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1,"invalid user data");
lua_settop(L,0);
lua_pushstring(L,(*s)->getName().c_str());
return 1;
}
static int auto_gc(lua_State *L)
{
Student **s = (Student**)lua_touserdata(L,1);
if(s)
delete *s;
return 0;
}
static const structluaL_reg arraylib_f [] = {
{"create", newStudent},
{NULL,NULL}
};
static const structluaL_reg arraylib_m [] = {
{"setName",l_setName},
{"getName",l_getName},
{"__gc",auto_gc},
{NULL,NULL}
};
int main()
{
lua_State *L =lua_open();
luaL_openlibs(L);
luaL_newmetatable(L,"LuaBook.array");
lua_pushstring(L,"__index");
lua_pushvalue(L,-2);
lua_settable(L,-3); /*metatable.__index = metatable */
luaL_openlib(L,NULL, arraylib_m, 0);
luaL_openlib(L,"student", arraylib_f, 0);
luaL_dofile(L,"wo.lua");
lua_close(L);
printf("over----\n");
return 0;
}
/
wo.lua文件中
local a = student.create()
a:setName("nnnle")
print("wohat",a:getName())
s=nil
会有相应的输出:
/
wohat nnn le
wwwwwww
over----
请按任意键继续. . .
Lua自己管理的对象(上例为a)被gc的时候,会自动调用该对象的__gc方法。luaL_openlib(L,NULL, arraylib_m, 0);函数,如果第二个参数表名没填的时候默认为当前正在操作的表,此处表示为该元表
我们也可以不在main()中设置元表的__index域。可以在Lua设置也是一样的:
将wo.lua中修改为:
local s = student.create()
local metaarray =getmetatable(s)
metaarray.__index =metaarray
metaarray.setName =student.setName
metaarray.getName =student.getName
metaarray.__gc =student.__gc
s:setName("nnnle")
print(s:getName())
s=nil
//将main()中的设置元表方法去掉,结果是一样的