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 标准库中的 io
、os
、math
等都是用 C 实现的,而 Lua 代码可以像调用普通 Lua 函数一样调用它们。
C 调用 Lua
C 代码也可以调用 Lua 函数。这通常通过创建一个 Lua 状态机,加载 Lua 脚本,然后使用 Lua C API 提供的函数来调用这些脚本中的函数实现。
例如,C 代码可以使用 luaL_dostring
或 lua_pcall
函数执行 Lua 代码片段,并可以使用 lua_getglobal
和 lua_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;
}