绑定C++对象到Lua中
写这篇帖子之前,我看过许多关于绑定C++对象到Lua中的文章。总结一下他们的做法,用到元表、注册表、以及一些表中函数操作的一些基础知识以及相关的细节。相信大家对Lua的表一点也不陌生,Lua表是个神奇的东西(本质上就是hash表),可以当做数组,可以当做map,还可用来模拟面向对象,这很Lua。
我们在Lua中模拟面向对象的步骤中大家有没有仔细去研究过用 "." 与 ":" 调用表中函数时的区别呢,下面带大家一起来研究一下。
--code 1:
--定义一张表obj
obj = {}
--下面的这行代码等价于obj["func"]=function(a,b,c)...
obj.func = function(a, b, c)
print(a, b, c)
end
print("obj:", obj)
obj.func(1,2)
obj:func(1,2)
输出:
obj: obj表地址
1 2 nil
obj表地址 1 2
从上面的输出可以看出,当我们可以发现,当用"."来调用表内的函数时,与普通的函数调用没啥区别。
但是当我们用":"来调用时,会把调用这个函数的那张表的传进去第一个参数中,后面的参数与实参一样。
有了这个发现我们就可以继续往下探究。
但是有一种情况是不会传入表到第一个参数的。看如下代码:
--下面这种定义表中函数的当大相信大家都不陌生
obj = {}
function obj:func(a, b, c)
print(a, b, c)
end
print("obj:", obj)
obj.func(1, 2)
obj:func(1, 2)
输出:
obj:obj表地址
2 nil nil
1 2 nil
发现了吧,当我们用如"function 表:函数名(......) .... end"的形式去定义一个表中的函数时,如果我们用"."来调用这个函数,就会得到莫名其妙的东西,这个现象不再我们的讨论之内;对于上面的定义方式,如果是用":"来调用那么跟调用普通的函数没啥区别。
这个了解一下就好,因为我们用lua c api 操作表中函数时用的是上上面的那种定义形式。
那么如果把这个obj表作为一个元表__index键的值再把这个元表与其他的表进行绑定,在触发__index时传的是哪张表进第一的参数呢
有如下代码:
obj = {}
obj.func = function(a, b, c)
print(a, b, c)
end
mtable = {__index = obj}
otherobj = {}
setmetatable(otherobj, mtable)
print("obj:", obj)
print("mtable", mtable)
print("otherobj:", otherobj)
otherobj.func(1, 2)
otherobj:func(1, 2)
输出:
obj:obj表地址
mtable:mtable表地址
otherobj:otherobj表地址
1 2 nil
otherobj表地址 1 2
从上面的结果可以知道,就算是在触发__index元方法调用的函数,如果是用":"来调用,那么一样会传入调用这个函数的那张表进第一个参数,就算是嵌套多层元表也是如此,大家感兴趣可以试一试嵌套多层元表,结果是一样的。
元表还有一些小知识,就是__index键对应的是一个函数的时候也会把调用这个函数的表传进去,然后第二个参数是要调用的那个函数的名字,这时你就得手动处理是返回一个函数还是返回一个其他的量了。这个大家感兴趣也可以做做实验,这也是元表的基础知识。
那么,如果这个函数时我们自己写的CFunction呢,还能这么如愿么,看下面的代码:
//filename:funct.c
#define LUA_LIB
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
static int _c_l_testfunc(lua_State* L)
{
unsigned char argc, index;
const char *typename;
if ((argc = lua_gettop(L)) != 0) {
printf("共传入 %d 个参数\n", argc);
for (index = 1; index <= argc; index++) {
printf(
"第 %d 个参数类型为: %s\n",
index, lua_typename(L, lua_type(L, index))
);
}
} else {
puts("0 个参数传入");
}
//清空栈
lua_settop(L, 0);
//把参数个数压入栈作为返回值
lua_pushinteger(L, argc);
return 1;
}
#if define(__cplusplus)
#define EXP_FUNC __declspec(dllexport)
#else
#define EXP_FUNC
#endif
LUA_API EXP_FUNC int luaopen_funct(lua_State* L)
{
lua_pushcfunction(L, _c_l_testfunc);
return 1;
}
上面的这个函数又来输出调用这个函数时的参数以及参数的类型。
把上面的代码编译成动态库,我们来在lua中调用这个函数
funct = require "funct"
--建一个表来存这个函数
obj = {}
obj.func = funct
print("\".\"调用:")
obj.func(1, "str")
print("\":\"调用:")
obj:func(1, "str")
输出:
"."调用:
共传入 2 个参数
第 1 个参数类型为: number
第 2 个参数类型为: string
":"调用
共传入 3 个参数
第 1 个参数类型为: table
第 2 个参数类型为: number
第 3 个参数类型为: string
发现了吧,与lua内函数定义的函数传参没区别。如果大家半信半疑的话再把刚才那个C代码改一下,再添加一个把某个参数位置的值输出出来。这跑一下这个例子就明白了。现在告诉大家的是就算是CFunction,我们使用":"调用时也会把调用这个函数的表传进第一个参数,这使得我么把C++绑进lua中有方法可寻。
还有一点,就是我们的userdata可以与元表绑定,lightuserdata不可以与元表绑定。如果我们在Lua中是对一个userdata进行":"调用的话,那么就会触发与之绑定的元表的__index方法,而且这回传进去的不是表了,是这个userdata,我们用lua_touserdata转换得到相应的对象指针。
在Lua中,我们不能直接对userdata进行操作,但是我们可以间接的通过与之绑定的元表的__index进行操作。
有一个点我们得注意的是,CFunction是普通成员函数指针类型,与类成员函数指针不一样,我们得再封装一层函数,这样子才能把函数绑定在我们的函数表中。
大致思路:
1.与类成员函数同名的CFunction,并且在其中完成对相应类成员函数的调用。
2.把定义的CFunction打包到一张函数表methods中
3.创建元表,设置键值对:__index = methods
4.把这张元表绑定到userdata上(我们的对象指针)
5.垃圾回收(主动回收和__gc被动回收)
按照上面的大致思路写成代码:
//filename: lbind.cpp
#include <iostream>
#include <string>
#define LUA_LIB
#include <lua.hpp>
//起名字时尽量起一个独一无二的名字,可以用UUID字符串作为名字
const char* ClassName = "{1111-2222-3333-4444}";
class MyClass {
public:
MyClass(const char* name, int a, int b):na(a),nb(b){
this->name = new std::string(name);
std::cout<< "Hello, I'm " << *(this->name) << ". MyClass was Created!" << std::endl;
};
~MyClass(void){
std::cout<< "Goodbye, I'm " << *(this->name) << ". MyClass was Destroyed!" << std::endl;
delete this->name;
};
int SumThem(void) {
return (this->na + this->nb);
}
void SetNumA(int a) {
this->na = a;
}
void SetNumB(int b) {
this->nb = b;
}
int GetNumA(void) {
return this->na;
}
int GetNumB(void) {
return this->nb;
}
//这里只是测试,暴露死私有成员是不安全的
void GetName(std::string **name) {
*name = (this->name);
}
void SetName(const char* name) {
delete this->name;
this->name = new std::string(name);
}
private:
int na, nb;
std::string* name;
};
//检查参数
static bool c_CheckPointer(lua_State* L, MyClass** po)
{
MyClass** pobj;
//检查是否有参数
if (lua_gettop(L) == 0) {
luaL_error(L, "参数错误!\n请用\":\"方式调用!");
return false;
}
//检查第一个参数是否是userdata
if (lua_type(L, 1) != LUA_TUSERDATA) {
luaL_error(L, "调用方式错误!\n请用\":\"方式调用!");
return false;
}
//取出userdata
pobj = (MyClass**)lua_touserdata(L, 1);
//判断用来装对象指针的那块内存是否已经被回收
if (pobj != NULL) {
//判断对象是否已经被销毁
if ((*po = *pobj) == NULL)
luaL_error(L, "对象已销毁!");
return true;
} else {
luaL_error(L, "内存已经被回收!");
return false;
}
}
static int c_SumThem(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
lua_pushinteger(L, obj->SumThem());
return 1;
}
static int c_SetNumA(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
//从第二个参数开始为第一个参数
//因为":"调用用掉第一个参数了,显示调用的参数的位置都往后挪一位
int a = luaL_checkinteger(L, 2);
obj->SetNumA(a);
return 0;
}
static int c_SetNumB(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
//从第二个参数开始为第一个参数
//因为":"调用用掉第一个参数了,显示调用的参数的位置都往后挪一位
int b = luaL_checkinteger(L, 2);
obj->SetNumB(b);
return 0;
}
static int c_GetNumA(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
int a = obj->GetNumA();
lua_pushinteger(L, a);
return 1;
}
static int c_GetNumB(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
int b = obj->GetNumB();
lua_pushinteger(L, b);
return 1;
}
static int c_GetName(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
std::string *name;
obj->GetName(&name);
lua_pushstring(L, name->c_str());
return 1;
}
static int c_SetName(lua_State* L)
{
MyClass* obj;
if (c_CheckPointer(L, &obj) == false)
return 0; //返回nil
const char* name = luaL_checkstring(L, 2);
obj->SetName(name);
return 0;
}
//主动回收
static int c_destroy(lua_State* L)
{
MyClass** pobj;
if (lua_gettop(L) == 0) {
luaL_error(L, "此函数无参数!请用\":\"方式调用!");
} else {
if (lua_type(L, 1) == LUA_TUSERDATA) {
pobj = (MyClass**)lua_touserdata(L, 1);
if (pobj != NULL) {
if (*pobj != NULL) {
delete *pobj;
*pobj = NULL;
} else {
luaL_error(L, "对象已销毁!");
}
} else {
luaL_error(L, "内存已经被回收!");
}
}
}
return 0;
}
//靠__gc 被动回收
static int c_gc_destroy(lua_State* L)
{
//肯定会有参数传进来
//直接对类型进行判断
if (lua_type(L, 1) == LUA_TUSERDATA)
return c_destroy(L);
}
//C函数表
const struct luaL_Reg methods[] = {
{"SumThem", c_SumThem},
{"SetNumA", c_SetNumA},
{"SetNumB", c_SetNumB},
{"GetNumA", c_GetNumA},
{"GetNumB", c_GetNumB},
{"GetName", c_GetName},
{"SetName", c_SetName},
{"destroy", c_destroy},
{NULL, NULL}
};
static int c_CreateMyClassObject(lua_State* L)
{
//需要一个二级指针,指向一个用来装对象指针的内存
MyClass** pobj;
//参数检测
if (lua_gettop(L) != 3)
return 0; //返回nil
const char* name = luaL_checkstring(L, 1);//第一个实参
int a = luaL_checkinteger(L, 2); //第二个实参
int b = luaL_checkinteger(L, 3); //第三个实参
//清空当前栈
lua_settop(L, 0);
pobj = (MyClass**)lua_newuserdata(L, sizeof(MyClass**));
if (pobj == NULL)
return 0; //空间分配错误
try {
*pobj = new MyClass(name, a, b);
} catch (...) {return 0;}
//userdata在栈中的位置
int userdata_index = lua_gettop(L);
//深入了解过C++的朋友都知道,不管创建多少个对象,成员函数只有一份
//我们就创建一张函数表,可以存进Lua注册表里,也可以存放到Lua全局表里
//如果我们要把函数表存放到注册表中,我们可以用luaL_newmetatable
//如果我们要把函数表存放到全局表中,我们可以用lua_setglobal
//创建函数表,这个函数表可以只有一份,太多会浪费内存
//下面就以存进_G表为例
//先判断现在的全局表里是否已经存在张函数表
lua_getglobal(L, ClassName);
//如果是栈顶的值是nil,则不存在,我们得创建新表
if (lua_type(L, -1) == LUA_TNIL) {
//接着再创建函数表,直接用lua给我们的函数直接创建一张函数表
luaL_newlib(L, methods);
//创建一个表副本,因为等会把表写入_G表中后会被弹出,后面还得用到
lua_pushvalue(L, -1);
//写入_G表中
lua_setglobal(L, ClassName);
}
//methods表在栈中的位置
int methods_index = lua_gettop(L);
//我们还需要一张元表,这张元表是存在注册表中的
//先判断,如果不存在就创建,存在就将这样表压栈
//名字可以和_G表中的表同名,因为是存在注册表中的
if (luaL_newmetatable(L, ClassName) != 0) {
if (luaL_getmetatable(L, ClassName) == 0) //获取出错
return 1; //nil被压栈,直接返回
}
//元表在栈中的索引
int mtable_index = lua_gettop(L);
//设置元表的__index
lua_pushliteral(L, "__index");
//先拷贝一份methods表
lua_pushvalue(L, methods_index);
//添加到元表中
lua_settable(L, mtable_index);
//我们还可以保护好元表(隐藏起来)
lua_pushliteral(L, "__metatable");
lua_pushstring(L, "就不让你知道,嘤嘤嘤~~~");
lua_settable(L, mtable_index);
//接下来就是垃圾回收问题了
//我们得考虑的是:垃圾回收的控制权在谁的手里。
//我们将两种都加入到我们的表中
//被动回收
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, c_gc_destroy);
lua_settable(L, mtable_index);
//设置弱引用
lua_pushliteral(L, "__mode");
lua_pushliteral(L, "kv");
lua_settable(L, mtable_index);
//最后,把元表与userdata绑定
lua_pushvalue(L, mtable_index);
lua_setmetatable(L, userdata_index);
//降低栈,让userdata位于栈顶
lua_settop(L, userdata_index);
return 1;
}
//这里就是把C函数导出到Lua中的接口
extern "C" __declspec(dllexport) int luaopen_lbind(lua_State* L)
{
//我们可以选择把函数名写到全局表里
//就可以保证函数名和导入名字一样
//也可以返回一个函数,由Lua来处理这个函数的存留
//我们这里就直接返回这个构造函数吧
lua_pushcfunction(L, c_CreateMyClassObject);
return 1;
}
下面写Lua测试代码,把测试刚编译出来的模块(动态库(windows下.dll文件))
--导入CFunction
--因为是通过返回CFunctoin的形式,名字随便起
MyClass = require "lbind"
--创建对象
o1 = MyClass("xxxx", 1, 2)
o2 = MyClass("yyyy", 3, 4)
o3 = MyClass("zzzz", 5, 6)
print("o1: ", o1)
print("o2: ", o2)
print("o3: ", o3)
--测试一下获取元表,我们设置了__metatable,所以不会得到真正的元表
print("__metatable: ", getmetatable(o3))
--当我们直接打印函数返回值时是不会输出nil的
--我们得主动的用":"方式去调用,不然就会报错
print("call o1 method:")
print("o1:GetName ->", o1:GetName())
print("o1:GetNumB -> ", o1:GetNumB())
print("o1:SetNumB -> ", o1:SetNumB(666))
print("o1:GetNumB -> ", o1:GetNumB())
print("o1:GetNumA -> ", o1:GetNumA())
print("o1:SumThem -> ", o1:SumThem())
print("call o2 method:")
print("o2:GetName -> ", o2:GetName())
print("o2:SetName -> ", o2:SetName("嘤嘤嘤"))
print("o2:GetName -> ", o2:GetName())
print("o2:GetNumA -> ", o2:GetNumA())
print("o2:GetNumB -> ", o2:GetNumB())
print("o2:SumThem -> ", o2:SumThem())
--我们有两种方式去销毁对象
--第一种是主动去销毁;第二种是让垃圾回收机制回收
print("主动销毁:")
o2:destroy()
print("在创建一个类用来测试:")
testobj = MyClass("Test", 2333, 666)
testobj = nil --将其设为nil
collectgarbage() --等待的话太久了,我们直接强制启动垃圾回收也一样
print("call o3 method:")
print("o3:GetName -> ", o3:GetName())
print("o3:GetNumA -> ", o3:GetNumA())
print("o3:GetNumB -> ", o3:GetNumB())
print("o3:SumThem -> ", o3:SumThem())
print("Lua脚本执行完会自动处理完所有的对象:")
运行这段代码,输出:
Hello, I'm xxxx. MyClass was Created!
Hello, I'm yyyy. MyClass was Created!
Hello, I'm zzzz. MyClass was Created!
o1: {1111-2222-3333-4444}: 003FECD0
o2: {1111-2222-3333-4444}: 003FEDC0
o3: {1111-2222-3333-4444}: 003FEE10
__metatable: 就不让你知道,嘤嘤嘤~~~
call o1 method:
o1:GetName -> xxxx
o1:GetNumB -> 2
o1:SetNumB ->
o1:GetNumB -> 666
o1:GetNumA -> 1
o1:SumThem -> 667
call o2 method:
o2:GetName -> yyyy
o2:SetName ->
o2:GetName -> 嘤嘤嘤
o2:GetNumA -> 3
o2:GetNumB -> 4
o2:SumThem -> 7
主动销毁:
Goodbye, I'm 嘤嘤嘤. MyClass was Destroyed!
在创建一个类用来测试:
Hello, I'm Test. MyClass was Created!
Goodbye, I'm Test. MyClass was Destroyed!
call o3 method:
o3:GetName -> zzzz
o3:GetNumA -> 5
o3:GetNumB -> 6
o3:SumThem -> 11
Lua脚本执行完会自动处理完所有的对象:
Goodbye, I'm zzzz. MyClass was Destroyed!
Goodbye, I'm xxxx. MyClass was Destroyed!
看来一切都很顺利嘛
代码中,已经做了详细的注释了,这里就不再赘述。
你们发现一个问题没,如果我要继续在类中添加函数,那么我就得在外面再封装一层函数,每添加一个类成员函数都得在外部添加一个成员函数,这样子也太累人了,有什么比较好的解决办法呢。
这里我们就得用到upvalue了,我们定义一张类成员函数表,将类成员函数的地址存进去。
我们定义一个Thunk(函数)用来统一做统一调用。emmmmm
之前说过,可以一个函数共享一份upvalue,也可以同一个函数拥有不同的upvalue。
我们还更直接得把类成员函数绑定到Lua中,里面也有详细的代码注释,相信上面的看懂了下面的也一定能看懂:
//filename: lbindthunk.cpp
#include <iostream>
#include <string>
#define LUA_LIB
#include <lua.hpp>
//声明
class MyClass;
typedef int (MyClass::*cmtype)(lua_State* L);
#define METHOD(CLASS, METHODNAME) {#METHODNAME, &CLASS::METHODNAME}
//定义
class MyClass {
public:
//用一个结构体把类成员函数名,以及成员函数指针放到结构体里
//等会把结构体作为upvalue绑定到不同name的thunk
//其实都是同一个函数thunk啦,只不过在Lua中被赋予了不同的upvalue
struct methods{
const char* name;
cmtype method;
};
static char ClassName;
public:
static int Create(lua_State* L) {
MyClass** pobj;
//如果下次想要再添加函数在这里修改就好了
static struct MyClass::methods methodtable[] = {
METHOD(MyClass, SumThem),
METHOD(MyClass, SetNumA),
METHOD(MyClass, SetNumB),
METHOD(MyClass, GetNumA),
METHOD(MyClass, GetNumB),
METHOD(MyClass, SetName),
METHOD(MyClass, GetName),
{NULL, NULL}
};
//参数检测
if (lua_gettop(L) != 3)
return 0; //返回nil
const char* name = luaL_checkstring(L, 1);//第一个实参
int a = luaL_checkinteger(L, 2); //第二个实参
int b = luaL_checkinteger(L, 3); //第三个实参
//清空当前栈
lua_settop(L, 0);
pobj = (MyClass**)lua_newuserdata(L, sizeof(MyClass**));
if (pobj == NULL)
return 0; //空间分配错误
try {
*pobj = new MyClass(name, a, b);
} catch (...) {return 0;}
//userdata在栈中的位置
int userdata_index = lua_gettop(L);
//这回我们也来自己实现一手函数表
//我们把对应的成员函数地址存进一个结构体数组里
//把结构体作为上值,当我们在lua中调用时,就可以通过thunk调用到相应的函数啦
//老规矩,先判断全局表中是否已经存在这张函数表
lua_getglobal(L, MyClass::ClassName);
int method_index = lua_gettop(L);
if (lua_type(L, method_index) == LUA_TNIL) {
//创建一张用来存放函数的表
lua_newtable(L);
//函数表在栈中的位置
method_index = lua_gettop(L);
//这里和官方的例子的做法一模一样
for (struct MyClass::methods* p = ( MyClass::methods*)methodtable;
(p->name) && (p->method); p++) {
lua_pushstring(L, p->name);
lua_pushlightuserdata(L, p);
lua_pushcclosure(L, MyClass::Thunk, 1);
lua_settable(L, method_index);
}
//主动回收
lua_pushliteral(L, "destroy");
lua_pushcfunction(L, MyClass::destroy);
lua_settable(L, method_index);
}
if (luaL_newmetatable(L, ClassName) != 0) {
if (luaL_getmetatable(L, ClassName) == 0) //获取出错
return 1; //nil被压栈,直接返回
}
//元表在栈中的索引
int mtable_index = lua_gettop(L);
//下面的步骤就跟上面的之前的例子中一模一样
//设置元表的__index
lua_pushliteral(L, "__index");
//先拷贝一份methods表
lua_pushvalue(L, method_index);
//添加到元表中
lua_settable(L, mtable_index);
//我们还可以保护好元表(隐藏起来)
lua_pushliteral(L, "__metatable");
lua_pushstring(L, "就不让你知道,嘤嘤嘤~~~");
lua_settable(L, mtable_index);
//接下来就是垃圾回收问题了
//我们得考虑的是:垃圾回收的控制权在谁的手里。
//我们将两种都加入到我们的表中
//被动回收
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, MyClass::destroy);
lua_settable(L, mtable_index);
//设置弱引用
lua_pushliteral(L, "__mode");
lua_pushliteral(L, "kv");
lua_settable(L, mtable_index);
//最后,把元表与userdata绑定
lua_pushvalue(L, mtable_index);
lua_setmetatable(L, userdata_index);
//降低栈,让userdata位于栈顶
lua_settop(L, userdata_index);
return 1;
}
//这是调用函数的核心
static int Thunk(lua_State* L) {
MyClass* o;
//确保万无一失
if (MyClass::CheckPointer(L, &o) == false)
return 0;
struct MyClass::methods* p = (MyClass::methods*)lua_touserdata(L, lua_upvalueindex(1));
if (!p) {
luaL_error(L, "函数表出错!");
return 0;
}
//把栈底的userdata删了,因为return那调用的函数中可以使用this指针
lua_remove(L, 1);
//调用成员函数
return (o->*(p->method))(L);
}
static bool CheckPointer(lua_State* L, MyClass** po)
{
MyClass** pobj;
//检查是否有参数
if (lua_gettop(L) == 0) {
luaL_error(L, "参数错误!\n请用\":\"方式调用!");
return false;
}
//检查第一个参数是否是userdata
if (lua_type(L, 1) != LUA_TUSERDATA) {
luaL_error(L, "调用方式错误!\n请用\":\"方式调用!");
return false;
}
//取出userdata
pobj = (MyClass**)lua_touserdata(L, 1);
//判断用来装对象指针的那块内存是否已经被回收
if (pobj != NULL) {
//判断对象是否已经被销毁
if ((*po = *pobj) == NULL)
luaL_error(L, "对象已销毁!");
return true;
} else {
luaL_error(L, "内存已经被回收!");
return false;
}
}
static int destroy(lua_State* L) {
MyClass** pobj;
if (lua_gettop(L) == 0) {
luaL_error(L, "此函数无参数!请用\":\"方式调用!");
} else {
if (lua_type(L, 1) == LUA_TUSERDATA) {
pobj = (MyClass**)lua_touserdata(L, 1);
if (pobj != NULL) {
if (*pobj != NULL) {
delete *pobj;
*pobj = NULL;
} else {
luaL_error(L, "对象已销毁!");
}
} else {
luaL_error(L, "内存已经被回收!");
}
}
}
return 0;
}
~MyClass(void){
std::cout<< "Goodbye, I'm " << *(this->name) << ". MyClass was Destroyed!" << std::endl;
delete this->name;
};
//////////////////////////////////////////////////////
//下面这些类成员方法直接绑定到Lua中
int SumThem(lua_State* L) {
lua_pushinteger(L, this->na + this->nb);
return 1;
}
int SetNumA(lua_State* L) {
this->na = luaL_checkinteger(L, 1);
return 0;
}
int SetNumB(lua_State* L) {
this->nb = luaL_checkinteger(L, 1);
return 0;
}
int GetNumA(lua_State* L) {
lua_pushinteger(L, this->na);
return 1;
}
int GetNumB(lua_State* L) {
lua_pushinteger(L, this->nb);
return 1;
}
int GetName(lua_State* L) {
lua_pushstring(L, this->name->c_str());
return 1;
}
int SetName(lua_State* L) {
const char* name = luaL_checkstring(L, 1);
delete this->name;
this->name = new std::string(name);
return 1;
}
////////////////////////////////////////////////////////
private:
int na, nb;
std::string* name;
MyClass(const char* name, int a, int b):na(a),nb(b){
this->name = new std::string(name);
std::cout<< "Hello, I'm " << *(this->name) << ". MyClass was Created!" << std::endl;
};
MyClass(void){}
};
char MyClass::ClassName = {"{1111-2222-3333-4444}"};
extern "C" __declspec(dllexport) int luaopen_lbindthunk(lua_State* L)
{
//直接把MyClass中的静态成员函数Create作为CFunction返回
lua_pushcfunction(L, MyClass::Create);
return 1;
}
我们接下来就是测试能不能用,我们还是用test.lua这个文件来测试
运行后输出:
Hello, I'm xxxx. MyClass was Created!
Hello, I'm yyyy. MyClass was Created!
Hello, I'm zzzz. MyClass was Created!
o1: {1111-2222-3333-4444}: 0082A288
o2: {1111-2222-3333-4444}: 0082DA50
o3: {1111-2222-3333-4444}: 0082DBB8
__metatable: 就不让你知道,嘤嘤嘤~~~
call o1 method:
o1:GetName -> xxxx
o1:GetNumB -> 2
o1:SetNumB ->
o1:GetNumB -> 666
o1:GetNumA -> 1
o1:SumThem -> 667
call o2 method:
o2:GetName -> yyyy
o2:SetName -> 嘤嘤嘤
o2:GetName -> 嘤嘤嘤
o2:GetNumA -> 3
o2:GetNumB -> 4
o2:SumThem -> 7
主动销毁:
Goodbye, I'm 嘤嘤嘤. MyClass was Destroye
在创建一个类用来测试:
Hello, I'm Test. MyClass was Created!
Goodbye, I'm Test. MyClass was Destroyed!
call o3 method:
o3:GetName -> zzzz
o3:GetNumA -> 5
o3:GetNumB -> 6
o3:SumThem -> 11
Lua脚本执行完会自动处理完所有的对象:
Goodbye, I'm zzzz. MyClass was Destroyed!
Goodbye, I'm xxxx. MyClass was Destroyed!
其实我写的这个贴的东西并不是很多,仔细看下来也就这几样。
这些东西都是看了官方的那个例子的读后感,官方的那个例子跟这个也差不多,用的也是thunk的方式。
用类模板的方式实现的,直接就是个小框架,复用性高。
这里就不把官方的那个例子贴出来的,在文章结尾会把连接放上去。
另外的是,我会把这帖子里的例子的代码,以及编译好的模块传上来,顺便把官方的那个例子的页面给download下来,防止404。。。。
这文章太长了,懒得美化了。。。。。如果我写的这篇文章对你有用的话支持一下哟~~~mua~~~
这是一些官方的例子:http://lua-users.org/wiki/SampleCode
这是本文所用的例子:http://lua-users.org/wiki/SimplerCppBinding
小甲鱼:Change the World by Programming.
沙发,顶一个 学习学习 学习一下。。。希望能有帮助
页:
[1]