当前位置: 首页 > news >正文

LuaC API知识点汇总

一.概述

首先,关于学习LuaC API的目的,本人是为了为深度学习Unity的ToLua框架做知识储备,ToLua里很多类中都调用的是LuaC的API

本文意在对LuaC API的重要概念进行精简和总结,信息出自Programming in Lua Part IV,如下图所示

二 栈的概念

尝试在LUA和C之间交换值时,面临两个问题:动态和静态类型系统之间的不匹配以及自动和手动内存管理之间的不匹配。虚拟栈的作用正是为了解决这两个问题。

LUA到C以及从C到LUA的所有数据交换都是通过此栈进行的,进行过入栈出栈的操作后才能访问到Lua或C的值。

C的API不是以严格的后进先出操作栈;它在任何任意位置插入和删除元素。

三.栈内简单操作

3.1 栈内索引

要引用栈中的元素,API 使用索引。栈中的第一个元素(即最先被压入的元素)索引为 1,下一个元素索引为 2,依此类推。我们也可以以栈顶为参照使用负索引访问元素。在这种情况下,-1 指的是栈顶的元素(即最后被压入的元素),-2 指的是其前一个元素,依此类推。例如,调用 lua_tostring(L, -1) 会将栈顶的值作为字符串返回。

3.2 元素入栈

API 为可在 C 语言中表示的每种 Lua 类型都提供了一个 push 函数

void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t length);
void lua_pushstring (lua_State *L, const char *s);

每当您将一个元素压入栈中时,您有责任确保栈有足够的空间容纳它。请记住,您现在是一名 C 语言程序员;Lua 不会宠坏您。当 Lua 启动以及每次 Lua 调用 C 时,栈至少有 20 个空闲槽位(此常量在 lua.h 中定义为 LUA_MINSTACK)。对于大多数常见用途来说,这已经绰绰有余,所以通常我们甚至都不会考虑这个问题。然而,某些任务可能需要更多的栈空间(例如,调用具有可变数量参数的函数)。在这种情况下,您可能需要调用

int lua_checkstack (lua_State *L, int sz);

这个函数用于检查栈是否有足够的空间来满足您的需求。

3.3 元素类型查询


为了检查一个元素是否具有特定类型,该 API 提供了一组以 lua_is 开头的函数,其中 * 可以是任何 Lua 类型。因此,有 lua_isnumber、lua_isstring、lua_istable 等等。所有这些函数都具有相同的原型:

int lua_is... (lua_State *L, int index);

lua_isnumber 和 lua_isstring 函数并非检查值是否具有特定类型,而是检查值是否可以转换为该类型。例如,任何数字都满足 lua_isstring 的条件。

还有一个函数可返回栈中指定索引位置元素的类型:

int lua_type (lua_State *L, int index);

类型的定义在lua.h 中:LUA_TNILLUA_TBOOLEANLUA_TNUMBERLUA_TSTRINGLUA_TTABLELUA_TFUNCTIONLUA_TUSERDATA, ,LUA_TTHREAD

此函数主要用于与 switch 语句结合使用。当需要检查字符串和数字而不进行强制转换时,它也很有用。

3.4 元素取值

要从栈中获取值,可以使用 lua_to* 系列函数,这类函数不会移除访问的元素:

    int            lua_toboolean (lua_State *L, int index);double         lua_tonumber (lua_State *L, int index);const char    *lua_tostring (lua_State *L, int index);size_t         lua_strlen (lua_State *L, int index);

即使给定的元素类型不正确,也可以调用这些函数。在这种情况下,lua_toboolean、lua_tonumber 和 lua_strlen 函数返回零,其他函数返回 NULL。零本身没有用处,但 ANSI C 没有提供我们可以用来表示错误的无效数值。然而,对于其他函数,我们通常不需要使用相应的 lua_is* 函数:我们只需调用 lua_to* 函数,然后测试结果是否不为 NULL 即可。

3.5 其他栈的操作

 API还提供了以下通用栈操作功能

int   lua_gettop (lua_State *L);

返回栈顶部元素的索引。该结果等于栈中元素的数量;0 表示空栈。

void  lua_settop (lua_State *L, int index);

接受任意索引或 0,并将栈顶设置为该索引。如果新的栈顶大于旧的栈顶,则新元素将填充 nil。如果索引为 0,则所有栈元素都将被移除。

void  lua_pushvalue (lua_State *L, int index);

将指定索引处的元素副本压入栈中。

void  lua_remove (lua_State *L, int index);

