前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++调用lua 运行方式、案例亲测 及 常用API

C++调用lua 运行方式、案例亲测 及 常用API

作者头像
看、未来
发布2022-05-06 14:34:44
1.1K0
发布2022-05-06 14:34:44
举报
文章被收录于专栏:CSDN搜“看,未来”

文章目录

我也琢磨了好一会儿,这篇文章改了又改,主要是每一part的顺序问题。

运行方式

先看张图:

在这里插入图片描述
在这里插入图片描述

它的执行方式,有点熟悉,但是一时又实在想不起来那个案例。

波兰式,对,思考一下波兰式。

在Lua和C语言之间进行数据交换时,由于两种语言之间有着较大的差异,比如Lua是动态类型,C语言是静态类型,Lua是自动内存管理,而C语言则是手动内存管理。为了解决这些问题,Lua的设计者使用了虚拟栈作为二者之间数据交互的介质。在C/C++程序中,如果要获取Lua的值,只需调用Lua的C API函数,Lua就会将指定的值压入栈中。要将一个值传给Lua时,需要先将该值压入栈,然后调用Lua的C API,Lua就会获取该值并将其从栈中弹出。为了可以将不同类型的值压入栈,以及从栈中取出不同类型的值,Lua为每种类型均设定了一个特定函数。

常用API

压入元素

代码语言:javascript
复制
    Lua针对每种C类型,都有一个C API函数与之对应,如:
    void lua_pushnil(lua_State* L);  --nil值
    void lua_pushboolean(lua_State* L, int b); --布尔值
    void lua_pushnumber(lua_State* L, lua_Number n); --浮点数
    void lua_pushinteger(lua_State* L, lua_Integer n);  --整型
    void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据
    void lua_pushstring(lua_State* L, const char* s);  --以零结尾的字符串,其长度可由strlen得出。

   //对于字符串数据,Lua不会持有他们的指针,而是调用在API时生成一个内部副本,因此,即使在这些函
   //数返回后立刻释放或修改这些字符串指针,也不会有任何问题。
   //在向栈中压入数据时,可以通过调用下面的函数判断是否有足够的栈空间可用,一般而言,Lua会预留20
   //个槽位,对于普通应用来说已经足够了,除非是遇到有很多参数的函数。
   //int lua_checkstack(lua_State* L, int extra) --期望得到extra数量的空闲槽位,如果不
   //能扩展并获得,返回false。

类型相关

API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推。

代码语言:javascript
复制
    Lua提供了一组特定的函数用于检查返回元素的类型,如: 
    int lua_isboolean (lua_State *L, int index);
    int lua_iscfunction (lua_State *L, int index);
    int lua_isfunction (lua_State *L, int index);
    int lua_isnil (lua_State *L, int index);
    int lua_islightuserdata (lua_State *L, int index);
    int lua_isnumber (lua_State *L, int index);
    int lua_isstring (lua_State *L, int index);
    int lua_istable (lua_State *L, int index);
    int lua_isuserdata (lua_State *L, int index);

以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型。 Lua还提供了一个函数lua_type,用于获取元素的类型,函数原型如下:

代码语言:javascript
复制
int lua_type (lua_State *L, int index);

除了上述函数之外,Lua还提供了一组转换函数,如:

代码语言:javascript
复制
    int lua_toboolean (lua_State *L, int index);
    lua_CFunction lua_tocfunction (lua_State *L, int index);
    lua_Integer lua_tointeger (lua_State *L, int index);    
    const char *lua_tolstring (lua_State *L, int index, size_t *len);
    lua_Number lua_tonumber (lua_State *L, int index);
    const void *lua_topointer (lua_State *L, int index);
    const char *lua_tostring (lua_State *L, int index);
    void *lua_touserdata (lua_State *L, int index);

对于上述函数,如果调用失败,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen均返回0,错误的通过判断返回值是否为NULL即可。 对于lua_tolstring函数返回的指向内部字符串的指针,在该索引指向的元素被弹出之后,将无法保证仍然有效。该函数返回的字符串末尾均会有一个尾部0。

查询相关

代码语言:javascript
复制
//除了上面给出的数据交换函数之外,Lua的C API还提供了一组用于操作虚拟栈的普通函数,如:
    int lua_gettop(lua_State* L); //返回栈中元素的个数。
    void lua_settop(lua_State* L, int index); //将栈顶设置为指定的索引值。
    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); //弹出栈顶元素,并将该值设置到指定索引上。
    //Lua还提供了一个宏用于弹出指定数量的元素:#define lua_pop(L,n)  lua_settop(L, -(n) - 1) 

执行lua代码

代码语言:javascript
复制
void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

如上,lua_call 中指定了函数的传入参数个数 nargs 和函数返回结果个数 nresults

