导出 C/C++ API 给 Lua 使用

 

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

使用 tolua++ 的基本步骤:

  1. 从 C/C++ 源代码复制头文件的内容到 .tolua(tolua++ 文档中称为 .pkg)文件中。
  2. 修改 .tolua 文件内容,去掉 tolua++ 无法识别的内容,以及不需要导出到 Lua 的定义。
  3. 运行 tolua++ 工具,根据 .tolua 文件生成 luabinding 接口文件(由 .cpp 文件和 .h 文件自称)。
  4. 在 AppDelegate.cpp 中加载 luabinding 文件。
  5. 在 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 CCObject
{
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 -E MyClass -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 /d "%DIR%"
    call %MAKE_LUABINDING% -E MyClass -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 中使用的内容。

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

    #define kCCHTTPRequestMethodGET  0
    #define kCCHTTPRequestMethodPOST 1

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

    #define kMyConstantString "HELLO"
  2. 删除所有无法识别的宏,例如 CC_DLL。

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

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

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

处理 CCObject 继承类

CCObject 及其继承类都具备“引用计数”和“自动释放”机制。如果你的 C++ 对象是从 CCObject 继承的,那么必须告诉 tolua++ 做相应处理,否则可能出现内存泄漏等问题。

对于 quick,只需要在 build 脚本中通过 -E CCOBJECTS 参数指定这些 class 的名字即可。

例如前面 MyClass 的示例中,用 -E CCOBJECTS=MyClass 告诉 tolua++ 应该将 MyClass 当作 CCObject 的继承类进行处理。

如果有多个类,那么每个类名之间用“,”分隔即可,例如:

$MAKE_LUABINDING -E CCOBJECTS=MyClass,MyClass2,MyClass3 -d "$OUTPUT_DIR" MyClass_luabinding.tolua

展开宏

有些宏是不能直接删除的,例如 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 修饰符。唯一例外的就是 const char* 不需要修改为 char*。

例如:

CCPoint convertToNodeSpace(const CCPoint& worldPoint);

应该修改为:

CCPoint convertToNodeSpace(CCPoint& worldPoint);

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

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

如果一个函数的所有参数都是引用或指针类型,并且不是 const char*,那么在 luabinding 接口文件中,该函数会返回多个值。

例如:

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

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

local x, y = node:getPosition()

将 Lua 函数传入 C/C++

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++ 很简单,使用 const char* 参数类型和 int 类型参数分别指定二进制数据的指针和数据长度。

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

更多用法

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

- END -

导出 C/C++ API 给 Lua 使用[转]的更多相关文章

  1. HttpLuaModule——翻译(Nginx API for Lua) (转)

    现在我已经将翻译的内容放到:http://wiki.nginx.org/HttpLuaModuleZh Nginx API for Lua Introduction 各种各样的*_by_lua和*_b ...

  2. HttpLuaModule——翻译(Nginx API for Lua)

    现在我已经将翻译的内容放到:http://wiki.nginx.org/HttpLuaModuleZh Nginx API for Lua Introduction 各种各样的*_by_lua和*_b ...

  3. openresty HTTP status constants nginx api for lua

    https://github.com/openresty/lua-nginx-module context: init_by_lua, set_by_lua, rewrite_by_lua, acce ...

  4. luars232库中用到的一些C API for lua

    代码就不贴了,这里只是梳理一下前两篇里面忽略的一些东西,作为读代码的记录吧. 1.头文件 #include <lauxlib.h> #include <lua.h> All A ...

  5. Lua调用C++时打印堆栈信息

    公司的手游项目,使用的是基于cocos2d-x绑lua的解决方案(参数quick-x的绑定),虽然使用了lua进行开发,更新很爽了,但是崩溃依然较为严重,从后台查看崩溃日志时,基本上只能靠" ...

  6. 如何学习 cocos2d-x ?

    发表于 04/23/2014 作者 zrong — 24 条评论 ↓ 11,687 次查看 本站文章除注明转载外,均为本站原创或者翻译. 本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处 ...

  7. 【精选】Nginx模块Lua-Nginx-Module学习笔记(一)Nginx Lua API 接口详解

    源码地址:https://github.com/Tinywan/Lua-Nginx-Redis 一.介绍 各种* _by_lua,* _by_lua_block和* _by_lua_file配置指令用 ...

  8. Nginx模块Lua-Nginx-Module学习笔记(一)Nginx Lua API 接口详解

    源码地址:https://github.com/Tinywan/Lua-Nginx-Redis 一.介绍 各种* _by_lua,* _by_lua_block和* _by_lua_file配置指令用 ...

  9. 在MyEclipse中使用javadoc导出API文档详解

    本篇文档介绍如何在MyEclipse中导出javadoc(API)帮助文档,并且使用htmlhelp.exe和jd2chm.exe生成chm文档. 具体步骤如下: 打开MyEclipse,选中想要制作 ...

随机推荐

  1. Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET W ...

  2. Memcached分布式缓存初体验

    1 Memcached简介/下载/安装 Memcached是一个高性能的不是内存对象缓存系统,用于动态Web应用以减轻数据库负载.Memcached基于一个存储键/值对的HashMap.其客户端可以使 ...

  3. C++ Primer 变量和基本类型

    <C++ Primer 4th>读书摘要 基本上所有的语言都要提供下列特征: • 内置数据类型,如整型.字符型等. • 表达式和语句:表达式和语句用于操纵上述类型的值. • 变量:程序员可 ...

  4. javax.persistence.PersistenceException: No Persistence provider for EntityManager named ...

    控制台下输出信息 原因:persistence.xml必须放在src下META-INF里面. 若误放在其他路径,就会迷路.

  5. error C3861: “LOG4CPLUS_DEBUG”: 找不到标识

    头文件#include <log4cplus/loggingmacros.h>解决问题

  6. 浅谈NSBundle

    图片.xib等资源文件无法直接封入静态库,要想在静态库中使用他们,就必须借助于bundle 那么什么是bundle呢? 简单来说,bundle就是一个内部结构按照标准规则组织的特殊目录,即direct ...

  7. Leetcode-237 Delete Node in a Linked List

    #237.    Delete Node in a Linked List Write a function to delete a node (except the tail) in a singl ...

  8. 2013年的一些常用PHP資源整理下載

    這些資源基本上都是一些免積分或只需要1個積分就能下載的資源,經過整理篩選最後分享給大家,希望大家喜歡: PHP手册(chm) http://download.csdn.net/detail/u0118 ...

  9. [C/C++] zltabout(带缩进的格式化输出)v1.0。能以相同的代码绑定到 C FILE 或 C++流

    作者:zyl910 一.缘由 在写一些生成文本的程序时,经常需要使用带缩进的格式化输出的功能.以前为此写过不少类似的函数,可惜它们的可重用性很差. 这是因为——1) C语言的FILE*不支持重定向到自 ...

  10. 【Android源代码下载】收集整理android界面UI效果源码

    在Android开发中,Android界面UI效果设计一直都是很多童鞋关注的问题,今天给大家分享下大神收集整理的多个android界面UI效果,都是源码,都是干货,贡献给各位网友! 话不多说,直接上效 ...