Seite 1 von 3
"EventPipe"
Verfasst: Mi Jan 16, 2013 2:56 pm
von Glocke
Hi, in einer MVC-artigen Anwendung möchte ich vom Controller an das Model einige Daten und eine "Arbeitsanweisung" schicken. Jedoch möchte ich keine Model-Methode aufrufen, sondern ihm das ganze als Ereignis zuschieben

Folgendes habe ich mir dazu gebastelt (btw der SDL_mutex basiert auf SDL ^^ da ich dessen Thread-Systematik verwende):
Code: Alles auswählen
typedef unsigned char EventID;
extern const EventID NO_EVENT = 0;
struct Event {
EventID event_id;
};
class EventPipe {
protected:
std::queue<Event*> events;
SDL_mutex* event_mutex;
public:
EventPipe();
~EventPipe();
void push(Event* event);
Event* pop();
};
Auf die Implementierung verzichte ich mal. Push schiebt das gegebene Event auf die Schlage, Pop liest es und gibt es zurück. Beide verwenden den event_mutex.
Nun habe ich folgendes "Problem": Ich will weitere Events vom Basisevent ableiten im Sinne
Code: Alles auswählen
extern const EventID DO_FOO = 1;
struct Foo: Event {
int data;
Foo() { this->event_id = DO_FOO; }
};
Damit kann ich Foo als Event in mein EventPipe geben und auf der anderen Seite (in Form eines allgemeinen Events) rausholen. Allerdings muss ich dann (wenn ich auf meine Daten zugreifen will) das Event in seine richtige Form casten. Dazu verwende ich diese "event_id":
Code: Alles auswählen
if (unknown_event.event_id == DO_FOO) {
Foo* known_event = (Foo*)unknown_event; // hab es nicht getestet (das ganze an der stelle noch ein gedankenexperiment^^)
// handle known event
}
So und jetzt (wie immer am Ende meiner Posts xD) meine Frage: Wie mach ich das "richtig"?
LG Glocke

Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 3:21 pm
von Yoghurt
Ich hoffe ich hab dich jetzt richtig verstanden.
Du willst ein Event in eine Queue schieben und das Event dann an einer anderen Stelle verarbeiten, oder?
Du könntest zum Beispiel das Event als Objekt betrachten.
Dann machst du dir eine (abstrakte) Event-Klasse und um die Funktion des Events zu bestimmen leitest du entweder davon ab oder übergibst sie in Form eines anderen Objekts.
Die Modeldaten können dann ja beim anstoßen der Verarbeitung des Events mitgegeben werde.
Hoffe das hilft dir ein wenig weiter. Wenn auch nur als Denkanstoß.

Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 3:32 pm
von Glocke
Yoghurt hat geschrieben:Ich hoffe ich hab dich jetzt richtig verstanden.
Du willst ein Event in eine Queue schieben und das Event dann an einer anderen Stelle verarbeiten, oder?
Jap!
Yoghurt hat geschrieben:Du könntest zum Beispiel das Event als Objekt betrachten.
Dann machst du dir eine (abstrakte) Event-Klasse [...]
Das hab ich - nur als struct.
Yoghurt hat geschrieben:[...] und um die Funktion des Events zu bestimmen leitest du entweder davon ab oder übergibst sie in Form eines anderen Objekts.
Die Modeldaten können dann ja beim anstoßen der Verarbeitung des Events mitgegeben werde.
Wie genau meinste das? Von meinem Basis-Event leite ich speziellere Events ab.
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 3:50 pm
von Yoghurt
Glocke hat geschrieben:Wie genau meinste das? Von meinem Basis-Event leite ich speziellere Events ab.
Ja genau. Dein Basis-Event hat meinetwegen eine Methode "do_event" oder so ähnlich und diese Methode kannst du in den spezialisierten Klassen dann überschreiben.
Oder du übergibst einem Event-Objekt (nicht abgeleitet) ein Objekt das die Funktionalität kapselt (EventFunc) das wiederum auch eine Methode "do_event" hat. Dann müsstest du allerdings auch von der EventFunc Klasse erben um eine konkrete Funktion abzubilden.
In C++ könntest für diese Lösung auch Funktionspointer verwenden und folglich nur die Funktion übergeben. Das hätte den Vorteil, dass du nicht tausend (je nachdem wie viele Event-Typen du brauchst) Ableitungen von Event hast.
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 4:16 pm
von Glocke
Sagen wir mal ich habe ein Event:
Code: Alles auswählen
struct Event {
virtual void do_event(void* foo); // hier kenne ich das exakte Model ja noch nicht
};
struct PlayerLogin: Event {
void do_event(SpecificModel* model) {
model->handle_login(this);
}
char username[255];
char password[255];
};
und das Struct kommt so wie es ist via TCP am Server an. Der Server will nun vom Model wissen, ob der Login erfolgreich ist. Würde die do_event-Methode so aussehen?
Und im Model (beim pop'en des Events) käme dann:
Code: Alles auswählen
void SpecificModel::handle_events() {
next_event = this->pipe->pop();
next_event.do_event(this);
}
Oder?
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 8:48 pm
von Xin
Glocke hat geschrieben:Hi, in einer MVC-artigen Anwendung möchte ich vom Controller an das Model einige Daten und eine "Arbeitsanweisung" schicken. Jedoch möchte ich keine Model-Methode aufrufen,
Geht das ganze über's Netz? In dem Fall musst Du "gepackte", komplette Objekte verschicken, also keine Zeiger auf Strings oder virtuelle Funktionen...
Glocke hat geschrieben:Code: Alles auswählen
typedef unsigned char EventID;
extern const EventID NO_EVENT = 0;
struct Event {
EventID event_id;
};
Nun habe ich folgendes "Problem": Ich will weitere Events vom Basisevent ableiten im Sinne
Code: Alles auswählen
extern const EventID DO_FOO = 1;
struct Foo: Event {
int data;
Foo() { this->event_id = DO_FOO; }
};
Damit kann ich Foo als Event in mein EventPipe geben und auf der anderen Seite (in Form eines allgemeinen Events) rausholen. Allerdings muss ich dann (wenn ich auf meine Daten zugreifen will) das Event in seine richtige Form casten. Dazu verwende ich diese "event_id":
Code: Alles auswählen
if (unknown_event.event_id == DO_FOO) {
Foo* known_event = (Foo*)unknown_event; // hab es nicht getestet (das ganze an der stelle noch ein gedankenexperiment^^)
// handle known event
}
So und jetzt (wie immer am Ende meiner Posts xD) meine Frage: Wie mach ich das "richtig"?
Punkt 1: Du gibst Event einen Konstruktor, der die Initialisierung von event_id verlangt, also
muss jede Ableitung Event initialisieren.
Punkt 2: Wieso void*? Wieso ist das Model noch unbekannt? Oder ist es eigentlich ein struct BaseModel *, das Du noch nicht definiert hast.
Ich glaube, ich verstehe die Frage noch nicht... falls doch, denke ich, das Yoghurt da schon richtig liegt, außer dass die Klasse eben nicht abstrakt sein darf, sondern eben wie oben beschrieben alle Informationen beinhalten musst, wie Du es mit der event_id ja auch bereits richtig machst.
Pack die Konstante noch static in den Namensraum, damit Du mit den Ids nicht den globalen Namensraum belegst.
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 9:22 pm
von Glocke
Xin hat geschrieben:Punkt 1: Du gibst Event einen Konstruktor, der die Initialisierung von event_id verlangt, also muss jede Ableitung Event initialisieren.
Also so, wie ich es oben hatte, dass jede Ableitung von Event die entsprechende EventID sich selber gibt?
Xin hat geschrieben:Punkt 2: Wieso void*? Wieso ist das Model noch unbekannt? Oder ist es eigentlich ein struct BaseModel *, das Du noch nicht definiert hast.
Das war vorhin irgendwie geistiges Dünnes von mir ^^
Xin hat geschrieben:Pack die Konstante noch static in den Namensraum, damit Du mit den Ids nicht den globalen Namensraum belegst.
Also im Sinne
mit
this->event_id = eventids::foobar im Konstruktor - also so vom Prinzip her?
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 11:52 pm
von Xin
Glocke hat geschrieben:Xin hat geschrieben:Punkt 1: Du gibst Event einen Konstruktor, der die Initialisierung von event_id verlangt, also muss jede Ableitung Event initialisieren.
Also so, wie ich es oben hatte, dass jede Ableitung von Event die entsprechende EventID sich selber gibt?
Yepp. Die Id ist kein Zeiger und bedarf keiner virtuellen Funktionen.
Glocke hat geschrieben:Xin hat geschrieben:Pack die Konstante noch static in den Namensraum, damit Du mit den Ids nicht den globalen Namensraum belegst.
Also im Sinne
mit
this->event_id = eventids::foobar im Konstruktor - also so vom Prinzip her?
Ich dachte in dem Moment an:
Code: Alles auswählen
struct Foo : public Event {
static Event const MyId = 1;
int data;
Foo() : Event( MyId ) {}
};
Aber Dein Vorschlag gefällt mir doch besser, weil man alle Event-Ids beisammen hat und direkt sieht, falls zwei Ids die gleichen Wert haben. Eine enum würde ich tatsächlich nicht nutzen, die zählt zwar zuverlässig, dafür kann man kein Element mehr entfernen, ohne die nachfolgenden durcheinander zu bringen.
Re: "EventPipe"
Verfasst: Mi Jan 16, 2013 11:57 pm
von Glocke
Xin hat geschrieben:Die Id ist kein Zeiger und bedarf keiner virtuellen Funktionen.
Okay, wie würdest du dann das Event am Ende der Pipe wieder in seine Ursprungsgestalt übertragen?
Wenn ich es über das Netzwerk sende, schiebe ich erst die EventID in den Socket und dann das Event, damit die Gegenseite weiß "aah Event XY" und entsprechend viele Bytes liest.
Schiebe ich das Teil aber durch meine EventPipe bekommt die Gegenseite "nur" ein Event*. Schau ich dann (wie eingangs geschrieben) nach der ID und caste meinen Event-Pointer zurecht?
LG Glocke
/EDIT: In anderen Worten (mal mit weggelassenem Mutex in diesem Fall):
Code: Alles auswählen
#include <iostream>
#include <queue>
#include <string.h>
typedef unsigned char EventID;
namespace eventid {
extern const EventID BASE = 0;
extern const EventID LOGIN = 1;
}
struct Event {
EventID event_id;
Event(EventID event_id): event_id(event_id) {}
};
struct Login: Event {
char username[255];
Login(): Event(eventid::LOGIN) {}
};
class EventPipe {
protected:
std::queue<Event*> events;
public:
EventPipe() {}
~EventPipe() {}
void push(Event* event) {
this->events.push(event);
}
Event* pop() {
while (this->events.empty()) {}
Event* tmp = this->events.front();
this->events.pop();
return tmp;
}
};
int main() {
// Sender
EventPipe* pipe = new EventPipe();
Login* login = new Login();
memcpy(login->username, "Glocke", 7);
pipe->push(login);
// Empfänger
Event* next_event = pipe->pop();
if (next_event->event_id == eventid::LOGIN) {
Login* next_login = (Login*)next_event;
std::cout << "Das Event war ein Login mit username=" << next_login->username << std::endl;
}
}
Re: "EventPipe"
Verfasst: Do Jan 17, 2013 12:17 am
von Xin
Den Event-Konstruktor solltest Du Dir nochmal ansehen.
Was das Casten angeht, wirst Du da kaum drumrumkommen.
Wenn Du vermeiden möchtest, viele Fragen stellen zu müssen, erstellst Du ein Array mit Funktionspointern und benutzt die event_id, um die Funktion zu finden, die das entsprechende Event bearbeitet.