模板类的循环依赖问题

问题描述 投票:0回答:1

我有

EventBus
类管理回调,
EBHandle
类旨在在添加回调的对象被销毁时自动清理回调。所以,我认为循环依赖在这里是合理的。我也有
BaseEvent
,但这并不重要,只是为了代码可以运行。

#pragma once

#include <unordered_map>
#include <functional>
#include <typeindex>

struct BaseEvent {
    BaseEvent() = default;
    virtual ~BaseEvent() = default;
};

class EventBus;

class EBHandle {
private:
    int m_key = -1;
    bool m_isRemoved = false;
    std::type_index m_type = typeid(int);
    EventBus* m_eventBus = nullptr;

public:
    EBHandle(EventBus *eventBus, std::type_index type, int key);;

    void remove();
};

class EventBus {
    std::unordered_map<std::type_index, std::unordered_map<int, std::function<void(const BaseEvent &)>>> m_callbacks;
    int m_keyCounter = 0;
public:
    EventBus() = default;

    template<typename EventType>
    EBHandle addEventListener(const std::function<void(const EventType &)> &callback) {
        // passing m_callback by copy is necessary. otherwise if callback passed to addEventListener by reference and it is destroyed, lambda will have dangling reference
        auto wrapper = [callback](const BaseEvent &baseEvent) {
            callback(static_cast<const EventType &>(baseEvent));
        };
        std::type_index type = typeid(EventType);
        m_callbacks[type][m_keyCounter] = wrapper;
        return {this, type, m_keyCounter++};
    };

    template<typename EventType>
    void removeEventListener(int key) {
        m_callbacks[typeid(EventType)].erase(key);
    };

    void removeEventListener(std::type_index type, int key) {
        m_callbacks[type].erase(key);
    };

    void emit(const BaseEvent &event) {
        auto &eventTypeCallbacks = m_callbacks[typeid(event)];
        for (auto &pair: eventTypeCallbacks) {
            pair.second(event);
        }
    };

    EventBus(const EventBus &) = delete;
    EventBus &operator=(const EventBus &) = delete;
    EventBus(EventBus &&) = delete;
    EventBus &operator=(EventBus &&) = delete;
};

EBHandle::EBHandle(EventBus *eventBus, std::type_index type, int key) : m_key(key), m_type(type), m_eventBus(eventBus) {}

void EBHandle::remove() {
    m_eventBus->removeEventListener(m_type, m_key);
    m_isRemoved = true;
}

在尝试打破依赖性时,我首先尝试将声明和定义拆分为 .cpp 和 .h 文件。然后意识到我的方法是模板化的。然后尝试像上面的代码(在单个文件中)一样执行此操作,并且它有效。但是,我想将 EBHandle 和 EventBus 拆分到不同的文件。 这个问题的答案说这是可行的。我试过了:

EventBus.h:

#pragma once

#include <unordered_map>
#include <functional>
#include <typeindex>
#include "EBHandle.h"

struct BaseEvent {
    BaseEvent() = default;
    virtual ~BaseEvent() = default;
};

class EventBus {
    std::unordered_map<std::type_index, std::unordered_map<int, std::function<void(const BaseEvent &)>>> m_callbacks;
    int m_keyCounter = 0;
public:
    EventBus() = default;

    template<typename EventType>
    EBHandle addEventListener(const std::function<void(const EventType &)> &callback) {
        // passing m_callback by copy is necessary. otherwise if callback passed to addEventListener by reference and it is destroyed, lambda will have dangling reference
        auto wrapper = [callback](const BaseEvent &baseEvent) {
            callback(static_cast<const EventType &>(baseEvent));
        };
        std::type_index type = typeid(EventType);
        m_callbacks[type][m_keyCounter] = wrapper;
        return {this, type, m_keyCounter++};
    };

    template<typename EventType>
    void removeEventListener(int key) {
        m_callbacks[typeid(EventType)].erase(key);
    };

    void removeEventListener(std::type_index type, int key) {
        m_callbacks[type].erase(key);
    };

    void emit(const BaseEvent &event) {
        auto &eventTypeCallbacks = m_callbacks[typeid(event)];
        for (auto &pair: eventTypeCallbacks) {
            pair.second(event);
        }
    };

    EventBus(const EventBus &) = delete;
    EventBus &operator=(const EventBus &) = delete;
    EventBus(EventBus &&) = delete;
    EventBus &operator=(EventBus &&) = delete;
};

EBHandle.h:

class EventBus;
class EBHandle {
private:
    int m_key = -1;
    bool isRemoved = false;
    std::type_index m_type = typeid(int);
    EventBus* m_eventBus = nullptr;

public:
    EBHandle(EventBus *eventBus, std::type_index type, int key);

    void remove();
};

#include "EventBus.h"

EBHandle::EBHandle(EventBus *eventBus, std::type_index type, int key) : m_key(key), m_type(type), m_eventBus(eventBus) {};

void EBHandle::remove() {
    m_eventBus->removeEventListener(m_type, m_key);
    isRemoved = true;
}

抱歉,代码太多了。我不想错过任何重要的事情。

在 EBHandle 中调用 removeEventListener 的地方,我仍然收到“无效使用不完整类型”错误 我做错了什么?

c++ circular-dependency
1个回答
0
投票

您只需将

EBHandle
的成员函数定义移动到单独的源文件中,如下所示。 工作演示

EBHandle.cpp

#include "EBHandle.h"
EBHandle::EBHandle(EventBus *eventBus, std::type_index type, int key) : m_key(key), m_type(type), m_eventBus(eventBus) {};

void EBHandle::remove() {
    m_eventBus->removeEventListener(m_type, m_key);
    isRemoved = true;
}

EBHandle.h

#pragma once
#include <typeindex>
class EventBus;
class EBHandle {
private:
    int m_key = -1;
    bool isRemoved = false;
    std::type_index m_type = typeid(int);
    EventBus* m_eventBus = nullptr;

public:
    EBHandle(EventBus *eventBus, std::type_index type, int key);

    void remove();
};

#include "EventBus.h"

EventBus.h

#pragma once

#include <unordered_map>
#include <functional>
#include <typeindex>
#include "EBHandle.h"

struct BaseEvent {
    BaseEvent() = default;
    virtual ~BaseEvent() = default;
};

class EventBus {
    std::unordered_map<std::type_index, std::unordered_map<int, std::function<void(const BaseEvent &)>>> m_callbacks;
    int m_keyCounter = 0;
public:
    EventBus() = default;

    template<typename EventType>
    EBHandle addEventListener(const std::function<void(const EventType &)> &callback) {
        // passing m_callback by copy is necessary. otherwise if callback passed to addEventListener by reference and it is destroyed, lambda will have dangling reference
        auto wrapper = [callback](const BaseEvent &baseEvent) {
            callback(static_cast<const EventType &>(baseEvent));
        };
        std::type_index type = typeid(EventType);
        m_callbacks[type][m_keyCounter] = wrapper;
        return {this, type, m_keyCounter++};
    };

    template<typename EventType>
    void removeEventListener(int key) {
        m_callbacks[typeid(EventType)].erase(key);
    };

    void removeEventListener(std::type_index type, int key) {
        m_callbacks[type].erase(key);
    };

    void emit(const BaseEvent &event) {
        auto &eventTypeCallbacks = m_callbacks[typeid(event)];
        for (auto &pair: eventTypeCallbacks) {
            pair.second(event);
        }
}
};
© www.soinside.com 2019 - 2024. All rights reserved.