当前位置: 首页>编程语言>正文

lua 简单接口 lua c接口

cocos2d-x和 quick-cocos2d-x 的底层代码都是使用 C++ 语言开发的。为了使用 Lua脚本语言进行开发,我们利用 tolua++ 工具,将大量的 C/C++ API 导出到了 Lua中。

使用 tolua++的基本步骤:

从 C/C++源代码复制头文件的内容到 .tolua(tolua++文档中称为 .pkg)文件中。

.tolua 文件内容,去掉 tolua++无法识别的内容,以及不需要导出到 Lua 的定义。

运行 tolua++工具,根据 .tolua 文件生成 luabinding 接口文件(由 .cpp文件和 .h 文件自称)。

在 AppDelegate.cpp中加载 luabinding 文件。

在 AppDelegate初始化 Lua 虚拟机后,调用 luabinding 接口文件中的 luaopen函数,注册 C/C++ API。

根据实践,我们建议采用如下的方案来完成整个导出工作。

从 C/C++源文件创建 .tolua 文件

MyClass.h 头文件内容如下:

#ifndef __MY_CLASS_H_
#define __MY_CLASS_H_

class MyClass
{
public:
    static void addTwoNumber(float number1, float number2);
    
private:
    MyClass(void) {}
};

#endif // __MY_CLASS_H_


为了便于维护,应该将 .h文件对应的 tolua 命名为 XXX_luabinding.tolua。这样生成的 luabinding接口文件名就是 XXX_luabinding.cpp 和 XXX_luabinding.h,不会和已有的 C/C++源文件冲突。

创建 MyClass_luabinding.tolua文件,并修改内容为:

class MyClass
{
public:
    static void addTwoNumber(float number1, float number2);
};


.tolua 的内容有明显简化。详细的内容修改规则,会在本文后续部分说明。

生成 luabinding接口文件

quick 为了简化这一步工作,提供了相应工具,我们只需要创建一个脚本文件来调用工具即可。

1.创建 build_luabinding.sh文件,内容如下:

#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR"
OUTPUT_DIR="$DIR"
MAKE_LUABINDING="$QUICK_COCOS2DX_ROOT"/bin/compile_luabinding.sh
$MAKE_LUABINDING -d "$OUTPUT_DIR" MyClass_luabinding.tolua


记得在命令行中 chmod 755 build_luabinding.sh,否则无法执行该脚本。

Windows 版的批处理内容如下:

@echo off
set DIR=%~dp0
set OUTPUT_DIR=%DIR%
set MAKE_LUABINDING="%QUICK_COCOS2DX_ROOT%\bin\compile_luabinding.bat"
pushd
cd "%DIR%"
call %MAKE_LUABINDING% -d %OUTPUT_DIR% MyClass_luabinding.tolua


注意:运行脚本前请确保已经下载了 quick-cocos2d-x,并且正确设置了 QUICK_COCOS2DX_ROOT 环境变量。环境配置请参考《入门指引》。

2.在命令行下运行我们创建的脚本,如果一切顺利,我们会看到如下输出信息:

creating file: MyClass_luabinding.cpp
creating file: MyClass_luabinding.h

// add to AppDelegate.cpp
#include "MyClass_luabinding.h"

// add to AppDelegate::applicationDidFinishLaunching()
CCLuaStack* stack = CCScriptEngineManager::sharedManager()
->getScriptEngine()
->getLuaStack();
lua_State* L = stack->getLuaState();
luaopen_MyClass_luabinding(L);


载入 luabinding接口文件

打开我们的项目,将 MyClass_luabinding.cpp和 MyClass_luabinding.h 文件加入工程。然后修改 AppDelegate.cpp 文件:

1.在 AppDelegate.cpp头部区域添加:

#include "MyClass_luabinding.h"


2.在 AppDelegate::applicationDidFinishLaunching()函数内添加:

luaopen_MyClass_luabinding(L);

luaopen 函数后面,例如:

// register lua engine
CCLuaEngine *pEngine = CCLuaEngine::defaultEngine();
CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);

CCLuaStack *pStack = pEngine->getLuaStack();
lua_State* L = pStack->getLuaState();

// load lua extensions
luaopen_lua_extensions(L);
// load cocos2dx_extra luabinding
luaopen_cocos2dx_extra_luabinding(L);

// thrid_party
luaopen_third_party_luabinding(L);

// CCBReader
tolua_extensions_ccb_open(L);

// MyClass
luaopen_MyClass_luabinding(L);


应该将我们的代码追加到 tolua_extensions_ccb_open()后面。

Lua 脚本中使用我们导出的 MyClass对象极其方法了。

