interface zu void casten
-
Super, danke

-
philipp2100 schrieb:
Was ich noch sagen wollte: In vielen (aber nicht allen) Fällen zeugt es von schlechtem Design/Stil, wenn man void* benutzt, da man damit ein wenig Typprüfung verschenkt (eben weil die cast-Bedingungen da so lax sind). Wenn die Art der Daten bekannt sind, sollte man als Zeigertyp einen auf deren (gemeinsame Ober-) Klasse verwenden.
Das kann ich ja leider nicht ändern, das interface ist ja von box2d vorgegeben.
camper schrieb:
Das ist ok so (Syntaxfehler ignoriert). Es könnte allerdings sinnvoll sein, den Zugriff auf userData geeignet zu kapseln.
Hmm was schwebt dir denn da vor? Ich wüßt gerade nicht wie ich das sinnvoll machen würde.
-
philipp2100 schrieb:
In C++ kann nur implizit von void* gecastet werden (Kompabilität zu malloc), aber nicht nach void*.
Ne, ist genau umgekehrt. Es kann implizit nach void* konvertiert werden, aber nicht von void*. Und dass es eben keine Kompabilität zu malloc gibt ist eines der Paradebeispiele dafür, warum nicht jeder C Code von einem C++ Compiler geschluckt wird.
-
Stimmt..

-
Stimmt. Also void* sollte man wenn irgendwie möglich vermeiden. Mir ist auch derzeit, abgesehen von vordefinierten Interfaces kein sinnvoller Anwendungsfall untergekommen, wo es keine andere Lösung als void* gab...
-
It0101 schrieb:
Stimmt. Also void* sollte man wenn irgendwie möglich vermeiden. Mir ist auch derzeit, abgesehen von vordefinierten Interfaces kein sinnvoller Anwendungsfall untergekommen, wo es keine andere Lösung als void* gab...
Ein erster Ansatz wäre wohl, einfach den Typ zu nehmen, den die Daten tatsächlich haben. Also den userdatapointer vom Typ userdata* zu machen, statt void*. Da es so aussieht, als solle hier Polymorphie gemacht werden, nehme man einen Zeiger vom entsprechenden Basisklassentyp (Interface2?*).
Anderer Ansatz, falls void* hier benutzt wird, um ein Funktion universell für mehrere Datentypen anzubieten, wären natürlich Templates.Das sind die beiden Standardansätze zur Vermeidung von void* (der zweite eher als der erste, denn beim ersten wäre der void* auch in C komisch benutzt und das Problem läge tiefer).
*: Leider ist das Codebeispiel bei all der Vererbung und Pointerei etwas schwer zu durchschauen. Zudem stark verkürzt. Ich bin mir nicht sicher, was hier wie zusammen hängt und was überhaupt erreicht werden soll.
-
It0101 schrieb:
Stimmt. Also void* sollte man wenn irgendwie möglich vermeiden. Mir ist auch derzeit, abgesehen von vordefinierten Interfaces kein sinnvoller Anwendungsfall untergekommen, wo es keine andere Lösung als void* gab...
void* ist die höchste Stufe von Type-Erasure und immer anzustreben um gegen Template-Bloat vorzugehen und die Compilezeiten herunterzuschrauben.
Was nichts daran ändert, dass ein void* im Interface nichts zu suchen hat.
Im gegebenen Fall bin ich auch gegen das Listener-Pattern, das kann alles in ohne explizite Vererbunghierarchie gelöst werden
C++03/11-Lösung: http://www.boost.org/doc/libs/release/doc/html/signals/tutorial.html
C++11-Lösung: std::vector<std::function<>>
Warum kein Observer: http://stackoverflow.com/questions/11619680/why-should-the-observer-pattern-be-deprecated
-
Ich kann ja mal grob zusammenfassen wie das bei box2d abläuft. Grob gibt es die welt (b2World) in welcher man bodies (b2Body) erstellen kann welche aus mehreren fixtures (b2Fixture) bestehen. Jede fixture hat eine shape (b2CircleShape, b2ChaiSshape, b2PolygonShape...), diese definiert die form der fixture. Wenn man die welt also mit bodies gefüllt hat kann man das ganze simulieren, wenn jetzt zwei fixtures eine kollision haben wird der definierte contact listener von box2d aufgerufen (in diesem fall MyContactListener aud dem ersten post) und man bekommt die information welche fixtures kollidieren (die fixtures kennen ihren body, daher kann man sich den holen). Mein Actor ist jetzt ein body in dieser welt. Das Interface das box2d anbietet hatte ich ja gepostet, das ist 1zu1 die funktion
virtual void BeginContact(b2Contact* contact)im b2Contact stehen dann die zwei kollidierenden fixtures. Die einzige möglichkeit von der kollision auf mein eigenes objekt zu schließen ist der userData pointer. D.h. ich setz im actor bei der erstellung des bodies den userData pointer auf "this" (das war halt mein eigentliches problem, wo ich nicht wussete ob ich da UB habe wenn ich das rumcaste). Ich seh jetzt nicht wie ich das eleganter lösen könnte, bin aber gerne offen für vorschläge