代码语言:javascript
复制
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how");                        /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t");   /* table to be indexed */
lua_getfield(L, -1, "x");        /* push result of t.x (2nd arg) */
lua_remove(L, -2);                  /* remove 't' from the stack */
lua_pushinteger(L, 14);                          /* 3rd argument */
lua_call(L, 3, 1);     /* call 'f' with 3 arguments and 1 result */
lua_setfield(L, LUA_GLOBALSINDEX, "a");        /* set global 'a' */

如果 Lua 代码执行过程中没有任何错误,lua_pcall 的行为与 lua_call 是相同的。如果在执行的过程中有错误发生,lua_pcall 会捕捉该错误,并将错误信息推送到 Lua 栈上,并返回一个错误码。

lua_pcall 最后一个参数 errfunc,指定错误处理函数在 Lua 栈中的位置

一般系统嵌入 Lua 代码,都是使用 lua_pcall,调用方法一般都是:

代码语言:javascript
复制
lua_pcall (l, 0, 0, 0)

获取 Lua 代码执行结果

使用 lua_call 或 lua_pcall 执行完一个函数后,会将执行结果放到栈顶,如果有两个返回值,栈索引 -1 和 -2 就是返回值,如果有三个值,栈索引 -1,-2,-3 就是返回值,以此类推

其他常用API

代码语言:javascript
复制
lua_State* L=luaL_newstate(); //luaL_newstate()函数返回一个指向堆栈的指针
lua_createtable(L,0,0);	//新建并压入一张表
lua_pushstring(L,0,0);	//压入一个字符串
lua_pushnumber(L,0,0);	//压入一个数字
lua_tostring(L,1);	//取出一个字符串
lua_tointeger(L,1);	//取出数字
double b=lua_tonumber();	//取出一个double类型的数字
lua_load();	//当这个函数返回0时表示加载luaL_loadfile(filename) 
//这个函数也是只允许加载lua程序文件,不执行lua文件。它是在内部去用lua_load()去加载指定名为filename的lua程序文件。当返回0表示没有错误。
luaL_dofile(); //这个函数不仅仅加载了lua程序文件,还执行lua文件。
//返回0表示没有错误。
lua_close(L);	//释放lua资源
lua_getglobal(L, "val");	//获取全局变量的val的值,并将其放入栈顶

案例亲测

1、创建一个文件夹,里面放上 lua.h 文件。文件在哪里自行检索,find 命令不是很好用,可以检索lualib.h。

2、放上一个 test.lua

代码语言:javascript
复制
mystr = "I'm lua"

myTable = {name = "xiaomign", id = 123456}

function print_hello()
	print("hello lua")
end

function _add(a, b)
	return a + b
end

3、创建 main.cpp

代码语言:javascript
复制
#include <iostream>
#include <string>

#include "lua.hpp"

using namespace std;

int main(int argc, char *argv[])
{
	///< 创建lua句柄并初始化
	lua_State *pState = luaL_newstate();

	if (nullptr == pState)
	{
		cout << "Lua 初始化失败" << endl;
		return -1;
	}

	///< 加载相关库文件
	luaL_openlibs(pState);

	///< 加载lua文件
	if (luaL_loadfile(pState, "./test.lua"))
	{
		cout << "Lua 文件加载失败" << endl;
	}
	else
	{
		///< 执行lua文件
		if (lua_pcall(pState, 0, 0, 0))
		{
			cerr << lua_tostring(pState, -1) << endl;
		}
		else
		{
			///< 获取值
			lua_getglobal(pState, "mystr");

			string str = lua_tostring(pState, -1);
			cout << str << endl;

			///< 获取表中数据
			lua_getglobal(pState, "myTable");
			lua_getfield(pState, -1, "name");
			cout << lua_tostring(pState, -1) << endl;

			lua_getglobal(pState, "myTable");
			lua_getfield(pState, -1, "id");
			cout << lua_tonumber(pState, -1) << endl;

			///< 调用函数
			lua_getglobal(pState, "print_hello");
			lua_pcall(pState, 0, 0, 0);

			///< 调用计算函数
			lua_getglobal(pState, "_add");
			lua_pushnumber(pState, 10);
			lua_pushnumber(pState, 20);

			if (lua_pcall(pState, 2, 1, 0))
			{
				cout << lua_tostring(pState, -1) << endl;
				lua_close(pState);
			}
			else
			{
				if (lua_isnumber(pState, -1))
				{
					cout << lua_tonumber(pState, -1) << endl;
				}
			}
		}
	}

	lua_close(pState);

	return 0;
}

4、编译

代码语言:javascript
复制
g++ *.cpp -o cpplua -llua -lm -ldl
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 运行方式
  • 常用API
    • 压入元素
      • 类型相关
        • 查询相关
          • 执行lua代码
            • 获取 Lua 代码执行结果
              • 其他常用API
              • 案例亲测
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档