------3D游戏研发

LUA和C之间的函数调用

1.1 从C程序调用LUA函数

LUA的函数和普通变量一样也是First Class Variable类型,可以看作函数指针变量参与栈操作。因此调用过程分为如下几个步骤:

  1. 请求LUA函数(指针)入(GLOBAL)栈。
  2. 将函数需要的参数入栈,入栈顺序按照参数被声明的顺序。
  3. 告知LUA虚拟机入栈参数的个数、函数返回值的个数,并调用此LUA函数。
  4. 从栈定获得返回值,先返回的先入栈,然后将返回值显式出栈。

1.2 从LUA脚本调用C函数

LUA没有提供PYTHON那样丰富的类库,因此复杂的功能需要在C程序中定义好,然后通过lua决定调用时机。在LUA库中定义了可以被LUA虚拟机识别的C函数模型:

int functionName (lua_State* L) {....; return 1;}

这样的函数被是一个合法的lua_CFunction类型,将函数注册到LUA虚拟机中以后,就可以在LUA中以普通LUA函数的方式被调用。注册一个C函数的步骤如下:

  1. 声明并定义一个满足上述模型的函数 (eg. myFunInC)
  2. 用字符串为此C函数取一个名称并入栈(eg. myFunInLua)
  3. 将函数(指针)入栈
  4. 调用LUA库的注册函数功能,将上述的名称与函数指针关联

这样就可以在LUA中用myFunInLua()来调用C中的int myFunInC()了

2. 从C调用LUA函数示例

在下面的代码中,我们调用了LUA脚本中的fnEx2函数,返回值从栈中取得,并且要手动出栈。这里,入栈的函数参数会由pcall自动清理。

2.1 LUA测试脚本代码

function fnex2(str_a, num_b, num_c)
    print(str_a);
    return num_b*100 + num_c*10, "Thank you";
end;

2.2 VC代码

//初始化LUA虚拟机
void InitLuaState(lua_State* L)
{
    /* Load Libraries */
    luaopen_base(L);
    luaopen_table(L);
    luaL_openlibs(L);
    luaopen_string(L);
    luaopen_math(L);
}

