Lua调用C++类

   Lua调用C++主要使用自定义类型userdata。通过创建对象指针与自定义类型关联,并利用元表校验指针以及实现面向对象语法。本文使用了以前编译的Lua静态库

一、Lua访问C++类

1.介绍几个函数:

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()中的设置元表方法去掉,结果是一样的