本篇在介绍lua绑定C++的一个最基本的模型,能够很方便的理解lua绑定C++对象后具体使用方式,主要是充分利用的元表的__index特性。
代码文件r_oo.cpp:
1 #include <iostream>
2 #include <cstring>
3 #include <stdlib.h>
4 extern "C" {
5 #include <lua.h>
6 #include <lualib.h>
7 #include <lauxlib.h>
8 }
9 #include "comm.h"
10 #include "luna.h"
11 #include "lunar.h"
12
13 using namespace std;
14
15 class Student
16 {
17 public:
18 Student(int iAge, int iScore):m_age(iAge), m_score(iScore){};
19 ~Student(){cout<<"delete Student"<<endl;}
20 int getAge(){return m_age;}
21 void setAge(int iAge){m_age = iAge;}
22 static int autoGc(lua_State *L){
23 Student** p = (Student**)lua_touserdata(L, 1);
24 cout << "auto gc. age: " << (*p)->m_age << " score: " << (*p)->m_score <<endl;
25 }
26 public:
27 int m_age;
28 int m_score;
29 };
30
31 int create_stdent(lua_State *L)
32 {
33 Student** p = (Student**)lua_newuserdata(L, sizeof(Student*));
34 *p = new Student(15, 100);
35
36 luaL_getmetatable(L, "MetaStu");
37 lua_setmetatable(L, -2);
38
39 return 1;
40 }
41
42 int get_age(lua_State *L)
43 {
44 Student** p = (Student**)lua_touserdata(L, 1);
45 int iAge = (*p)->getAge();
46 lua_pushinteger(L, iAge);
47 return 1;
48 }
49
50 int set_age(lua_State *L)
51 {
52 Student** p = (Student**)lua_touserdata(L, 1);
53 int iAge = lua_tointeger(L, 2);
54 (*p)->setAge(iAge);
55 return 0;
56 }
57
58 int auto_gc (lua_State *L)
59 {
60 Student** p = (Student**)lua_touserdata(L, 1);
61 (*p)-> autoGc(L);
62 return 0;
63 }
64
65 int lua_openStudent(lua_State *L)
66 {
67 const struct luaL_Reg list[] = {{"create", create_stdent}, {NULL, NULL}};
68 luaL_register(L, "student", list);
69
70 if (luaL_newmetatable(L, "MetaStu"))
71 {
72 lua_pushcfunction(L, &get_age);
73 lua_setfield(L, -2, "getAge");
74 lua_pushcfunction(L, &set_age);
75 lua_setfield(L, -2, "setAge");
76 lua_pushcfunction(L, &auto_gc);
77 lua_setfield(L, -2, "__gc");
78 lua_pushvalue(L, -1);
79 lua_setfield(L, -2, "__index");
80 }
81
82 return 1;
83 }
84
85 int main(int argc, char* argv[])
86 {
87 lua_State *L = luaL_newstate();
88 luaL_openlibs(L);
89 luaL_dofile(L, "tree.lua");
90
91 //bind to object use metatable
92 lua_openStudent(L);
93 print_stack(L);
94
95 luaL_dofile(L, "r_oo.lua");
96 print_stack(L);
97 lua_settop(L, 0);
98 lua_close(L);
99 return 0;
100 }
如上,主要思路有2步:
1、 lua_openStudent。只执行一次。
- 注册一个create_student的函数,方便在lua中调用student.create_student()创建student对象。注意,这时create_student创建的是一个userdata结构,存放C++ student的对象指针。
- 针对Student类生成一个全局的元表结构MetaStu, 注册一些元表函数类似setAge, getAge, setScore, getScore等,并且把__index指向元表本身。这样后期通过userdata调用方法时,可以自动在元表中进行查找,看起来更符合OO的使用特点。
2、 create_student。按需调用,多次调用创建不同的student对象实例。并且设置userdata的元表为MetaStu。
对应执行的r_oo.lua:
do
local stu = student.create();
print_tree(stu);
print_metatable(stu);
local age = stu:getAge();
stu:setAge(18);
local age2 = stu:getAge();
print("old: " .. age .. " new: " .. age2);
print ""
stu.setAge(stu,19)
print("old: " .. age .. " new: " .. stu:getAge(stu));
end
collectgarbage("collect");
如上所示:这里使用了stu:setAge(18)和std. setAge (stu,19),实际是因为在使用:符号时,系统默认把对象本身作为第一个参数传入,不用再显示,两者语法效果其实是一样的。
代码执行结果如下:
==========Total:2==========
idx:-1 type:5(table) 0x23ca830
idx:-2 type:5(table) 0x23ca8c0
===========================
MetaStu: 0x23c9e38
not a table
table: 0x23ca830
getScore function: 0x402b16
setAge function: 0x4027be
setScore function: 0x402b5a
__index table: 0x23ca830+
__gc function: 0x402ba8
getAge function: 0x40276f
__name MetaStu
old: 15 new: 18
old: 15 new: 19
auto gc. age: 19 score: 100
==========Total:2==========
idx:-1 type:5(table) 0x23ca830
idx:-2 type:5(table) 0x23ca8c0
===========================
如上所示:红色标记的就是print_metatable(stu)打印userdata元表的情况。gc垃圾收集会调用注册的__gc元方法,Student::autoGc被成功调用。
总结一下:这是让lua绑定C++对象的最基本模型。使用新创建userdata存放C++对象实例指针,并针对userdata设置元表,元表中注册各种C++的方法,并让__index指向元表自身,这样当使用诸如stu.setAge时,会通过__index指向的table进行索引,当__index指向自身就直接索引自身即可。这样在lua中创建userdata后,create_student接口也会自动创建C++对象关联在一起,并且在使用userdata过程中可以像C++引用成员方法一样去使用。
请发表评论