lua与C/C++互调函数方法

本文详细介绍Lua与C/C++代码互相调用的方法,包括如何从Lua脚本调用C/C++函数及从C/C++代码调用Lua脚本中的函数。此外还介绍了LUAJIT_MODE_ON模式下如何调用C/C++函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

luaC/C++互调函数方法



1.在lua脚本中调用C/C++代码中的函数


C/C++函数定义形式

       lua中调用的C++函数的定义必须满足如下形式: typedef int(*lua_cFunction)(lua_State *L);

       函数参数必须是lua_State,返回值必须是int型,只有这样的函数Lua才能调用。


C函数参数获取设置协议

      C函数中的参数通过Lua中堆栈L来接收到,参数以正序入栈,第一个参数首先入栈。第一个参数的索引是1,最后一个参数的索引就是参数个数。C函数刚开始处,没有进行栈中数据pop、push操作之前,lua_gettop(L)可以返回函数接收到的参数个数。


C函数向lua中返回值

      需要向lua返回值,可以通过把需要返回的值以正序方式压入栈即可(第一个返回值首先压入)。


C函数的返回值

     C函数的返回值(int类型)= C函数向lua中返回的值个数(压入栈的变量个数)


例子

     接收若干数字参数,并返回它们的平均数与和:


#include 
 
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include  // lua是用纯C语言写的
#include 
#include 
#ifdef __cplusplus
}
#endif /* __cplusplus */
 
int c_average(lua_State* L)
{
   int n = lua_gettop(L); /* 返回栈顶元素的索引。因为索引是从1开始编号的(1表示栈底,-1表示栈顶),所以这个结果等于堆栈上的元素个数(返回0表示堆栈为空)。这里栈中元素的个数就是传入的参数个数 */
   double sum = 0;
   int i;
 
   for (i = 1; i <= n; i++)
    {
       if (!lua_isnumber(L, i))
       {
           lua_pushstring(L, "Incorrect argument to 'average'"); // 将错误信息压入栈中
           lua_error(L); // 抛出栈顶的错误
           /*
           int lua_error (lua_State *L);
           产生一个 Lua 错误。错误信息(实际上可以是任何类型的 Lua 值)必须被置入栈顶。这个函数会做一次长跳转,它不会再返回。
           */
       }
       sum += lua_tonumber(L, i); // lua_tonumber 将栈中指定index的值转换成数值类型的值,注意并不会从栈中弹出这个值
    }
 
   double avg = sum / n;
   lua_pushnumber(L, avg); // 将avg压入栈中,第1个返回值是avg
   lua_pushnumber(L, sum); // 将sum压入栈中,第2个返回值是sum
 
   return 2; /* return the number of results,该函数有2个返回值,即上面入栈的avg和sum */
}


int main(int argc, char* argv[])
{
   lua_State* L = luaL_newstate(); // 创建一个新的独立的状态机
   
   /* register our function,告诉lua脚本其中调用的average函数(lua中的变量名)对应的是一个叫c_average的c语言函数 */
   lua_register(L, "average", c_average);
   /*
   void lua_register (lua_State *L,
   const char *name,
   lua_CFunction f);
    把 C 函数 f 设到全局变量 name 中。它通过一个宏定义:
   #define lua_register(L,n,f) \
   (lua_pushcfunction(L, f), lua_setglobal(L, n))
   */
 
   /* run the script */
   luaL_dofile(L, "e1.lua"); // 加载脚本,脚本中的可执行语句将会得到执行
   
   /* Loads a file as a Lua chunk and then 以保护模式调用一个函数,等价于(luaL_loadfile(L,filename) || lua_pcall(L, 0, LUA_MULTRET, 0)) */
   lua_getglobal(L, "avg"); // 将全局变量avg的值入栈,等价于lua_getfield(L,LUA_GLOBALSINDEX, "name”);此时el.lua中avg变量已经有值,等于把这个变量的值压入栈。
   printf("avg is: %d\n", lua_tointeger(L, -1)); // 读栈顶的int值,注意不会从栈中弹出这个元素
   lua_pop(L, 1); // 弹出栈顶的一个元素
   
   lua_getglobal(L, "sum"); // 将全局变量sum的值入栈
   printf("sum is: %d\n", lua_tointeger(L, -1));
   lua_pop(L, 1); // 弹出栈顶的一个元素
   
   /* cleanup Lua */
   lua_close(L);
 
   return 0;
}
/*
-- e1.lua
avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)
*/
/*
输出:
The average is  30
The sum is      150
avg is: 30
sum is: 150
*/


