繁体   English   中英

C++ - 将任何 class 的成员函数传递给另一个 class

[英]C++ - Pass member functions of any class to another class

我正在编写一个模板 class ,它将为我的应用程序中需要它的每个组件实现一个监听器接口。 我希望一个组件能够侦听已侦听的 object 中单个字段的更改。 每个 object 的字段由一个枚举表示,我有一个简单的 Notifier/Publisher 可以很好地工作:

template <typename EFieldsEnum>
class AbstractNotifier
{
public:
    void addListener(EFieldsEnum listenedField, INotifierListener<EFieldsEnum>* listener)
    {
        if (_listeners.find(listenedField) == _listeners.cend())
        {
            std::unordered_set<INotifierListener<EFieldsEnum>*> listeners;
            listeners.insert(listener);
            _listeners.emplace(listenedField, listeners);
        }
        else
        {
            _listeners.find(listenedField)->second.insert(listener);
        }
    }

    void notify(EFieldsEnum updatedField)
    {
        for (INotifierListener<EFieldsEnum>* listener : _listeners[updatedField])
        {
            listener->onUpdate(updatedField);
        }
    }

protected:
    std::unordered_map<EFieldsEnum, std::unordered_set<INotifierListener<EFieldsEnum>*>> _listeners;
};

我正在尝试稍微更改方法签名,以便通过传递将作为回调调用的void()方法来注册每个侦听组件,而不是实现onUpdate泛型方法后跟 switch case。 理想情况下,我希望我的监听组件(比如 Component1)注册,而不必为我将拥有的每种类型的 Notifier 定义一个特定的 Listener 接口,如下所示:

notifier.addListener(FieldEnum::MyField1, this, &Component1::onMyField1Updated);

然后 Component2 也可以这样注册:

notifier.addListener(FieldEnum::MyField1, this, &Component2::someOtherMethod);

没有 Component1 和 Component2 需要实现一个通用接口。 这可能吗? 我正在努力弄清楚什么类型通常代表通过的成员方法:

class IListener {};

void (IListener::*ListenerCallback)();

template <typename EFieldsEnum>
class AbstractNotifier
{
public:
    void addListener(EFieldsEnum listenedField, IListener* listener, ListenerCallback listenerCallback)
    {
        std::pair<IListener*, ListenerCallback> pairToAdd = std::make_pair<IListener*, ListenerCallback>(listener, ListenerCallback);
        if (_listeners.find(listenedField) == _listeners.cend())
        {
            std::unordered_set<std::pair<IListener*, ListenerCallback>> listenersAndCallbacks;
            listenersAndCallbacks.insert(pairToAdd);
            _listeners.emplace(listenedField, listenersAndCallbacks);
        }
        else
        {
            _listeners.find(listenedField)->second.insert(pairToAdd);
        }
    }

    void notify(EFieldsEnum updatedField)
    {
        for (const std::pair<IListener*, ListenerCallback>& listenerAndCallback : _listeners[updatedField])
        {
            IListener* listener = listenerAndCallback.first;
            ListenerCallback listenerCallback = listenerAndCallback.second;
            listener->*listenerCallback();
        }
    }

protected:
    std::unordered_map<EFieldsEnum, std::unordered_set<std::pair<IListener*, ListenerCallback>>> _listeners;
};

使用std::function<void()>

这是您的AbstractNotifier的一个稍微简化的版本,它将接受来自任何 class(或根本没有 class)的回调:

template <typename EFieldsEnum>
class AbstractNotifier
{
public:
    void addListener(EFieldsEnum listenedField, std::function<void()> listenerCallback)
    {
        listeners_[listenedField].push_back(listenerCallback);
    }
    
    // Helper method template to wrap member function pointers
    template <typename Listener>
    void addListener(EFieldsEnum listenedField, Listener* listener, void(Listener::*listenerCallback)())
    {
        addListener(listenedField, [=]() { (listener->*listenerCallback)(); });
    }
    
    void notify(EFieldsEnum updatedField)
    {
        for (auto& callback : listeners_[updatedField])
        {
            callback();
        }
    }

protected:
    std::unordered_map<EFieldsEnum, std::vector<std::function<void()>>> listeners_;
};

演示

请注意,我放弃了您当前实现所具有的“唯一性”约束并使用了std::vector 这是因为很难或不可能真正比较std::function s,而且很容易得到看起来非常不同的std::function s,最终回调相同的底层代码。

即以下所有三个最终都调用同一个成员 function,但持有完全不同的类型,不能轻易比较:

// Different lambdas, so different types
std::function<void()> f1([&someObject]() { someFunc.foo(); });
std::function<void()> f2([&someObject]() { someFunc.foo(); });
std::function<void()> f3(std::bind(&SomeType::foo, someObject));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM