Gibt es sowas wie <?> unter C++?
-
Quad ist in dem Fall eher als eine Art typedef anzusehen und ruft bisher einfach nur Polygon(4) auf. Zudem kann ich z.B. bei Vergleichsoperatoren einfach alle 4 Punkte vergleichen, ohne eine Schleife zu machen (wie es bei einem beliebig grossen Polygon der Fall waere).
Das mit den gemeinsamen Eckpunkten kommt in meinem Anwendungsfall kaum vor, da handelt es sich eher um Polygone, die sich ueberlappen.
EDIT: Uebrigens werden auch die anderen Polygone spaeter nie in ihrer Groesse veraendert, daher waere das mit Templates halt schon praktisch. Was mich gerade wundert: ich kann in der Quad-Klasse das points-Array einfach nochmal neu definieren, aber was laeuft dann eigentlich ab, wenn die Polygon-Methoden aufgerufen werden? Ist das dann einfach in Polygon::points und Quad::points unterteilt?
-
Generics != Templates
Die Syntax mit den <>-Klammern ist zwar gleich, aber es passiert doch letztendlich was anderes. Generics werden zwar vom Compiler überprüft, aber der erzeugte Code ist wieder die
java.lang.Object.
-
Rock Lobster schrieb:
Servus,
einige Java-Programmierer kennen sicherlich das <?>-Konstrukt, um quasi zu sagen, dass einem die Typparameter wurscht sind.Gibt es sowas auch unter C++?
Nein. "Java Generics" ist etwas völlig anderes. Die Typparameter einer generischen Klasse gehören nicht wirklich zum Typ der Klasse ("Type Erasure"). Sie werden nur dazu benutzt, zur Compilezeit etwas mehr Typechecking durchzuführen und dem Nutzer das Typecasting abzunehmen. Das ganze funktioniert wirklich nur wegen der Indirektion (Objekte kann man nie "direkt halten" sondern nur über Referenzen erreichen) und über Laufzeitpolymorphie.
In C++ könnte man das so machen:
class abstraktes_poly { public: virtual ~abstraktes_poly() {} virtual abstraktes_poly* clone() const = 0; virtual int ecken() const = 0; virtual punkt ecke(int index) const = 0; }; template<int Ecken> class konkretes_poly : public abstraktes_poly { punkt m_ecken[Ecken]; public: ...Füge Code für Initialisierung ein... konkretes_poly<Ecken>* clone() const {return new poly<Ecken>(*this);} int ecken() const {return Ecken;} punkt ecke(int index) const {return m_ecken[index];} } int main() { auto_ptr<abstraktes_poly> apap1 ( new konkretes_poly<4> ); auto_ptr<abstraktes_poly> apap2 ( new konkretes_poly<5> ); cout << apap1->ecken(); cout << apap2->ecken(); }Statt auto_ptr kann man diese Zeiger natürlich auch in einem Objekt einer eigens dafür geschriebenen Klasse verwalten. Dadurch kann man auch Wert-Semantik emulieren. Das ganze nennt sich dann auch "C++ Type Erasure" und wird u.a. bei Boost.Function und Boost.Any angewendet.
Gruß,
SP
-
Hallo Rock Lobster,
Rock Lobster schrieb:
Ich moechte kein new im Konstruktor haben, damit die Werte moeglichst auf dem Stack abgelegt werden. Im Moment habe ich die new-Version und das ist einfach zu langsam (da extrem viele dieser Polygone instanziiert werden).
Ich frage mich gerade wie du so viele Objekte auf dem Stack ablegst.
(Rekursion schliesse ich einfach mal aus)
Wo verwaltest du denn die ganzen Polygone? Kurz gesagt, ich befürchte dass
du entweder jedesmal den Copy-Konstruktor aufrufst oder auf identische
Speicherbereiche verweist.Gruß,
CSpille
-
Also in einer wichtigen Initialisierungsroutine am Anfang des Programms wird erstmal ein Haufen Quads in einen std::vector gespeichert (und zwar direkt als Objekt, nicht als Pointer). Da waere es jetzt halt unguenstig, wenn pro Quad im Konstruktor ein new aufgerufen wird, da das ja den gesamten Vorgang sehr ausbremsen kann. Also ist es mir lieber, wenn die Arrays von vorneherein initialisiert werden.
Ich werde nun aber ueberlegen ob ich Polygon und Quad nicht einfach getrennt als Klasse haben soll. Die Anzahl der Methoden und Operatoren haelt sich sehr stark in Grenzen, und es ist wahrscheinlich auch nicht notwendig, dass sie ineinander gecasted werden muessen, von daher ist es vielleicht wirklich die sinnvollste Loesung, einfach zwei unterschiedliche Klassen zu verwenden. Das urspruengliche Design ging halt von schoenem OOP aus, aber jetzt ist die Geschwindigkeit deutlich wichtiger als das Klassendesign.
-
am Anfang des Programms ... im Konstruktor ein new aufgerufen wird, da das ja den gesamten Vorgang sehr ausbremsen kann
Omg. Das passiert am Anfang und nur ein Mal. D.h. es spielt fuer das restliche Programm keine Rolle? Und dieser Konjunktiv stoert mich. Hast du man getestet und nachgemessen, ob es ueberhaupt ein Performanceloch ist?
ueberlegen ob ich Polygon und Quad nicht einfach getrennt
Haeh, hast du dir die Posts nicht durchgelesen. Du kannst OOP und Geschwindigkeit haben. Warum stellst du erst die Frage und machst es dann doch ganz anders. Beratungsresistent ...
-
Rock Lobster schrieb:
...Nun moechte ich aber von manchen Funktionen aus ein Polygon beliebiger Groesse zurueckgeben, ...
Warum nicht wieder ein Funktionstemplate? Dann muss allerdings der Aufrufer die Dimension schon wissen (und sei es nur implizit).
template <int DIM> Polygon<DIM>* f(Polygon<DIM> const& dummy); template <int DIM> Polygon<DIM>* f(int DIM) { return f(Polygon<DIM>); }Gruß,
Simon2.
-
Ich haette vielleicht nicht "Programm" sondern "Vorgang" sagen sollen. Dieser Vorgang dauert im ersten Schritt sehr lang, es kommen danach noch ein paar Schritte die weniger lange dauern. Und der Vorgang kommt in meinem jetzigen "Programm" nur einmal vor, aber spaeter soll er immer wieder vorkommen koennen.
Nachgemessen habe ich auch. Es kommen Daten uebers Netzwerk, und die CPU ist auf Voll-Last, das Netzwerk aber nicht. Also ist wohl davon auszugehen, dass in meinem Code zu viel passiert, und aus diesem Grund waere es sinnvoll, dort ein paar Dinge zu aendern. Da nun in jedem einzelnen Quad-Konstruktor jedoch ein unnoetiges "new" gemacht wird, waere das zumindest mal ein erster Anhaltspunkt.
Ich habe mich gerade mit einem Kollegen unterhalten und ihm auch die Version mit der gemeinsamen IPolygon-Basisklasse vorgeschlagen (hatte sogar schon angefangen zu implementieren), er meinte jedoch, ich soll auf OOP in dem Fall verzichten, weil durch virtuelle Funktionen auch wieder unnoetig Overhead entsteht, der bei solch einer simplen Extrem-Basis-Klasse wie es ein Quad nunmal ist einfach nicht sein muss. Das war der Grund, warum ich sagte, dass ich nun wohl doch lieber eine voellig eigene, OOP-freie Implementierung einer Quad-Klasse bevorzugen wuerde. Hat aber nix mit beratungsresistent zu tun.
-
@ Simon2: Vielen Dank, auf diese Idee bin ich bisher nicht gekommen. Jedoch denke ich, dass auch das in meinem Anwendungsfall nicht sinnvoll sein wird (da man vorher nicht weiss, wie viele Punkte das Polygon dann haben wird). Mir ist nun auch klar warum das mit Templates nicht funktionieren kann. Trotzdem danke fuer den Hinweis, vielleicht kann ich das mal an einer anderen Stelle gut gebrauchen.
-
CPU ... Volllast ... Netzwerk nicht ... erster Anhaltspunkt ...
Mit Nachmessen meine ich eine Profiler zu benutzen und genau zu schauen, wo viel Rechenleistung verbraten wird und nicht aufs geradewohl irgendwas zu optimieren.
Quad-Konstruktor jedoch ein unnoetiges "new"
Und es wurde gezeigt, wie das zu beheben ist, mit OOP. Ausserdem waere dann ein Objektpool wahrscheinlich angebrachter.
ich soll auf OOP in dem Fall verzichten, weil durch virtuelle Funktionen auch wieder unnoetig Overhead entsteht
in jedem Fall ... Hast du ihn auch gefragt, aus was der Overhead besteht und wieviel Zeit er kosten wird? Es ist eine zusaetzlich Dereferenzierung eines Zeigers. Also verdammt billig.
Ich empfehle dir die Buecher von Scott Meyers: "Effektiv C++ programmieren" und "Mehr effektiv C++ programmieren".
-
Simon2 schrieb:
template <int DIM> Polygon<DIM>* f(Polygon<DIM> const& dummy); template <int DIM> Polygon<DIM>* f(int DIM) { return f(Polygon<DIM>); }Ich bezweifle dass das funktioniert, denn erstens hast du für zwei verschiedene Identifyer denselben Bezeichner gewählt (einmal den Templateparameter, einmal den Funktionsparameter), zweitens wird erst zur Laufzeit entschieden, welches Argument der Funktion übergeben wird, der Templateparameter muss aber schon zur Laufzeit feststehen.
Das Ganze müsste daher eher mit einer Art Int2Type als Funktionsargument geschehen:template <int DIM> Polygon<DIM>* f(Int2Type<DIM> /*dummy*/);
-
pumuckl schrieb:
...Ich bezweifle dass das funktioniert, ...
Sollte auch eher eine Ideenskizze sein (hätte ich wohl besser hingeschrieben).
Außerdem war die Idee zur 2. Funktion eher ein "Spontanblitz" .... was wohl (wie so oft) unter die Kategorie "Der Erste aber nicht der Beste" fällt
Deine Kritikpunkte zielen im Wesentlichen auf mein zweites template ab, oder?...
Gruß,
Simon2.
-
Rock Lobster schrieb:
...die CPU ist auf Voll-Last...
Das ist an sich ein gutes Zeichen.
Die Frage ist eher, ob der Prozess insgesamt zu lange dauert ... und ob Deine ergriffenen Maßnahmen helfen.Gruß,
Simon2.
-
Simon2 schrieb:
Deine Kritikpunkte zielen im Wesentlichen auf mein zweites template ab, oder?...
Jup.
@Rock: wie schon gesagt wurde, und wie du auch richtig erkannt hast gibts zwei Möglichkeiten: die "Größe" (= Zahl der Ecken) jedes Polygons zur Compilezeit festlegen -> alles mit Templates, oder zur Laufzeit -> keine Templates, dafür dynamische Speicherverwaltung.
-
pumuckl schrieb:
...zwei Möglichkeiten: die "Größe" (= Zahl der Ecken) jedes Polygons zur Compilezeit festlegen -> alles mit Templates, oder zur Laufzeit -> keine Templates, dafür dynamische Speicherverwaltung.
Plus die "Frickellösungen" (die bisweilen tatsächlich optimal sein können); z.B.:
- eigene Speicherverwaltung
- "feste templates"(Ideenskizze !!!)
template <int DIM> struct Polygon { }; template <typename T> T f(T const& dummy){ ... } int main() { //... if(irgendeinKriterium) { vector<Polygon<4> > myPolys(1000); myPoly[2] = f(myPoly[0]); } else { vector<Polygon<16> > myPolys(1000); myPoly[2] = f(myPoly[0]); }Nicht unbedingt schön und auch nur für kleine konkrete Zahlen anzuraten, aber bei recht aufwendigem f() kann das schon das geringere Übel sein....
Gruß,
Simon2
-
Rock Lobster schrieb:
Ich moechte kein new im Konstruktor haben, damit die Werte moeglichst auf dem Stack abgelegt werden. Im Moment habe ich die new-Version und das ist einfach zu langsam (da extrem viele dieser Polygone instanziiert werden).
Bist du sicher, dass das am
newalleine liegt? Hat dir dein Profiler gesagt, dass die Funktionoperator newden kritischen Teil der Zeit beansprucht?Ich würde eher raten, eine eigene Speicherverwaltung einzurichten, was in diesem Thread auch schon mehrmals erwähnt wurde. Dadurch kannst du dir vielen Speicher aufs Mal anfordern und den dann nach und nach verteilen. Aber der Stack eignet sich nicht für extrem viele oder grosse Objekte.
-
Hallo Christian,
gib uns mal etwas mehr Code. Ich kann mir beim besten Willen nicht vorstellen,
dass das wirklich das Problem in deinem Programm ist. Über wieviele Objekte
innerhalb welches Zeitraums unterhalten wir uns denn?Gruß,
CSpille
-
Im Moment ist der Server down, aber im Laufe des Tages werd ich nochmal testen und kann dann auch mehr zur Datenmenge und zur Laufzeit sagen. Ich habe keinen Profiler verwendet, aber innerhalb der Schleife passiert eigentlich auch nicht wesentlich mehr als das Anlegen von Objekten, und laut System Monitor ist die CPU mehr ausgelastet als das Netzwerk. Wie gesagt, ich werd heut nochmal ein bisschen rumprobieren, dann werd ich hier mehr dazu sagen koennen.
-
Rock Lobster schrieb:
laut System Monitor ist die CPU mehr ausgelastet als das Netzwerk.
Na und? Das heißt doch nur dass dein Rechner derweil nicht unnötig Zeit verschwendet indem er auf Antworten aus dem Netz wartet sondern die Zeit zum Rechnen nutzt.
-
Nur gibt es da nicht viel zu rechnen, daher wundert es mich schon sehr stark. Er muss nur Daten lesen und in eine Struktur schreiben und diese in einen vector packen. Naja ich werds austesten sobald der Server wieder laeuft, ich hoffe mal das wird heut noch was