Nochmal mit ein bischen mehr code zur verdeutlichung
class MyContactListener : public b2ContactListener { void BeginContact(b2Contact* contact) { // try to notify A void *userDataA = contact->GetFixtureA()->GetBody()->GetUserData(); if(userDataA) { PhysicBodyEntity *entity = static_cast<PhysicBodyEntity*>(userDataA); entity->BeginContact(contact); } // then try notify B void *userDataB = contact->GetFixtureB()->GetBody()->GetUserData(); if(userDataB) { PhysicBodyEntity *entity = static_cast<PhysicBodyEntity*>(userDataB); entity->BeginContact(contact); } } void EndContact(b2Contact* contact) { ... } } class Actor : public DrawableNode, public Drawable, public PhysicBodyEntity { Actor(b2World &world) { b2BodyDef bodyDef; ... bodyDef.userData = static_cast<PhysicBodyEntity*>(this); world.CreateBody(bodyDef); ... } } int main() { MyContactListener contactlistener; b2World world(b2Vec2(0, -10)); world.SetContactListener(&contactlistener); Actor actor(world); // hier gibt mir box dann über den listener die collisionen zurück world.Step(...); }
-
Ja. Naja die haben die Userdaten unschön über void* gemacht. Vielleicht wärs mit einem Template auch gegangen oder vielleicht ging es wirklich nicht anders. Ich zumindest kann das, ohne genau Kenntniss der Hintergründ und der lib nicht beurteilen...
-
Ausnahmsweise ist es mal bei Klassen mit Standardlayout erlaubt - aber selten sinnvoll.
Es ist zudem auch erlaubt, den Zeiger zu ihrem (der Deklarationsreihenfolge nach) ersten Member zu casten.
(Um die Liste vollstaendiger zu machen)D.h. ich setz im actor bei der erstellung des bodies den userData pointer auf "this" (das war halt mein eigentliches problem, wo ich nicht wussete ob ich da UB habe wenn ich das rumcaste).
Ich kann grad' den Thread nicht komplett durchlesen... daher bitte ignorieren falls unpassend: Du brauchst die Adresse der Body-Basisklasse (bzw. des Basisklassen-Subobjekts)? Die kannst du doch direkt bekommen.
-
- schrieb:
Ausnahmsweise ist es mal bei Klassen mit Standardlayout erlaubt - aber selten sinnvoll.
Es ist zudem auch erlaubt, den Zeiger zu ihrem (der Deklarationsreihenfolge nach) ersten Member zu casten.
(Um die Liste vollstaendiger zu machen)Allerdings ist das kein Cast innerhalb der Vererbungshierarchie, auf die ich mich (nur) bezogen habe.
-
Ich habe sozuagen darauf geantwortet:
Nein, auch auf Oberklassen.