删除给定有效索引处的元素,向下移动此索引以上的元素以填补空白。

void  lua_insert (lua_State *L, int index);

将顶部元素移动到给定的有效索引中,将此索引上方的元素上移到空白处。

void  lua_replace (lua_State *L, int index);

将顶部元素移动到给定的有效索引中,而不移动任何元素(因此替换该给定索引处的值),然后弹出顶部元素。

下面给出一个例子,演示API的应用

extern "C"
{
#include <Lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>static void stackDump(lua_State* L) {int i;int top = lua_gettop(L);for (i = 1; i <= top; i++) {  /* repeat for each level */int t = lua_type(L, i);switch (t) {case LUA_TSTRING:  /* strings */printf("`%s'", lua_tostring(L, i));break;case LUA_TBOOLEAN:  /* booleans */printf(lua_toboolean(L, i) ? "true" : "false");break;case LUA_TNUMBER:  /* numbers */printf("%g", lua_tonumber(L, i));break;default:  /* other values */printf("%s", lua_typename(L, t));break;}printf("  ");  /* put a separator */}printf("\n");  /* end the listing */
}int main()
{lua_State* L = luaL_newstate();lua_pushboolean(L, 1); lua_pushnumber(L, 10);lua_pushnil(L); lua_pushstring(L, "hello");stackDump(L);/* true  10  nil  `hello'  */lua_pushvalue(L, -4); stackDump(L);/* true  10  nil  `hello'  true  */lua_replace(L, 3); stackDump(L);/* true  10  true  `hello'  */lua_settop(L, 6); stackDump(L);/* true  10  true  `hello'  nil  nil  */lua_remove(L, -3); stackDump(L);/* true  10  true  nil  nil  */lua_settop(L, -5); stackDump(L);/* true  */lua_close(L);return 0;
}

四. 栈内table操作

先来看一个例子:先在栈中创建一个空table,然后赋值table["level"]=10,再读取table["level"]并打印的操作

