Lua/C 相互调用

1. 简介

Lua 和 C 之间的相互调用是通过 Lua 的 C API 实现的。Lua C API 是一组预定义的 C 函数和宏,它们允许 C 代码创建、操作和销毁 Lua 状态机(Lua state),以及在 Lua 和 C 之间传递数据和控制流。

Lua 调用 C

Lua 代码可以通过 require 语句加载用 C 语言编写的库(这些库通常以 .so.dll 文件的形式存在),并调用库中的函数。这些库通常是用 Lua C API 编写的,并暴露给 Lua 作为可调用的函数。

例如,Lua 标准库中的 ioosmath 等都是用 C 实现的,而 Lua 代码可以像调用普通 Lua 函数一样调用它们。

C 调用 Lua

C 代码也可以调用 Lua 函数。这通常通过创建一个 Lua 状态机,加载 Lua 脚本,然后使用 Lua C API 提供的函数来调用这些脚本中的函数实现。

例如,C 代码可以使用 luaL_dostringlua_pcall 函数执行 Lua 代码片段,并可以使用 lua_getgloballua_call 函数调用 Lua 函数。

数据交换

在 Lua 和 C 之间传递数据时,Lua C API 提供了一系列函数来在 Lua 值和 C 数据结构之间转换。例如,lua_pushnumber 可以将一个 C double 值推送到 Lua 栈上,而 lua_tonumber 可以从 Lua 栈上弹出一个 Lua 数字并将其转换为 C double

错误处理

当从 C 调用 Lua 代码时,需要处理可能出现的错误。Lua C API 提供了错误处理机制,例如 lua_pcall 函数允许你捕获 Lua 代码执行过程中发生的错误,并返回错误信息。

线程安全

在多线程环境中使用 Lua 和 C 的交互时,需要注意线程安全性。Lua C API 提供了线程安全的版本,这些版本需要在每个线程中单独创建和管理 Lua 状态机。

总结

通过 Lua C API,Lua 和 C 可以实现无缝的相互调用,从而允许开发者在需要高性能或需要直接访问系统资源时使用 C,而在需要快速原型设计或脚本化功能时使用 Lua。这种灵活性使得 Lua 成为一种非常受欢迎的嵌入式脚本语言。

2. 基础

#include <Windows.h>
#include <stdint.h>

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static int get_sum(lua_State* L)
{
	int n = lua_gettop(L);
	double sum = 0;
	int i;
	for (i = 1; i <= n; ++i) {
		if (!lua_isnumber(L, i)) {
			printf("invalid argument i=");
		}
		else {
			sum += lua_tonumber(L, i);
		}
	}
	lua_pushnumber(L, n);
	lua_pushnumber(L, sum);
	return 2;
}

int main()
{
	int count = 0;

	lua_State* L = luaL_newstate();
	if (L == NULL)
	{
		printf("LuaL_newstate Failed!\n");
		exit(-1);
	}
	// 打开标准库
	luaL_openlibs(L);

	// 注册函数,提供给脚本调用
	{
		lua_register(L, "get_sum", get_sum);
	}

	while (1)
	// 1. 加载脚本并执行
	// 2. 调用函数
	{
		int data = 15;
		int result = 0;

		count++;
		printf("---------- [%d] ----------\n",count);
		if (luaL_dofile(L, "test.lua"))
		{
			printf("luaL_dofile Failed!\n");
			exit(-1);
		}

		// 函数的传参和返回
		lua_getglobal(L, "echo");
		lua_pushnumber(L, data);
		lua_call(L, 1, 1);
		// lua_pcall(L, 1, 1, 0);           //! 两种方式都ok
		result = (int)lua_tonumber(L, -1);
		if (!lua_isnumber(L, 1)) {
			printf("invalid result!");
			exit(-1);
		}
		lua_pop(L, 1);
		printf("[C] result = %d\n", result);

		Sleep(2000);
	}

	lua_close(L);
	L = NULL;
	system("pause");
	return 0;
}
-- lua与c相互调用参考示例
-- lua优势:
-- 1. 动态更新(也即主程序运行期间,可以随时修改lua程序,不影响主程序的运行)
--    // 当然了,lua修改保存的时候,如果有语法错误,还是会主程序异常的哦(^.^)

local n, sum = get_sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
print("num: ", n);
print("sum: ", sum);

function echo(data)
    print("data: ", data)
    return data
end

----------------------------------------------------------

function closure()
    local count = 0
    function closure_fun()
        count = count + 1
        return count
    end
    return closure_fun
end

function main()
    -- 闭包测试
    c = closure()
    print(type(c))
    for i = 1, 10, 1 do
        print(c())
    end
end

-- main()

3. Lua调用C库


#define DLL_EXPORTS
#include "usbdisk.h"

#include "lauxlib.h"
#include "lualib.h"
#include "lua.h"

#define BUFFER_SIZE_MAX    (4096+16)

/*
 * handle = usbdisk.open(symbolic_link)
 */
