在Lua中调用c++函数

来源:互联网 发布:日志记录访问次数java 编辑:程序博客网 时间:2024/06/05 13:48

现在我们需要在Lua中调用c++的函数,完成Lua语言实现起来不太方便的功能,这种方法网上有很多说明,但是写得不够详细,初学者看了一头雾水,下面我写一下自己的方法,目的仅仅是让我们先把程序跑起来!

首先来看下代码,然后说配置(在Mac下演示),再解释代码。

1#include <iostream>
2 
3#ifdef __cplusplus
4extern "C"
5{
6#endif
7 
8#include <lua.h>
9#include <lualib.h>
10#include <lauxlib.h>
11 
12#ifdef __cplusplus
13}
14#endif
15 
16//要想注册进lua,函数的定义为 typedef int (*lua_CFunction)(lua_State* L)
17int printHello(lua_State * l)
18{
19    lua_pushstring(l,"hello lua");
20 
21    //返回值代表向栈内压入的元素个数
22    return 1;
23}
24 
25int foo(lua_State * l)
26{
27    //获得Lua传递过来的参数个数
28    int n = lua_gettop(l);
29    if(n != 0)
30    {
31        //获得第一个参数
32        int i = lua_tonumber(l,1);
33        //将传递过来的参数加一以后最为返回值传递回去
34        lua_pushnumber(l,i+1);
35        return 1;
36    }
37 
38    return 0;
39}
40 
41//相加
42int add(lua_State * l)
43{
44    int n = lua_gettop(l);
45    int sum = 0;
46    for (int i=0;i<n;i++)
47    {
48        sum += lua_tonumber(l,i+1);
49    }
50    if(n!=0)
51    {
52        lua_pushnumber(l,sum);
53        return 1;
54    }
55 
56    return 0;
57}
58 
59//把需要用到的函数都放到注册表中,统一进行注册
60const luaL_Reg lib[]=
61{
62    {"printHello",printHello},
63    {"foo",foo},
64    {"add",add},
65    {nullptr,nullptr}
66};
67 
68int main(int argc, const char * argv[])
69{
70    //创建一个新的Lua环境
71    lua_State * l= lua_open();
72 
73    //打开需要的库
74    luaL_openlibs(l);
75 
76    //把foo函数注册进lua,第二个参数代表Lua中要调用的函数名称,第三个参数就是c层的函数名称
77    //单独注册一个函数
78//    lua_register(l,"foo",foo);
79 
80    //统一注册lua中调用的函数
81    const luaL_Reg* libf =lib;
82    for (; libf->func; libf++)
83    {
84        //把foo函数注册进lua,第二个参数代表Lua中要调用的函数名称,第三个参数就是c层的函数名称
85        lua_register(l,libf->name,libf->func);
86        //将栈顶清空
87        lua_settop(l,0);
88    }
89 
90    //加载并且执行lua文件
91    luaL_dofile(l,"test.lua");
92    //使用dostring直接执行字符串代表的内容,是一段Lua代码
93    //    luaL_dostring(l,"print(foo(100))");
94 
95    //关闭
96    lua_close(l);
97 
98    return 0;
99}

如果你运行这个程序,会发现编译错误,连头文件都找不到的,所以,我们就要配置一下环境了。首先我们需要下载lua源码,然后解压出来进入Lua的目录,打开控制台切换到Lua目录下,输入make macosx。学过Linux的人就知道这个命令是什么意思,他会编译源码,产生我们要得库,最后会在src目录下产生三个文件lua (the interpreter), luac (the compiler), and liblua.a (the library)。最后为了验证编译的正确性,可以运行make test,过程如图所示。

在Lua中调用c++函数在Lua中调用c++函数在Lua中调用c++函数

接下来打开Mac的工程,我们需要进行如图所示的配置。我们需要做的就是对工程配置一下头文件的路径和库的路径,大家根据自己刚才存放的源码目录进行配置,我把用到的库放到了工程下,所以库的路径就是工程的路径。

在Lua中调用c++函数

