不止八股---Lua原理笔记(4)和c语言交互

本篇文章为之后的xlua打铺垫

环境配置

没绷住,鼓捣了半天才弄好

主要配置方法见下链接

lua5.3与c交互环境_lua ex-CSDN博客

按照上面的还不够,跑的时候还是报错了

进行下面修改后跑通了

[2]

#include时要加上extern "C",告知编译器链接时不要按C++方法查找符号,这一步很重要。

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

Lua和c交互的原理---虚拟栈

        Lua 和 C之间通信的主要组件是无处不在的虚拟栈(stack), 几乎所有的API调用都是 在操作这个栈中的值, Lua与C之间所有的数据交换都是通过这个栈完成的。 此外,还可以 利用栈保存中间结果。

        栈中的每个元素都能保存Lua中任意类型的值。 当我们想要从Lua中获取一个值 (例如一个全局变量的值)时,只需调用Lua, Lua就会将指定的值压入栈中。 当想要将一个 值传给Lua时, 首先要将这个值压入栈,然后调用Lua将其从栈中弹出即可

        如图栈顶索引为-1,栈底索引为1

压入元素C API

获取元素C API

  

检查类型

小实验



void stackTest()
{
	lua_State* L = luaL_newstate();
	lua_pushstring(L, "testForCCallLua");
	lua_pushnumber(L, 123);

	//读栈取值
	if (lua_isstring(L, -2))//或if(lua_isstring(L,1))
	{
		std::cout << lua_tostring(L, -2) << std::endl;
	}
	if (lua_isnumber(L, -1))
	{
		std::cout << lua_tonumber(L, -1) << std::endl;
	}
   

	//关闭state
	lua_close(L);
}

int main()
{
	stackTest();
	system("pause");
	return 0;
}

c调用Lua

Lua代码

-- test.lua
name = "ccc"
id = 33
user = { uid = 33, uname = "forward256"}

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

获取值

  1. 使用 lua_getglobal 来获取值并将其压栈。
  2. 使用 lua_toXXX 将栈中元素取出(此时元素并不会出栈)转成相应的 C/C++ 类型的值。

void stackDump(lua_State* L)
{
	cout << "\nbeging dump lua stack" << endl;

	//栈顶下标
	int top = lua_gettop(L);
	//从栈底向上遍历
	for (int i = 1; i <= top; ++i)
	{
		int t = lua_type(L, i);
		switch (t) {
		case LUA_TSTRING: {
			printf("'%s' ", lua_tostring(L, i));
		}
						break;
		case LUA_TBOOLEAN: {
			printf(lua_toboolean(L, i) ? "true " : "false ");
		}
						 break;
		case LUA_TNUMBER: {
			printf("%g ", lua_tonumber(L, i));
		}
						break;
		default: {
			printf("%s ", lua_typename(L, t));
		}
			   break;
		}
		cout << "\nend dump lua stack" << endl;
	}
}
void testGetLuaValue()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}
	
	//获取的时候会压入栈,top name
	lua_getglobal(L, "name");
	cout << "name=" << lua_tostring(L, -1) << endl;

	//top -> id name  <-bottom
	lua_getglobal(L, "id");
	cout << "id=" << lua_tonumber(L,-1) << endl;

	stackDump(L);
	
	lua_close(L);
}

获取和设置表

  1. 使用 lua_getglobal 来获取值并将其压栈。
  2. 使用 lua_getfield(L,索引,key)  获取表的值    会压栈

设置表

        1.先压一个值

        2.再使用lua_setfield(L,索引, key) 会使得之前的值出栈


void testOperaTableLua()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	lua_getglobal(L,"user");
	if (!lua_istable(L, -1))
	{
		std::cout << "error:it is not a table" << std::endl;
	}
	// 取表中元素
	lua_getfield(L, -1, "uid");
	cout << "uid=" << lua_tonumber(L, -1) << endl;
	lua_getfield(L, -2, "uname");
	cout << "uname=" << lua_tostring(L, -1) << endl;
	

	cout << endl;
	// 修改表中元素
	lua_pushstring(L, "007");
	stackDump(L);
	lua_setfield(L, -4, "uname");
	stackDump(L);
	lua_getfield(L, -3, "uname");
	cout << "************" << endl;
	std::cout << " newName = " << lua_tostring(L, -1) << std::endl;
	stackDump(L);
	lua_close(L);
}

调用Lua 函数

  1. 使用 lua_getglobal 来获取函数并将其压栈。
  2. 如果这个函数有参数的话,就需要依次将函数的参数也压入栈。
  3. 调用 lua_pcall 开始调用函数,调用完成以后,会将返回值压入栈中。
  4. 取返回值,调用完毕。

void testFuncLua()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	lua_getglobal(L, "add");
	stackDump(L);
	lua_pushnumber(L, 15);
	lua_pushnumber(L, 5);
	stackDump(L);
	// 2-参数个数,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
	lua_pcall(L, 2, 1, 0);
	stackDump(L);
	cout << endl;
	std::cout << "5 + 15 = " << lua_tonumber(L, -1) << std::endl;

	
	lua_close(L);
}

Lua调用c语言

Lua 可以调用 C/C++ 的函数,步骤为:

  1. 将 C 的函数包装成 Lua 环境认可的函数。
  2. 将包装好的函数注册到 Lua 环境中。
  3. 像使用普通 Lua 函数那样使用注册函数。

包装C/C++函数

需要将函数包装成 Lua_CFunction 格式,并需要在函数中将返回值压入栈中,并返回返回值个数

int (Lua_CFunction*)(lua_state*)
{
    // c code        // 实现逻辑功能
    // lua_push code // 需要将返回值压入堆栈
    return n;        // n为具体的返回值个数,告诉解释器,函数向堆栈压入几个返回值
}

