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中得到。
//lua->stack
lua_getfield(L, LUA_REGISTRYINDEX, "StudentClass"); ////-------等价于下面两个函数------
//lua_pushstring("StudentClass");
//lua_gettable(L, LUA_REGISTRYINDEX);
②可以使用相应的lua_setfield函数设置table,下面的-1使用LUA_REGISTRYINDEX,就是设置全局表中Key为"StudentClass"的值(后面的代码就是将元表作为值)
lua_pushinteger(L, ); //val
lua_setfield(L, -, "StudentClass"); ////-------等价于下面函数------
//lua_pushstring("StudentClass"); //key
//lua_pushinteger(L, 66); //val
//lua_settable(L, -1);
3.将所有函数分为两部分进行注册
①第一部分:构造函数,和原来一样注册到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 },
{ NULL, NULL },
};
4.修改注册函数:创建元表,设置元表的__index为元表本身,注册成员操作函数到元表中
int luaopen_student_libs(lua_State* L)
{
//创建全局元表(里面包含了对LUA_REGISTRYINDEX的操作),元表的位置为-1
luaL_newmetatable(L, "StudentClass"); //将元表作为一个副本压栈到位置-1,原元表位置-2
lua_pushvalue(L, -); //设置-2位置元表的__index索引的值为位置-1的元表,并弹出位置-1的元表,原元表的位置为-1
lua_setfield(L, -, "__index"); //将成员函数注册到元表中(到这里,全局元表的设置就全部完成了)
luaL_setfuncs(L, lua_reg_student_member_funcs, ); //注册构造函数到新表中,并返回给Lua
luaL_newlib(L, lua_reg_student_constructor_funcs); return ;
}
5.修改创建对象函数:创建对象的userdata,将全局元表设置到userdata上
int lua_create_new_student(lua_State* L)
{
//创建一个对象指针放到stack里,返回给Lua中使用,userdata的位置-1
Student** s = (Student**)lua_newuserdata(L, sizeof(Student*));
*s = new Student(); //Lua->stack,得到全局元表位置-1,userdata位置-2
luaL_getmetatable(L, "StudentClass"); //将元表赋值给位置-2的userdata,并弹出-1的元表
lua_setmetatable(L, -); return ;
}
6.修改Lua中的调用方式为面向对象方式
local student_obj = Student.create()
student_obj:set_name("Jack")
student_obj:print() --下面的代码也是可行的
--student_obj.set_name(student_obj,"Jack")
--student_obj.print(student_obj)
以上,就完成了面向对象的内容了。
7.使用luaL_checkudata宏替换lua_touserdata函数
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
除了可以转换userdata之外,还可以检查是否有"StudentClass"的元表,增加程序健壮性。
8.自动GC
①当Lua进行自动内存回收GC时,会调用内部的__gc函数,所以定义一个函数和其进行注册对应
②函数实现
int lua_auto_gc(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); if (s){
delete *s;
} return ;
}
③在注册成员函数lua_reg_student_member_funcs中增加对应的函数
{ "__gc", lua_auto_gc }, //注册Lua内部函数__gc
④修改对应的Lua代码,增加对象回收的代码
--让其进行自动gc
student_obj = nil --手动强制gc
--collectgarbage("collect")
⑤还有比较常用的内部函数是__tostring
下面列出整个项目的所有文件:
一.main.cpp文件
#include <iostream> //这个头文件包含了所需的其它头文件
#include "lua.hpp" #include "Student.h"
#include "StudentRegFuncs.h" static const luaL_Reg lua_reg_libs[] = {
{ "base", luaopen_base }, //系统模块
{ "Student", luaopen_student_libs}, //模块名字Student,注册函数luaopen_student_libs
{ NULL, NULL }
}; int main(int argc, char* argv[])
{
if (lua_State* L = luaL_newstate()){ //注册让lua使用的库
const luaL_Reg* lua_reg = lua_reg_libs;
for (; lua_reg->func; ++lua_reg){
luaL_requiref(L, lua_reg->name, lua_reg->func, );
lua_pop(L, );
}
//加载脚本,如果出错,则打印错误
if (luaL_dofile(L, "hello.lua")){
std::cout << lua_tostring(L, -) << std::endl;
} lua_close(L);
}
else{
std::cout << "luaL_newstate error !" << std::endl;
} system("pause"); return ;
}
二.Student.h文件
#pragma once #include <iostream>
#include <string> class Student
{
public:
//构造/析构函数
Student();
~Student(); //get/set函数
std::string get_name();
void set_name(std::string name);
unsigned get_age();
void set_age(unsigned age); //打印函数
void print(); private:
std::string _name;
unsigned _age;
};
三.Student.cpp文件
#include "Student.h" Student::Student()
:_name("Empty"),
_age()
{
std::cout << "Student Constructor" << std::endl;
} Student::~Student()
{
std::cout << "Student Destructor" << std::endl;
} std::string Student::get_name()
{
return _name;
} void Student::set_name(std::string name)
{
_name = name;
} unsigned Student::get_age()
{
return _age;
} void Student::set_age(unsigned age)
{
_age = age;
} void Student::print()
{
std::cout << "name :" << _name << " age : " << _age << std::endl;
}
四.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文件
#include "StudentRegFuncs.h" int lua_create_new_student(lua_State* L)
{
//创建一个对象指针放到stack里,返回给Lua中使用,userdata的位置-1
Student** s = (Student**)lua_newuserdata(L, sizeof(Student*));
*s = new Student(); //Lua->stack,得到全局元表位置-1,userdata位置-2
luaL_getmetatable(L, "StudentClass"); //将元表赋值给位置-2的userdata,并弹出-1的元表
lua_setmetatable(L, -); return ;
} int lua_get_name(lua_State* L)
{
//得到第一个传入的对象参数(在stack最底部)
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); //清空stack
lua_settop(L, ); //将数据放入stack中,供Lua使用
lua_pushstring(L, (*s)->get_name().c_str()); return ;
} int lua_set_name(lua_State* L)
{
//得到第一个传入的对象参数
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); luaL_checktype(L, -, LUA_TSTRING); std::string name = lua_tostring(L, -);
(*s)->set_name(name); return ;
} int lua_get_age(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); lua_pushinteger(L, (*s)->get_age()); return ;
} int lua_set_age(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); luaL_checktype(L, -, LUA_TNUMBER); (*s)->set_age((unsigned)lua_tointeger(L, -)); return ;
} int lua_print(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); (*s)->print(); return ;
} int lua_student2string(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); lua_pushfstring(L, "This is student name : %s age : %d !", (*s)->get_name().c_str(), (*s)->get_age()); return ;
} int lua_auto_gc(lua_State* L)
{
Student** s = (Student**)luaL_checkudata(L, , "StudentClass");
luaL_argcheck(L, s != NULL, , "invalid user data"); if (s){
delete *s;
} return ;
} int luaopen_student_libs(lua_State* L)
{
//创建全局元表(里面包含了对LUA_REGISTRYINDEX的操作),元表的位置为-1
luaL_newmetatable(L, "StudentClass"); //将元表作为一个副本压栈到位置-1,原元表位置-2
lua_pushvalue(L, -); //设置-2位置元表的__index索引的值为位置-1的元表,并弹出位置-1的元表,原元表的位置为-1
lua_setfield(L, -, "__index"); //将成员函数注册到元表中(到这里,全局元表的设置就全部完成了)
luaL_setfuncs(L, lua_reg_student_member_funcs, ); //注册构造函数到新表中,并返回给Lua
luaL_newlib(L, lua_reg_student_constructor_funcs); return ;
}
六.hello.lua文件
local student_obj = Student.create()
student_obj:set_name("Jack")
student_obj:print() --使用内部的__tostring函数进行打印
print(student_obj) --下面的代码也是可行的
--student_obj.set_name(student_obj,"Jack")
--student_obj.print(student_obj) --让其进行自动gc
student_obj = nil --手动强制gc
--collectgarbage("collect")
Lua和C++交互系列:
《Lua和C++交互 学习记录之七:C++全局函数注册为Lua模块》
《Lua和C++交互 学习记录之八:C++类注册为Lua模块》
《Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类》
最新文章
- MySQL学习笔记十七:复制特性
- OpenXml Sdk 根据Word模板导出到word
- chrome + vi
- oracle 查询最近执行过的 SQL语句(转载)
- Windows下编译使用Aliyun OSS PHP SDK
- javascript/jquery读取和修改HTTP headers
- 【转】rinex
- python 内置函数range和xrange
- 包装类及 LeetCode 每日一题
- adv生成控制器手腕位置倾斜原因以及解决方案
- #JS 窗口resize避免触发多次
- 服务器主体 ";sa"; 无法在当前安全上下文下访问数据库 XXX[SQLSTATE 08004] (错误 916). 该步骤失败。
- php 常用英语小汇
- 理解ROC和AUC
- Django Rest Framework(分页、视图、路由、渲染器)
- Algorithm2: 重复查过半数的元素
- STL : 反向迭代器(Reverse Iterator)
- JSON初体验(三):FastJson解析
- 使用Array的原型使对象具有length,和数组的内容
- 让C#可以像Javascript一样操作Json DynamicJson
热门文章
- vue中的指令v-model
- python简说(二十六)异常
- 20155201 李卓雯 《网络对抗技术》实验一 逆向及Bof基础
- jQuery 表单内容的获取
- nginx: [emerg] BIO_new_file(";/etc/nginx/ssl_key/server.crt";) failed (SSL: error:02001002:syste
- SVM学习笔记5-SMO
- <;Python>;判断变量是否是DataFrame 或者 Series
- POJ3580 SuperMemo
- Ubuntu yindaoxiufu 引导修复(Boot Repair)
- NLP--- How to install the tool NLTK in Ubuntu ?