olua 导出工具的使用

本文将详细阐述如何配置和使用 olua 自动导出脚本功能。

如果想了解 olua 的设计细节,请参见:

1. 使用示例

1.1. 创建

新建一个目录存放配置和 olua 脚本,结构如下:

tree -L 2 .
.
├── build.lua
├── conf
│   ├── clang-args.lua
│   └── lua-example.lua
└── olua -> [email protected]:zhongfq/olua.git

1.2. clang 配置

clang 参数相关的选项配置都应该配置在 clang-args.lua 文件中。

clang-args.lua
clang {
'-DOLUA_DEBUG',
'-Isrc',
'-I../common',
'-I../..',
}

1.3. 类配置

conf 目录中,可以一个模块一个 lua 配置文件,具体需求可以自己决定。

lua-example.lua
module "example"

path "src"

headers [[
#include "Example.h"
#include "xlua.h"
]]

include "../common/lua-object.lua"

typeconf "example::Hello"

1.4. 构建脚本

build.lua
require "olua.tools"

OLUA_AUTO_EXPORT_PARENT = true

autoconf "conf/clang-args.lua"
autoconf "conf/lua-example.lua"

1.5. 生成

当配置好所以需要导出的类之后,就可以执行以下命令导出绑定。

lua build.lua

2. 可选配置

可以在 build.lua 文件中,设置变量以调整扫描行为:

  • OLUA_AUTO_BUILD:默认 true,扫描完成后,自动导出绑定代码。
  • OLUA_AUTO_GEN_PROP:默认 true,是否自动为 getNameisVisible 生成 namevisible 属性。
  • OLUA_AUTO_EXPORT_PARENT:默认 false,当没有用 typeconf 指定父类时,是否自动导出。
  • OLUA_ENABLE_DEPRECATED:默认 false,是否导出已丢弃方法或变量。
  • OLUA_ENABLE_WITH_UNDERSCORE:默认 false,是否导出以 _ 开头的变量或方法。

3. 配置指令

3.1. module

模块名称是由 module 指定的,也是构建导出文件名称的一部分。

module "example"

导出信息:

  • 导出文件名称 lua_example.hlua_exmaple.cpp
  • 模块函数是 luaopen_example

3.2. path

导出文件的目录是由 path 指定的,可以是绝对路径,也可以相当路径。

path '../../src'

3.3. headers

导出的头文件中的 include 部分是由 headers 指定的,这是保证编译成功的前置条件。

headers [[
#include "lua-bindings/lua_conv.h"
#include "lua-bindings/lua_conv_manual.h"
#include "cclua/xlua.h"
#include "Example.h"
]]

3.4. chunk

模块中,如果需要引入手写代码,可以由 chunk 指定,此代码原封不动拷贝至导出的文件中。

chunk [[
static const std::string makeScheduleCallbackTag(const std::string &key)
{
return "schedule." + key;
}]]

3.5. luaopen

luaopen 函数中,插入代码。

module 'example'

luaopen 'printf("hello luaopen!");'
生成代码:
static int luaopen_example(lua_State *L)
{
olua_require(L, "Hello", luaopen_Hello);
...
printf("hello luaopen!");
return 1;
}

3.6. excludetype

指定不需要导出的类型,一旦排除了一个类型,那么包含此类型的方法和变量都将忽略。

-- exclude example::Command and example::Command *
excludetype 'example::Command'

-- exclude example::Command * and example::Command **
excludetype 'example::Command *'

3.7. import

如果要包含一个配置文件,可以使用 import 指令,比如引入 lua-types.lua

import 'olua/lua-types.lua'

3.8. luacls

lua 类名的定制是由 luacls 指令实现的。

luacls(function (cppname)
return string.gsub(cppname, "::", ".")
end)

3.9. macro

macro 一般用于条件编译。

macro '#ifdef CCLUA_BUILD_EXAMPLE'
typeconf "Object"
macro ''

Object 所生成的代码都被 CCLUA_BUILD_EXAMPLE 包裹。

#ifdef CCLUA_BUILD_EXAMPLE
// Object 生成的代码
#endif

3.10. typedef

typedef 定义了一个类型,一般来说,你已经手动实现该类型的转换器,只是使用 typedef 将其关联。

3.10.1. 语法
typedef 'ClassName'
[.luacls]
[.conv]
[.packable]
[.packvars]
[.smartptr]
[.replace]
3.10.2. 指令 .conv

指定转换器,若未指定申明类型或未指定转换器,则默认是 olua_$$_ClassName

