Superuser

Autor Thema: Eine harte Nuss für C++ Gurus  (Gelesen 12994 mal)

majix

  • Gast
Eine harte Nuss für C++ Gurus
« am: 19. April 2004, 01:48:11 »
Ich habe ein kleines Problem mit C++. Man stelle sich vor


class A {
  virtual f();
}

class B : public A {
  virtual f();
}


Ich habe also zwei Klassen A und B, und die haben jeweils eine virtuelle Funktion. Nun bekomme ich irgendwo im Programm zwei Pointer des Typs A*, und möchte nun gerne wissen, ob beide Objekte die gleiche Implementierung von f haben (sonst können sie sich unterscheiden). Also sowas wie


A* o1,o2;
if (o1->f == o2->f)
   mach was tolles


Ich will sozusagen die Funktionspointer vergleichen. Nur leider - das geht eben nicht. Bei virtuellen Funktionen sind beide pointer immer identisch, obwohl die Implementierungen verschieden sind (!). Dieses Phänomen wird auch als solches beschrieben, und das ist wohl auch so gewollt. Nur eben von mir nicht.

Kann mir jemand einen Tipp geben, wie ich wirklich die Funktionspointer vergleichen kann?

Grüße,
Kaya

mood-indigo.org - Das unabhängige Silicon Graphics User Forum

Eine harte Nuss für C++ Gurus
« am: 19. April 2004, 01:48:11 »

rob_gester

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #1 am: 19. April 2004, 02:19:48 »
vielleicht findest hier was:
http://www.function-pointer.org/
rob

Jasper

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #2 am: 19. April 2004, 03:02:58 »
Hallo Kaya,

ich bin leider auch kein ausgesprochener C++-Spezialist :-(

Dass beide Funktionen denselben Pointer haben, liegt wohl im wesentlichen daran, dass die Auflösung des Methodenaufrufs eben indirekt über eine virtuelle Funktionentabelle funktionert, d.h. der Caller erhält im wesentlichen nur einen Zeiger in die VFT, und auf den Rest hast Du keinen Einfluss.

Sowas lässt sich m.E. nur mittels Metaklassen und/oder Funktionsobjekten statt Funktionen lösen, das ist aber bei Dir wohl keine Option. MIt Assemblerpfusch kriegst Du das sicher auch hin, willst Du aber nicht :-)

Zu Deiner Frage siehe z.B. auch:

http://groups.google.de/groups?hl=de&lr=&ie=UTF-8&oe=UTF-8&threadm=760hin%24qva%241%40engnews1.eng.sun.com&rnum=10&prev=/groups%3Fhl%3Dde%26ie%3DUTF-8%26oe%3DUTF-8%26q%3Dvirtual%2Bmethod%2Bcompare%2Bimplementations%2Bc%252B%252B%26spell%3D1

Frage: Wozu brauchst Du das denn? Vielleicht kannst DU Dein Ziel auf anderen Wegen erreichen.

Gruss

JM

Brombaer

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #3 am: 19. April 2004, 10:55:43 »
Hallo Kaya,

eigentlich willst Du ja wissen von welchem Typ A oder B sind ? Schau mal unter RTTI (Run Time Type Information) nach.

Viel Glück

Matthias

majix

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #4 am: 19. April 2004, 13:32:05 »
Hi Matthias, Jasper,

das ist eben mein Problem: Ich will nicht wirklich wissen, ob A und B den gleichen Typ haben, ich habe nämlich wesentlich mehr Ableitungen von A, und diese haben auch wieder Ableitungen. Ich will eben nur wissen, ob zwei abgeleitete Objekte die gleiche Funktion f besitzen - in anderen Punkten können und werden sich die Klassen sehr wohl unterscheiden.

Es hierbei um folgendes: Ich habe verschiedene Shaderklassen, und jede Klasse besitzt eine virtuelle Methode zum aktivieren des Shaders und eine zum deaktivieren. Wenn ich nun einen neuen Shader aktiviere, dessen Deaktivierungs-Funktion mit der des aktuellen Shaders übereinstimmt, dann brauch ich den alten auch erst gar nicht zu deaktivieren. Spart ein paar OpenGL-Aufrufe. Denn in vielen Fällen werden unterschiedlich Shader identisch dektiviert, aber verschieden aktiviert.

Grüße,
Kaya
« Letzte Änderung: 19. April 2004, 13:33:01 von majix »

Brombaer

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #5 am: 19. April 2004, 13:45:17 »
Hört sich ja ganz schön fies an  ::)

Wie wäre es denn wenn die abgeleitete Klasse gar kein f() definiert (wodurch f der Oberklasse aufgerufen wird) bzw. a::f() aufruft ? Das ist doch kein Laufzeitproblem, wenn ich das richtig verstehe, jede Klasse sollte ja wissen wie der Shader zu deaktivieren ist.

Gruß

Matthias

hchris

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #6 am: 19. April 2004, 14:14:18 »
Moin,

koenntest Du nicht einfach jedes unterschiedliche f() in seine eigene kleine (Wrapper-)Klasse stecken und dann als Methode aufrufen ? Damit funktioniert der Vergleich ueber RTTI wieder.

