主要內容轉載自:子龍山人博客(強烈建議去子龍山人博客完全學習一遍)
部分內容查閱自:《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++注冊的類》
