基于cocos2dx-lua,一个简易通用的,注册c++ std::function 回调给lua的工具,大家来交流讨论一下。
最近自己在学spine,想做练习的demo,但是确实喜欢上lua开发不用编译这一点。所以不想用c++,
cocos2dx-spine 对lua层的支持不多,基本上只支持了创建动画,播放动画,还有帧事件回调。 而可能
常会用到的一些attachment组件的功能就不支持了,所以写了一些对spine原始类型的包装类,然后用
cocos2dx工程中集成的tolua导出。 在需要手动添加的部分,遇到了一个问题,就是关于回调的问题。
lua注册一个函数给c++调用肯定是要手写的。不过我还是觉得可以有一个简单的通用处理方案,所以写了
下面这个工具,大量用到模板,并且需要c++11提供的变长模板参数,std::function, lambda表达式。
一个简单的用法在工具代码后面。
#pragma once
extern "C" {
#include "lua.h"
}
#include "CCLuaEngine.h"
#include "CCLuaStack.h"
#include "CCLuaValue.h"
#include <functional>
#include <type_traits>
#include <string>
#include "lcx/LuaExtension/RefLuaTypeNames.hpp"
using namespace cocos2d;
using namespace std;
namespace lcx{
struct NullType{};
/*********************************** begin of typelist **************************************/
template< typename ...__Types >
struct TypeList;
//recursion partial specialization
template< typename __Head, typename ...__Others >
struct TypeList<__Head, __Others...>{
typedef __Head First;
typedef TypeList<__Others...> Others;
};
//tail specialization
template<>
struct TypeList<void>{
typedef NullType First;
};
/*********************************** end of typelist **************************************/
template< typename __Return, typename ... __Args>
struct MakeStdFunctionType{
typedef std::function<__Return(__Args...)> result;
};
template <typename __StdFunction>
struct LuaCallback{
LuaCallback(LUA_FUNCTION handler, __StdFunction func) :
_handler(handler),
_stdFunc(func){
}
const LUA_FUNCTION _handler;
const __StdFunction _stdFunc;
};
/************************************* begin of push single parm to lua ***********************************************/
template <typename __Arg>
struct PushPramToLua;
template<typename __Arg>
struct PushPramToLua{
private:
//通过重载方式特化,避免编译错误
static inline void pushInteger(LuaStack* luaStack, __Arg arg, std::true_type){
luaStack->pushInt(int(arg));
}
static inline void pushInteger(LuaStack* luaStack, __Arg arg, std::false_type){
//do nothing
}
static inline void pushFloat(LuaStack* luaStack, __Arg arg, std::true_type){
luaStack->pushFloat( float(arg) )
}
static inline void pushFloat(LuaStack* luaStack, __Arg arg, std::false_type){
//do nothing
}
static inline void pushCocosRef(LuaStack* luaStack, __Arg arg, std::true_type){
cocos2d::Ref* cobj = arg;
int ID = (int)cobj->_ID;
int* luaID = &cobj->_luaID;
const char * tName = RefLuaTypeNameManager::getInstance()->getTypeName( &typeid(__Arg) );
if (nullptr == tName){
luaL_error(luaStack->getLuaState(), "no lua name for cpp type %s", typeid(__Arg).name());
}
toluafix_pushusertype_ccobject(luaStack->getLuaState(), ID, luaID, (void*)cobj, tName);
}
static inline void pushCocosRef(LuaStack* luaStack, __Arg arg, std::false_type){
//do nothing
}
public:
static inline void doPush(LuaStack* luaStack, __Arg arg){
typedef std::remove_pointer<__Arg>::type ArgWithOutPoint;
if (std::is_integral<__Arg>::value){
pushInteger(luaStack, arg, std::is_integral<__Arg>::type());
return;
}else if (std::is_floating_point<__Arg>::value){
pushFloat(luaStack, arg, std::is_floating_point<__Arg>::type());
return;
}else if (std::is_base_of<cocos2d::Ref, ArgWithOutPoint>::value){
pushCocosRef(luaStack, arg, std::is_base_of<cocos2d::Ref, ArgWithOutPoint>::type());
}
else{
luaL_error(luaStack->getLuaState(), "do not support push type %s to lua stack", typeid(__Arg).name());
}
}
};
//begin support for string
template<>
struct PushPramToLua<char*>{
static inline void doPush(LuaStack* luaStack, char* arg){
luaStack->pushString(arg);
}
};
template<>
struct PushPramToLua<const char*>{
static inline void doPush(LuaStack* luaStack, const char* arg){
luaStack->pushString(arg);
}
};
template<>
struct PushPramToLua<std::string>{
static inline void doPush(LuaStack* luaStack, std::string arg){
luaStack->pushString(arg.c_str());
}
};
template<>
struct PushPramToLua<const std::string>{
static inline void doPush(LuaStack* luaStack, const std::string arg){
luaStack->pushString(arg.c_str());
}
};
template<>
struct PushPramToLua<const std::string&>{
static inline void doPush(LuaStack* luaStack, const std::string& arg){
luaStack->pushString(arg.c_str());
}
};
//end support for string
/************************************* end of push single parm to lua ***********************************************/
/************************************* begin of push parms to lua ***********************************************/
template<typename ...__Args>
struct PushParmsToLua;
//对于参数个数,这里不使用递归方式来处理,而直接编写对应参数数量的特化版本,如果需要支持更多参数,请添加特化版本,或者改用递归实现方式
template<>
struct PushParmsToLua<>{
static void doPush(LuaStack* luaStack){
}
};
template<typename __T1>
struct PushParmsToLua<__T1>{
static void doPush(LuaStack* luaStack, __T1 arg1){
PushPramToLua<__T1>::doPush(luaStack, arg1);
}
};
template<typename __T1, typename __T2>
struct PushParmsToLua<__T1,__T2>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
}
};
template<typename __T1, typename __T2, typename __T3>
struct PushParmsToLua<__T1, __T2, __T3>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2, __T3 arg3){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
PushPramToLua<__T3>::doPush(luaStack, arg3);
}
};
template<typename __T1, typename __T2, typename __T3, typename __T4>
struct PushParmsToLua<__T1, __T2, __T3, __T4>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2, __T3 arg3, __T4 arg4){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
PushPramToLua<__T3>::doPush(luaStack, arg3);
PushPramToLua<__T4>::doPush(luaStack, arg4);
}
};
template<typename __T1, typename __T2, typename __T3, typename __T4, typename __T5>
struct PushParmsToLua<__T1, __T2, __T3, __T4, __T5>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2, __T3 arg3, __T4 arg4, __T5 arg5){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
PushPramToLua<__T3>::doPush(luaStack, arg3);
PushPramToLua<__T4>::doPush(luaStack, arg4);
PushPramToLua<__T5>::doPush(luaStack, arg5);
}
};
template<typename __T1, typename __T2, typename __T3, typename __T4, typename __T5, typename __T6>
struct PushParmsToLua<__T1, __T2, __T3, __T4, __T5, __T6>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2, __T3 arg3, __T4 arg4, __T5 arg5, __T6 arg6){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
PushPramToLua<__T3>::doPush(luaStack, arg3);
PushPramToLua<__T4>::doPush(luaStack, arg4);
PushPramToLua<__T5>::doPush(luaStack, arg5);
PushPramToLua<__T6>::doPush(luaStack, arg6);
}
};
template<typename __T1, typename __T2, typename __T3, typename __T4, typename __T5, typename __T6, typename __T7>
struct PushParmsToLua<__T1, __T2, __T3, __T4, __T5, __T6, __T7>{
static void doPush(LuaStack* luaStack, __T1 arg1, __T2 arg2, __T3 arg3, __T4 arg4, __T5 arg5, __T6 arg6, __T7 arg7){
PushPramToLua<__T1>::doPush(luaStack, arg1);
PushPramToLua<__T2>::doPush(luaStack, arg2);
PushPramToLua<__T3>::doPush(luaStack, arg3);
PushPramToLua<__T4>::doPush(luaStack, arg4);
PushPramToLua<__T5>::doPush(luaStack, arg5);
PushPramToLua<__T6>::doPush(luaStack, arg6);
PushPramToLua<__T7>::doPush(luaStack, arg7);
}
};
template< typename ...__Args >
void pushParmToLua(LuaStack* luaStack, __Args... args){
PushParmsToLua<__Args...>::doPush(luaStack, args...);
}
/************************************* end of push parms to lua ***********************************************/
//function for user
template<typename __Return, typename ...__Args>
LuaCallback< typename MakeStdFunctionType<__Return, __Args...>::result > registerScriptCallback(int lo){
LuaStack* luaStack = LuaEngine::getInstance()->getLuaStack();
lua_State* tolua_S = luaStack->getLuaState();
LUA_FUNCTION handler = toluafix_ref_function(tolua_S, lo, 0);
typedef MakeStdFunctionType<__Return, __Args...>::result TStdFunction;
//我是试一下,没想到这里可以这样写,直接展开参数,这样不用再去用别的偏特化机制实现。
TStdFunction stdFunc = (__Args... args)->void{
LuaStack* luaStack = LuaEngine::getInstance()->getLuaStack();
pushParmToLua(luaStack, args...);
const auto parm_count = sizeof...(__Args);
luaStack->executeFunctionByHandler(handler, parm_count);
};
return LuaCallback<TStdFunction>(handler, stdFunc);
}
}
使用的方法:
我有一个碰撞器管理类:
class ColliderManager : cocos2d::Ref
有一个实例方法:
请把代码粘贴在这里
<pre class="brush:cpp; toolbar: true; auto-links: false;"> 请把代码粘贴在这里
void setScriptListener( unsigned int luaFuncHandler, std::function<void(Collider*, Collider*)>);
那么手动代码,注册回调那部分可以写成这样:
argc = lua_gettop(tolua_S) - 1; if (argc == 1) { auto func = lcx::registerScriptCallback<void, lcxCollision::Collider*, lcxCollision::Collider*>(1); cobj->setScriptListener(func._handler, func._stdFunc); } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "lcxCollision.ColliderManager:setScriptListener", argc, 1); return 0;
大家在此基础上更改,应该可以做出更多的支持,或者大家已经有了什么更好的,完整的实现,可以共享一下~~。
请把代码粘贴在这里