3.10.3. 指令 .luacls

指定 lua 类名。

3.10.4. 指令 .packable

指定此类型支持 @pack@unpack

3.10.5. 指令 .packvars

指定此类型由多少个成员变量组成。

3.10.6. 指令 .smartptr

指定类型是否为智能指针类型,如果是,会把 std::shared_ptr<Node *> 当作一个整体,而不是一个模版容器。

3.10.7. 指令 .replace

替换已有的类型信息。

3.10.8. 示例
typedef 'example::vector'
typedef 'example::Color'
.packable 'true'
.packvars '4'
typedef 'Uint'
.conv 'olua_$$_integer'
typedef 'Uint *'
.conv 'olua_$$_array'
.luacls 'olua.uint'

3.11. typeconf

typeconf 用于指定类或枚举的导出,包括类的静态方法、静态变量、对象方法、对像变量,但不包括模版函数。
同时会根据已经扫描到的信息,生成 typedef 信息,相当于:

// c++ 对象
typedef 'Object'
.conv 'olua_$$_object'
.luacls 'Object'

// 枚举
typedef 'Object'
.conv 'olua_$$_enum'
.luacls 'Object'
3.11.1. 语法
typeconf 'ClassName'
[.chunk]
[.luaname]
[.supercls]
[.packable]
[.packvars]
[.luaopen]
[.indexerror]
[.exclude]
[.include]
[.macro]
[.extend]
[.enum]
[.value]
[.const]
[.value]
[.typename]
[.var]
[.body]
[.optional]
[.ret]
[.arg1...N]
[.insert_before]
[.insert_after]
[.insert_cbefore]
[.insert_cafter]
[.func]
[.body]
[.optional]
[.ret]
[.arg1...N]
[.insert_before]
[.insert_after]
[.insert_cbefore]
[.insert_cafter]
[.callback]
[.localvar]
[.tag_maker]
[.tag_mode]
[.tag_store]
[.tag_scope]
[.optional]
[.ret]
[.arg1...N]
[.insert_before]
[.insert_after]
[.insert_cbefore]
[.insert_cafter]
[.prop]
[.get]
[.set]
[.alias]
3.11.2. 指令 .chunk

导出手写代码。

typeconf 'Object'
.chunk [[
static std::string makeForeachTag(int value)
{
return "foreach" + std::to_string(value);
}
]]
3.11.3. 指令 .luaname

自定义方法或变量的 lua 名称。

typeconf 'Object'
.luaname(function (name)
if name == 'print' then
name = 'dump'
end
return name
end)
生成代码:
static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_func(L, "dump", _Hello_print);
...
return 1;
}
3.11.4. 指令 .supercls

指定此类的父类名称,默认为 nil,由导出工具根据扫描信息而定。

typeconf 'Hello'
.supercls 'Object'
3.11.5. 指令 .packable

指定此类支持 @pack@unpack 标记,同时在导出时自动生成以下几个函数:

OLUA_LIB void olua_pack_object(lua_State *L, int idx, Object *value);
OLUA_LIB int olua_unpack_object(lua_State *L, const Object *value);
OLUA_LIB bool olua_canpack_object(lua_State *L, int idx, const Object *);
3.11.6. 指令 .packvars

指定此类型由多少个成员变量组成,一旦设置此变量,将不会自动生成上面三个函数,必须由使用者自己提供这三个函数。

3.11.7. 指令 .luaopen

luaopen_Hello 函数中,插入代码。

typeconf 'Hello'
.luaopen 'printf("hello luaopen!");'
生成代码:
static int luaopen_Hello(lua_State *L)
{
oluacls_class<Hello, Object>("Hello");
...
printf("hello luaopen!");
return 1;
}
3.11.8. 指令 .indexerror

指定类的可访问性

typeconf 'Object'
.indexerror 'rw'
  • r 访问不存的属性时抛出错误
  • w 写入新的属性时抛出错误
3.11.9. 指令 .exclude

所有方法和变量默认导出,除了指定的。

typeconf 'Object'
.exclude 'visible'
.exclude 'retain'

.exclude 还支持通配符形式:「.exclude '^m_.*'

3.11.10. 指令 .include

所有方法和变量默认不导出,除了指定的。

typeconf 'Object'
.include 'visible'
.include 'retain'
3.11.11. 指令 .macro

指定哪些方法需要根据宏来决定要不要编译。

