在Lua中调用c++函数

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

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

1 #include <iostream>
2  
3 #ifdef __cplusplus
4 extern "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)
17 int printHello(lua_State * l)
18 {
19     lua_pushstring(l,"hello lua");
20  
21     //返回值代表向栈内压入的元素个数
22     return 1;
23 }
24  
25 int 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 //相加
42 int 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 //把需要用到的函数都放到注册表中,统一进行注册
60 const luaL_Reg lib[]=
61 {
62     {"printHello",printHello},
63     {"foo",foo},
64     {"add",add},
65     {nullptr,nullptr}
66 };
67  
68 int 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如下。

1 print(printHello())
2 print(foo(99))
3 print(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)
2 int printHello(lua_State * l)
3 {
4     lua_pushstring(l,"hello lua");
5  
6     //返回值代表向栈内压入的元素个数
7     return 1;
8 }
9  
10 int 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 //相加
27 int 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 //把需要用到的函数都放到注册表中,统一进行注册
45 const luaL_Reg lib[]=
46 {
47     {"printHello",printHello},
48     {"foo",foo},
49     {"add",add},
50     {nullptr,nullptr}
51 };
52  
53 //把上边的函数封装到一个模块里边
54 int luaopen_tt(lua_State * l)
55 {
56     //首先创建一个table,然后把成员函数名做key,成员函数作为value放入该table中
57     luaL_newlib(l,lib);
58  
59     return 1;
60 }
61  
62 int 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++要导出来的函数封装到一个模块中了。

1 require "tt"
2 print(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  
4 extern "C" {
5 #include "lua.h"
6 #include "lualib.h"
7 #include "lauxlib.h"
8 }
9  
10 int test(lua_State * l);
1 #include "TestLua.h"
2  
3 int 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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值