2.在C/C++代码中调用lua脚本中的函数


      核心步骤:在C/C++中将使用的lua函数名以及其函数参数一次压入栈中(参数按正序入栈),返回值由lua按正序自动压入栈供C/C++访问。函数调用成功后,函数名、参数出栈,返回值自动入栈(不需要手动写压入栈代码)


示例:


#include 
 
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include  // lua是用纯c语言写的
#include 
#include 
#ifdef __cplusplus
}
#endif /* __cplusplus */
 
lua_State* L = NULL;
 
int luaAdd(int x, int y)
{
   int sum;
 
   /* the function name,lua脚本中的函数名 */
   lua_getglobal(L, "add"); // lua函数名入栈
 
   int savedTop = lua_gettop(L); // 保存栈中的元素个数,后面要还原
 
   /* the first argument */
   lua_pushnumber(L, x); // 参数x入栈
   /* the second argument */
   lua_pushnumber(L, y); // 参数y入栈
 
   // 此时栈中有3个值,栈顶是参数y,栈底是函数名add
   /* call the function with 2 arguments, return 1 result */
   lua_call(L, 2, 1);
 
   // 因为add只有一个返回值,所以此时栈中只有1个值(如果有多个返回值,也是按正序入栈)
   /* get the result */
   sum = (int) lua_tonumber(L, -1); // -1是栈顶

   lua_settop(L, savedTop); // 函数退出之前恢复原来的栈
   return sum;
}
 
int main(int argc, char* argv[])
{
    L= luaL_newstate(); // 创建lua运行环境
   /* load the script,加载脚本,为后面读取其中的变量做准备 */
   luaL_dofile(L, "e2.lua");
   /* call the add function */
   int sum = luaAdd(10, 15);
   /* print the result */
   printf("The sum is %d\n", sum);
   /* cleanup Lua */
   lua_close(L);
   return 0;
}

-- e2.lua
-- add two numbers
function add(x, y)
   return x + y
end
 



The sum is 25



3. LUAJIT_MODE_ON


lua_pushlightuserdata(m_luastack, (void*) script_exception_wrapper);

luaJIT_setmode(m_luastack, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);


int script_exception_wrapper(lua_State *L, lua_CFunction f)

{

try {

return f(L);  // Call wrapped function and return result.

} catch (const char *s) {  // Catch and convert exceptions.

lua_pushstring(L, s);

} catch (LuaError& e) {

lua_pushstring(L, e.what());

}

return lua_error(L);  // Rethrow as a Lua error.

}


LUAJIT_MODE_ON时,执行lua中函数,当调用C/C++函数时,会跳到script_exception_wrapper然后通过f(L)再跳到lua函数名对应的CFuntion;此模式必须使用lua_pushlightuserdata同时执行一个script_exception_wrapper指针函数;包装器功能可以用于调试目的,也可以用于捕获和转换外部异常。建议使用

lightuserdata使用时,只存储了函数指针,不需要存储真实数据,减少了内存使用。


LUAJIT_MODE_OFF时,执行lua中函数,当调用C/C++函数时,会直接跳到lua函数名对应的CFuntion;




参考资料:

http://blog.youkuaiyun.com/rain_qingtian/article/details/44781183

http://blog.youkuaiyun.com/ly131420/article/details/8794615

http://luajit.org/ext_c_api.html

http://dev.minetest.net/Script_Engine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值