// 示例
/*
int add(int a,int b)
{
    return a+b;
}
*/
int add(lua_State* L)
{
	int a = lua_tonumber(L,-1);
	int b = lua_tonumber(L,-2);
	int sum = a + b;
	lua_pushnumber(L, sum);
	return 1;
}

把函数注册

// lua_register 是一个宏,对应两个函数 lua_pushfunction(L,f) 和 lua_setglobal(L,n),将函数存放在一个全局 table 中。
lua_register(L,"Add2Number",add);//将 c/c++ 函数 add 注册到全局 table[Add2Number] 中

使用

参考的文章案例没有用上面的,我这里用上面的示例


void testLuaCallC()
{
	// 创建一个 state
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);

	// 为 Lua 注册名为第一个参数的函数,实际上是调用 c/c++ 中第三个参数名的函数
	lua_register(L, "Add2Number", add);

	// 读 lua 文件并运行 Lua code
	int fret = luaL_dofile(L, "test3.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	// 关闭state
	lua_close(L);

}

Lua代码 ,test2.lua

res=Add2Number(15,5)
print(res)

 完整的CPP代码

#include <stdio.h>
#include <stdlib.h>
//#include "lua.h"
//#include "lauxlib.h"
#include<iostream>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}



void stackDump(lua_State* L)
{
	cout << "\nbeging dump lua stack" << endl;

	//栈顶下标
	int top = lua_gettop(L);
	//从栈底向上遍历
	for (int i = 1; i <= top; ++i)
	{
		int t = lua_type(L, i);
		switch (t) {
		case LUA_TSTRING: {
			printf("'%s' ", lua_tostring(L, i));
		}
						break;
		case LUA_TBOOLEAN: {
			printf(lua_toboolean(L, i) ? "true " : "false ");
		}
						 break;
		case LUA_TNUMBER: {
			printf("%g ", lua_tonumber(L, i));
		}
						break;
		default: {
			printf("%s ", lua_typename(L, t));
		}
			   break;
		}
		cout << endl;
	}
	cout << "end dump lua stack" << endl;
}

void stackTest()
{
	lua_State* L = luaL_newstate();
	lua_pushstring(L, "testForCCallLua");
	lua_pushnumber(L, 123);

	//读栈取值
	if (lua_isstring(L, -2))//或if(lua_isstring(L,1))
	{
		std::cout << lua_tostring(L, -2) << std::endl;
	}
	if (lua_isnumber(L, -1))
	{
		std::cout << lua_tonumber(L, -1) << std::endl;
	}
	

	//关闭state
	lua_close(L);
}


void testGetLuaValue()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}
	
	//获取的时候会压入栈,top name
	lua_getglobal(L, "name");
	cout << "name=" << lua_tostring(L, -1) << endl;

	//top -> id name  <-bottom
	lua_getglobal(L, "id");
	cout << "id=" << lua_tonumber(L,-1) << endl;

	stackDump(L);
	
	lua_close(L);
}

void testOperaTableLua()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	lua_getglobal(L,"user");
	if (!lua_istable(L, -1))
	{
		std::cout << "error:it is not a table" << std::endl;
	}
	// 取表中元素
	lua_getfield(L, -1, "uid");
	cout << "uid=" << lua_tonumber(L, -1) << endl;
	lua_getfield(L, -2, "uname");
	cout << "uname=" << lua_tostring(L, -1) << endl;
	

	cout << endl;
	// 修改表中元素
	lua_pushstring(L, "007");
	stackDump(L);
	lua_setfield(L, -4, "uname");
	stackDump(L);
	lua_getfield(L, -3, "uname");
	cout << "************" << endl;
	std::cout << " newName = " << lua_tostring(L, -1) << std::endl;
	stackDump(L);
	lua_close(L);
}

void testCCallLua()
{
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);//打开lua给我们提供的标准库
	// 读 lua 文件
	int fret = luaL_dofile(L, "test.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	lua_getglobal(L, "add");
	stackDump(L);
	lua_pushnumber(L, 15);
	lua_pushnumber(L, 5);
	stackDump(L);
	// 2-参数个数,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
	lua_pcall(L, 2, 1, 0);
	stackDump(L);
	cout << endl;
	std::cout << "5 + 15 = " << lua_tonumber(L, -1) << std::endl;

	
	lua_close(L);
}

int add(lua_State* L)
{
	int a = lua_tonumber(L,-1);
	int b = lua_tonumber(L,-2);
	int sum = a + b;
	lua_pushnumber(L, sum);
	return 1;
}

void testLuaCallC()
{
	// 创建一个 state
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);

	// 为 Lua 注册名为第一个参数的函数,实际上是调用 c/c++ 中第三个参数名的函数
	lua_register(L, "Add2Number", add);

	// 读 lua 文件并运行 Lua code
	int fret = luaL_dofile(L, "test3.lua");
	if (fret)
	{
		std::cout << "read lua file error!" << std::endl;
	}

	// 关闭state
	lua_close(L);

}
int main()
{
	/*stackTest();
	testGetLuaValue();*/
	//testOperaTableLua();
	//testCCallLua();
	testLuaCallC();
	system("pause");
	return 0;
}

引用

配置环境

lua5.3与c交互环境_lua ex-CSDN博客[1]

lua源码编译及与C/C++交互调用细节剖析 - 知乎 (zhihu.com)[2]

主要内容来自

Lua 跨语言调用 - 知乎 (zhihu.com)[3]

 截图来自

Lua程序设计(第4版) (豆瓣) (douban.com)[4]

后记

笔者能力有限,见谅。侵权请联系删除。

  • 18
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值