Gruesse,

Chris

majix

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #7 am: 19. April 2004, 15:47:58 »
@Matthias:
Ich verstehe nicht ganz, was Du meinst... f soll ja gerade den Shader deaktivieren, und das kann ja von Klasse zu Klasse unterschiedlich sein. Beispiel: Environment Texture Mapping, hier würde f dann glDisable(GL_TEX_GEN_S) etc aufrufen.

@Chris:
Wäre eine Möglichkeit, aber solche komplizierten Konstrukte wollte ich vermeiden.

Ist eigentlich echt ein Witz: Ich will einfach nur die Pointer auf die Funktion vergleichen, genau diese Adressen, die im VTABLE stehen, nur C++ scheint mich da nicht ran zu lassen. Vielleicht schaffe ich es ja doch durch geschicktes casten, da hatte ich gestern zwar keinen Erfolg mit, aber ich werde es nochmals versuchen. Das MUSS doch irgendwie gehen.

Grüße,
Kaya

Jasper

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #8 am: 19. April 2004, 16:20:14 »

Zitat


Es hierbei um folgendes: Ich habe verschiedene Shaderklassen, und jede Klasse besitzt eine virtuelle Methode zum aktivieren des Shaders und eine zum deaktivieren. Wenn ich nun einen neuen Shader aktiviere, dessen Deaktivierungs-Funktion mit der des aktuellen Shaders übereinstimmt, dann brauch ich den alten auch erst gar nicht zu deaktivieren. Spart ein paar OpenGL-Aufrufe. Denn in vielen Fällen werden unterschiedlich Shader identisch dektiviert, aber verschieden aktiviert.


Hallo Kaya,

generell finde ich es keine so tolle Idee, das Laufzeitsystem aus Performancegründen überlisten zu wollen. Meistens weder portabel noch nachvollziehbar. Ausserdem verbirgst Du dann die Implementierung nicht vor dem verwendenden Programm.
Du könntest z.B. Metainformationen mittels (leeren) Interfaces und Mehrfachvererbung organisieren, ähnlich wie das in Java geht, die z.B. nur klassifizieren, welche Shader eine gemeinsame Deaktivierung besitzen. Dann müsstest Du mit RTTI an die gewünschte Information kommen können. Ein optimierender Compiler sollte da nicht viel langsameren Code generieren.

Gruss

JM

Offline sgt_barnes

  • Mood Master
  • ****
  • Beiträge: 250
  • Der frühe Vogel fängt den Wurm, aber die zweite Maus kriegt den Käse!
    • Profil anzeigen
Re: Eine harte Nuss für C++ Gurus
« Antwort #9 am: 19. April 2004, 16:32:13 »
Also das wäre mein Vorschlag (ist dem von Chris ähnlich):

Zuerst ein paar Basisklassen:


class Activator
{
public:
   virtual void activate () = 0;
};

class Deactivator
{
public:
   virtual void deactivate () = 0;
};


Von denen leitest Du für jede Möglichkeit, die Dir im Moment einfällt, eine Klasse ab. Diese registrieren sich bei einem Factory-Objekt:


class Factory
{
public:
   static Activator *getActivator (const char *name);
   static Deactivator *getDeactivator (const char *name);
   static registerActivator (Activator *act, const char *name);
   static registerDeactivator (Deactivator *deact, const char *name);
   
   ...
};


Die Shader-Basis-Klasse:


class Shader
{
public:
   Shader (Activator *a, Deactivator *d)
       : m_activator (a)
       , m_deactivator (b)
   { }
   
   virtual ~Shader ()
   {
       delete (m_activator);
       delete (m_deactivator);
   }
   
   Activator *activator ()     { return (m_activator); }
   Deactivator *deactivator ()     { return (m_deactivator); }
   