int call_lua_function(void)
{
    const char* szInParam = "This is an [IN] parameter";
    const int iParam1 = 20, iParam2 = 50;
    cout << "=================================" << endl
         << "02_Call_Function" << endl
         << "=================================" << endl
         << "This demo calls functions in LUA scripts." << endl
         << "Argument 1:" << szInParam << endl
         << "Argument 2:" << iParam1 << endl
         << "Argument 3:" << iParam2 << endl
         << "---------------------------------" << endl
         << "#OUTPUTS#" << endl;

    lua_State* L = lua_open();
    InitLuaState(L);

    int iError;

    /* Load Script */
    iError = luaL_loadfile(L, "../test02.lua");
    if (iError)
    {
        cout << "Load script FAILED!" 
             << lua_tostring(L, -1)
             << endl;
        lua_close(L);
        return 1;
    }

    /* Run Script */
    iError = lua_pcall(L, 0, 0, 0);
    if (iError)
    {
        cout << "pcall FAILED"
             << lua_tostring(L, -1)
             << iError 
             << endl;
        lua_close(L);
        return 1;    }        /* Push a FUNCTION_VAR to STACK */    lua_getglobal(L, "fnex2");    /* Push PARAMETERS to STACK */    lua_pushstring(L, szInParam);    lua_pushnumber(L, iParam1);    lua_pushnumber(L, iParam2);    /* Call FUNCTION in LUA */    iError = lua_pcall( L,    //VMachine                        3,    //Argument Count                        2,    //Return Value Count                        0 );    if (iError)    {        cout << "pcall FAILED"             << lua_tostring(L, -1)             << iError              << endl;        lua_close(L);    }    /* Check Return Value Types */    if (lua_isstring(L, -1) && lua_isnumber(L, -2))    {        cout << "Ret_1(string): " << lua_tostring(L, -1) << endl;        cout << "Rec_2(double): " << lua_tonumber(L, -2) << endl;    }    else    {        cout << "Wrong Return Values" << endl;    }    /* POP STACK */    lua_pop(L,2);    //只需要清理Return Value,pcall调用的入栈参数会自动清理    lua_close(L);    return 0;}

2.3 工具

下面的宏可以简化调用lua函数的代码:

#define CallLuaFunc(FuncName, Params, Results) 

    lua_getglobal (g_pLuaState, FuncName); 
    lua_call (g_pLuaState, Params, Results); 
}

3. 从LUA调用C函数示例

在下面的例子中,我们注册一个名为rmath的LUA函数,他在C中的函数名为RMath_LUA()

3.1 LUA脚本代码

print (">>> LUA程序开始运行了 ");

function fnex3(num_a, num_b)
    local c = rmath(num_a, num_b);
    print("LUA PRINTTING:", c);
    return c;
end;

3.2 VC程序代码

//LUA脚本调用C函数
int call_c_function(void)
{
    int iArg1 = 3, iArg2 = 10, iError;
    cout << "=================================" << endl
         << "下面的程序演示从LUA脚本中调用C函数" << endl
         << "Argument 1:" << iArg1 << endl
         << "Argument 2:" << iArg2 << endl
         << "---------------------------------" << endl
         << "#OUTPUTS#" << endl;
    lua_State* L = lua_open();
    InitLuaState(L);

    iError = luaL_loadfile(L, "../test03.lua");
    if (iError) cout << "载入脚本失败" << endl;

    iError = lua_pcall(L, 0, 0, 0);
    if (iError) cout << "执行LUA脚本失败" << endl;

    /* 将C函数(指针)压栈 */
    lua_pushstring(L, "rmath");
    lua_pushcfunction(L, RMath_LUA);
    lua_settable(L, LUA_GLOBALSINDEX);

    /* LUA函数也是变量(指针),可以压入栈 */
    lua_getglobal(L, "fnex3");

    /* 将提供给LUA函数的参数入栈 */
    lua_pushnumber(L, iArg1);
    lua_pushnumber(L, iArg2);

    /* 调用LUA函数(pcall函数会自动清除入栈的变量) */
    int Error = lua_pcall(  L,        //虚拟机指针
                            2,        //2个参数
                            1,        //1个返回值
                            0 );

    if (Error) cout << "pcall调用fnex3函数失败" << endl;

    /* 检验返回值类型 */
    if (lua_isnumber(L, -1))
    {
        cout << "有1个(double)返回值 = "
             << lua_tonumber(L, -1)
             << endl;    }    /* 将LUA函数返回值出栈 */    lua_pop(L, 1);    lua_close(L);    return 0;}//可供LUA调用的C函数原型int RMath_LUA(lua_State* L){    if (!lua_isnumber(L, 1))    {        lua_pushstring(L, "Arg_1不是数字");        lua_error(L);    }    if (!lua_isnumber(L, 2))    {        lua_pushstring(L, "Arg_2不是数字");        lua_error(L);    }    /* GET ARGUMENT FROM STACK */    double a = lua_tonumber(L, 1);     double b = lua_tonumber(L, 2);    /* PUSH RESULT TO STACK */    lua_pushnumber(L, a * b);    /* COUNT OF RETURN VARS*/    return 1;}

4. 程序解释

4.1 调用LUA脚本中的函数

调用LUA脚本函数主要用到如下几个LUA库函数:

    /* Push a FUNCTION_VAR to STACK */
    lua_getglobal(L, "fnex2");

    /* Push PARAMETERS to STACK */
    lua_pushstring(L, szInParam);
    lua_pushnumber(L, iParam1);
    lua_pushnumber(L, iParam2);

    /* Call FUNCTION in LUA */
    iError = lua_pcall( L,3,2,0);

通过lua_getglobal请求函数(指针)入栈,然后将函数参数按声明顺序入栈,调用lua_pcall执行函数。lua_pcall的第一个参数 指向LUA虚拟机,第二个参数表示栈顶有多少个函数参数,第三个参数表示此函数将返回几个值。(pcall自动清理入栈的参数,返回值则需要手动 pop。)

4.2 从LUA调用C函数

主要用到如下几个函数,为求方便您也可以自己定义这样的一个宏。

    lua_pushstring(L, "rmath");
    lua_pushcfunction(L, RMath_LUA);
    lua_settable(L, LUA_GLOBALSINDEX);
  • 函数名入栈
  • lua_CFunction类型的函数指针入栈
  • 调用lua_settable注册函数

这样就可以在lua脚本中调用rmath()函数了。

[转]LUA C 互调的更多相关文章

  1. C/C++与lua实现互调

    1,在lua脚本中调用C/C++代码中的函数 在C++中定义函数时必须以lua_State为参数, 以int为返回值才能被Lua所调用. /* typedef int (*lua_CFunction) ...

  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. java中汉字自动转换成拼音

    java中汉字自动转换成拼音 1.需要下载jar包 pinyin4j.2.5.0.jar ,加入到WEB-INF下的lib里边,右键add to bulid path. 2.[代码]PinYinUti ...

  2. js便签笔记(13)——jsonp其实很简单【ajax跨域请求】

    前两天被问到ajax跨域如何解决,还真被问住了,光知道有个什么jsonp,迷迷糊糊的没有说上来.抱着有问题必须解决的态度,我看了许多资料,原来如此... 为何一直知道jsonp,但一直迷迷糊糊的不明白 ...

  3. 初学ExtJs 表格显示后台数据

    最近开始接触ExtJs,贴出自己的代码,一个简单的表格显示 版本 3.4.1 需要json包 代码清单1.jsp引入的ExtJs文件 <!-- 资源文件 ExtJs --> <lin ...

  4. HtmlAgilityPack解析全国区号页面到XML

    需求:完成一个城市和区号的xml配置文件 处理思路:通过HtmlAgilityPack解析一个区号页面,生产xml文件 页面:http://www.hljboli.gov.cn/html/code.h ...

  5. 关于linux的磁盘管理

    普通的做法就是检索文件的时间,并使用rm进行删除. 另外有一种做法,可以通过检索inode,进一步进行文件的删除. 下面的文章为摘录,帮助了解inode: 文件名 -> inode -> ...

  6. 基于TCP协议的网络通信

    TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信.Java对基于TC ...

  7. hduacm 2888 ----二维rmq

    http://acm.hdu.edu.cn/showproblem.php?pid=2888 模板题  直接用二维rmq 读入数据时比较坑爹  cin 会超时 #include <cstdio& ...

  8. POJ 1739

    楼教主男人八题之一... 题目大意: 求从左下角经过所有非障碍点一次到达右下角的方案数 这里不是求回路,但是我们可以考虑,在最下面一行再增加一行,那么就可以当做求此时左下角到右下角的回路总数,那么就转 ...

  9. Rhel6-lvs配置文档

    系统环境: rhel6 x86_64 iptables and selinux disabled 相关网址:http://zh.linuxvirtualserver.org/ yum仓库配置: [rh ...

  10. https需要的类

    import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import j ...