主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍)
部分内容查阅自:《Lua 5.3 参考手册》中文版 译者 云风 制作 Kavcc
vs2013+lua-5.3.3
在上一节《Lua和C++交互 学习记录之八:注册C++类为Lua模块》里介绍了在Lua中以模块的方式使用C++注册的类。
下面将其修改为熟悉的面向对象调用方式。
1.Lua中面向对象的方式
①在Lua中使用student_obj:get_age()其实相当于student_obj.get_age(student_obj)
②给student_obj增加一个元表metatable,并设置元表里key为"__index"的值的为metatable本身,然后将成员操作方法添加到元表metatable里,这样通过:操作符就可以找到对应的方法了。
③这个增加的元表会放在Lua的全局表中用一个自定义的字符串,比如"StudentClass",为key值进行保存(为了避免冲突,最好能起比较特殊的key值)。
2.Lua的全局表
①这个全局表可以使用LUA_REGISTRYINDEX索引从Lua中得到。
1 //lua->stack
2 lua_getfield(L, LUA_REGISTRYINDEX, "StudentClass"); 3
4 ////-------等价于下面两个函数------
5 //lua_pushstring("StudentClass"); 6 //lua_gettable(L, LUA_REGISTRYINDEX);
②可以使用相应的lua_setfield函数设置table,下面的-1使用LUA_REGISTRYINDEX,就是设置全局表中Key为"StudentClass"的值(后面的代码就是将元表作为值)
1 lua_pushinteger(L, 66); //val
2 lua_setfield(L, -1, "StudentClass"); 3
4 ////-------等价于下面函数------
5 //lua_pushstring("StudentClass"); //key 6 //lua_pushinteger(L, 66); //val 7 //lua_settable(L, -1);
3.将所有函数分为两部分进行注册
①第一部分:构造函数,和原来一样注册到Lua使用
1 //构造函数
2 static const luaL_Reg lua_reg_student_constructor_funcs[] = { 3 { "create", lua_create_new_student }, 4 { NULL, NULL } 5 };
②第二部分:成员操作函数,需要注册到元表里
1 //成员操作函数
2 static const luaL_Reg lua_reg_student_member_funcs[] = { 3 { "get_name", lua_get_name }, 4 { "set_name", lua_set_name }, 5 { "get_age", lua_get_age }, 6 { "set_age", lua_set_age }, 7 { "print", lua_print }, 8 { NULL, NULL }, 9 };
4.修改注册函数:创建元表,设置元表的__index为元表本身,注册成员操作函数到元表中
1 int luaopen_student_libs(lua_State* L) 2 { 3 //创建全局元表(里面包含了对LUA_REGISTRYINDEX的操作),元表的位置为-1
4 luaL_newmetatable(L, "StudentClass"); 5
6 //将元表作为一个副本压栈到位置-1,原元表位置-2
7 lua_pushvalue(L, -1); 8
9 //设置-2位置元表的__index索引的值为位置-1的元表,并弹出位置-1的元表,原元表的位置为-1
10 lua_setfield(L, -2, "__index"); 11
12 //将成员函数注册到元表中(到这里,全局元表的设置就全部完成了)
13 luaL_setfuncs(L, lua_reg_student_member_funcs, 0); 14
15 //注册构造函数到新表中,并返回给Lua
16 luaL_newlib(L, lua_reg_student_constructor_funcs); 17
18 return 1; 19 }
5.修改创建对象函数:创建对象的userdata,将全局元表设置到userdata上
1 int lua_create_new_student(lua_State* L) 2 { 3 //创建一个对象指针放到stack里,返回给Lua中使用,userdata的位置-1
4 Student** s = (Student**)lua_newuserdata(L, sizeof(Student*)); 5 *s = new Student(); 6
7 //Lua->stack,得到全局元表位置-1,userdata位置-2
8 luaL_getmetatable(L, "StudentClass"); 9
10 //将元表赋值给位置-2的userdata,并弹出-1的元表
11 lua_setmetatable(L, -2); 12
13 return 1; 14 }
6.修改Lua中的调用方式为面向对象方式
1 local student_obj = Student.create() 2 student_obj:set_name("Jack") 3 student_obj:print() 4
5 --下面的代码也是可行的
6 --student_obj.set_name(student_obj,"Jack")
7 --student_obj.print(student_obj)
以上,就完成了面向对象的内容了。
7.使用luaL_checkudata宏替换lua_touserdata函数
Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass");
除了可以转换userdata之外,还可以检查是否有"StudentClass"的元表,增加程序健壮性。
8.自动GC
①当Lua进行自动内存回收GC时,会调用内部的__gc函数,所以定义一个函数和其进行注册对应
②函数实现
1 int lua_auto_gc(lua_State* L) 2 { 3 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 4 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 5
6 if (s){ 7 delete *s; 8 } 9
10 return 0; 11 }
③在注册成员函数lua_reg_student_member_funcs中增加对应的函数
{ "__gc", lua_auto_gc }, //注册Lua内部函数__gc
④修改对应的Lua代码,增加对象回收的代码
1 --让其进行自动gc
2 student_obj = nil
3
4 --手动强制gc
5 --collectgarbage("collect")
⑤还有比较常用的内部函数是__tostring
下面列出整个项目的所有文件:
一.main.cpp文件
1 #include <iostream>
2
3 //这个头文件包含了所需的其它头文件
4 #include "lua.hpp"
5
6 #include "Student.h"
7 #include "StudentRegFuncs.h"
8
9 static const luaL_Reg lua_reg_libs[] = { 10 { "base", luaopen_base }, //系统模块
11 { "Student", luaopen_student_libs}, //模块名字Student,注册函数luaopen_student_libs
12 { NULL, NULL } 13 }; 14
15 int main(int argc, char* argv[]) 16 { 17 if (lua_State* L = luaL_newstate()){ 18
19 //注册让lua使用的库
20 const luaL_Reg* lua_reg = lua_reg_libs; 21 for (; lua_reg->func; ++lua_reg){ 22 luaL_requiref(L, lua_reg->name, lua_reg->func, 1); 23 lua_pop(L, 1); 24 } 25 //加载脚本,如果出错,则打印错误
26 if (luaL_dofile(L, "hello.lua")){ 27 std::cout << lua_tostring(L, -1) << std::endl; 28 } 29
30 lua_close(L); 31 } 32 else{ 33 std::cout << "luaL_newstate error !" << std::endl; 34 } 35
36 system("pause"); 37
38 return 0; 39 }
二.Student.h文件
1 #pragma once
2
3 #include <iostream>
4 #include <string>
5
6 class Student 7 { 8 public: 9 //构造/析构函数
10 Student(); 11 ~Student(); 12
13 //get/set函数
14 std::string get_name(); 15 void set_name(std::string name); 16 unsigned get_age(); 17 void set_age(unsigned age); 18
19 //打印函数
20 void print(); 21
22 private: 23 std::string _name; 24 unsigned _age; 25 };
三.Student.cpp文件
1 #include "Student.h"
2
3 Student::Student() 4 :_name("Empty"), 5 _age(0) 6 { 7 std::cout << "Student Constructor" << std::endl; 8 } 9
10 Student::~Student() 11 { 12 std::cout << "Student Destructor" << std::endl; 13 } 14
15 std::string Student::get_name() 16 { 17 return _name; 18 } 19
20 void Student::set_name(std::string name) 21 { 22 _name = name; 23 } 24
25 unsigned Student::get_age() 26 { 27 return _age; 28 } 29
30 void Student::set_age(unsigned age) 31 { 32 _age = age; 33 } 34
35 void Student::print() 36 { 37 std::cout << "name :" << _name << " age : " << _age << std::endl; 38 }
四.StudentRegFuncs.h文件
#pragma once #include "Student.h" #include "lua.hpp"
//------定义相关的全局函数------ //创建对象
int lua_create_new_student(lua_State* L); //get/set函数
int lua_get_name(lua_State* L); int lua_set_name(lua_State* L); int lua_get_age(lua_State* L); int lua_set_age(lua_State* L); //打印函数
int lua_print(lua_State* L); //转换为字符串函数
int lua_student2string(lua_State* L); //自动GC
int lua_auto_gc(lua_State* L); //------注册全局函数供Lua使用------ //构造函数
static const luaL_Reg lua_reg_student_constructor_funcs[] = { { "create", lua_create_new_student }, { NULL, NULL } }; //成员操作函数
static const luaL_Reg lua_reg_student_member_funcs[] = { { "get_name", lua_get_name }, { "set_name", lua_set_name }, { "get_age", lua_get_age }, { "set_age", lua_set_age }, { "print", lua_print }, { "__gc", lua_auto_gc }, //注册Lua内部函数__gc
{ "__tostring", lua_student2string }, { NULL, NULL }, }; int luaopen_student_libs(lua_State* L);
五.StudentRegFuncs.cpp文件
1 #include "StudentRegFuncs.h"
2
3 int lua_create_new_student(lua_State* L) 4 { 5 //创建一个对象指针放到stack里,返回给Lua中使用,userdata的位置-1
6 Student** s = (Student**)lua_newuserdata(L, sizeof(Student*)); 7 *s = new Student(); 8
9 //Lua->stack,得到全局元表位置-1,userdata位置-2
10 luaL_getmetatable(L, "StudentClass"); 11
12 //将元表赋值给位置-2的userdata,并弹出-1的元表
13 lua_setmetatable(L, -2); 14
15 return 1; 16 } 17
18 int lua_get_name(lua_State* L) 19 { 20 //得到第一个传入的对象参数(在stack最底部)
21 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 22 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 23
24 //清空stack
25 lua_settop(L, 0); 26
27 //将数据放入stack中,供Lua使用
28 lua_pushstring(L, (*s)->get_name().c_str()); 29
30 return 1; 31 } 32
33 int lua_set_name(lua_State* L) 34 { 35 //得到第一个传入的对象参数
36 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 37 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 38
39 luaL_checktype(L, -1, LUA_TSTRING); 40
41 std::string name = lua_tostring(L, -1); 42 (*s)->set_name(name); 43
44 return 0; 45 } 46
47 int lua_get_age(lua_State* L) 48 { 49 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 50 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 51
52 lua_pushinteger(L, (*s)->get_age()); 53
54 return 1; 55 } 56
57 int lua_set_age(lua_State* L) 58 { 59 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 60 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 61
62 luaL_checktype(L, -1, LUA_TNUMBER); 63
64 (*s)->set_age((unsigned)lua_tointeger(L, -1)); 65
66 return 0; 67 } 68
69 int lua_print(lua_State* L) 70 { 71 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 72 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 73
74 (*s)->print(); 75
76 return 0; 77 } 78
79 int lua_student2string(lua_State* L) 80 { 81 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 82 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 83
84 lua_pushfstring(L, "This is student name : %s age : %d !", (*s)->get_name().c_str(), (*s)->get_age()); 85
86 return 1; 87 } 88
89 int lua_auto_gc(lua_State* L) 90 { 91 Student** s = (Student**)luaL_checkudata(L, 1, "StudentClass"); 92 luaL_argcheck(L, s != NULL, 1, "invalid user data"); 93
94 if (s){ 95 delete *s; 96 } 97
98 return 0; 99 } 100
101 int luaopen_student_libs(lua_State* L) 102 { 103 //创建全局元表(里面包含了对LUA_REGISTRYINDEX的操作),元表的位置为-1
104 luaL_newmetatable(L, "StudentClass"); 105
106 //将元表作为一个副本压栈到位置-1,原元表位置-2
107 lua_pushvalue(L, -1); 108
109 //设置-2位置元表的__index索引的值为位置-1的元表,并弹出位置-1的元表,原元表的位置为-1
110 lua_setfield(L, -2, "__index"); 111
112 //将成员函数注册到元表中(到这里,全局元表的设置就全部完成了)
113 luaL_setfuncs(L, lua_reg_student_member_funcs, 0); 114
115 //注册构造函数到新表中,并返回给Lua
116 luaL_newlib(L, lua_reg_student_constructor_funcs); 117
118 return 1; 119 }
六.hello.lua文件
1 local student_obj = Student.create() 2 student_obj:set_name("Jack") 3 student_obj:print() 4
5 --使用内部的__tostring函数进行打印
6 print(student_obj) 7
8 --下面的代码也是可行的
9 --student_obj.set_name(student_obj,"Jack")
10 --student_obj.print(student_obj)
11
12 --让其进行自动gc
13 student_obj = nil
14
15 --手动强制gc
16 --collectgarbage("collect")
Lua和C++交互系列:
《Lua和C++交互 学习记录之七:C++全局函数注册为Lua模块》
《Lua和C++交互 学习记录之八:C++类注册为Lua模块》
《Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类》