   void begin ()
   {
       if ((m_lastActivator == 0) || (m_lastActivator != m_activator)
       {
           m_activator->activate ();
           m_lastActivator = m_actiavator ();
       }
   }
   
   void end ()
   {
       if ((m_lastDeactivator == 0) || (m_lastDeactivator != m_deactivator)
       {
           m_deactivator->activate ();
           m_lastDeactivator = m_deactivator ();
       }
   }

private:
   Activator   *m_activator;
   Deactivator *m_deactivator;
   
   static Activator    *m_lastActivator;
   static Deactivator  *m_lastDeactivator;
};

// static initializations (lives in Shader.cpp file)
Activator   *Shader::m_lastActivator = 0;
Deactivator *Shader::m_lastDeactivator = 0;


Jetzt bastelst Du Dir einen Shader zusammen:


class MyShader : public Shader
{
public:
   MyShader ()
       : Shader (Factory::getActivator ("test"), Factory::getDeactivator ("test"))
   { }
   
   ...
};


Das letzte Problem ist jetzt, dass Du eigentlich die Shader::begin() und Shader::end() nicht implementieren kannst, wenn Du nicht weisst, welcher Shader als nächstes kommt. Aber das sollte auch kein Problem sein, wenn Du beide Funktionen in die Renderer-Klasse einbaust, die die Shader benutzt.

MfG,
Tilmann
« Letzte Änderung: 19. April 2004, 16:33:58 von sgt_barnes »

majix

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #10 am: 19. April 2004, 17:10:18 »
@Jasper:
Für so unelegant halte ich meine Methode nicht - ich habe natürlich eine kleine Wrapper-Methode, die man aufruft, um weiterhin die Implementierung zu verbergen. D.h. die Programme werden sowieso nicht direkt die Funktion f aufrufen, sondern eine andere Funktion, die eben testet, ob der Aufruf von f überhaupt notwendig ist. Und ja, für höchstmöglichste Performance sollte man etwas tricksen, und vor allem redundante OpenGL Aufrufe eliminieren. Ich denke gerade im Grafikbereich kann sowas durchaus entscheidend sein.

@Tilman:
Wow, ein ganzer Haufen Code. Danke :)
Ist auf jeden Fall eine Überlegung wert - es stört mich halt nur, dass jetzt die beiden Funktionen (aktivieren, deaktivieren) in getrennte Klassen wandern, wobei diese ja eigentlich sehr eng miteinander verwandt sind.

Ich sehe schon, meine einfache Idee ist leider so nicht ohne weiteres machbar... naja, ich werde heute abend nochmal ein wenig rumspielen, vielleicht finde ich ja doch noch eine unkomplizierte Lösung.

Grüße,
Kaya

Brombaer

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #11 am: 19. April 2004, 17:17:57 »
Zitat

Und ja, für höchstmöglichste Performance sollte man etwas tricksen, und vor allem redundante OpenGL Aufrufe eliminieren. Ich denke gerade im Grafikbereich kann sowas durchaus entscheidend sein.


Hallo Kaya,

definitiv ;-) Kannst ja mal in den Code vom OpenSG (http://www.opensg.org) reinschauen, der ist selbst auf SGIs mittlerweile sehr performant und stellt das ein oder andere kommerzielle Produkt in den Schatten.

Gruß

atthias

Offline sgt_barnes

  • Mood Master
  • ****
  • Beiträge: 250
  • Der frühe Vogel fängt den Wurm, aber die zweite Maus kriegt den Käse!
    • Profil anzeigen
Re: Eine harte Nuss für C++ Gurus
« Antwort #12 am: 19. April 2004, 17:26:49 »
Noch 'n Link, der in eine ähnliche Kerbe haut (wobei auf die ID-Software-eigene Definition von "shader" geachtet werden muss):

http://home.planet.nl/~monstrous/tutshader.html

MfG, Tilmann


Jasper

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #13 am: 19. April 2004, 17:43:42 »
Hallo Kaya,

unelegant ist das sicher nicht, was Du vorhast, aber eben problematisch.  M.E. ist Tilmans Ansatz sicher eine gute Idee, denn er kapselt den Zugriff auf den Shader auf definierte Weise und kommt ohne Mehrfachvererbung und RTTI aus. Ob das von der Laufzeit besser ist? Ausprobieren.

Andere Vorschläge:
-> und oder == überladen, und statt einer Funktion f() ein Funktionsobjekt herzunehmen (nur 'ne Idee, keine Ahnung, ob man das so machen kann...)
Templateklassen verwenden, und auf virtual verzichten: So verzweifelt bist Du nicht
Du könntest natürlich auch für jede Shaderklasse eine virtuelle Methode f_overrides_baseclass einführen und das abfragen...

Und nein, die Funktionen activate und deactivate sind für dein Problem eben nicht eng miteinander verwandt...

Gruss

Jasper

majix

  • Gast
Re: Eine harte Nuss für C++ Gurus
« Antwort #14 am: 19. April 2004, 18:14:47 »
@Matthias:
Bis heute habe ich mir OpenSG noch nicht angesehen, aber das werd ich demnächst mal nachholen. Gerüchteweise ist der Code aber derart komplex, dass es kaum jemanden mehr gibt, der ihn noch vollständig durchschaut.

@Tilman:
Danke für den Link, werd mir das mal etwas genauer ansehen. Allerdings sind die dortigen Struktuen bei mir eher Materialen, Shader sind für mich wirklich aktive Programme, die durch die Materialen parametrisiert werden. Das scheint so am meisten Sinn zu machen, was Flexibilität anbetrifft.

@Jasper:
Es kommt auf die Sichtweise an, ob die beiden Funktionen verwandt sind oder nicht. Aus Sicht des Shaders sind sie definitiv verwandt, und genau deshalb will ich sie nicht auseinanderreißen. Denn die "deactivate" Funktion muss immer einige OpenGL States ausschalten, die von der "activate" Funktion eingeschaltet worden sind. Sonst gibt es hässliche Resultate auf dem Monitor. Durch Auseinanderreißen von logisch eng zusammenhängenden Dingen scheint mir eine potentielle Fehlerquelle zu sein, deshalb sträube ich mich da ein wenig.

Aber ein gutes Buch über Design Pattern in C++ könnte mir nicht schaden... Kennt jemand eines?

Grüße,
Kaya