zoukankan      html  css  js  c++  java
  • lua绑定C++对象—luna模板

    lua绑定C++对象—luna模板

    绑定C++对象到lua,每个类需要写大量的代码,从类的元表创建、方法注册到实例创建,都需要自己重复写类似的代码。

    如果涉及N个不同类,会有大量重复的代码,能否创建一个模板类,把这些重复的代码进行简化,通过模板的方式绑定成不同的类?

    下面的luna<T>就是完成这样一个壮举,例如针对Car类,只需要luna<Car>::regist(L)即可完成注册。在lua层面 local car = Car()就能自动创建Car对象,然后方便的通过car.xxx()调用成员方法。

    代码文件luna.h

     1 #include <iostream>
     2 #include <cstring>
     3 extern "C" {
     4 #include <lua.h>
     5 #include <lualib.h>
     6 #include <lauxlib.h>
     7 }
     8 
     9 using namespace std;
    10 
    11 #define DECLARE_LUNA_CLASS(obj) 
    12     static const char *_className;
    13     static luna<obj>::TMethod methods[];
    14 
    15 #define EXPORT_LUNA_FUNCTION_BEGIN(obj) 
    16     const char* obj::_className = #obj;
    17     luna<obj>::TMethod obj::methods[] = {
    18 
    19 //#define EXPORT_LUNA_MEMBER_INT(obj, member) 
    20 //    {#member, nullptr},
    21 
    22 #define EXPORT_LUNA_FUNCTION(obj, func) 
    23     {#func, &obj::func},
    24 
    25 #define EXPORT_LUNA_FUNCTION_END(obj) 
    26     {nullptr, nullptr}
    27     };
    28 
    29 template<typename T>
    30 class luna
    31 {
    32 public:
    33     typedef struct {T* _u;} TObject;
    34     typedef int (T::*TPfn)(lua_State* L);
    35     typedef struct {const char* _methodName; TPfn pf;} TMethod;
    36 public:
    37     static int regist(lua_State* L) {
    38         //原表Shape
    39         if (luaL_newmetatable(L, T::_className))
    40         {
    41             //注册Shape到全局
    42             lua_newtable(L);
    43             lua_pushvalue(L, -1);
    44             lua_setglobal(L, T::_className);
    45 
    46             //设置Shape的原表,主要是__call,使其看起来更像C++初始化
    47             lua_newtable(L);
    48             lua_pushcfunction(L, luna<T>::create);
    49             lua_setfield(L, -2, "__call");
    50             lua_setmetatable(L, -2);
    51             lua_pop(L, 1); //这时候栈只剩下元表
    52 
    53             //设置元表Shape index指向自己
    54             lua_pushvalue(L, -1);
    55             lua_setfield(L, -2, "__index");
    56             lua_pushcfunction(L, luna<T>::gc);
    57             lua_setfield(L, -2, "__gc");
    58         }
    59         return 0;
    60     }
    61     static int create(lua_State* L) {
    62         lua_remove(L, 1);
    63         TObject* p = (TObject*)lua_newuserdata(L, sizeof(TObject));
    64         cout<<"luna<T>::create:"<<T::_className<<endl;
    65         p->_u = new T();
    66 
    67         luaL_getmetatable(L, T::_className);
    68         lua_setmetatable(L, -2);
    69 
    70         luaL_getmetatable(L, T::_className);
    71         for (auto* l = T::methods; l->_methodName; l++)
    72         {
    73             lua_pushlightuserdata(L,(void*)l);
    74             lua_pushlightuserdata(L,(void*)p);
    75             lua_pushcclosure(L, luna<T>::call, 2);
    76             lua_setfield(L, -2, l->_methodName);
    77         }
    78 
    79         lua_pop(L, 1);
    80 
    81         return 1;
    82     }
    83     static int call(lua_State* L) {
    84         TMethod* v = (TMethod*)lua_topointer(L, lua_upvalueindex(1));
    85         cout<<"luna<T>::call:"<<v->_methodName<<endl;
    86 
    87         TObject* p = (TObject*)lua_topointer(L, lua_upvalueindex(2));
    88 
    89         return ((p->_u)->*(v->pf))(L);
    90     }
    91     static int gc(lua_State* L) {
    92         TObject* p = (TObject*)lua_touserdata(L, 1);
    93         cout<<"luna<T>::gc:"<<T::_className<<endl;
    94         (p->_u)->~T();
    95         return 0;
    96     }
    97 };

    通过上述代码发现:luna<T>模板类,把一些行为固化下来了。主要改进有几点:

    1、通过EXPORT_LUNA_XXX系列宏定义,把每个业务类需要classname和methods列表固化下来,尽可能减少业务层工作量,避免出错。

    2、通过模板的方式,抽象出regist、create、call、gc几个公共接口,流程极为简单。所有不同类都遵循这样的原则。其中:

    • Luna<T>::regist: 注册T::_className元表和T::_className全局表,成员函数注册到元表,统一通过闭包的方式注册到一个公共的调用函数call进行分发调用。全局表T::_className只保留__call方法,只是为了保留类似local car = Car() 这种类C++的初始化方式。
    • Luna<T>::create:在lua层使用local car = Car()创建对象实例时,注册成员函数,并且通过闭包的形式把成员method地址和对象指针都通过pushcclosure绑定在一起
    • Luna<T>::call: 当通过car.xxx()调用成员函数时,通过触发call函数,因为闭包的upvalue不同,通过upvalue包含的不同的method信息,也能取到实例句柄,就能触发不同的成员函数调用,相当于统一通过call进行派发。
    • Luna<T>::gc:注册元表的__gc方法,当跟对象实例绑定的userdata被gc回收时,会触发gc调用。

    整体结构如如下:

    代码文件car.cpp

     1 #include "luna.h"
     2 
     3 class Car{
     4 public:
     5     char name[100];
     6     int len;
     7     //getName()
     8     int getName(lua_State *L){
     9         lua_pushstring(L, name);
    10         return 1;
    11     }
    12     //setName(char*)
    13     int setName(lua_State *L){
    14         strcpy(name,lua_tostring(L,-1) );
    15         return 0;
    16     }
    17     //getLen()
    18     int getLen(lua_State *L){
    19         lua_pushinteger(L, len);
    20         return 1;
    21     }
    22     //setLen(int)
    23     int setLen(lua_State *L){
    24         len=lua_tointeger(L,-1);
    25         return 0;
    26     }
    27     DECLARE_LUNA_CLASS(Car)
    28 };
    29 
    30 EXPORT_LUNA_FUNCTION_BEGIN(Car)
    31 EXPORT_LUNA_FUNCTION(Car,getName)
    32 EXPORT_LUNA_FUNCTION(Car,setName)
    33 EXPORT_LUNA_FUNCTION(Car,getLen)
    34 EXPORT_LUNA_FUNCTION(Car,setLen)
    35 EXPORT_LUNA_FUNCTION_END(Car)
    36 
    37 extern "C" {
    38 //注册 Car 类
    39 int luaopen_car(lua_State *L){
    40     luna<Car>::regist(L);
    41     return 1;
    42 }
    43 }

    编译动态库

     g++ -o2 -fPIC -std=c++11 -shared -o car.so car.cpp -llua

    代码文件car_test.lua

    1 require 'car'
    2 
    3 local car=Car();
    4 car.setLen(123);
    5 print('car.getLen() = '..car.getLen())
    6 
    7 car.setName("my Car 321")
    8 print('car.setName() = '..car.getName())

    执行测试

    [root@localhost luatest]# lua car_test.lua 
    luna<T>::create:Car
    luna<T>::call:setLen
    luna<T>::call:getLen
    car.getLen() = 123
    luna<T>::call:setName
    luna<T>::call:getName
    car.setName() = my Car 321
    luna<T>::gc:Car

    GAME OVER !

    参考:https://www.cnblogs.com/liao0001/p/9791495.html (需修改模板)

  • 相关阅读:
    Git工具简介
    Windows10搭建开发环境----Maven工具安装
    SpringBoot----01.SpringBoot基本配置
    创建一个SpringBoot项目
    软件工程----02. 需求分析
    软件工程----01. 软件工程简介
    JavaSE----11.Java 内部类
    JavaSE----10.Java 面向对象
    JavaSE----09.Java 数组
    前端面试总结
  • 原文地址:https://www.cnblogs.com/lzpong/p/13427120.html
Copyright © 2011-2022 走看看