static int lua_usb_open(lua_State* L)
{
	HANDLE handle = NULL;
	uint8_t* symbolic = (uint8_t*)luaL_checkstring(L, 1);
	handle = usb_open(symbolic);
	lua_pushinteger(L, (lua_Integer)handle);
	return 1;
}

/*
 * bool = usbdisk.close(handle)
 */
static int lua_usb_close(lua_State* L)
{
	BOOL ret = FALSE;
	HANDLE handle = NULL;
	handle = (HANDLE)luaL_checkinteger(L, 1);
	ret = usb_close(handle);
	lua_pushboolean(L, ret);
	return 1;
}

/*
 * wrote_cnt = usbdisk.write(handle,table)
 *     param2 -> table or byte, usually the table
 */
static int lua_usb_write(lua_State* L)
{
	HANDLE handle = NULL;
	// 固定cmd
	uint8_t cmd = 0xff;
	uint8_t cmd_len = 1;
	uint8_t* sbuf = NULL;
	uint32_t slen = 0;

	uint32_t argn = 0;
	uint32_t wrote_cnt = 0;

	int32_t data = 0;
	uint8_t temp = 0;
	uint32_t i = 0;

	handle = (HANDLE)luaL_checkinteger(L, 1);
	if (lua_gettop(L) < 2)
	{
		return luaL_error(L, "invalid number of arguments");
	}

	for (argn = 2; argn <= (uint32_t)lua_gettop(L); argn++)
	{
		if (lua_type(L, argn) == LUA_TNUMBER)
		{
			data = (int)luaL_checkinteger(L, argn);

			if (data < 0 || data > 255)
			{
				return luaL_error(L, "invalid number of arguments");
			}
			temp = data;
			if (TRUE != usb_write(handle, &cmd, cmd_len, &temp, 1))
			{
				return luaL_error(L, "usb_write failed!");
			}

			wrote_cnt++;
		}
		else if(lua_istable(L,argn))
		{
			slen = (uint32_t)lua_rawlen(L, argn);

			sbuf = (uint8_t*)malloc(BUFFER_SIZE_MAX);
			if (sbuf == NULL)
			{
				return 0;
			}
			memset(sbuf, 0x00, BUFFER_SIZE_MAX);

			for (i = 0; i < slen; i++)
			{
				lua_rawgeti(L, argn, (lua_Integer)i + 1);
				data = (int)luaL_checkinteger(L, -1);
				lua_pop(L, 1);
				if (data < 0 || data > 255)
				{
					free(sbuf);
					return luaL_error(L, "numeric data must be from 0 to 255");
				}
				sbuf[i] = data;
			}
			if(TRUE != usb_write(handle, &cmd, cmd_len, sbuf, slen))
			{
				return luaL_error(L, "usb_write failed!");
			}

			wrote_cnt += i;
			if (i < slen)
				break;
			free(sbuf);
		}
		else
		{
			return luaL_error(L, "invalid arguments");
		}
	}

	lua_pushinteger(L, wrote_cnt);
	return 1;
}

/*
 * table = usbdisk.read(handle,size)
 */
static int lua_usb_read(lua_State* L)
{
	HANDLE handle = NULL;
	// 固定cmd
	uint8_t cmd = 0xff;
	uint8_t cmd_len = 1;
	uint8_t* rbuf = NULL;
	uint32_t rlen = BUFFER_SIZE_MAX;

	uint32_t size;
	// luaL_Buffer b;

	handle = (HANDLE)luaL_checkinteger(L, 1);
	if (lua_gettop(L) < 2)
	{
		return luaL_error(L, "invalid number of arguments");
	}
	size = (uint32_t)luaL_checkinteger(L, 2);
	if (size == 0)
	{
		return 0;
	}

	rbuf = (uint8_t*)malloc(BUFFER_SIZE_MAX);
	if (rbuf == NULL)
	{
		return 0;
	}
	memset(rbuf, 0x30, BUFFER_SIZE_MAX);
	if (TRUE != usb_read(handle, &cmd, cmd_len, rbuf, &rlen))
	{
		return luaL_error(L, "usb_read failed!");
	}

#if 0
	// return string
	luaL_buffinit(L, &b);
	for (uint32_t i = 0; i < rlen; i++)
	{
		luaL_addchar(&b, (char)rbuf[i]);
	}
	luaL_pushresult(&b);
#else
	// return table
	lua_newtable(L);
	for (uint32_t i = 0; i < rlen; i++)
	{
		// 需要注意的一点就是
		// lua的下标是从1开始的
		lua_pushnumber(L, i + 1);
		lua_pushinteger(L, (lua_Integer)rbuf[i]);
		lua_settable(L, -3);
	}
#endif

	free(rbuf);
	return 1;
}

static const struct luaL_Reg usbdisk[] = {
	{"open", lua_usb_open},
	{"close", lua_usb_close},
	{"write", lua_usb_write},
	{"read", lua_usb_read},
	{NULL, NULL},
};

/*
 * dll -> xxx.dll
 * lua -> require(xxx)
 * c -> luaopen_xxx
 */
int DLL_API luaopen_lusbdisk(lua_State* L)
{
	luaL_newlib(L, usbdisk);
	return 1;
}