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

部分内容查阅自:《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. Python求最大可能

    也称为求一个集合的所有的子集 采用二进制方法: def PowerSetsBinary(items): #generate all combination of N items N = len(ite ...

  2. P3332 [ZJOI2013]K大数查询(线段树套线段树+标记永久化)

    P3332 [ZJOI2013]K大数查询 权值线段树套区间线段树 把插入的值离散化一下开个线段树 蓝后每个节点开个线段树,维护一下每个数出现的区间和次数 为了防止MLE动态开点就好辣 重点是标记永久 ...

  3. fjwc2019 D6T2 密文(trie+贪心)

    #194. 「2019冬令营提高组」密文 设$s[i]$表示前$i$个密文的异或和 容易发现,只要知道$s[0]~s[n](s[0]=0)$就可以知道每一位的值. 转化一下,就变成了在完全图上求最小生 ...

  4. markdownpad2-注册码-2017-02-23

    MarkdownPad2.5/2 注册码   User: Soar360@live.com 授权: GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImD ...

  5. Eclipse java项目将普通文件转化为Source文件的操作

    前提:该项目中已经将原有的Source folder删除掉. 右键单击普通文件>Build path>Use as Source Folder.

  6. 【lg1799】数列

    题目描述 虽然msh长大了,但她还是很喜欢找点游戏自娱自乐.有一天,她在纸上写了一串数字:1,l,2,5,4.接着她擦掉了一个l,结果发现剩下l,2,4都在自己所在的位置上,即1在第1位,2在第2位, ...

  7. thinkphp在前端页面的js代码中可以使用 U方法吗? 可以使用模板变量如__URL__等吗?

    thinkphp在前端页面的js代码中可以使用 U方法吗? : 可以的! tp的U方法, 是"全局的", 什么是全局的? 就是, 可以在 "任何地方"使用的: ...

  8. hihoCoder week227 Longest Subsequence

    题目链接 https://hihocoder.com/contest/hiho227/problem/1 题目详解 https://hihocoder.com/discuss/question/558 ...

  9. python 之 知识点(1)

    在python是使用bif=built in functions 即内置函数 dir(__builtins__) 可以查看所有的内置函数.注:pycharm中无法使用,不知道原因 help(input ...

  10. 日系插画学习笔记(一):SAI软件基础

    检测驱动是否安装正确:1.画笔没有压感,线条没有粗细变化2.画笔线条有锯齿 一.文件:新建文件:预设尺寸:一般选择A3(8k),A4(16k),A5(32k)作业要求:A4A5 - 300dpi,像素 ...