virtual function templates



  • cooky451 schrieb:

    So etwas wäre schön:

    void foo(Iterator<A*> begin, Iterator<A*> end)
    { // "Gib mir Zeiger, deren arithmetisches Verhalten egal ist, die aber auf einen A* zeigen."
    }
    

    Bekommst Du:
    vector<unique_ptr<A>>::iterator --> boost::indirect_iterator<…> --> any_iterator<A>

    wobei der any_iterator ähnlich wie boost/std::function auf den Freispeicher ausweicht, was eigentlich nur dann nötig ist, wenn man diese Iteratoren per Wert übergeben will und wenn sie kopierbar sein müssen. Das habe ich mir bei abstract_range vorhin extra gespart. Auch blöd bei diesem any_iterator-Ansatz ist, dass man sie vergleichen können sollte. In der entsprechenden operator== und operator!= Funktion wird erstmal geprüft, ob die versteckten Typen überhaupt zusammen passen. Das ist eben der Haken daran, wenn man zwei Objekte braucht, um eine Sequenz darzustellen.



  • Wenn du einfach nur die tick() Methode all deiner Spielobjekte aufrufen willst, dann mach das doch einfach? Wieso musst du da irgendwo eine abstrakte Collection dieser Objekte an eine virtuelle Methode übergeben?



  • pumuckl schrieb:

    krümelkackers Code Erklärung

    Ja, das habe ich dann auch gemerkt. ^^

    pumuckl schrieb:

    Anderer Ansatz, um das virtual in der Klasse zu lassen und nicht irgendwelche Iteratoren abstrahieren zu müssen: Template Method Pattern.

    Das Problem ist halt, dass die Objekte / Clienten untereinander agieren müssen, soweit ich das hier sehe funktioniert das so leider nicht.

    Hm.. ich glaube ich schreibe erst mal eine std::list<std::unique_ptr<A>>::iterator Version, und teste irgendwann später die Version mit den virtuellen Iteratoren. Dann habe ich auch gleich einen direkten Geschwindigkeitsvergleich.

    any_iterator habe ich auch schon gefunden, aber das ganze scheint mir etwas langsam. Eigentlich ähnlich der Lösung von krümelkacker?

    @dot
    Wie sollen sich denn Objekte in einer Welt zurechtfinden, in der sie nicht mit den anderen Objekten kommunizieren können? Angenommen bei einem Strategiespiel hat man eine Einheit, die feindliche Objekte im Radius von 5 Metern automatisch angreift. Wie soll diese Einheit denn nach feindlichen Objekten suchen?



  • Objekte werden normalerweise in einer Welt verwaltet. Eine Einheit fragt einfach die Welt "gib mir alle Objekte im Bereich ...". Danach kann ich entscheiden, welches Objekt angegriffen wird. Wenn man will kann man das gern in Sensoren, Aktuatoren, Trigger, etc. aufteilen.



  • cooky451 schrieb:

    Angenommen bei einem Strategiespiel hat man eine Einheit, die feindliche Objekte im Radius von 5 Metern automatisch angreift. Wie soll diese Einheit denn nach feindlichen Objekten suchen?

    Gar nicht. Wieso sollte die Einheit sämtliche Objekte der Spielwelt kennen und diese dann auch noch durchsuchen? Die Einheit kann niemals genug Information haben um das auch nur annähernd effizient durchzuführen.
    Aber irgendjemand kennt alle Objekte und hat die evtl. auch schon in einer Datenstruktur organisiert, mit der solche Abfragen effizient durchgeüfhrt werden können. Die bessere Lösung wäre also, dass deine Einheit einfach diesen jemand nach den 5 nähesten gengerischen Einheiten fragt 😉



  • Gib mal eine grobe Beschreibung deiner Gegebenheiten ab, aber etwas weniger abstrakt als A::foo 😉

    Ich vermute jetzt mal, dass du folgendes hast:

    a) Einen Haufen Spielobjekte
    b) Einen oder mehrere Container, die diese Spieolbjekte beinhalten

    A::foo() ist dann eine Methode dieser Spieolbjekte, mit der du WAS machen möchtest?



  • Da das mit den Iteratoren ja mehr oder weniger durchgekaut ist, können wir ja auch gleich mit Spieledesign weitermachen. 🤡

    Der Plan war eigentlich, quasi das gesamte Geschehen Objektintern zu regeln. Soll heißen, im Mainloop gibt es irgendwo so etwas:

    auto elpased_time = timer.tick();
    for (auto i = begin(objects); i != end(objects); ++i)
      (*i)->tick(elapsed_time, begin(objects), end(objects));
    

    So weit, so gut. Alle Objekte können jetzt in tick() machen was sie wollen. Das Grundgerüst schien mir eigentlich relativ angenehm.

    @dot:
    Das bedeutet, du würdest also für jede Objektinteraktion irgendeine Klasse einbauen, die das ganze regelt? Ich habe irgendwie noch nicht ganz verstanden wie sich ein Objekt zurechtfinden soll, ohne die anderen Objekte zu kennen.



  • cooky451 schrieb:

    @dot:
    Das bedeutet, du würdest also für jede Objektinteraktion irgendeine Klasse einbauen, die das ganze regelt? Ich habe irgendwie noch nicht ganz verstanden wie sich ein Objekt zurechtfinden soll, ohne die anderen Objekte zu kennen.

    Dein Objekt braucht die anderen Objekte nicht kennen. Dein Objekt braucht nur ein Objekt zu kennen, welches ihm sagen kann welche Objekte sich im Umkreis von 5m befinden oder so...



  • cooky451 schrieb:

    Alle Objekte können jetzt in tick() machen was sie wollen.

    Richtig. allerdings MÜSSEN sie so auch alles selbst machen. Denk ans "Single Responsibility Principle". Deine "Panzer" sind dafür zuständig, die anderen abzuschießen. Dazu schauen sie in ihre "Umgebung", wer da ist, und rechnen nicht für alle Objekte der großen weiten Welt mit Zettel und Stift selber aus, wer denn da alles nicht ist.
    Die "Umgebung" sollte ein eigenes Objekt sein, das auf solche Distanzberechnungen spezialisiert ist - schließlich werden nicht nur die Panzer sondern auch andere solche Berechnungen benötigen 😉

    Range operator"" _m(char const* meters); //yes, we can in C++11!
    
    void Panzer::tick()
    {
      auto inRange = map.getObjectsInRadius(*this, 5_m);
      for (auto targetIter = inRange.begin(); targetIter != inRange.end() && shotsRemain(); ++targetIter)
      {
        if (isEnemy(*targetIter))
        {
          shootAt(*targetIter);
        }
      }
    }
    


  • Leute, packt mal die Kanonen wieder ein, ihr verschreckt die Spatzen.

    struct A 
     { 
       virtual void foo(std::list<std::unique_ptr<A>>& as) 
       {} 
     };
    

    Einfache und oft anwendbare Lösung:

    struct A 
    { 
       template <class T>
       void foo(T const& as)
       {
           // ... Vorbereitung ...
    
           BOOST_FOREACH(typename T::reference a, as) // ich hoffe das typename T::reference passt, aber wenn nicht so, dann so ähnlich :D
               do_one_foo(*get(a));
    
           // ... Nacharbeiten ...
       }
    
    private:
       virtual void do_one_foo(A& a) 
       {} 
    };
    

    Tadaaaaa. Und ganz ohne Type-Erasure oder 100 Hilfsklassen 😉



  • pumuckl schrieb:

    Range operator"" _m(char const* meters); //yes, we can in C++11!
    

    Warum char* und nicht long double ?



  • knivil schrieb:

    Warum char* und nicht long double ?

    Touché, natürlich 🙂


Anmelden zum Antworten