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

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

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

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

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

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

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

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

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

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

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

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

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  7. Lua和C++交互 学习记录之二:栈操作

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  8. Lua和C++交互 学习记录之一:C++嵌入脚本

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  9. Android学习记录(3)—Android中ContentProvider的基本原理学习总结

    一.ContentProvider简介        当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据 ...

随机推荐

  1. VMware14安装centos7

    win10专业版 虚拟机:14 Pro 1. 新建虚拟机选择典型安装 2. 稍后安装操作系统 3. 选择Linux,版本选择centso7 64位(根据系统选择) 4. 设置虚拟机名称并选择安装位置 ...

  2. API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd(转)

    前几天拜读了 OpsGenie 公司(一家致力于 Dev & Ops 的公司)的资深工程师 Turgay Çelik 博士写的一篇文章(链接在文末),文中介绍了他们最初也是采用 Nginx 作 ...

  3. ubuntu18.04 安装新版本openssl

    首先我们应该知道ubuntu18.04内置了1.1.0g版本的openssl: 使用下面的apt命令更新Ubuntu存储库并安装软件包编译的软件包依赖项: sudo apt update sudo a ...

  4. rpm command

    rpm 实现程序管理 安装:-ivh ,--nodeps ,--replacepkgs 卸载: -e,  --nodeps 升级: -Uvh -Fvh , --nodeps,  --oldpackag ...

  5. vsCode设置中文

    1.安装软件之后,关闭欢迎界面,Ctrl+shift+p打开命令窗口,输入lang,选择configuration display language,改为 "locale":&qu ...

  6. ODAC(V9.5.15) 学习笔记(八)TOraScript

    名称 类型 说明 DataSet 如果脚本中返回了数据结果,则通过该数据集进行获取 Delimiter string 脚本语句之间的分隔符 EndLine Integer 脚本中最后一行的行号 End ...

  7. 【做题】uoj#370滑稽树上滑稽果——巧妙dp

    一个显然的结论是最终树的形态必然是一条链.具体证明只要考虑选定树上的某一条链,然后把其他部分全部接在它后面,这样答案一定不会变劣. 那么,一开始的想法是考虑每一位的最后出现位置,但这并不容易实现.注意 ...

  8. Spring整合Redis&JSON序列化&Spring/Web项目部署相关

    几种JSON框架用法和效率对比: https://blog.csdn.net/sisyphus_z/article/details/53333925 https://blog.csdn.net/wei ...

  9. 深度学习课程笔记(八)GAN 公式推导

    深度学习课程笔记(八)GAN 公式推导 2018-07-10  16:15:07

  10. HDU 5607 graph(矩阵乘法)

    题意 在一个 \(n\) 个节点 \(m\) 条边的有向图上随机游走,有 \(Q\) 个询问,每次给定一个起点 \(u\) 和步数 \(K\) ,每次回答最后停在每个节点的概率. \(1 \leq n ...