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