C++ 与 Lua 数据交互载体——栈

本文详细介绍了C++与Lua之间通过虚拟栈进行数据交互的过程,包括Lua栈的基本操作、C++如何压栈、检查栈空间、查询栈中元素以及各种栈操作。着重讨论了lua_push系列函数、lua_Type、lua_checkstack和lua_len等关键API的使用,阐述了栈操作原则及其在数据交互中的重要性。

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

一、栈

Lua 和 C 之间的通讯主要组件是无处不在的虚拟栈,两者间的数据交换都是通过这个栈进行

栈中可以保存 Lua 任意类型的值。

1、Lua 和 C 之间的数据交互存在的差异

  1. Lua 是动态类型,C 是静态类型,两者不匹配
  2. Lua 是自动内存管理,C 是手动内存管理

2、垃圾收集器

Lua 与 C/C++ 的数据交互,都是通过在一方压栈(存入数据),在另一方出栈(获取数据),所以栈是能感知到数据的。

而栈是 LuaState 的一部分,所以垃圾收集器能够知道 C/C++ 正在使用哪些数据,也就知道该如何管理内存。

3、栈操作原则

Lua 的栈严格遵守 LIFO (Last in first out,后进先出) , Lua 中只有栈顶的部分会发生改变。

C 语言代码则有更大的自由度,可以检视栈中的任何一个元素,甚至可以在栈的任意位置插入或删除元素。

二、C++ 压栈

针对每一种能用 C 语言直接表示的 Lua 数据类型,C API 中都有一个对应的压栈函数

下面是 C++ 所有的压栈函数

C API 函数 描述
void (lua_pushnil) (lua_State *L); 将 nil 压栈
void (lua_pushnumber) (lua_State *L, lua_Number n); 将 双精度浮点数 压栈
void (lua_pushinteger) (lua_State *L, lua_Integer n); 将 整型数 压栈
void (lua_pushboolean) (lua_State *L, int b); 将 布尔值 压栈
const char *(lua_pushstring) (lua_State *L, const char *s); 将 字符串(以 “\0” 结尾) 压栈
const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); 将 字符串(会结合 “\0” 和长度的参数决定字符串的长度) 压栈
const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp); 将 字符串(格式化字符串,接收可变参数) 压栈
const char *(lua_pushfstring) (lua_State *L, const char *fmt, …); 将 字符串(格式化字符串) 压栈
void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); 将 C 函数 压栈
void (lua_pushlightuserdata) (lua_State *L, void *p); 将 用户数据 压栈
int (lua_pushthread) (lua_State *L); 将 线程状态(即 lua_State ) 压栈

举个例子:

lua_pushcclosurelua_pushlightuserdata 后续文章进行分享

使用以上的所有 api ,进行相应数据类型的压栈

lua_pushnil(L);
lua_pushnumber(L, 0.1);
lua_pushnumber(L, 0);
lua_pushinteger(L, 10);
lua_pushboolean(L, 1);
// lua_pushstring 会在 "\0" 终止
lua_pushstring(L, "lua_pushstring hello 江澎涌\0jiang peng yong");

// lua_pushlstring 同样会在 "\0" 终止,但会结合考虑长度参数
const char *sayHello = "lua_pushlstring hello jiang peng yong.\0 会被忽略的字符";
lua_pushlstring(L, sayHello, strlen(sayHello) - 6);

// lua_pushvfstring
const char *name = "jiang peng yong";
int age = 29;
showInfo(L, name, age);

lua_pushfstring(L, "lua_pushfstring name: %s, age: %d", name, age);

lua_pushthread(L);

showInfo 函数如下所示,为了能演示 va_listlua_pushvfstring 的结合使用

void showInfo(lua_State *L, ...) {
   
    va_list args;
    // 用于开启遍历可变参数,第二个参数是可变参数列表的前一参数
    va_start(args, L);
    // 在这里可以使用 va_list 对象 args
    lua_pushvfstring(L, "lua_pushvfstring name: %s, age: %d", args);
    va_end(args);
}

最后通过以下的函数将栈打印出来,就一目了然了

这一函数主要使用了以下的 api ,先有一个大概了解,接下来的小节会更加详细的讲解

  • lua_gettop 获取栈中元素个数
  • lua_typename 获取类型名称
  • lua_toxxx 获取栈对应索引的元素值,并转为相应类型 xxx
void stackDump(lua_State *L) {
   
    int i;
    // 获取栈中元素个数
    int top = lua_gettop(L);
    printf("栈顶\n");
    for (i = top; i >= 1; i--) {
   
        // 元素类型
        int t = lua_type(L, i);
        // 元素类型名称
        printf("^ typename: %s, ", lua_typename(L, t));
        switch (t) {
   
            case LUA_TSTRING:   // 字符串类型
                printf("value: '%s'", lua_tostring(L, i));
                break;
            case LUA_TBOOLEAN:  // 布尔类型
                printf(lua_toboolean(L, i) ? "value: true" : "value: false");
                break;
            case LUA_TNUMBER:   // 数值类型
                if (lua_isinteger(L, i)) {
     // 是否整型
                    printf("value(integer): %lld", lua_tointeger(L, i));
                } else {
      // 浮点类型
                    printf("value(number): %g", lua_tonumber(L, i));
                }
                break;
            default:     // 其他类型
                // 类型名称
                printf("value: %s", lua_typename(L, t));
        }
        printf("    \n");
    }
    printf("栈底\n");
    printf("\n");
}

打印之后,刚才一系列操作的栈内容如下:

栈顶
^ typename: thread, value: thread    
^ typename: string, value: 'lua_pushfstring name: jiang peng yong, age: 29'    
^ typename: string, value: 'lua_pushvfstring name: jiang peng yong, age: 29'    
^ typename: string, value: 'lua_pushlstring hello jiang peng'    
^ typename: string, value
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值