[✔️]lua中的function,在c++进行callback

191 阅读4分钟

在lua中我们这么注册touch事件

local listenner = cc.EventListenerTouchOneByOne:create()
listenner:registerScriptHandler(function(touch, event)
        return true;
end, cc.Handler.EVENT_TOUCH_BEGAN)

在c++里面

typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
ccTouchBeganCallback onTouchBegan;

auto lis = EventListenerTouchOneByOne::create();
lis->onTouchBegan = CC_CALLBACK_2(TestLayer::touchBegan, this);

很明显onTouchBegin是一个std::function,如果在c++中,如果我们的参数是一个function,那么生成的lua binding代码中会有这么一段逻辑:

std::function<void ()> arg0;

do {
        // Lambda binding for lua is not supported.
        assert(false);
} while(0)
;

很明显,这里主动报错了,注释中也说明了,function在lua中是不支持的

 void scheduleOnce(const std::function<void(float)>& callback, float delay, const std::string &key);
static EventListenerCustom* create(const std::string& eventName, const std::function<void(EventCustom*)>& callback);

EventCustom

在lua中创建自定义事件:

local lis = cc.EventListenerCustom:create("APP_EVENT", function (){
    log("EVENT")
});
cc.Director:getInstance():getEventDispatcher():addEventListenerWithFixedPriority(lis, 1)

cc.EventListenerCustom:create对应的c++ lua binding代码:

const std::string eventName = ((const std::string)  tolua_tocppstring(tolua_S,2,0));
LUA_FUNCTION handler = toluafix_ref_function(tolua_S,3,0);
cocos2d::EventListenerCustom* tolua_ret = LuaEventListenerCustom::create(eventName);
// 你会发现最终其实它也是到了ScriptHandlerMgr.addObjectHandler,会将lua function存储在map表
ScriptHandlerMgr::getInstance()->addObjectHandler((void*)tolua_ret, handler, ScriptHandlerMgr::HandlerType::EVENT_CUSTIOM);

在tolua工具模板代码中并没有出现以上的c++片段,那么就是后期人为修正的一段逻辑

lua中触发

local event = cc.EventCustom:new("APP_EVENT")
event.data="12";
cc.Director:getInstance():getEventDispatcher():dispatchEvent(event)
void EventDispatcher::dispatchEvent(Event* event){
    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())
    {
        auto listeners = iter->second;
        
        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            listener->_onEvent(event); // 响应触发事件,对于custom
            return event->isStopped();
        };
        
        (this->*pfnDispatchEventToListeners)(listeners, onEvent);
    }
} 
bool EventListenerCustom::init(const ListenerID& listenerId, const std::function<void(EventCustom*)>& callback)
{
    bool ret = false;
    
    _onCustomEvent = callback;
    
    auto listener = [this](Event* event){
        if (_onCustomEvent != nullptr)
        {
            // 所以事件会派发到这里,lambda套娃,真实的函数实体在callback参数
            _onCustomEvent(static_cast<EventCustom*>(event));
        }
    };
    // 第三个参数就是一个lambda,对应的是listener->_onEvent
    if (EventListener::init(EventListener::Type::CUSTOM, listenerId, listener))
    {
        ret = true;
    }
    return ret;
}
// lua_cocos2dx_manual.cpp
EventListenerCustom* LuaEventListenerCustom::create(const std::string& eventName)
{
    EventListenerCustom* eventCustom = new (std::nothrow) EventListenerCustom();
    if (nullptr == eventCustom)
        return nullptr;
    //                                callback参数
    if ( eventCustom->init(eventName, [=](EventCustom* event){
        // 最终是到达了这里
        BasicScriptData data((void*)eventCustom,(void*)event);
        LuaEngine::getInstance()->handleEvent(ScriptHandlerMgr::HandlerType::EVENT_CUSTIOM, (void*)&data );
    }))
    {
        eventCustom->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(eventCustom);
    }
    return eventCustom;
}
// 最后完成到lua层的调用,里面会有对参数的转换
int LuaEngine::handleEvenCustom(void* data){
    //                                                   ↓ 绑定的数据
    EventCustom* eventCustom = static_cast<EventCustom*>(basicData->value);
    
    // 处理要传递到lua层的参数
    lua_State* L = _stack->getLuaState();
    toluafix_pushusertype_ccobject(L, eventCustom->_ID, &(eventCustom->_luaID), (void*)(eventCustom),"cc.EventCustom");
    int ret = _stack->executeFunctionByHandler(handler, 1);
    _stack->clean();
}

TOLUA_API int toluafix_pushusertype_ccobject(lua_State* L,
                                             int refid,
                                             int* p_refid,
                                             void* ptr,
                                             const char* type)
{

    tolua_pushusertype_and_addtoroot(L, vPtr, vType);

}

custrom 数据怎么传递

vector<EditorDragInfo> files;
for (int i = 0;i < data->urls().size();i++)
{
    QString file = data->urls().at(i).toLocalFile();
    files.push_back(file.toStdString());
}
EventCustom eventCustom(EditorDragInfo::getEventName());
eventCustom.setUserData(&files);
auto dispatcher = cocos2d::Director::getInstance()->getEventDispatcher();
dispatcher->dispatchEvent(&eventCustom);
local lis = cc.EventListenerCustom:create("drag-event", function(event)
    log("event drag file")
    log(event:getUserData())
end);

c++层处理button传递参数的处理

LUA_FUNCTION handler = (  toluafix_ref_function(L,2,0));
        
self->addTouchEventListener([=](cocos2d::Ref* ref,Widget::TouchEventType eventType){
    handleUIEvent(handler, ref, (int)eventType);
});
static int handleUIEvent(int handler, cocos2d::Ref* sender, int eventType)
{
    LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
    
    stack->pushObject(sender, "cc.Ref");
    stack->pushInt(eventType);
    
    stack->executeFunctionByHandler(handler, 2);
    stack->clean();
    
    return 0;
}

保存lua回调函数的逻辑分析

先看下_mapObjectHandlers的数据结构

 enum class HandlerType: int
{
}
typedef int Handler;
typedef std::pair<HandlerType, Handler> HandlerPair;
typedef std::vector<HandlerPair> VecHandlerPairs;
typedef std::map<void*,VecHandlerPairs> MapObjectHandlers;
MapObjectHandlers _mapObjectHandlers;

合并一下类型看的更加清楚

std::map<
    void*, // first
    std::vector<
        std::pair<enum int,int>
    >
>

json格式举例:

{
    "0x1234": [
        {"EVENT_TOUCH_BEGAN": "lua handler"}
    ]
}

具体的代码逻辑分析:

int  ScriptHandlerMgr::getObjectHandler(void* object,ScriptHandlerMgr::HandlerType handlerType)
{
    if (nullptr == object || _mapObjectHandlers.empty() )
        return 0;
    
    auto iter = _mapObjectHandlers.find(object);// 指针是key
    if (_mapObjectHandlers.end() != iter)
        for (auto &handlerPair : iter->second) // 遍历vector< pair<int, int> >
            if (handlerPair.first == handlerType)
                return handlerPair.second;// 其实这里找到的是第一个
    
    return 0;
}
ScriptHandlerMgr::HandlerType ScriptHandlerMgr::addCustomHandler(void* object, int handler)
{
    assert(nullptr != object);
    
    auto iter = _mapObjectHandlers.find(object);
    VecHandlerPairs vecHandlers;
    vecHandlers.clear();
    HandlerType handlerType = HandlerType::EVENT_CUSTOM_BEGAN;
    
    if (_mapObjectHandlers.end() != iter)// 找到object
    {
        vecHandlers = iter->second;// vector< pair<int, int> >
        if (!vecHandlers.empty())
            //                                                       ↓找到末尾的 enum int
            handlerType = static_cast<HandlerType>((int)vecHandlers.back().first + 1);
    }
    // EVENT_CUSTOM_BEGAN = 10000,
    // EVENT_CUSTOM_ENDED = 11000,
    // 因为是EVENT_CUSTOM_BEGAN开头,判断是不是到达了EVENT_CUSTOM_ENDED
    assert(handlerType <= HandlerType::EVENT_CUSTOM_ENDED);
    //                                        ↓ 此时已经被递增1了
    HandlerPair eventHanler = std::make_pair(handlerType, handler);
    vecHandlers.push_back(eventHanler);
    _mapObjectHandlers[object] = vecHandlers;
    // 不会重复
    return handlerType;
}

void ScriptHandlerMgr::addObjectHandler(void* object,int handler,ScriptHandlerMgr::HandlerType handlerType)
{
    if (nullptr == object)
        return;
    
    //may be not need
    removeObjectHandler(object,handlerType); 
    // 移除重复的type,保证了接下来只有一个
    // 这样也保证了get正常,逻辑有点危险
    
    auto iter = _mapObjectHandlers.find(object);
    VecHandlerPairs vecHandlers;
    vecHandlers.clear();
    if (_mapObjectHandlers.end() != iter)
        vecHandlers = iter->second;
    
    HandlerPair eventHanler = std::make_pair(handlerType, handler);
    vecHandlers.push_back(eventHanler);
    _mapObjectHandlers[object] = vecHandlers;
}

仔细分析过后:CUSTOM类型会递增,其他的类型不会重复

{
    "0x1234": [
        {"EVENT_TOUCH_BEGAN": "lua handler"},
        {"EVENT_CUSTOM_BEGAN": "lua handler"},
        {"EVENT_CUSTOM_BEGAN + 1": "lua handler"}
    ]
}