lua绑定cpp对象(闭包与非闭包) 实现

本文介绍了如何使用Lua绑定C++对象,包括非闭包和闭包的实现方式。通过创建C++类并定义相关方法,然后使用userdata和元表在Lua中调用这些方法。非闭包实现中,通过Lua传递userdata到C++,再由C++调用相应对象的方法。闭包实现则允许直接通过对象和函数调用来简化静态函数的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

lua绑定cpp对象(闭包与非闭包) 实现

csdn的账号找不回来了 所以就复制过来
最近在研究lua,自己写了一下lua调用c++对象,思路大概就是用userdata与对象绑定 然后通过元表来实现函数的调用
先随便写个类
头文件:

#pragma once
#include<iostream>
class LuaTestObj
{
private:
    int i;
public:
    LuaTestObj(int i);
    ~LuaTestObj();
    void sayHello();
    int add(int a, int b);
    int getI();
    void setI(int i);
};

cpp:

#include "stdafx.h"
#include "LuaTestObj.h"

LuaTestObj::LuaTestObj(int i)
{
    LuaTestObj::i = i;
}

LuaTestObj::~LuaTestObj()
{
}

void LuaTestObj::sayHello()
{
    std::cout << "hello" <<std::endl;
}

int LuaTestObj::add(int a, int b)
{
    return a+b;
}

int LuaTestObj::getI()
{
    return LuaTestObj::i;
}

void LuaTestObj::setI(int i)
{
    LuaTestObj::i = i;
}

很简单,就是一个构造函数 一个set一个get 还有个两数相加的add
然后开始实现c++对象与lua的绑定
先写非闭包的实现,大概思路就是lua向cpp传入userdata cpp得到userdata后再来调用指定方法
h:

#pragma once
#include "LuaTestObj.h"
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#define CLASS_NAME "LuaTestObj"
class LuaTestObjWrapper:LuaTestObj
{
public:
    LuaTestObjWrapper(lua_State * L) :LuaTestObj(luaL_checkint(L, 1)) {}
    ~LuaTestObjWrapper();
    static void Register(lua_State * L);//注册函数
    static int Constructor(lua_State * L);//构造器

    //下面是要被注册的函数
    static int _sayHello(lua_State * L);
    static int _add(lua_State * L);
    static int _getI(lua_State * L);
    static int _setI(lua_State * L);
};

cpp:

#include "stdafx.h"
#include "LuaTestObjWrapper.h"


LuaTestObjWrapper::~LuaTestObjWrapper()
{
}

void LuaTestObjWrapper::Register(lua_State * L)
{
    lua_pushcfunction(L, &LuaTestObjWrapper::Constructor);
    lua_setglobal(L, CLASS_NAME);//设置全局的构造函数
}

int LuaTestObjWrapper::Constructor(lua_State * L)
{
    //获取元表 如果元表不存在就新建一个
    if (luaL_newmetatable(L, CLASS_NAME) == 0)
    {
        luaL_getmetatable(L, CLASS_NAME);
    }
    else 
    {
        //这里是用来配置元表的 比如__gc之类的 
        //example:
        /*
        lua_pushstring(L, "__gc");
        lua_pushcfunction(L, &func);
        lua_settable(L, -3);
        */

        /*-------------------------分割线-------------------------------------------*/

        //这部分是注册函数的
        //新建一个__index表
        lua_newtable(L);//table metatable
        //添加函数
        lua_pushstring(L, "sayHello");//函数名
        lua_pushcfunction(L, &_sayHello);//函数
        lua_settable(L,-3);

        lua_pushstring(L, "add");//函数名
        lua_pushcfunction(L, &_add);//函数
        lua_settable(L, -3);

        lua_pushstring(L, "getI");//函数名
        lua_pushcfunction(L, &_getI);//函数
        lua_settable(L, -3);

        lua_pushstring(L, "setI");//函数名
        lua_pushcfunction(L, &_setI);//函数
        lua_settable(L, -3);
        //绑定到__index
        lua_pushstring(L, "__index");//key table metatable
        lua_insert(L, -2);// table key metatable
        lua_settable(L, -3);//metatable
        //保护元表
        lua_pushstring(L,"__metatable");
        lua_pushstring(L,"lock");
        lua_settable(L,-3);
    }

    auto wrapper = new LuaTestObjWrapper(L);
    auto obj = (LuaTestObjWrapper **)lua_newuserdata(L, sizeof(LuaTestObjWrapper *));//obj metatable
    *obj = wrapper;
    lua_insert(L, -2);//metatable obj
    lua_setmetatable(L, -2);//obj
    return 1;
}

int LuaTestObjWrapper::_sayHello(lua_State * L)
{
    auto obj = (LuaTestObjWrapper **)lua_touserdata(L, 1);
    (*obj)->sayHello();
    return 0;
}

int LuaTestObjWrapper::_add(lua_State * L)
{
    auto obj = (LuaTestObjWrapper **)lua_touserdata(L, 1);
    int v = (*obj)->add(luaL_checkint(L, 2), luaL_checkint(L, 3));//这里的参数列表是从2开始的 如果因为栈底是userdata 如果要从1开始那就在这之前把栈底给remove掉,闭包写法里有用到
    lua_pushnumber(L, v);
    return 1;
}

int LuaTestObjWrapper::_getI(lua_State * L)
{
    auto obj = (LuaTestObjWrapper **)lua_touserdata(L, 1);
    lua_pushnumber(L, (*obj)->getI());
    return 1;
}

