• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Problem z systemem zdarzeń (Event system)

0 głosów
352 wizyt
pytanie zadane 2 marca 2019 w C i C++ przez Marcin Siniarski Gaduła (4,420 p.)
zamknięte 2 marca 2019 przez Marcin Siniarski

Nie dawno napisałem system zdarzeń (ktoś klika na okno -> wywołuje się funkcja/-e ). Większość ładnie działa, ale od dwóch dni zmagam się z pewnym problemem. Otóż chciałbym usunąć pewne callbacki po dodaniu ich do systemu. Próbowałem już kilku sposobów, ale cały czas coś jest źle.

#include <iostream>
#include <iomanip>
#include <functional>
#include <map>
#include <utility>
#include <algorithm>
#include <vector>

enum EventType {
    eKeyPressedType,
    eKeyReleasedType,
    eWindowMovedType,
    eWindowResizedType,
    eWindowClosedType
};

enum EventCategory {
    eKeyEventCategory,
    eWindowEventCategory
};

class Event {
public:
    virtual EventType GetType() = 0;
    virtual EventCategory GetCategory() = 0;
};

class KeyEvent : public Event {
public:
    virtual EventCategory GetCategory() { return eKeyEventCategory; }
};

class KeyPressedEvent : public KeyEvent {
public:
    static EventType StaticGetType() { return eKeyPressedType; }
    virtual EventType GetType() { return StaticGetType(); }
};
class KeyReleasedEvent : public KeyEvent {
public:
    static EventType StaticGetType() { return eKeyReleasedType; }
    virtual EventType GetType() { return StaticGetType(); }
};

class IEventCallback {
public:
    virtual void call(Event& e) = 0;
    virtual void setId(uint8_t id) = 0;
    virtual uint8_t getId() = 0;
};

template<typename T>
class EventCallback : public IEventCallback {
    std::function<void(T&)> m_fn;
    uint8_t m_id;

public:
    EventCallback(std::function<void(T&)> fn)
        : m_fn(fn), m_id(0) {}

    virtual void call(Event& e) {
        if(m_fn != nullptr)
            m_fn(*(reinterpret_cast<T*>(&e)));
    }

    virtual void setId(uint8_t id) {
        m_id = id;
    }
    virtual uint8_t getId() {
        return m_id;
    }
};

class EventManager {
public:
    EventManager()
        : m_idCount(0) {}

    void Register(EventType type, IEventCallback& cb) {
        if(cb.getId() == 0)
            cb.setId(++m_idCount);

        m_callbacks.emplace_back(type, cb);
    }

    // Unregister specific type from callback
    void Unregister(EventType type, IEventCallback& cb) {
        if(m_callbacks.empty())
            return;

        /*auto it = std::find_if(m_callbacks.begin(), m_callbacks.end(), [&cb, &type](std::pair<EventType, IEventCallback&>& pair) {
            return ((pair.second.getId() == cb.getId()) && (pair.first == type));
        });
        if(it != m_callbacks.end())
            m_callbacks.erase(it);
        else
            return;*/
    }

    // Unregister callback from all types
    void UnregisterCallback(IEventCallback& cb) {
        if(m_callbacks.empty())
            return;

        /*

		for(auto it = m_callbacks.begin(); it != m_callbacks.end(); it++) {
			if((*it).second.getId() == cb.getId())
				m_callbacks.erase(it);
		}*/
        /*
        auto it = m_callbacks.begin();
        while(it != m_callbacks.end()) {

			if((*it).second.getId() == cb.getId())
                m_callbacks.erase(it);

            it++;
        }*/
        /*
        for(size_t i = 0; i < m_callbacks.size(); i++) {
            auto& pair = m_callbacks[i];
            if(pair.second.getId() == cb.getId()) {
                auto it = m_callbacks.begin() + i;
                m_callbacks.erase(it);
                if(i != 0)
                    i--;
            }
        }*/
    }

    void Dispatch(Event& e) {
        for(auto pair : m_callbacks) {
            if(pair.first == e.GetType()) {
                pair.second.call(e);
            }
        }
    }

private:
    uint8_t m_idCount;
    std::vector<std::pair<EventType, IEventCallback&>> m_callbacks;
};