typeconf 'Object'
.macro '#ifdef CCLUA_OS_ANDROID'
.func 'pay'
.macro ''
生成代码:
#ifdef CCLUA_OS_ANDROID
static _Object_pay(lua_State *L)
{
...
return 0;
}
#endif

static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
#ifdef CCLUA_OS_ANDROID
oluacls_func("pay", _Object_pay);
#endif
...
printf("hello require!");
return 1;
}
3.11.12. 指令 .extend

扩展指定的类。

typeconf 'Object'
.extend 'ObjectExtend'

把所有 ObjectExtend 的静态方法合并至 Object 中。

3.11.13. 指令 .enum

定义枚举。

typeconf 'Object'
.enum 'RED' .value '1'
.enum 'BLUE' .value '2'
生成代码:
static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_enum(L, "RED", 1);
oluacls_enum(L, "BLUE", 2);
...
return 1;
}
3.11.14. 指令 .const

定义常量。

typeconf 'Object'
.const 'VALUE1' .value 'true' .typename 'bool'
.const 'VALUE2' .value '2' .typename 'int'
.const 'VALUE3' .value 'hello' .typename 'const char *'
生成代码:
static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_const(L, "VALUE1", true);
oluacls_const(L, "VALUE2", 2);
oluacls_const(L, "VALUE3", "hello");
...
return 1;
}
3.11.15. 指令 .var

定义一个变量。

typeconf 'Object'
.var 'x'
.body 'int x'
生成代码:
static int _Object_get_x(lua_State *L)
{
...
int ret = self->x;
int num = olua_push_integer(L, ret);
...
return num;
}

static int _Object_set_x(lua_State *L)
{
...
int arg1;
...
olua_check_integer(L, 2, &arg1);
...
self->x = arg1;
...
return 0;
}

static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_prop(L, "x", _Object_get_x, _Object_set_x);
...
return 1;
}

.ret.arg1...N 使用说明参见 参数标记

.insert_before.insert_after.insert_cbefore、和 .insert_cafter 使用说明参见 插入代码

3.11.16. 指令 .func

定义方法。

typeconf 'Object'
.func 'dump'
.body [[
{
printf("call dump!");
return 0;
}]]
生成代码:
...
static int _Object_dump(lua_State *L)
{
printf("call dump!");
return 0;
}
...

static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_func(L, "dump", _Object_dump);
...
return 1;
}

.ret.arg1...N 使用说明参见 参数标记

.insert_before.insert_after.insert_cbefore、和 .insert_cafter 使用说明参见 插入代码

3.11.17. 指令 .callback

定义 std::function 回调细节。回调函数实现参见:olua 回调函数设计

typeconf 'Object'
.callback 'onClick'
.localvar 'true'
.tag_mode 'replace|new|startwith|equal'
.tag_store '0'
.tag_maker 'click'
.tag_scope 'object|once|function'

回调函数存储细节:

callback functions
obj.uservalue {
|---id----|--class--|--tag--|
.olua.cb#1$classname@onClick = lua_func
.olua.cb#2$classname@onClick = lua_func
.olua.cb#3$classname@update = lua_func
.olua.cb#4$classname@onRemoved = lua_func
...
}
  • localvar 回调函数的参数是否使用对象池,默认值 true
  • tag_mode 标签匹配模式,如果回调函数作为参数,默认值为 replace;如果回调函数作为返回值,默认值为 equal
    • replace 如果存在指定 tag 的回调函数,则替换,否则创建新 tag 存储回调函数。
    • new 始终创建新 tag 存储回调函数。
    • startwith 删除开头包含 tag 的回调函数。
    • equal 删除与 tag 相同的回调函数。
  • tag_store 回调函数存储位置,默认值为 0,合法值有:
    • 0 如果是静态方法,存储在 .classobj 中;如果对象方法,则存储在 userdata 中。
    • -1 存储在返回值中。
    • 1、2...N 从左到右数,存储在第 N 个参数中。
  • tag_maker 指定存储回调函数的键值,有两种形式:
    • string 纯字符串。
    • makeTag(#N)makeTag(#-N),将第 N 个参数作为参数调用 makeTag 生成键值。
  • tag_scope 回调函数的生命周期,默认为 object,合法值有:
    • object 由对象管理。
    • once 调用一次就移除。
    • function 调用完底层函数后就移除.
生成代码:
static int _Object_onClick(lua_State *L)
{
...
void *cb_store = (void *)self;
std::string cb_tag = "click";
std::string cb_name;
if (olua_isfunction(L, 2)) {
cb_name = olua_setcallback(L, cb_store, 2, cb_tag.c_str(), OLUA_TAG_REPLACE);
olua_Context cb_ctx = olua_context(L);
arg1 = [cb_store, cb_name, cb_ctx](Object *arg1) {
lua_State *L = olua_mainthread(NULL);
olua_checkhostthread();
if (olua_contextequal(L, cb_ctx)) {
int top = lua_gettop(L);
size_t last = olua_push_objpool(L);
olua_enable_objpool(L);
olua_push_obj(L, arg1, "Object");
olua_disable_objpool(L);
olua_callback(L, cb_store, cb_name.c_str(), 1);
olua_pop_objpool(L, last);
lua_settop(L, top);
}
};
} else {
olua_removecallback(L, cb_store, cb_tag.c_str(), OLUA_TAG_equal);
arg1 = nullptr;
}

// void onClick(@nullable const Object::ClickCallback &callback)
self->onClick(arg1);

return 0;
}

.ret.arg1...N 使用说明参见 参数标记

.insert_before.insert_after.insert_cbefore、和 .insert_cafter 使用说明参见 插入代码

3.11.18. 指令 .prop

定义属性。

typeconf 'Object'
.prop 'visible'
.get 'bool isVisible()'
.prop 'y'
.get 'int getY()'
.set 'void setY(int value)'
.prop 'z'
.get [[
{
Object *obj = olua_toobj<Object>(L, 1);
int ret = self->getZ();
lua_pushinteger(L, ret);
return 1;
}]]
.set [[
{
Object *obj = olua_toobj<Object>(L, 1);
int arg1 = (int)olua_checkinteger(L, 2);
self->setZ(arg1);
return 0;
}]]
生成代码:
...
static int _Object_get_z(lua_State *L)
{
Object *obj = olua_toobj<Object>(L, 1);
int ret = self->getZ();
lua_pushinteger(L, ret);
return 1;
}
...

static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_prop(L, "visible", _Object_isVisible, NULL);
oluacls_prop(L, "y", _Object_getY, _Object_setY);
oluacls_prop(L, "z", _Object_get_z, _Object_set_z);
...
return 1;
}
3.11.19. 指令 .alias

给指定的方法设置别名。

typeconf 'Object'
.alias 'print' .to 'dump'
生成代码:
static int luaopen_Object(lua_State *L)
{
oluacls_class<Object>(L, "Object");
oluacls_func(L, "dump", _Object_print);
oluacls_func(L, "print", _Object_print);
...
return 1;
}

3.12. typeonly

只导出类型信息,不导出任何方法和变量,等价于:

typeconf 'Object'
.exclude '*'

3.13. 插入代码

在导出插入代码,共有 4 个可以插入:

  • insert_before 函数调用前。
  • insert_after 函数调用后。
  • insert_cbefore 回调函数调用前。
  • insert_cafter 回调函数调用后。
typeconf 'Object'
.func 'pay'
.insert_before [[
printf("hello before!");
]]
.insert_after [[
printf("hello after!");
]]
.insert_cbefore [[
printf("hello callback_before!");
]]
.insert_cafter [[
printf("hello callback_after!");
]]
生成代码:
static int _Object_pay(lua_State *L)
{
...
printf("hello before!");
self->pay([cb_store, cb_name, cb_ctx]() {
...
printf("hello callback_before!");
olua_callback(L, cb_store, cb_name.c_str(), 0);
printf("hello callback_after!");
...
});
printf("hello after!");
...
return 0;
}

3.14. 参数标记

.ret.arg1...N 都支持 @ 关键字的标记,给参数添加更多的行为。

3.14.1. @postnew

标记返回值属于新创建,要使用 olua_postnew

typeconf 'Object'
.callback 'create' .ret '@postnew'
生成代码:
static int _Object_create(lua_State *L)
{
...
Object ret = Object::create()
// insert code after call
olua_postnew(L, ret);
...
return 0;
}
3.14.2. @nullable

标记参数是否可以为 nil

