zoukankan      html  css  js  c++  java
  • 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++注册的类

  • 相关阅读:
    JSF
    filter用户例子
    分析LogFilter
    理解session
    了解xml文件
    软件工程期末项目总结
    阅《软件工程》——之感
    自我介绍
    期末课程设计《天猫后台管理系统》
    JSON
  • 原文地址:https://www.cnblogs.com/chevin/p/5897220.html
Copyright © 2011-2022 走看看