完成了以上的工作就需要解释一下代码的含义了。头文件的包含这个就不用说了,然后在main函数中,执行了打开lua环境,打开需要用到的库,将要被Lua调用的函数注册一下,然后调用Lua文件,最后关闭Lua环境。代码上都有注释,大家可以查看。而我们注册给Lua使用的函数,必须采用以下的定义方式,typedef int (*lua_CFunction)(lua_State* L),返回值代表的就是要返回给Lua层多少个返回值,这些返回值都使用push压入了栈中,而函数的参数代表的是Lua的全局状态。涉及到得具体函数作用,代码已经有说明,test.lua如下。

1print(printHello())
2print(foo(99))
3print(add(1,5,3,6))

为了打印出结果,我们还需要做的一个步骤如图。

在Lua中调用c++函数

在Lua中调用c++函数

然而为了更好的理解编译链接库的原理,我们在控制台下对刚才的工程做一个编译,其实用到的仅仅就是工程中得一个main函数。我们用到的编译工具是gcc,而不是Xcode集成好的环境。直接使用g++ main.cpp -o main编译main.cpp,希望产生的可执行文件是main。然后接着就报错了,说没有找到lua.h的头文件,好吧,那我们就使用一个-I参数,后边跟上头文件所在的路径,就是你下载下来的源码src文件夹所在的路径:g++ main.cpp -I /Users/mac/Documents/lua-5.1/src -o main(-I参数的含义就是指定头文件的路径),想不到继续报错,说链接失败,原因当然是没有找到Lua库了,所以我们使用-l参数加上库的名称来指定一下库名,接着报错,说库名没有找到,好吧,继续加上-L指定你的库路径。最后的命令是g++ main.cpp -I /Users/mac/Documents/lua-5.1/src -o main -llua -L .这次真的是OK了,然后运行程序,出现了我们要得结果。

在Lua中调用c++函数在Lua中调用c++函数

现在我们需要将我们用到的这些函数封装到一个模块中,就像cocos的做法一样,使用模块名来调用这些函数,这样组织代码看起来才会好一点。

1//要想注册进lua,函数的定义为 typedef int (*lua_CFunction)(lua_State* L)
2int printHello(lua_State * l)
3{
4    lua_pushstring(l,"hello lua");
5 
6    //返回值代表向栈内压入的元素个数
7    return 1;
8}
9 
10int foo(lua_State * l)
11{
12    //获得Lua传递过来的参数个数
13    int n = lua_gettop(l);
14    if(n != 0)
15    {
16        //获得第一个参数
17        int i = lua_tonumber(l,1);
18        //将传递过来的参数加一以后最为返回值传递回去
19        lua_pushnumber(l,i+1);
20        return 1;
21    }
22 
23    return 0;
24}
25 
26//相加
27int add(lua_State * l)
28{
29    int n = lua_gettop(l);
30    int sum = 0;
31    for (int i=0;i<n;i++)
32    {
33        sum += lua_tonumber(l,i+1);
34    }
35    if(n!=0)
36    {
37        lua_pushnumber(l,sum);
38        return 1;
39    }
40 
41    return 0;
42}
43 
44//把需要用到的函数都放到注册表中,统一进行注册
45const luaL_Reg lib[]=
46{
47    {"printHello",printHello},
48    {"foo",foo},
49    {"add",add},
50    {nullptr,nullptr}
51};
52 
53//把上边的函数封装到一个模块里边
54int luaopen_tt(lua_State * l)
55{
56    //首先创建一个table,然后把成员函数名做key,成员函数作为value放入该table中
57    luaL_newlib(l,lib);
58 
59    return 1;
60}
61 
62int main(int argc, const char * argv[])
63{
64    //创建一个新的Lua环境
65    lua_State * l= luaL_newstate();
66 
67    //打开需要的库
68    luaL_openlibs(l);
69 
70    //注册模块
71    luaL_requiref(l,"tt",luaopen_tt,1);
72 
73    //执行test2.lua文件
74    luaL_dofile(l,"test2.lua");
75 
76    //关闭
77    lua_close(l);
78 
79    return 0;
80}