int LuaTestObjWrapper::_setI(lua_State * L)
{
    auto obj = (LuaTestObjWrapper **)lua_touserdata(L, 1);
    int i = luaL_checkint(L, 2);
    (*obj)->setI(i);
    return 0;
}

下面是闭包的实现:
闭包的话,就是先得到对象与要调用的函数 通过(obj)->(func)()来实现,这个时候 就不需要写那么多的静态函数了
h:

#pragma once
#include "LuaTestObj.h"
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#define CLASS_NAME "LuaTestObj"   //用来表示函数名
class LuaTestObjWrapper :LuaTestObj
{
public:
    LuaTestObjWrapper(lua_State * L) :LuaTestObj(luaL_checkint(L, 1)) {}
    ~LuaTestObjWrapper();
    static void Register(lua_State * L);//注册函数
    static int Constructor(lua_State * L);//构造器
    static int Closure(lua_State * L);//闭包函数
    //下面注册函数  这里就不需要添加static了
    int _sayHello(lua_State * L);
    int _add(lua_State * L);
    int _getI(lua_State * L);
    int _setI(lua_State * L);
};

cpp:

#include "stdafx.h"
#include "LuaTestObjWrapper.h"


LuaTestObjWrapper::~LuaTestObjWrapper()
{
}

void LuaTestObjWrapper::Register(lua_State * L)
{
    lua_pushcfunction(L, &LuaTestObjWrapper::Constructor);
    lua_setglobal(L, CLASS_NAME);//设置全局的构造函数
}

int LuaTestObjWrapper::Constructor(lua_State * L)
{
    //获取元表 如果元表不存在就新建一个
    if (luaL_newmetatable(L, CLASS_NAME) == 0)
    {
        luaL_getmetatable(L, CLASS_NAME);
    }
    else
    {
        //这里是用来配置元表的 比如__gc之类的 
        //example:
        /*
        lua_pushstring(L, "__gc");
        lua_pushcfunction(L, &func);
        lua_settable(L, -3);
        */

        /*-------------------------分割线-------------------------------------------*/

        //这部分是注册函数的
        //新建一个__index表
        lua_newtable(L);//table metatable
        //添加函数 这里与后面的switch 其实完全可以用一个数组跟循环来写,因为我这里只有4个函数 所以就复制复制省点事儿
        lua_pushstring(L, "sayHello");//函数名 name table metatable 
        lua_pushnumber(L, 0);//外部参数(其实也就是一个函数的id 方便以后查找函数) id name table metatable
        lua_pushcclosure(L, &Closure, 1);//闭包函数 执行后 他会把前面第三个参数数目的值扔到upvalue里 closure name table metatable
        lua_settable(L, -3);//table metatable

        lua_pushstring(L, "add");//函数名
        lua_pushnumber(L, 1);
        lua_pushcclosure(L, &Closure, 1);       
        lua_settable(L, -3);

        lua_pushstring(L, "getI");//函数名
        lua_pushnumber(L, 2);
        lua_pushcclosure(L, &Closure, 1);
        lua_settable(L, -3);

        lua_pushstring(L, "setI");//函数名
        lua_pushnumber(L, 3);
        lua_pushcclosure(L, &Closure, 1);
        lua_settable(L, -3);
        //绑定到__index
        lua_pushstring(L, "__index");//key table metatable
        lua_insert(L, -2);// table key metatable
        lua_settable(L, -3);//metatable
        //保护元表
        lua_pushstring(L,"__metatable");
        lua_pushstring(L,"lock");
        lua_settable(L,-3);
    }

    auto wrapper = new LuaTestObjWrapper(L);
    auto obj = (LuaTestObjWrapper **)lua_newuserdata(L, sizeof(LuaTestObjWrapper *));//obj metatable
    *obj = wrapper;
    lua_insert(L, -2);//metatable obj
    lua_setmetatable(L, -2);//obj
    return 1;
}

int LuaTestObjWrapper::Closure(lua_State * L)
{
    int key = (int)lua_tonumber(L, lua_upvalueindex(1));//得到之前传入的upvalue
    auto obj = (LuaTestObjWrapper **)lua_touserdata(L, 1);//得到userdata 因为此时函数已经被调用 所以第一个参数就是userdata
    lua_remove(L, 1);//从参数列表中移除userdata 因为我比较懒 移除后 从栈底看 第i个值就是传入的第i个参数
    //当然 如果以后还要用它当参数的话可以删掉这句 但是下面如果从栈底查找参数的时候记得参数的位置要+1
    int v = 0;

    switch (key)
    {
    case 0:
        v = (*obj)->_sayHello(L);
        break;
    case 1:
        v = (*obj)->_add(L);
        break;
    case 2:
        v = (*obj)->_getI(L);
        break;
    case 3:
        v = (*obj)->_setI(L);
        break;
    default:
        break;
    }
    return v;
}

int LuaTestObjWrapper::_sayHello(lua_State * L)
{
    LuaTestObj::sayHello();
    return 0;
}

int LuaTestObjWrapper::_add(lua_State * L)
{
    int a = luaL_checkint(L, 1);//得到第一个参数  因为之前self被我remove了  所以从栈底看1就是第一个参数  
    int b = luaL_checkint(L, 2);//得到第二个参数
    lua_pushnumber(L, LuaTestObj::add(a, b));
    return 1;
}

int LuaTestObjWrapper::_getI(lua_State * L)
{
    lua_pushnumber(L, LuaTestObj::getI());
    return 1;
}

int LuaTestObjWrapper::_setI(lua_State * L)
{
    int i = luaL_checkint(L, 1);
    LuaTestObj::setI(i);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值