J'ai écrit un système d'événement pour mon jeu. Ça fonctionne bien mais a un gros défaut - il n'est pas sûr en termes de types, nécessitant ainsi que la fonction de rappel abonnée effectue manuellement la conversion de l'événement de base reçu. Maintenant, j'essaie de mettre en œuvre une version sûre en termes de types en utilisant des modèles. En général, je me débrouille avec le sujet mais clairement je ne suis pas un expert.
Tout d'abord, voici un peu de code démontrant comment je veux utiliser l'événement :
Définissez les événements dérivés
// Événements dérivés
class ClickEvent : public Event
{
public:
float x;
float y;
};
class RenderNodeCreatedEvent : public Event
{
public:
unsigned long long int renderNodeId;
};
Créez-les et utilisez-les (par exemple à des fins de test dans le programme principal)
// Créer les fonctions sur l'événement
std::function onClickFunction = [](const ClickEvent & event)
{
std::cout << std::endl << "Clic de souris à la position : " << event.x << event.y;
};
std::function onRenderNodeCreatedFunction = [](const RenderNodeCreatedEvent & event)
{
std::cout << std::endl << "Nœud de rendu créé avec l'identifiant : " << event.renderNodeId;
};
// Créer les événements
ClickEvent clickEvent;
clickEvent.x = 300.f;
clickEvent.y = 255.5f;
RenderNodeCreatedEvent renderNodeCreatedEvent;
renderNodeCreatedEvent.renderNodeId = 26234628374324;
// Créer le gestionnaire d'événements et s'abonner aux fonctions sur les événements
EventManager eventManager;
eventManager.Subscribe(onClickFunction);
eventManager.Subscribe(onRenderNodeCreatedFunction);
// Déclencher les événements
eventManager.Raise(clickEvent);
eventManager.Raise(renderNodeCreatedEvent);
Voici ce que j'ai essayé pour générer un identifiant unique de type std::size_t
pour chaque classe d'événement dérivée :
class BaseEvent
{
public:
typedef std::size_t ID;
BaseEvent() = default;
virtual ~BaseEvent() = default;
protected:
static ID GetNextID()
{
static ID id = 0;
return id++;
}
};
class Event : public BaseEvent
{
public:
Event() = default;
virtual ~Event() = default;
// Définit un identifiant unique pour l'événement la première fois que cette fonction est appelée
ID GetID()
{
static ID id = BaseEvent::GetNextID();
return id;
}
};
Enfin, voici ma (terrible) tentative de gestionnaire d'événements qui peut fournir les fonctionnalités décrites ci-dessus. J'ai vraiment du mal à gérer correctement la conversion et/ou à stocker différents types de fonctions de rappel. L'enveloppe de rappel ne fonctionne pas - c'est juste une idée de base que j'ai eue pour contourner ce problème, je l'ai donc incluse dans le message.
class EventManager
{
public:
// Définir le type de rappel de modèle
template
using TCallback = std::function;
public:
EventManager() = default;
~EventManager() = default;
template
void Subscribe(TCallback callback)
{
// Obtenir l'indice de la liste de rappels à laquelle ce rappel sera ajouté
Event::ID id = DerivedEvent::GetID();
// Cela ne fonctionnera pas car TCallback est un type différent de TCallback
callbackListList[id].push_back(callback);
}
template
void Raise(DerivedEvent event)
{
// Obtenir le type de l'événement et donc l'index dans callbackListList
Event::ID = DerivedEvent::GetID();
/// Comment convertir les événements en arrière
// Obtenir la liste respective des fonctions de rappel
std::vector> /*&*/ callbackList;
// Créer une enveloppe de rappel avec le type 'événement dérivé' ????
CallbackWrapper callbackWrapper(/*rappel dérivé*/);
// Appeler l'enveloppe de rappel en utilisant l'événement de base ????
}
template
class CallbackWrapper
{
public:
CallbackWrapper(TCallback callback) : callback(callback) {}
void operator () (const Event & event)
{
callback(static_cast &>(event).event);
}
private:
TCallback callback;
};
private:
std::vector>> callbackListList;
};
Je sais que c'est beaucoup de code, mais je pensais que ce serait le moyen le plus simple de démontrer de quoi je parle.
Merci pour votre aide, Adrian
MODIFICATION : La classe Event devrait être déclarée en tant que modèle pour obtenir un identifiant unique pour les types dérivés.
template
class Event : public BaseEvent
Maintenant, lors de l'héritage de Event :
class ClickEvent : public Event
class RenderNodeCreatedEvent : public Event
Enfin, l'EventManager ne pourrait stocker qu'un vecteur de vecteurs de rappels de type BaseEvent
private:
std::vector>> callbackListList;