extern "C"
{
#include <Lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>
using namespace std;int main()
{lua_State* L = luaL_newstate();lua_newtable(L);lua_pushstring(L, "level");lua_pushnumber(L, 10);lua_settable(L, 1);//完成栈中table["level"] = 10的赋值cout << "lua_settable后栈长:" << lua_gettop(L) << endl;lua_pushstring(L, "level");lua_gettable(L, 1);//从栈中获取table["level"]的值,并将栈顶元素替换cout << "lua_gettable后栈长:" << lua_gettop(L) << endl;int num = lua_tonumber(L, -1);cout << num << endl;lua_pop(L, 1);cout << "lua_pop后栈长:" << lua_gettop(L) << endl;cout << lua_typename(L, lua_type(L, 1)) << endl;lua_close(L);return 0;
}

API分析:

void lua_newtable (lua_State *L);

创建一个新的空表并将其推送到栈上

void lua_settable (lua_State *L, int index);

等价于t[k]=v,其中t是给定索引处的值,v是栈顶部的值,k是栈顶部正下方的值。
此函数从栈中弹出键和值。

int lua_gettable (lua_State *L, int index);

将栈顶元素弹出,并将t[k]的值推入栈顶,其中t是给定索引处的值,k是栈顶部的值。

void lua_pop (lua_State *L, int n);

从栈中弹出n个元素。

五. C调用Lua函数

先来看一个demo

extern "C"
{
#include <Lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>
using namespace std;int main()
{lua_State* L = luaL_newstate();luaL_openlibs(L);const char* lua_script = "function AddOne(num) return num + 1 end";int status = luaL_dostring(L, lua_script);lua_getglobal(L, "AddOne");lua_pushnumber(L, 5);cout << "before lua_pcall栈长:" << lua_gettop(L) << endl;lua_call(L, 1, 1, 0);//lua_pcall(L, 1, 1, 0);cout << "after lua_pcall栈长:" << lua_gettop(L) << endl;cout<<"栈顶取返回值:" << lua_tonumber(L, -1) << endl;lua_close(L);
}

int lua_getglobal (lua_State *L, const char *name);

将全局名称的值推送到栈上。返回该值的类型。

void lua_call (lua_State *L, int nargs, int nresults);

调用一个函数,必须使用以下协议:

首先,将要调用的函数入栈;

然后,函数的参数按直接顺序入栈,

最后你调用lua_call;nargs是参数数量。nresults是返回值的数量。

调用函数时,所有参数和函数值都会出栈。函数的返回结果会按先后顺序入栈。

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

在保护模式下调用函数。如果调用期间没有错误,lua_pcall的行为与lua_call完全相同。

六.Lua调用C函数

当C调用Lua函数时,它必须遵循一个简单的协议来传递参数并获得结果。同样,对于从Lua调用的C函数,它必须遵循协议来获取参数并返回结果。先来看一个demo

extern "C"
{
#include <Lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include <iostream>
using namespace std;static int AddOne(lua_State* L) {cout << "calling C Func AddOne" << endl;int d = lua_tonumber(L, 1);  /* get argument */int ret = d + 1;lua_pushnumber(L, ret);  /* push result */return 1;  /* number of results */
}int main()
{lua_State* L = luaL_newstate();luaL_openlibs(L);lua_pushcfunction(L, AddOne);lua_setglobal(L, "myAdd");const char* lua_script = "print(myAdd(10))";luaL_dostring(L, lua_script);lua_close(L);
}

任何在Lua中注册的函数都必须具有相同的原型,在Lua.h中定义为Lua_CFunction:

接收lua_state*为参数,返回一个整数,代表返回值的数量

typedef int (*lua_CFunction) (lua_State *L);

void lua_pushcfunction (lua_State *L, lua_CFunction f);

将C函数推送到栈上。此函数接收指向C函数的指针,并将函数类型的Lua值推送到栈上,当调用该函数时,将调用相应的C函数。

void lua_setglobal (lua_State *L, const char *name);

从栈中弹出一个值,并将其设置为全局名称的新值。

结论就是这两个函数搭配使用,完成C函数的注册后,Lua就可以调用了

七. API的错误处理

Lua中的所有结构都是动态的:它们会根据需要生长,并在可能的情况下再次收缩。这意味着在Lua中内存分配失败的可能性是普遍存在的。几乎任何行动都可能面临这种可能性。Lua没有在其API中为每个操作使用错误代码,而是使用异常来发出这些错误信号。这意味着几乎所有API函数都可能抛出错误而不是返回。当我们编写应用程序代码(C代码)时,我们必须提供一种方法来捕获这些错误。
如果不希望应用程序退出,即使在内存分配失败的情况下,也必须在保护模式下运行代码。Lua代码的大部分(或全部)通常通过调用Lua_pcall来运行;因此,它以保护模式运行。即使在内存分配失败的情况下,lua_pcall也会返回错误代码

参考:

Lua 5.3 Reference Manual

http://www.dtcms.com/a/449163.html

相关文章:

  • mysql学习--DCL
  • 开源 C++ QT QML 开发(七)自定义控件--仪表盘
  • 论坛开源网站源码网站建设实验总结报告
  • Ansible实战:VMware下K8s自动化部署指南
  • Ansible(三)—— 使用Ansible自动化部署LNMP环境实战指南
  • 【深度学习新浪潮】有没有可能设计出一种统一架构,可以同时处理图像理解的各种下游任务?
  • 介绍一下什么是RabbitMQ的发送者可靠性?
  • 网站后台管理页面模板北京企业建网站定制价格
  • AI编辑器(二) ---调用模型的fim功能
  • UniApp 自定义导航栏适配指南:微信小程序胶囊遮挡、H5 与 App 全端通用方案
  • 数据结构其一 线性表
  • 2025年--Lc164--H14.最长公共前缀(数组和字符串)--Java版
  • 网站html有了怎么建设网站钉钉小程序开发
  • Linux基本指令(2)
  • 从工具到中枢:2025 年 AI 重构实体经济的实践图景
  • 虚幻基础:攻击 与 受击 之间的联动
  • 如何在不降低画质的前提下缩小图片体积?附实操方案
  • 个人网站注册费用互联网广告价格
  • 【学习笔记02】C++面向对象编程核心技术详解
  • vite与ts的结合
  • arcgis如何将一部分shp地图截取下来并处理成networkx格式
  • .NET Aspire深度解析:重新定义云原生分布式应用开发的“秘密武器“
  • 标准件网站开发手机淘宝网页版
  • 【网络编程】揭秘 HTTPS 数据安全:加密方案与证书体系的协同防护
  • Windows Server 2022 安装教程(从 ISO 文件安装 Server STD CORE 2022 64位系统)​
  • 【STM32】墨水屏驱动开发
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备故障预测与智能运维中的应用
  • 【MySQL】SQL的分类
  • Flutter GridView 使用指南
  • day86——有效的字母异位词(LeetCode-242)