上边的代码有俩处比较重要,一个是模块注册函数luaopen_tt,我想注册一个tt模块,所以这个模块的名字就叫做luaopen_tt,在Lua层require一个模块名的时候其实是去找一个函数名叫luaopen_模块名的函数,所以你的模块名的函数也需要按照这种格式来写,包括参数和返回值。在这个函数中,使用luaL_newlib函数,把用到的c++函数都放到了全局的表里边,这样就可以使用模块名调用了。另一个重要的地方是函数luaL_requiref(l,"tt",luaopen_tt,1),第二个参数是模块的名字,要和你上边的luaopen后边的名字相同。这样我们在test2.lua文件中就可以这么使用了。通过这种方法,我们就将c++要导出来的函数封装到一个模块中了。

1require "tt"
2print(tt.printHello())

现在需要做的一件事是,在Cocos2d-x的函数代码中导出函数给Lua用,这个过程是相同的,只不过要看看Cocos给我们提供了什么。使用Cocos Code IDE创建一个Cocos Lua工程,默认情况下工程下是没有c++层的源代码的,所以我们要让这些c++的源代码出来。点击工程选择Cocos Tools,然后选择Add Native Codes Support。这样,在你工程的目录下就会出现一个frameworks的目录,里边放的就是c++的源码。

在Lua中调用c++函数

有必要了解一下Cocos Code IDE生成的工程的目录都是神马意思,如下图所示。res和src不用说了,config.json文件是一个配置文件,配置窗口的大小和窗口标题以及调试的端口号等等这些东西,还有很多内容,需要我们配置的时候就去这里找好了。framework就是源码了,里边的cocos2d-x就是cocos的源码了,runtime-src下得classes下就是c++层用到的源码。其实我们使用Cocos Code IDE来做游戏开发,程序的启动是从c++层启动的,有时候虽然我们整个游戏项目写的都是Lua脚本,没写过一行c++代码,但是,程序启动是从c++代码启动的,是从c++层调用的Lua脚本,然后Lua脚本调用cocos绑定的函数。所以最后的那个runtime文件夹就是编译出来得运行环境,每当我们c++层改变代码的时候都需要借助Cocos Code IDE这个工具来重新生成一个runtime。

在Lua中调用c++函数

现在言归正传,打开AppDelegate.cpp文件,在applicationDidFinishLaunching函数的如下地方注册我们得导出函数。

在Lua中调用c++函数

我将要导出的函数写到了一个新的.cpp文件中了,单击工程创建一个.cpp文件和一个.h文件,我的文件内容如下。

1#ifndef __Test10__TestLua__
2#define __Test10__TestLua__
3 
4extern "C" {
5#include "lua.h"
6#include "lualib.h"
7#include "lauxlib.h"
8}
9 
10int test(lua_State * l);
1#include "TestLua.h"
2 
3int test(lua_State * l)
4{
5    lua_pushstring(l,"单独的c文件");
6    return 1;
7}

AppDelegate.cpp文件一定要包含TestLua.h文件哈,要不注册的时候怎么知道有test这个函数。关于导出函数的写法和在AppDelegate中怎么注册我就不用说了吧,和前边的一样,使用stack->getLuaState()就是获得运行时的全局环境,有了这个全局的环境就可以注册了。这里需要注意的是,新建的TestLua.h和TestLua.cpp文件一定是加入到这个工程中得,否则编译的时候不会编译这俩个文件,没有加入进去相信AppDelegate中就会报错了,这里个文件的创建就和我们平时写c++代码一样使用同样的方法做就OK了。接下来比较重要的是,在Lua工程中,我们需要重新编译一下runtime(在Cocos Tools菜单中找),原因我想不用说了吧,最后在工程的debug配置中看看是不是用的是新编译出来的runtime,不要用错了。

在Lua中调用c++函数

最后我们只需要在工程中简单得测试一下就OK了。

在Lua中调用c++函数在Lua中调用c++函数

版权声明:本文由( 小塔 )原创,转载请保留文章出处!

本文链接:http://www.zaojiahua.com/c-functions.html


0 0
原创粉丝点击