0

I wanted to implement an observer pattern for an event system, but the compiler gives me a bunch of errors and I'm currently not able to fix them. I have two templated class in two different headers, a listener and a event dispatcher and both needs to store pointers to the other class.

In Dispatcher.h

//Dispatcher.h
#pragma once
#include "Listener.h"

template <typename T>
    class Dispatcher {
    private:
        std::vector<Listener<T>*> m_listeners;

    public:
        Dispatcher() {}
        ~Dispatcher() {
            for (Listener<T>* l : m_listeners) {
                delete l;
            }
        }

        void Attach(Listener<T>* listener) {
            m_listeners.push_back(listener);
        }

        void Detach(Listener<T>* listener) {
            std::vector<Listener<T>*>::iterator it = std::find(m_listeners.begin(), m_listeners.end(), listener);
            if (it != m_listeners.end()) {
                m_listeners.erase(it);
            }
        }

        void Notify(T& event) {
            for (Listener<T>* l : m_listeners) {
                l->OnEvent(event);
            }
        }

    };

In Listener.h

//Listener.h
#pragma once
#include "Dispatcher.h"

template <typename T>
class Listener {
private:
    Dispatcher<T>* m_dispatcher;
protected:
    Listener(Dispatcher<T>* dispatcher) : m_dispatcher(dispatcher) {
        m_dispatcher->Attach(this);
    }

public:
    virtual ~Listener() {
        m_dispatcher->Detach(this);
    }

    virtual void OnEvent(T& event) = 0;
};

The compiler seems to complain about not being able to find declaration for the listener. What's wrong with this? Could someone explain? Thanks.

P.S. Obviously there's a cpp file which includes both headers.

Andrea
  • 23
  • 3

1 Answers1

-1

You can solve this problem of circular dependency by removing the #include "Listener.h" from Dispatcher.h and instead adding a forward declaration for class template Listener<> as shown below:

Dispatcher.h

#pragma once

#include <vector>
//no need to include Listener.h


//forward declaration 
template<typename T> class Listener;

template <typename T>
    class Dispatcher {
    private:
        std::vector<Listener<T>*> m_listeners;

    public:
        Dispatcher() {}
        ~Dispatcher() {
            for (Listener<T>* l : m_listeners) {
                delete l;
            }
        }

        void Attach(Listener<T>* listener) {
            m_listeners.push_back(listener);
        }

        void Detach(Listener<T>* listener) {
            std::vector<Listener<T>*>::iterator it = std::find(m_listeners.begin(), m_listeners.end(), listener);
            if (it != m_listeners.end()) {
                m_listeners.erase(it);
            }
        }

        void Notify(T& event) {
            for (Listener<T>* l : m_listeners) {
                l->OnEvent(event);
            }
        }

    };

Jason
  • 36,170
  • 5
  • 26
  • 60
  • I get this error on Godbolt: `source>:25:13: error: need 'typename' before 'std::vector*>::iterator' because 'std::vector*>' is a dependent scope 25 | std::vector*>::iterator it = std::find(m_listeners.begin(), m_listeners.end(), listener);` – kiner_shah Jan 16 '22 at 09:58
  • 1
    There is a need to include `Listener.h`, if you're using `Dispatcher`. This makes this header inconvenient to use, since you either need to include `Listener.h` first or you need to include both... The preferrable approach would imho be to add a forward declaration of `Dispatcher`/`Listener` before the declaration of the other and move the includes to the end of the header. – fabian Jan 16 '22 at 09:59
  • Also, if you don't include `` it will throw an error for `find`. – kiner_shah Jan 16 '22 at 09:59