重新回头看Luabinding, Windows平台
- 需要的环境
android-ndk-r10c
,并配置环境变量NDK_ROOT
。需要重启或者注销电脑生效- python2.7.3 (32bit) from (http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi).并配置环境变量,PATH下(e.g. C:\Python27)
- pyyaml http://pyyaml.org/download/pyyaml/PyYAML-3.11.win32-py2.7.exe 下载并安装
- pyCheetah from https://raw.github.com/dumganhar/my_old_cocos2d-x_backup/download/downloads/Cheetah.zip, 解压到Python安装目录 "C:\Python27\Lib\site-packages"
自定义C++类'MyClass',我放在了runtime-src\Classes目录下面。
修改ini文件
一般直接在 **\frameworks\cocos2d-x\tools\tolua目录下操作。复制一份cocos2dx_ui.ini,重命名my_class.ini, 然后进行修改,下面是我修改后的:
[my_class]
# the prefix to be added to the generated functions. You might or might not use this in your own
# templates
prefix = my_class
# create a target namespace (in javascript, this would create some code like the equiv. to `ns = ns || {}`)
# all classes will be embedded in that namespace
target_namespace = zwf
# the native namespace in which this module locates, this parameter is used for avoid conflict of the same class name in different modules, as "cocos2d::Label" <-> "cocos2d::ui::Label".
cpp_namespace =
android_headers = -I%(androidndkdir)s/platforms/android-14/arch-arm/usr/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/include
android_flags = -D_SIZE_T_DEFINED_
clang_headers = -I%(clangllvmdir)s/lib/clang/%(clang_version)s/include
clang_flags = -nostdinc -x c++ -std=c++11 -U __SSE__
cocos_headers = -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s/cocos/platform/android
cocos_flags = -DANDROID
cxxgenerator_headers =
# extra arguments for clang
extra_arguments = %(android_headers)s %(clang_headers)s %(cxxgenerator_headers)s %(cocos_headers)s %(android_flags)s %(clang_flags)s %(cocos_flags)s %(extra_flags)s
# what headers to parse
headers = %(cocosdir)s/../runtime-src/Classes/MyClass.h
# what classes to produce code for. You can use regular expressions here. When testing the regular
# expression, it will be enclosed in "^$", like this: "^Menu*$".
classes = MyClass
# what should we skip? in the format ClassName::[function function]
# ClassName is a regular expression, but will be used like this: "^ClassName$" functions are also
# regular expressions, they will not be surrounded by "^$". If you want to skip a whole class, just
# add a single "*" as functions. See bellow for several examples. A special class name is "*", which
# will apply to all class names. This is a convenience wildcard to be able to skip similar named
# functions from all classes.
skip =
rename_functions =
rename_classes =
# for all class names, should we remove something when registering in the target VM?
remove_prefix =
# classes for which there will be no "parent" lookup
classes_have_no_parents = Helper
# base classes which will be skipped when their sub-classes found them.
base_classes_to_skip =
# classes that create no constructor
# Set is special and we will use a hand-written constructor
abstract_classes = Helper AbstractCheckButton
# Determining whether to use script object(js object) to control the lifecycle of native(cpp) object or the other way around. Supported values are 'yes' or 'no'.
script_control_cpp = no
其中headers、classes、cpp_namespace是必须修改的。一般自定义的都不一样。
target_namespace标示的是到处的lua模块的命名空间,看个人喜好修改。
prefix这个是防止重命名的,一般都改成自定义的类应该就没问题了。
skip 不想导出的类级函数, 用于处理一些和lua不兼容的c++参数, 比如 std::function, std::pair 等, 这些参数没法通过栈空间传递给lua, 因此也就没办法导出给lua使用, 需要排除掉.
- 执行脚本genbindings.py,生成绑定文件。
执行脚本之前,先修改脚本的配置,只生成我们自定义类的绑定文件就行,
cmd_args = {
'my_class.ini' : ('my_class', 'lua_myclass_auto'), \
}
然后在**\frameworks\cocos2d-x\tools\tolua目录下运行脚本,等待生成就可以了。(尽量在这个目录下执行脚本,在其他目录有可能失败)
5.导入工程中使用。把新生成的lua_myclass_auto.hpp和lua_myclass_auto.cpp拷贝到runtime-src\Classes目录下,导入工程,然后注册一下就可以使用了,注册方法:
static int register_all_packages(lua_State*L)
{
register_all_my_class(L);//这个是新加的注册方法
return 0; //flag for packages manager
}
6.lua中调用
local myClass = zwf.MyClass:new("1111") --注意构造函数绑定的时候暴露到lua中的接口
myClass:printInfo()
到这里基本的binding流程就完成了。
需要注意的是自动binding并不支持std::function的回调,所以需要手动binding,贴一下我的C++代码和binding代码:
C++:
#pragma once
#include<functional> //这个是std::function的头文件
#include<iostream>
class MyClass
{
//定义一个函数类型
typedef std::function<void(const char* string)> myCallBack;
public:
MyClass(std::string name);
~MyClass();
const std::string & getName();
void setName(std::string name);
void printInfo();
//std::function作为参数传入
void setCallBack(const myCallBack& callBack);
private:
std::string _name;
myCallBack _callBack;
};
binding代码:
int lua_my_class_MyClass_setCallBack(lua_State* tolua_S)
{
int argc = 0;
MyClass* cobj = nullptr;
bool ok = true;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
#endif
#if COCOS2D_DEBUG >= 1
if (!tolua_isusertype(tolua_S,1,"MyClass",0,&tolua_err)) goto tolua_lerror;
#endif
//获取对象
cobj = (MyClass*)tolua_tousertype(tolua_S,1,0);
#if COCOS2D_DEBUG >= 1
if (!cobj)
{
tolua_error(tolua_S,"invalid 'cobj' in function 'lua_my_class_MyClass_setCallBack'", nullptr);
return 0;
}
#endif
argc = lua_gettop(tolua_S)-1;
if (argc == 1)
{
std::function<void (const char *)> arg0;
//获取参数
LUA_FUNCTION handler = (toluafix_ref_function(tolua_S, 2, 0));
//调用成员函数
cobj->setCallBack([=](const char* str) {
LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
stack->pushString(str);
stack->executeFunctionByHandler(handler, 1);
stack->clean();
});
lua_settop(tolua_S, 1);
return 1;
}
luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "MyClass:setCallBack",argc, 1);
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'lua_my_class_MyClass_setCallBack'.",&tolua_err);
#endif
return 0;
}
绑定流程主要也是这三步:
- 获取c++对象
- 获取参数, 校验参数类型
- 调用成员函数
调用成员函数的时候,用到了Lambda表达式。这个不太熟,需要恶补一下。先挖个坑
然后就是Lua跟C++调用的时候内存原理,也需要深入了解。