-- void onClick(const ClickCallback &callback);
typeconf 'Object'
.callback 'onClick' .arg1 '@nullable'
生成代码:
static int _Object_onClick(lua_State *L)
{
if (olua_isfunction(L, 2) {
arg1 = ...
} else {
arg1 = nullptr;
}
// void onClick(@nullable const ClickCallback &callback);
self->onClick(arg1);
return 0;
}
3.14.3. @addref

给参数添加使用引用标记:@addref(name mode [where])

name 引用名称。

mode 引用存储模式,有两种:

  • ^ 独立存在。
    -- void setScene(Object *scene);
    typeconf 'Object'
    .func 'setScene' .arg1 '@addref(scene ^) @nullable'
    生成代码:
    static int _Object_setScene(lua_State *L)
    {
    ...
    self->setScene(arg1);
    ...
    olua_addref(L, 1, "scene", -1, OLUA_REF_ALONE);
    ...
    }
  • | 共存。
    -- void addChild(Object *child);
    typeconf 'Object'
    .func 'addChild' .arg1 '@addref(children |)' .ret '@delref(children ~)'
    生成代码:
    static int _Object_addChild(lua_State *L)
    {
    ...
    olua_startcmpref(L, 1, "children");
    ...
    self->addChild(arg1);
    ...
    olua_addref(L, 1, "children", -1, OLUA_REF_MULTI);
    olua_endcmpref(L, 1, "children");
    ...
    }

where 引用存储的位置,如果提供此值,同时得使用插入代码,用于获取此值。

-- void show();
typeconf 'Object'
.func 'show' .ret '@addref(children | parent)'
.insert_before [[
olua_pushobj<Object>(L, Object::getRoot());
int parent = lua_gettop(L);
]]
生成代码:
static int _Object_show(lua_State *L)
{
...
olua_pushobj<Object>(L, Object::getRoot());
int parent = lua_gettop(L);
self->show();
...
olua_addref(L, parent, "children", 1, OLUA_REF_MULTI);
...
}

关于引用实现参见 olua 引用链

3.14.4. @delref

给参数添加移除引用标记:@delref(name mode [where])

name 引用名称。

mode 引用存储模式,有四种:

  • ^ 独立存在。
    -- void setScene(Object *scene);
    typeconf 'Object'
    .func 'setScene' .arg1 '@delref(scene ^) @nullable'
    生成代码:
    static int _Object_setScene(lua_State *L)
    {
    ...
    self->setScene(arg1);
    ...
    olua_delref(L, 1, "scene", -1, OLUA_REF_ALONE);
    ...
    }
  • | 共存。
    -- void removeChild(Object *child);
    typeconf 'Object'
    .func 'removeChild' .arg1 '@addref(children |)'
    生成代码:
    static int _Object_removeChild(lua_State *L)
    {
    ...
    self->removeChild(arg1);
    ...
    olua_delref(L, 1, "children", -1, OLUA_REF_MULTI);
    ...
    }
  • ~ 生成使用比较移除引用的代码。
    -- void removeChildByName(const std::string &name);
    typeconf 'Object'
    .func 'removeChildByName' .ret '@delref(children ~)'
    生成代码:
    static int _Object_removeChildByName(lua_State *L)
    {
    ...
    olua_startcmpref(L, 1, "children");
    ...
    self->removeChildByName(arg1);
    ...
    olua_endcmpref(L, 1, "children");
    ...
    }
  • * 移除所有引用。
    -- void removeChildren();
    typeconf 'Object'
    .func 'removeChildren' .arg1 '@delref(children *)'
    生成代码:
    static int _Object_removeChildren(lua_State *L)
    {
    ...
    self->removeChildren();
    ...
    olua_delallrefs(L, 1, "children");
    ...
    }

where 引用存储的位置,如果提供此值,同时得使用插入代码,用于获取此值。

-- void removeSelf();
typeconf 'Object'
.func 'removeSelf' .ret '@delref(children | parent)'
.insert_before [[
if (!self->getParent()) {
return 0;
}
olua_pushobj<Object>(L, self->getParent()));
int parent = lua_gettop(L);
]]
生成代码:
static int _Object_removeSelf(lua_State *L)
{
...
if (!self->getParent()) {
return 0;
}
olua_pushobj<Object>(L, self->getParent()));
int parent = lua_gettop(L);
self->removeSelf();
...
olua_delref(L, parent, "children", 1, OLUA_REF_MULTI);
...
}

关于引用实现参见 olua 引用链

3.14.5. @optional

标记参数是否可选,一般情况下用于 .var 命令,函数参数的 @optional 标记由自动扫描添加。

  • 用于 .var 命令:

    typeconv 'Object'
    .var 'x' .optional 'true'
    生成代码:
    void olua_check_Object(lua_State *L, int idx, Object *value)
    {
    ...
    int arg1 = 0; /** x */
    ...
    olua_getfield(L, idx, "x");
    if (!olua_isnoneornil(L, -1)) {
    olua_check_integer(L, -1, &arg1);
    value->x = arg1;
    }
    lua_pop(L, 1);
    ...
    }
  • 用于 .func.callback 命令:

    -- 原型
    -- void play(bool loop = true);
    -- 扫描得到
    -- void play(@optional bool loop = true);
    -- 导出时转换为两个函数
    -- void play();
    -- void play(bool loop);

    typeconf 'Object'
    生成代码:
    static _Object_play1(lua_State *L)
    {
    ...
    self->play();
    ...
    }

    static _Object_play2(lua_State *L)
    {
    ...
    self->play(arg1);
    ...
    }

    static _Object_play(lua_State *L)
    {
    if (num_args == 0) {
    reutrn _Object_play1(L);
    }
    if (num_args == 1) {
    reutrn _Object_play2(L);
    }
    luaL_error(L, "method 'Object::play' not support '%d' arguments", num_args);
    return 0;
    }
3.14.6. @pack

将多个参数打包一个值对象。

-- void setPosition(const Point &p);
-- Point convert(const Point &p);
typeconf 'Object'
.func 'setPosition' .arg1 '@pack'
.func 'convert' .arg1 '@pack'
lua层使用:
local obj = Object.new()

obj:setPosition(1, 1)
obj:setPosition({x = 1, y = 1})

local p = obj:convert({x = 1, y = 1})
local x, y = obj:convert(1, 1)
3.14.7. @unpack

将值对象拆解为多个值。

-- const Point &getPosition();
typeconf 'Object'
.func 'getPosition' .ret '@unpack'
lua层使用:
local obj = Object.new()
local x, y = obj:getPosition()
3.14.8. @readonly

只读变量标记,用于 .var 命令。使用此标记后,只生成 getter 函数。

typeconf 'Object'
.var 'id' .readonly 'true'
3.14.9. @type

类型替换,提供的类型必须是原类型的其它表现形式。

void read(char *buf, size_t *len);

正常情况下,buf 会被解析为字符串,使用的转换器是 olua_$$_string,但是这里可能是一个可写入的变量,这时就可以使用 @type 进行标记,以实现准确的意图。

-- typedef char olua_char_t;
-- typedef olua::array<olua_char_t> olua_char;
typeconf 'Object'
.func 'read'
.arg1 '@type(olua_char_t *)'

通过此配置,可以在生成代码时,使用 olua_char_t * 的转换器。

3.14.10. 头文件标注

可以直接使用宏命令对参数或方法进行标注,autoconf 脚本会在扫描阶段解析这些信息。

宏命令:

#define OLUA_EXCLUDE        __attribute__((annotate("@exclude")))
#define OLUA_TYPE(name) __attribute__((annotate("@type("#name")")))
#define OLUA_NAME(name) __attribute__((annotate("@name("#name")")))
#define OLUA_ADDREF(...) __attribute__((annotate("@addref("#__VA_ARGS__")")))
#define OLUA_DEFREF(...) __attribute__((annotate("@delref("#__VA_ARGS__")")))
#define OLUA_PACK __attribute__((annotate("@pack")))
#define OLUA_UNPACK __attribute__((annotate("@unpack")))
#define OLUA_NULLABLE __attribute__((annotate("@nullable")))
#define OLUA_POSTNEW __attribute__((annotate("@postnew")))
#define OLUA_READONLY __attribute__((annotate("@readonly")))
#define OLUA_OPTIONAL __attribute__((annotate("@optional")))
#define OLUA_GETTER __attribute__((annotate("@getter")))
#define OLUA_SETTER __attribute__((annotate("@setter")))

使用示例:

class Object {
public:
static OLUA_POSTNEW Object *create();

void setScene(OLUA_ADDREF(^) OLUA_NULLABLE Scene *v);
OLUA_ADDREF(^) Scene *getScene();

OLUA_ADDREF(root ^) Scene *getRoot();

static int pushParent(lua_State *L) OLUA_EXCLUDE;
OLUA_DELREF(children | ::pushParent) void removeFrameParent();

void addChild(OLUA_ADDREF(children |) Object *child);
OLUA_ADDREF(children |) Child getChildByName(const std::string &name);
void removeChild(OLUA_DELREF(children |) Object *child);

OLUA_EXCLUDE void update();

Point localToGlobal(OLUA_PACK const Point &p);

const char read(OLUA_RET size_t *len);

OLUA_READONLY int id;

void read(OLUA_TYPE(olua_char_t *) char *result, size_t *len)

OLUA_GETTER OLUA_NAME(name) std::string getName();
OLUA_SETTER OLUA_NAME(name) void setName(const std::string *);
}