.tolua文件内容的修改规则

MyClass 是一个非常简单的例子,但我们实际游戏中的 C/C++ API可能比较复杂。在修改 .tolua 文件内容时,应该仔细阅读以下内容。

删除所有无需在 Lua中使用的内容

API 越多,在 Lua虚拟机中占用的符号表空间就越多。因此我们第一步要做的就是删除所有无需在 Lua 中使用的内容。

enum、宏定义,如果需要导出,原文保留即可。但宏定义只能导出数值定义,例如:

#define kCCHTTPRequestMethodGET  0
#define kCCHTTPRequestMethodPOST 1


而非数值的宏定义无法导出,以下内容会导出失败:

#define kMyConstantString "HELLO"

CC_DLL。

3.删除 C++ class中所有非 public 的定义。

4.删除 C++ class中的类成员变量。

5.删除 inline关键词,以及 inline function 的实现,只保留声明。

展开宏

CC_PROPERTY。对于这类宏,需要根据宏定义,将宏展开为声明。

CC_PROPERTY(float, m_fDuration, Duration)


展开为:

float getDuration();
void setDuration(float v);


需要如此处理的宏包括:

CC_PROPERTY_READONLY, CC_PROPERTY, CC_PROPERTY_PASS_BY_REF, CC_SYNTHESIZE_READONLY, CC_SYNTHESIZE_READONLY_PASS_BY_REF, CC_SYNTHESIZE, CC_SYNTHESIZE_PASS_BY_REF, CC_SYNTHESIZE_RETAIN。


cocos2d-x 基础代码里,我们自己的 C++class还是不要用这些宏了。

处理名字空间

.tolua 的头部应该加入:

$using namespace myname;

“$”符号,后续内容会原样放入 luabinding文件。

添加必要的 #include指令

luabinding 接口文件无法编译,需要检查是否是需要 include相应的头文件,并添加如下代码:

$#include "MyClass.h"


修改函数参数和返回值类型,去除const修饰符

const修饰符。由于 tolua++ 的限制,并不能很好的处理这类定义,所以我们要从 .tolua文件中移除 const修饰符。唯一例外的就是 constchar*不需要修改为 char*。

例如:

CCPoint convertToNodeSpace(const CCPoint& worldPoint);


应该修改为:

CCPoint convertToNodeSpace(CCPoint& worldPoint);


tolua++ 把const CCPoint和 CCPoint 当做两个不同的类型来处理。如果不做修改,那么调用函数时会报告参数类型不符。

从 C/C++函数返回多个值

constchar*,那么在 luabinding接口文件中,该函数会返回多个值。

例如:

void getPosition(float* x = 0, float* y = 0);


Lua 中调用这个函数,会得到两个返回值:

local x, y = node:getPosition()



将 Lua函数传入

quick里,允许将 Lua 函数传入 C/C++,只要求 C/C++ 函数中使用 int 做参数类型。但在 .tolua文件里,则必须使用 LUA_FUNCTION 做参数类型。

例如:

static CCHTTPRequest* createWithUrlLua(int listener,
                                       const char* url,
                                       int method = kCCHTTPRequestMethodGET);


listener参数用于保存传入的 Lua 函数,所以 .tolua 文件里要改写为:

static CCHTTPRequest* createWithUrlLua(LUA_FUNCTION listener,
                                       const char* url,
                                       int method = kCCHTTPRequestMethodGET);


具体用法请参考 lib/cocos2dx_extra/extra/network/CCHTTPRequest中的 createWithUrlLua() 方法。

在 Lua和 C/C++ 间交换二进制数据

要从 C/C++返回二进制数据给 Lua,函数返回值类型必须是 int,而 .tolua文件中修改返回值为 LUA_STRING。函数中,需要用 CCLuaStack::pushString()将二进制数据放入 Lua stack。然后返回“需要传递给 Lua的值”的数量。

具体用法请参考 lib/cocos2dx_extra/extra/network/CCHTTPRequest中的 getResponseDataLua() 方法。

Lua 传递二进制数据给 C/C++很简单,使用 constchar*参数类型和 int类型参数分别指定二进制数据的指针和数据长度。

具体用法请参考 lib/cocos2dx_extra/extra/crypto/CCCrypto中的 decryptXXTEALua() 方法。

更多用法

关于利用 tolua++的更多用法,建议参考 lib/cocos2dx_extra 中的 CCCrypto、CCNative、CCHTTPRquest等 class。这些 class对 Lua 提供了良好的支持,具体用法上也覆盖了绝大多数 C/C++ 和 Lua交互的需求。

- END -


https://www.xamrdz.com/lan/5jg1941925.html

相关文章: