具有多态类型作为函数参数的std :: function容器 [英] A container of std::function with polymorphic types as function arguments

查看:52
本文介绍了具有多态类型作为函数参数的std :: function容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想拥有又一个"回调注册资料.扩展通用基本事件类型的不同事件类型将触发关联的回调函数.

I would like to have "yet another" callback registration stuff. Different event types extending a common base event type will trigger associated callback functions.

这是初步草案或想法

#include <functional>
#include <iostream>
#include <unordered_map>

class BaseEvent 
{ 
public: 
    virtual ~BaseEvent() {}
};

class DerivedEvent_1 : public BaseEvent {};
class DerivedEvent_2 : public BaseEvent {};


// a container holding callback functions
std::unordered_map<size_t/*event*/, std::function<void(BaseEvent)>/*callback*/> _callbacks;


// register callback funtion associated with specific event
template<typename EVT>  
void registerCallback(std::function<void(EVT)> cb)
{
    std::cout << "store callback associated with event " << typeid(EVT).name() << " [" << typeid(EVT).hash_code() << "]" << std::endl;
    //_functions[ typeid(EVT).hash_code() ] = cb; // FIXME
}


// trigger callback function
void triggerCallback(const BaseEvent* e)
{
    std::cout << "trigger callback with event " << typeid(*e).name() << " [" << typeid(*e).hash_code() << "]" << std::endl;
    //_functions[ typeid(*e).hash_code() ] (*e); // FIXME
}

// callback function for DerivedEvent_1
void callback_1(DerivedEvent_1 event_1)
{
    std::cout << "callback_1 called" << std::endl;
}

// callback function for DerivedEvent_2
void callback_2(DerivedEvent_2 event_2)
{
    std::cout << "callback_2 called" << std::endl;
}


int main(int argc, char *argv[])
{
    registerCallback<DerivedEvent_1>( [](DerivedEvent_1 e) { callback_1(e); } );
    registerCallback<DerivedEvent_2>( [](DerivedEvent_2 e) { callback_2(e); } );


    DerivedEvent_1 e1;
    DerivedEvent_2 e2;

    triggerCallback(&e1);
    triggerCallback(&e2);

    return 1;
}

到目前为止,还没有真正的实现...

so far so good without real implementation...

$ g++ -std=c++11 -o testStdFunction testStdFunvtion.cpp 
$ ./testStdFunction 
store callback associated with event 14DerivedEvent_1 [4527193776]
store callback associated with event 14DerivedEvent_2 [4527193680]
trigger callback with event 14DerivedEvent_1 [4527193776]
trigger callback with event 14DerivedEvent_2 [4527193680]

情况和问题是:

  • 事件是类或结构,可以具有有效负载的特定属性
  • 我想保留没有指针作为参数的回调函数(例如,无效的callback_1(DerivedEvent_1 event_1).原因:那些回调函数可能已经存在于代码库中,并且我不希望对其进行更改或进行额外的包装.
  • 如何让_callbacks映射具有不同签名的std :: function?即让_callbacks映射可以容纳std :: function
  • Events are class or struct which can have specific attributes as payload
  • I would like to keep callback functions (e.g. void callback_1(DerivedEvent_1 event_1) without pointer as argument. Reason: those callback functions may already exist in code base and I would like to not change it or make extra wrapper.
  • how can I let _callbacks map to have std::function with different signatures? i.e. to let the _callbacks map can hold std::function

目的是修复FIXME代码在registerCallback和triggerCallback中.这样他们在运行代码后会看起来像这样

The intention is to fix the FIXME code in registerCallback and triggerCallback. so they will look like this after running the code

$ g++ -std=c++11 -o testStdFunction testStdFunvtion.cpp 
$ ./testStdFunction 
store callback associated with event 14DerivedEvent_1 [4527193776]
store callback associated with event 14DerivedEvent_2 [4527193680]
trigger callback with event 14DerivedEvent_1 [4527193776]
callback_1 called
trigger callback with event 14DerivedEvent_2 [4527193680]
callback_2 called

推荐答案

您可以使用已擦除的包装器.
以下代码精确地打印OP在问题中发布的消息.
关键类是 BaseWrapper 和模板类 Wrapper .
此外,我对代码周围的函数签名进行了少许更改,以使其正常工作.

You can use an erased wrapper.
The following code prints exactly the messages the OP posted in the question.
Key classes are BaseWrapper and the template class Wrapper.
Moreover, I slightly changed function signatures all around the code to let it work correctly.

#include <functional>
#include <iostream>
#include <unordered_map>
#include<memory>
#include<utility>

class BaseEvent 
{ 
public: 
    virtual ~BaseEvent() {}
};

class DerivedEvent_1 : public BaseEvent {};
class DerivedEvent_2 : public BaseEvent {};

struct BaseWrapper {
    virtual void operator()(const BaseEvent *) = 0;
};

template<typename T>
struct Wrapper: BaseWrapper {
    std::function<void(T)> fn;
    void operator()(const BaseEvent *e) {
        fn(*static_cast<const T*>(e));
    }
};


// a container holding callback functions
std::unordered_map<size_t/*event*/, std::unique_ptr<BaseWrapper>/*callback*/> _functions;


// register callback funtion associated with specific event
template<typename EVT>  
void registerCallback(std::function<void(const EVT &)> cb)
{
    std::cout << "store callback associated with event " << typeid(EVT).name() << " [" << typeid(EVT).hash_code() << "]" << std::endl;
    auto w = std::make_unique<Wrapper<EVT>>();
    w->fn = cb;
    _functions[ typeid(EVT).hash_code() ] = std::move(w);
}


// trigger callback function
void triggerCallback(const BaseEvent* e)
{
    std::cout << "trigger callback with event " << typeid(*e).name() << " [" << typeid(*e).hash_code() << "]" << std::endl;
    (*_functions[ typeid(*e).hash_code() ] )(e);
}

// callback function for DerivedEvent_1
void callback_1(const DerivedEvent_1 &event_1)
{
    std::cout << "callback_1 called" << std::endl;
}

// callback function for DerivedEvent_2
void callback_2(const DerivedEvent_2 &event_2)
{
    std::cout << "callback_2 called" << std::endl;
}

int main(int argc, char *argv[])
{
    registerCallback<DerivedEvent_1>( [](DerivedEvent_1 e) { callback_1(e); } );
    registerCallback<DerivedEvent_2>( [](DerivedEvent_2 e) { callback_2(e); } );

    DerivedEvent_1 e1;
    DerivedEvent_2 e2;

    triggerCallback(&e1);
    triggerCallback(&e2);

    return 1;
}

可以提高包装器的性能(例如,使用静态成员方法而不是多态性,作为练习留给读者).
解决方案背后的基本思想是所谓的 type-erasure .
Google会帮助您找到有关此事的更多详细信息.

The wrapper can be improved in terms of performance (as an example, using static member methods instead of polymorphism, left to the reader as an exercise).
The basic idea behind the solution is the so called type-erasure.
Google will help you in finding further details about that.

这篇关于具有多态类型作为函数参数的std :: function容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