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

在C++中定义函数时必须以lua_State为参数, 以int为返回值才能被Lua所调用。

/*

typedef int (*lua_CFunction) (lua_State*L);

C 函数的类型。

为了正确的和 Lua 通讯,C 函数必须使用下列定义了参数以及返回值传递方法的协议: C 函数通过 Lua 中的堆栈来接受参数,参数以正序入栈(第一个参数首先入栈)。因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。第一个参数(如果有的话)在索引 1 的地方,而最后一个参数在索引 lua_gettop(L) 处。当需要向 Lua 返回值的时候,C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入),然后返回这些返回值的个数。在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值。

*/

下面这个例子中的函数将接收若干数字参数,并返回它们的平均数与和:

#include <stdio.h>

#ifdef __cplusplus

extern "C" {

#endif /* __cplusplus */

#include <lua.h> // lua是用纯c语言写的

#include <lualib.h>

#include <lauxlib.h>

#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")

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 <stdio.h>

#ifdef __cplusplus

extern "C" {

#endif /* __cplusplus */

#include <lua.h> // lua是用纯c语言写的

#include <lualib.h>

#include <lauxlib.h>

#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);

/*

void lua_call (lua_State *L, int nargs, int nresults);

调用一个函数。

要调用一个函数请遵循以下协议:首先,要调用的函数应该被压入堆栈;接着,把需要传递给这个函数的参数按正序压栈;这是指第一个参数首先压栈。最后调用一下 lua_call; nargs 是你压入堆栈的参数个数。当函数调用完毕后,所有的参数以及函数本身都会出栈。而函数的返回值这时则被压入堆栈。返回值的个数将被调整为 nresults 个,除非 nresults 被设置成 LUA_MULTRET。在这种情况下,所有的返回值都被压入堆栈中。 Lua 会保证返回值都放入栈空间中。函数返回值将按正序压栈(第一个返回值首先压栈),因此在调用结束后,最后一个返回值将被放在栈顶。

lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

以保护模式调用一个函数。

nargs 和 nresults 的含义与 lua_call 中的相同。如果在调用过程中没有发生错误, lua_pcall 的行为和 lua_call 完全一致。但是,如果有错误发生的话, lua_pcall 会捕获它,然后把单一的值(错误信息)压入堆栈,然后返回错误码。同 lua_call 一样, lua_pcall 总是把函数本身和它的参数从栈上移除。

如果 errfunc 是 0 ,返回在栈顶的错误信息就和原始错误信息完全一致。否则,errfunc 就被当成是错误处理函数在栈上的索引。(在当前的实现里,这个索引不能是伪索引。)在发生运行时错误时,这个函数会被调用而参数就是错误信息。错误处理函数的返回值将被 lua_pcall 作为出错信息返回在堆栈上。

典型的用法中,错误处理函数被用来在出错信息上加上更多的调试信息,比如栈跟踪信息 (stack traceback) 。这些信息在 lua_pcall 返回后,因为栈已经展开 (unwound) ,所以收集不到了。

lua_pcall 函数在调用成功时返回 0 ,否则返回以下(定义在 lua.h 中的)错误代码中的一个:

LUA_ERRRUN: 运行时错误。

LUA_ERRMEM: 内存分配错误。对于这种错,Lua 调用不了错误处理函数。

LUA_ERRERR: 在运行错误处理函数时发生的错误。

*/

// 因为add只有一个返回值,所以此时栈中只有1个值(如果有多个返回值,也是按正序入栈)

/* get the result */

sum = (int) lua_tonumber(L, -1); // -1是栈顶

lua_pop(L, 1); // 返回值出栈

/* 取出脚本中的变量z的值 */

lua_getglobal(L, "z"); // 变量z的值入栈

int z = (int) lua_tonumber(L, -1);

printf("z = %d\n", z);

lua_pop(L, 1); // 变量z出栈

lua_pushnumber(L, 4); // 4入栈

lua_setglobal(L, "r");

/*

void lua_setglobal (lua_State *L, const char *name);

从堆栈上弹出一个值,并将其设到全局变量 name 中。它由一个宏定义出来:

#define lua_setglobal(L,s)  lua_setfield(L, LUA_GLOBALSINDEX, s)

*/

lua_getglobal(L, "r"); // 变量r的值入栈

int r = (int)lua_tonumber(L, -1);

printf("r = %d\n", r);

lua_pop(L, 1); // 变量r出栈

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

z = 6

*/

/*

输出:

z = 6

r = 4

The sum is 25

*/

3,综合例子

#include <stdio.h>

#ifdef __cplusplus

extern "C" {

#endif /* __cplusplus */

#include <lua.h> // lua是用纯c语言写的

#include <lualib.h>

#include <lauxlib.h>

#ifdef __cplusplus

}

#endif /* __cplusplus */

#define err_exit(num,fmt,args...)  \

do{printf("[%s:%d]"fmt"\n",__FILE__,__LINE__,##args);exit(num);}while(0)

#define err_return(num,fmt,args...)  \

do{printf("[%s:%d]"fmt"\n",__FILE__,__LINE__,##args);return(num);}while(0)

// lua中调用的c函数定义,实现加法

int c_sum(lua_State* L)

{

int a = lua_tointeger(L, 1);

int b = lua_tointeger(L, 2);

lua_pushinteger(L, a + b);

return 1;

}

int main(int argc, char* argv[])

{

lua_State* L = luaL_newstate(); //创建lua运行环境

if (L == NULL)

err_return(-1, "luaL_newstat() failed");

int ret = 0;

ret = luaL_loadfile(L, "e3.lua"); // Loads a file as a Luachunk. 底层调用lua_load,功能是:加载一个 Lua chunk 。如果没有错误, lua_load 把一个编译好的 chunk 作为一个 Lua 函数压入堆栈。否则,压入出错信息。

if (ret != 0)

err_return(-1, "luaL_loadfile failed");

ret = lua_pcall(L, 0, 0, 0); // 以保护模式调用一个函数。 即刚刚加载的文件

if (ret != 0)

err_return(-1, "lua_pcall failed: %s", lua_tostring(L, -1));

lua_getglobal(L, "width"); // 将全局变量width的值入栈

lua_getglobal(L, "height");// 将全局变量height的值入栈

printf("height: %ld, width: %ld\n", (long) lua_tointeger(L,-1), (long) lua_tointeger(L, -2));

lua_pop(L, 1); // 从栈中弹出一个值,从栈顶开始

int a = 11;

int b = 12;

lua_getglobal(L, "mySum1"); // 将全局变量mySum1入栈,注意这里的mySum1是一个函数

lua_pushinteger(L, a); // 第1个整型参数入栈,从左往右的顺序

lua_pushinteger(L, b); // 第2个整型参数入栈,从左往右的顺序

ret = lua_pcall(L, 2, 1, 0); // 调用函数,2个参数,1个返回值。调用成功后,函数和参数均出战,返回值入栈。如果有多个返回值,则第一个返回值先入栈

if (ret != 0)

err_return(-1, "lua_pcall failed: %s", lua_tostring(L, -1));// 调用函数失败后栈顶存放失败原因字符串

printf("sum: %d + %d = %ld\n", a, b, (long) lua_tointeger(L,-1)); // sum只有一个返回值,调用成功后栈顶存放函数的返回值

lua_pop(L, 1);

const char str1[] = "hello";

const char str2[] = "world";

lua_getglobal(L, "myStrcat"); // 调用lua中的函数myStrcat

lua_pushstring(L, str1);

lua_pushstring(L, str2);

ret = lua_pcall(L, 2, 1, 0);

if (ret != 0)

err_return(-1, "lua_pcall failed: %s", lua_tostring(L, -1));

printf("mystrcat: %s %s = %s\n", str1, str2, lua_tostring(L,-1));

lua_pop(L, 1);

lua_pushcfunction(L, c_sum); // 将一个 C 函数压入堆栈

lua_setglobal(L, "mySum2");

/*

void lua_setglobal (lua_State *L, const char *name);

从堆栈上弹出一个值,并将其设到全局变量 name 中。它由一个宏定义出来:

#define lua_setglobal(L,s)  lua_setfield(L, LUA_GLOBALSINDEX, s)

*/

// 以上两句可以用lua_register(L, "mySum2", c_sum);来代替

/* register our function,告诉lua脚本其中调用的average函数(也是一个变量名)其实对应的是一个叫c_averaged的c代码函数 */

// 调用lua中的mySum2函数,该函数调用c_sum函数实现加法

lua_getglobal(L, "mySum2");

lua_pushinteger(L, a);

lua_pushinteger(L, b);

ret = lua_pcall(L, 2, 1, 0);

if (ret != 0)

err_return(-1, "lua_pcall failed: %s", lua_tostring(L, -1));

printf("mysum: %d + %d = %ld\n", a, b, (long) lua_tointeger(L,-1));

lua_pop(L, 1);

lua_close(L); // 释放lua运行环境

return 0;

}

/*

-- e3.lua

-- 变量定义

width = 1

height = 2

-- lua函数定义,实现加法

function mySum1(a, b)

return a + b

end

-- lua函数定义,实现字符串相加

function myStrcat(a, b)

return a.. "_" .. b

end

-- lua函数定义,通过调用c代码中的csum函数实现加法

function mysum(a, b)

return csum(a, b)

end

*/

/*

输出:

height: 2, width: 1

sum: 11 + 12 = 23

mystrcat: hello world = hello_world

mysum: 11 + 12 = 23

*/

C/C++与lua实现互调的更多相关文章

  1. [转]LUA C 互调

    组件工厂 ------3D游戏研发 LUA和C之间的函数调用 1.1 从C程序调用LUA函数 LUA的函数和普通变量一样也是First Class Variable类型,可以看作函数指针变量参与栈操作 ...

  2. 手游项目Crash的上报

    基于cocos2d-x开发的手游,免不了会遇到崩溃.闪退,在非debug状态下定位问题异常的艰难,像我们项目是在cocos2dx的基础上封装了一层,然后又与lua进行互调.因为接受C++/移动端开发比 ...

  3. 【cocos2d-x + Lua(2) C++和lua数据通讯之间的互调】

    我们主要解决如下几个问题: 转载注明出处:http://www.cnblogs.com/zisou/p/cocos2dx-lua2.html 1,C++如何获取Lua里面的一个变量值? 2,C++如何 ...

  4. Lua学习 2) —— Android与Lua互调

    2014-07-09 一.Android类调用lua并回调 Android调用Lua函数,同一时候把类作为參数传递过去.然后再Lua中回调类的函数 调用lua mLuaState = LuaState ...

  5. Lua 栈的理解

    提到C++与lua互调,不可不提栈. 栈是C++和Lua相互通讯的一个地方. 首先这个栈并不是传统意义上的栈(传统的栈需要放同一种数据类型,但在网上的某些资料说,每个栈元素是一个联合体). 栈从上向下 ...

  6. 玩转cocos2d-x lua-binding, 实现c++与lua混合编程

    引言 城市精灵GO(http://csjl.teamtop3.com/)是一款基于cocos2d-x开发的LBS社交游戏, 通过真实地图的探索, 发现和抓捕隐匿于身边的野生精灵, 利用游戏中丰富的玩法 ...

  7. lua调用C语言

    在上一篇文章(C调用lua函数)中,讲述了如何用c语言调用lua函数,通常,A语言能调用B语言,反过来也是成立的.正如Java 与c语言之间使用JNI来互调,Lua与C也可以互调.   当lua调用c ...

  8. tolua++实现lua层调用c++技术分析

    tolua++技术分析 cocos2dx+lua 前言 一直都使用 cocos2dx + lua 进行游戏开发,用 Lua 开发可以专注于游戏逻辑的实现,另外一方面可以实现热更新:而且 lua 是一个 ...

  9. cocos2d-x-lua如何导出自定义类到lua脚本环境

      这篇教程是基于你的工程是cocos2d-x-lua的项目,我假设你已经完全驾驭cocos-x/samples/Lua/HelloLua工程,基本明白lua和c++互调的一些原理. 我们的目的是要在 ...

随机推荐

  1. linux_grep

    grep常用的命令行选项: 选项 说明 -c 只显示有多少行匹配,而不具体显示匹配的行. -h 不显示文件名. -i 在字符串比较的时候忽略大小写. -l 只显示包含匹配模板的行的文件名清单. -L ...

  2. 性能测试 - some

    1.日常业务中测试过的最大并发数,其QPS为多少? 答: 从服务端开发处得知线上某台机器单接口访问量为63万 因该接口为视频类访问接口,大多数接口集中于晚间时段.可计算QPS = 63万/8/3600 ...

  3. Swift--存储属性-备

    Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据. 存储属性可以存储数据,分为常量属性(用关键字let定义) ...

  4. jquery easyui二次开发总结(二)

    1.easyui tab增加“关闭所有页”.“关闭非当前页”功能. //tab增加“关闭所有页”和“关闭非当前页”的功能 $("#tabs").tabs({ onAdd:funct ...

  5. Codeforces 161D Distance in Tree

    题目大意:给出一棵n个节点的树,统计树中长度为k的路径的条数(1<=n<=50000 , 1<=k<=500) 思路:树分治! #include<cstdio> # ...

  6. Delphi 函数指针(函数可以当参数)

    首先学习: 指向非对象(一般的)函数/过程的函数指针 Pascal 中的过程类型与C语言中的函数指针相似,为了统一说法,以下称函数指针.函数指针的声明只需要参数列表:如果是函数,再加个返回值.例如声明 ...

  7. BZOJ1635: [Usaco2007 Jan]Tallest Cow 最高的牛

    1635: [Usaco2007 Jan]Tallest Cow 最高的牛 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 346  Solved: 184 ...

  8. (7)如何得到所有的 "水仙花数" ?

    本程序转载自:如何得到所有的水仙花数 感谢Android_iPhone(日知己所无),preferme(冰思雨)等人: package test; import java.math.BigIntege ...

  9. Html5/Css3 向下兼容placeholder

    Css3下input标签的placeholder属性在IE10以下是不兼容的,页面加入这段JS脚本后,能够兼容IE6+ //@charset "utf-8"; /** * jque ...

  10. 定时关机命令-shutdown

    定时关机命令-shutdown 一般会用到的定时关机命令有两种: Shutdown -s -t 3600 1小时后自动关机(3600秒) at 12:00 Shutdown -s 12:00自动关闭计 ...