基于cocos2dx-lua,一个简易通用的,注册c++ std::function 回调给lua的工具,大家来交流讨论一下。

基于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;

大家在此基础上更改,应该可以做出更多的支持,或者大家已经有了什么更好的,完整的实现,可以共享一下~~。

请把代码粘贴在这里