C/C++设置LUA全局变量

10 篇文章 0 订阅
5 篇文章 0 订阅

前言

最近在看到freeswitch的LUA脚本有一个全局变量session。这个变量肯定是在freeswitch内部申请并且进行初始化,然后经过一系列设置后LUA可以直接使用。根据配置freeswitch每一通电话都会执行同一个LUA脚本,每通电话LUA脚本得到的session又不一样,很好奇这是如何实现的。经过大量资料查阅和代码测试,找到一种可靠的办法创建/销毁LUA全局变量,防止内存泄漏。
本文用一个示例模拟这个实现

代码实现

C/C++代码

#include <iostream>
#include <cstring>
#include <stdlib.h>
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

using namespace std;

/*
 * #define LUA_TNIL     0
 * #define LUA_TBOOLEAN     1
 * #define LUA_TLIGHTUSERDATA   2
 * #define LUA_TNUMBER      3
 * #define LUA_TSTRING      4
 * #define LUA_TTABLE       5
 * #define LUA_TFUNCTION        6
 * #define LUA_TUSERDATA        7
 * #define LUA_TTHREAD      8
 * */

char* get_val(lua_State *L, int idx)
{
    static char sData[32];
    sData[0] = '\0';

    int type = lua_type(L, idx);
    switch (type)
    {
        case 0: //nil
            {
            snprintf(sData, sizeof(sData), "%s", "nil");
            break;
            }
        case 1://bool
            {
            int val = lua_toboolean(L, idx);
            snprintf(sData, sizeof(sData), "%s", val == 1 ? "true" : "false");
            break;
            }
        case 3://number
            {
            double val = lua_tonumber(L, idx);
            snprintf(sData, sizeof(sData), "%f", val);
            break;
            }
        case 4://string
            {
            const char* val = lua_tostring(L, idx);
            snprintf(sData, sizeof(sData), "%s", val);
            break;
            }
        case 2:
        case 5:
        case 6:
        case 7:
        case 8:
        default:
            {
            const void* val = lua_topointer(L, idx);
            snprintf(sData, sizeof(sData), "%p", val);
            break;
            }

    }

    return sData;
}

int print_stack(lua_State *L)
{
    int iNum = lua_gettop(L);
    cout<<"==========Total:"<<iNum<<"=========="<<endl;
    for (int i = iNum; i >= 1; i--)
    {
        int idx = i - iNum - 1;
        int type = lua_type(L, i);
        const char* type_name = lua_typename(L, type);
        cout<<"idx:"<<idx<<" type:"<<type<<"("<< type_name<<") "<<get_val(L, i)<<endl;
    }
    cout<<"==========================="<<endl;
    return 0;
}

class Person
{
    public:
        Person(int iAge, int iScore):m_age(iAge), m_score(iScore){};
        ~Person(){cout<<"delete Person"<<endl;}
        int  getAge(){return m_age;}
        void setAge(int iAge){m_age = iAge;}
        static int autoGc(lua_State *L){
            Person** p = (Person**)lua_touserdata(L, 1); 
            cout << "auto gc. age: " << (*p)->m_age << " score: " << (*p)->m_score <<endl;
        }   
    public:
        int m_age;
        int m_score;
};

int create_person(lua_State *L)
{
    /* 创建userdata */
    Person** p = (Person**)lua_newuserdata(L, sizeof(Person*));
    /* 创建自定义类型 */
    *p = new Person(15, 100);

    /* 设置p的元表(LUA调用setAge/getAget就靠元表了) */
    luaL_getmetatable(L, "MetaPerson"); 
    lua_setmetatable(L, -2);

    /* 将p设置为全局变量 */
    lua_setglobal(L, "sess");
    
    return 1;
}

int get_age(lua_State *L)
{
    //print_stack(L);//观察进入该函数后LUA堆栈
    /* 获取自定义类型并进行函数调用 */
    Person** p = (Person**)lua_touserdata(L, 1);
    int iAge = (*p)->getAge();
    lua_pushinteger(L, iAge);
    return 1;
}

int set_age(lua_State *L)
{
    //print_stack(L);//观察进入该函数后LUA堆栈
    /* 获取自定义类型并进行函数调用 */
    Person** p = (Person**)lua_touserdata(L, 1);
    int iAge = lua_tointeger(L, 2);
    cout << "set_age " << *p << " " << iAge << endl;
    (*p)->setAge(iAge);
    return 0;
}

int auto_gc (lua_State *L)
{
    /* 释放内存 */
    Person** p = (Person**)lua_touserdata(L, 1);
    (*p)-> autoGc(L);
    delete *p;
    return 0;
}

int lua_openPerson(lua_State *L)
{
    /* 初始化元表,设置函数对应关系 */
    if (luaL_newmetatable(L, "MetaPerson"))
    {
        lua_pushcfunction(L, &get_age);
        lua_setfield(L, -2, "getAge");
        lua_pushcfunction(L, &set_age);
        lua_setfield(L, -2, "setAge");
        lua_pushcfunction(L, &auto_gc);
        lua_setfield(L, -2, "__gc");
        lua_pushvalue(L, -1);
        lua_setfield(L, -2, "__index");
    }

    return 1;
}

int main(int argc, char* argv[])
{
    /* LUA环境初始化 */
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_openPerson(L);

    /* 创建自定义变量后,LUA可以直接使用设置好的全局变量了 */
    create_person(L);
    luaL_dofile(L, "test.lua");

    /* 释放全局变量内存 */
    printf("delete sess global var memory\r\n");
    lua_getglobal(L,"sess");
    lua_pushnil(L);
    lua_setglobal(L, "sess");   /* 把全局变量设置为nil,LUA会自动调用元表的__gc函数(也就是auto_gc) */

    /* 关闭LUA环境 */
    lua_settop(L, 0);
    lua_close(L);
    return 0;
}

LUA脚本

function main()
    local age = sess:getAge();
    sess:setAge(18);
    local age2 = sess:getAge();
    print("old: " .. age .. " new: " .. age2);
    print ""
    sess:setAge(19);
    print("old: " .. age .. " new: " .. sess:getAge());
end

main();
collectgarbage("collect");

输出

[root@centos7 home]# g++ -g -llua ./test.cpp 
[root@centos7 home]# ./a.out 
set_age 0x12e9430 18
old: 15 new: 18

set_age 0x12e9430 19
old: 15 new: 19
delete sess global var memory
auto gc. age: 19 score: 100
delete Person
[root@centos7 home]# 

后记

由于lua_newuserdata创建的数据是在LUA堆栈上,一开始不知道如何释放该内存,在网上也找了很久都没找到如何正确的销毁LUA全局变量,后来想到直接在脚本结束回到C/C++后将全局变量赋值为nil试试,这招果然奏效,触发了__gc函数。有种瞎猫撞上死耗子的感觉
这个问题搞了好几天 ( ゜- ゜)つロ干杯 []( ̄▽ ̄)*

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值