现在我们需要在Lua中调用c++的函数,完成Lua语言实现起来不太方便的功能,这种方法网上有很多说明,但是写得不够详细,初学者看了一头雾水,下面我写一下自己的方法,目的仅仅是让我们先把程序跑起来!
首先来看下代码,然后说配置(在Mac下演示),再解释代码。
17 | int printHello(lua_State * l) |
19 | lua_pushstring(l, "hello lua" ); |
25 | int foo(lua_State * l) |
28 | int n = lua_gettop(l); |
32 | int i = lua_tonumber(l,1); |
34 | lua_pushnumber(l,i+1); |
42 | int add(lua_State * l) |
44 | int n = lua_gettop(l); |
48 | sum += lua_tonumber(l,i+1); |
52 | lua_pushnumber(l,sum); |
62 | { "printHello" ,printHello}, |
68 | int main( int argc, const char * argv[]) |
71 | lua_State * l= lua_open(); |
81 | const luaL_Reg* libf =lib; |
82 | for (; libf->func; libf++) |
85 | lua_register(l,libf->name,libf->func); |
91 | luaL_dofile(l, "test.lua" ); |
如果你运行这个程序,会发现编译错误,连头文件都找不到的,所以,我们就要配置一下环境了。首先我们需要下载lua源码,然后解压出来进入Lua的目录,打开控制台切换到Lua目录下,输入make macosx。学过Linux的人就知道这个命令是什么意思,他会编译源码,产生我们要得库,最后会在src目录下产生三个文件lua (the interpreter), luac (the compiler), and liblua.a (the library)。最后为了验证编译的正确性,可以运行make test,过程如图所示。



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

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


然而为了更好的理解编译链接库的原理,我们在控制台下对刚才的工程做一个编译,其实用到的仅仅就是工程中得一个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了,然后运行程序,出现了我们要得结果。


现在我们需要将我们用到的这些函数封装到一个模块中,就像cocos的做法一样,使用模块名来调用这些函数,这样组织代码看起来才会好一点。
2 | int printHello(lua_State * l) |
4 | lua_pushstring(l, "hello lua" ); |
10 | int foo(lua_State * l) |
13 | int n = lua_gettop(l); |
17 | int i = lua_tonumber(l,1); |
19 | lua_pushnumber(l,i+1); |
27 | int add(lua_State * l) |
29 | int n = lua_gettop(l); |
33 | sum += lua_tonumber(l,i+1); |
37 | lua_pushnumber(l,sum); |
47 | { "printHello" ,printHello}, |
54 | int luaopen_tt(lua_State * l) |
62 | int main( int argc, const char * argv[]) |
65 | lua_State * l= luaL_newstate(); |
71 | luaL_requiref(l, "tt" ,luaopen_tt,1); |
74 | luaL_dofile(l, "test2.lua" ); |
上边的代码有俩处比较重要,一个是模块注册函数luaopen_tt,我想注册一个tt模块,所以这个模块的名字就叫做luaopen_tt,在Lua层require一个模块名的时候其实是去找一个函数名叫luaopen_模块名的函数,所以你的模块名的函数也需要按照这种格式来写,包括参数和返回值。在这个函数中,使用luaL_newlib函数,把用到的c++函数都放到了全局的表里边,这样就可以使用模块名调用了。另一个重要的地方是函数luaL_requiref(l,"tt",luaopen_tt,1),第二个参数是模块的名字,要和你上边的luaopen后边的名字相同。这样我们在test2.lua文件中就可以这么使用了。通过这种方法,我们就将c++要导出来的函数封装到一个模块中了。
现在需要做的一件事是,在Cocos2d-x的函数代码中导出函数给Lua用,这个过程是相同的,只不过要看看Cocos给我们提供了什么。使用Cocos Code IDE创建一个Cocos Lua工程,默认情况下工程下是没有c++层的源代码的,所以我们要让这些c++的源代码出来。点击工程选择Cocos Tools,然后选择Add Native Codes Support。这样,在你工程的目录下就会出现一个frameworks的目录,里边放的就是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。

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

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

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


版权声明:本文由( 小塔 )原创,转载请保留文章出处!
本文链接:http://www.zaojiahua.com/c-functions.html