int main(int argc, char** argv) {
    {
        EventManager eManager;
        auto generalCallback = [](Event& e) {
            std::cout << "Event type: ";
            switch(e.GetType()) {
            case eKeyPressedType:
                std::cout << "KeyPressedEvent" << std::endl;
                break;
            case eKeyReleasedType:
                std::cout << "KeyReleasedEvent" << std::endl;
                break;
            default:
                std::cout << "Unknown" << std::endl;
            }
        };
        auto keyPressedCallback = [](KeyPressedEvent& e) {
            std::cout << "Key pressed" << std::endl;
        };
        auto keyReleasedCallback = [](KeyPressedEvent& e) {
            std::cout << "Key released" << std::endl;
        };

        EventCallback<KeyPressedEvent> generalCb(generalCallback);
        EventCallback<KeyPressedEvent> keyPressedCb(keyPressedCallback);
        EventCallback<KeyPressedEvent> keyReleasedCb(keyReleasedCallback);

        eManager.Register(eKeyPressedType, generalCb);
        eManager.Register(eKeyReleasedType, generalCb);
        eManager.Register(eKeyPressedType, keyPressedCb);
        eManager.Register(eKeyReleasedType, keyReleasedCb);

        KeyPressedEvent ekp;
        KeyReleasedEvent ekr;
        eManager.Dispatch(ekp);
        eManager.Dispatch(ekr);

        //eManager.Unregister(eKeyPressedType, generalCb);

        //std::cout << "Second run" << std::endl;

        //eManager.Dispatch(ekp);
        //eManager.Dispatch(ekr);
    }
    std::cin.get();
    return 0;
}

Edit: "ale cały czas coś jest źle", nie mogę tego bardzo opisać. Przy jednym sposobie wyjdzie wyjątek, przy drugim nie poprawnie działa (usuwa zły element).

komentarz zamknięcia: Problem rozwiązany

3 odpowiedzi

+1 głos
odpowiedź 2 marca 2019 przez criss Mędrzec (172,570 p.)
wybrane 2 marca 2019 przez Marcin Siniarski
 
Najlepsza

To powinno wyglądać tak:

        for(auto it = m_callbacks.begin(); it != m_callbacks.end();) {
            if((*it).second.getId() == cb.getId())
                m_callbacks.erase(it);
            else ++it;
        }

Nie wiem czy o to ci chodziło, nie rozpisałeś się jakoś specjalnie o co ci wlasciwie chodzi

PS: Jeśli nikt ci nigdy nie powiedział, to informuję: opis problemu ograniczający się do "ale cały czas coś jest źle." nie jest wystarczający.

komentarz 2 marca 2019 przez Marcin Siniarski Gaduła (4,420 p.)
edycja 2 marca 2019 przez Marcin Siniarski
"Powinno" no tak ale wyskakuje wyjątek: "vectors iterators incompatible".
Edit: Dzieje się tak po pierwszym przejściu przez pętlę
komentarz 2 marca 2019 przez criss Mędrzec (172,570 p.)
W którym miejscu dokładnie? (te informacje powinny być w opisie na samym początku..)

A w zasadzie możesz to zostawic, bo najlepiej jakbys skorzystal z std::remove_if anyway. https://en.cppreference.com/w/cpp/algorithm/remove (na dole jest przyklad jak uzywac w polaczeniu z erase).
komentarz 2 marca 2019 przez Marcin Siniarski Gaduła (4,420 p.)
std::remove_if(m_callbacks.begin(), m_callbacks.end(), [&cb](std::pair<EventType, IEventCallback&> pair) {
           return pair.second.getId() == cb.getId();
});

Nic się nie usuwa.

A co do pętli to na na jej początku, dokładnie w sprawdzeniu it != m_callback.end()

+1 głos
odpowiedź 2 marca 2019 przez j23 Mędrzec (195,240 p.)
std::vector<std::pair<EventType, IEventCallback&>> m_callbacks;

Zamień IEventCallback& na IEventCallback* (no i oczywiście resztę kodu też).

komentarz 2 marca 2019 przez Marcin Siniarski Gaduła (4,420 p.)
Bez rezultatu. Co i tak miałoby to zmienić?
komentarz 2 marca 2019 przez Hiskiel Pasjonat (22,830 p.)
AFAIK kontenery nie mogą przetrzymywać referencji.
0 głosów
odpowiedź 2 marca 2019 przez Marcin Siniarski Gaduła (4,420 p.)

Nareszcie się udało!
Zmieniłem referencje na wskaźniki (odpowiedź) i zastosowałem std::remove_if (odpowiedź).

m_callbacks.erase(
    std::remove_if(m_callbacks.begin(), m_callbacks.end(), [&cb](std::pair<EventType, IEventCallback*> pair) {
        return pair.second->getId() == cb.getId();
    }), 
    m_callbacks.end()
);

Dziękuje za pomoc!

Podobne pytania

0 głosów
1 odpowiedź 276 wizyt
pytanie zadane 20 lutego 2019 w PHP przez Rocket Gaduła (3,030 p.)
0 głosów
2 odpowiedzi 583 wizyt
pytanie zadane 24 maja 2015 w C i C++ przez majkkel Użytkownik (530 p.)

93,425 zapytań

142,421 odpowiedzi

322,646 komentarzy

62,785 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

VMware Cloud PRO - przenieś swoją infrastrukturę IT do chmury
...