Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类


主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍)

部分内容查阅自:《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和C++交互 学习记录之二:栈操作

 

Lua和C++交互 学习记录之三:全局值交互

 

Lua和C++交互 学习记录之四:全局table交互

 

Lua和C++交互 学习记录之五:全局数组交互

 

Lua和C++交互 学习记录之六:全局函数交互

 

Lua和C++交互 学习记录之七:C++全局函数注册为Lua模块

 

Lua和C++交互 学习记录之八:C++类注册为Lua模块

 

Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM