【问题标题】:A container of std::function with polymorphic types as function arguments具有多态类型作为函数参数的 std::function 容器
【发布时间】:2016-06-19 07:23:39
【问题描述】:

我想要“又一个”回调注册的东西。 扩展公共基本事件类型的不同事件类型将触发关联的回调函数。

这是初稿或想法

#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;
}

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

$ 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]

情况和问题是:

  • 事件是可以具有特定属性作为有效负载的类或结构
  • 我想保留没有指针作为参数的回调函数(例如 void callback_1(DerivedEvent_1 event_1)。原因:这些回调函数可能已经存在于代码库中,我不想更改它或制作额外的包装器。
  • 如何让_callbacks 映射到具有不同签名的std::function? 即让 _callbacks 映射可以保存 std::function

目的是修复 registerCallback 和 triggerCallback 中的 FIXME 代码。所以运行代码后它们会是这样的

$ 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

【问题讨论】:

  • how can I let _callbacks map to have std::function with different signatures?: 不清楚你的意思,你的具体问题是什么?
  • I would like to keep callback functions without pointer as argument:为什么?
  • "如何让 _callbacks 映射到具有不同签名的 std::function"。请显示预期用途。
  • 请使用编辑链接向问题正文添加信息。不要为此使用 cmets。如果您想回复特定评论,请使用如下用户名对其进行标记:@elgcom
  • @n.m 好的,感谢您的评论,我将编辑问题

标签: c++ function c++11 templates polymorphism


【解决方案1】:

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

#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 将帮助您找到有关这方面的更多详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多