1500 struct`s direkt in .cpp in vector schreiben
-
10500 Zeilen Code ist kein Problem. Swig hat mir für mein Projekt bisher >15000 Zeilen in eine Datei geschrieben, und das geht wunderbar prächtig.
Nur ein Unterschied: Hauptsächlich sind das Funktionen, die da generiert wurden.
Bei dir sind es 1500 structs mit je 6 strings, die alle im Speicher liegen! Mit einer DB kannst du dir Zur Laufzeit die Daten holen wie du lustig bist, ohne alles selber im Speicher zu halten. Das erledigt effizient die DB für dich.
SQLite kannst du z.B. statisch in dein Projekt linken. Und eine in-memory-table erzeugen, brauchst also keine Datei mitliefern. Den Code kannst du weiterhin generieren lassen.
-
Ja, DB scheidet eben aus...
Die structs sind ja dann über vector im speicher, so sit es auch gewollt.
Dann suche ich von einer Frage die Antworten und gebe diese dann aus.
etwas so:Frage ASK::search(string suche_frage) { Frage back = F_nofind; bool find = false; for(it = list.begin(); it != list.end(); ++it) { F_tmp = *it; if (F_tmp.frage.find(suche_frage)!=string::npos) { back = F_tmp; find = true; } if(find) break; } return back; }Jetzt hoffe ich nur das meine Suchfunktion auch einigermaßen schnell ist...
-
Gustl schrieb:
Jetzt hoffe ich nur das meine Suchfunktion auch einigermaßen schnell ist...
Ein paar Optimierungsvorschläge:
// Nimm Const-Referenz als Parameter, also const std::string& suche_frage. // Damit sparst du dir die unnötige Kopie des std::string-Objektes. // Zudem empfehle ich Rückgabetyp Frage*, Begründung folgt... Frage ASK::search(string suche_frage) { // Wie wäre es mit einem Zeiger, der standardmässig auf Null zeigt? // Wenn etwas gefunden wurde, lässt du ihn auf das entsprechende // Element zeigen, ansonsten bleibt er ein Nullzeiger. // Also Frage* back = 0; wobei back ein schlechter Name ist. // So fällt auch das hässliche Dummy-Objekt F_nofind weg. Frage back = F_nofind; // Dann brauchst du auch dieses find nicht. bool find = false; // list.end() wird grundsätzlich jede Iteration aufgerufen. // Kann zwar sein, dass das wegoptimiert wird, aber mach lieber: // for (it = list.begin(), end = list.end(); it != end; ++it) // Wo wird eigentlich it deklariert? Mach das lokal in der Schleife. for(it = list.begin(); it != list.end(); ++it) { // Zuweisung kann vermieden werden: wie gesagt durch Zeiger, // also back = &(*it) -> Adresse des Elements holen F_tmp = *it; if (F_tmp.frage.find(suche_frage)!=string::npos) { // ebenfalls Zeiger back = F_tmp; // dadurch ist das hier auch unnötig, Zeiger ist einfach Null, // wenn nichts gefunden wurde. find = true; } // Die Schleife kannst du gleich in der oberen If-Abfrage verlassen. if(find) break; } // Hier Zeiger zurückgeben. return back; }
-
Danke, habe die Funktion verbessert.

Und dann habe ich noch eine list als rückgabewert eingebaut, jetzt bricht er nicht ab, sondern fügt alle gefundenen in die List ein und gibt sie zurück, vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
-
Gustl schrieb:
Danke, habe die Funktion verbessert.

Und dann habe ich noch eine list als rückgabewert eingebaut, jetzt bricht er nicht ab, sondern fügt alle gefundenen in die List ein und gibt sie zurück, vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
Zeiger auf was? Ein lokales Objekt? Nein, das lass mal lieber.

Die Rückgabe per Kopie ist schon in Ordnung, da hier unter Umständen eh so optimiert wird, dass kein kopieren geschieht.Um sicher zu gehen, dass nichts kopiert wird, könntest du ja auch eine Referenz auf die Liste übergeben, welche du dann befüllst. Dann wird da bestimmt nichts kopiert, ist allerdings ein eher altmodischer Stil. (erinnert mich an die WinAPI ;))
-
Besser als Kopieren und exception safer (
) als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Auch mit Return value optimization wird eine Kopie bleiben.
-
brotbernd schrieb:
Besser als Kopieren und exception safer (
) als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Auch mit Return value optimization wird eine Kopie bleiben.Meinst du die Rückgabe eines Smart Pointers?
Warum meinst du, dass eine Kopier bleiben muss?
Laut dem Artikel hier:
http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspxIst das ja genau der Sinn eine solche Kopie zu vermeiden. Exceptionsafe wäre es das ganze dann ja auch.
-
Gustl schrieb:
Und dann habe ich noch eine list als rückgabewert eingebaut [...] vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
Nein, Zeiger bzw. Referenzen solltest du als Verweise auf etwas Bestehendes verwenden. Zum Beispiel auf Objekte in der Liste, die auch nach dem Funktionsaufruf erhalten bleiben. Lokale Objekte werden am Ende der Funktion zerstört, Verweise darauf sind sehr gefährlich.
Aber zeig doch sonst mal die momentane Funktion...

brotbernd schrieb:
exception safer [...] als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Wieso ist ein Smart-Pointer exceptionsicherer als ein Referenzparameter?
-
Nexus schrieb:
brotbernd schrieb:
exception safer [...] als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Wieso ist ein Smart-Pointer exceptionsicherer als ein Referenzparameter?
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

-
drakon schrieb:
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

Gerne

ref_list.clear();Ein Algorithmus, der einen Container füllt, darf wohl davon ausgehen, dass dieser zuvor leer ist. Bei der Smart-Pointer-Rückgabe hat man ja vorher auch nichts.
Aber selbst falls der Container bereits Elemente enthält, gibts immer noch den Swap-Trick. Dabei ist ein Vergleich mit der Smart-Pointer-Lösung aber abwegig.
std::list<T> new_list(ref_list); // kann fehlschlagen // hier weiter füllen; kann auch fehlschlagen std::swap(new_list, ref_list);Oder man merkt sich, wieviele Elemente bereits vorher drin waren und löscht den Rest.
-
Nexus schrieb:
drakon schrieb:
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

Gerne

ref_list.clear();Ein Algorithmus, der einen Container füllt, darf wohl davon ausgehen, dass dieser zuvor leer ist. Bei der Smart-Pointer-Rückgabe hat man ja vorher auch nichts.
Hmm. Muss imo nicht unbedingt sein.. Aber das kommt wohl auf das Anwendungsbeispiel an.
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
-
@Nexus
Gut, du hast recht, so pauschal kann man das nicht behaupten. Was wie sicher ist (oder überhaupt sicher sein muss) hängt natürlich von der Funktion und evtl. side effects ab.
Allerdings gefällt es mir nicht so, wenn eine Funktion Annahmen über ihre Benutzung treffen muss. Wenn sich eine falsche Bedienung vermeiden lässt, dann mach ich das auch. Sonst wundert man sich 2Jahre später vielleicht wo die Hälfte seiner Liste hingeschwappt ist.
-
drakon schrieb:
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Klar, die Funktion kann schlechter zu bedienen sein. Dafür ist sie flexibler, weil sie einen nicht zur Heap-Allokation zwingt. Aber wie du sagst, hängt es von den gegebenen Umständen ab.
drakon schrieb:
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
Hat man nur, wenn man die vorherigen Elemente bewahren will (nicht einmal da zwingend). Das war eigentlich nicht die Voraussetzung, weil die Smart-Pointer-Rückgabe diese Funktionalität auch nicht erfüllt.
brotbernd schrieb:
Allerdings gefällt es mir nicht so, wenn eine Funktion Annahmen über ihre Benutzung treffen muss. Wenn sich eine falsche Bedienung vermeiden lässt, dann mach ich das auch.
Preconditions festzulegen ist Aufgabe vieler Funktionen. Gegebenenfalls werden Fehler zur Laufzeit abgefangen (z.B. Exceptions oder je nach Fall Assertions), das muss jedoch nicht sein. Beispiele in C sind nahezu alle Stringverarbeitungsfunktionen, denen du keinen Nullzeiger übergeben darfst. Ein Beispiel in C++ ist die STL, die sich für gewisse Operationen auf Strict Weak Ordering verlässt (du würdest noch viel mehr finden, ist mir nur gerade eingefallen).
Aber mit der falschen Bedienung hast du völlig Recht, das handhabe ich meist auch so. Oft kann man alleine durch die Schnittstelle schon recht viel aussagen. Allerdings ziehe ich irgendwo auch die Grenzen und setze auf gesunden Menschenverstand, schliesslich darf man auch die Dokumentation lesen.

Hier ist das wohl so ein Streitfall. Ich bin zwar der Meinung, dass bei
// Durchsucht ... und speichert die Ergebnisse in results void FindXYZ(std::list<std::string>& results);klar ist, dass
resultslediglich die Suchresultate hält...Naja, mit C++0x und Move-Semantik ist das Thema eh vom Tisch.
-
Nexus schrieb:
drakon schrieb:
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Klar, die Funktion kann schlechter zu bedienen sein. Dafür ist sie flexibler, weil sie einen nicht zur Heap-Allokation zwingt. Aber wie du sagst, hängt es von den gegebenen Umständen ab.
drakon schrieb:
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
Hat man nur, wenn man die vorherigen Elemente bewahren will (nicht einmal da zwingend). Das war eigentlich nicht die Voraussetzung, weil die Smart-Pointer-Rückgabe diese Funktionalität auch nicht erfüllt.
Also ich bin davon ausgegangen, dass an die Funktion eine Liste (nicht unbedingt leer) übergeben wird. Und dann kann man da nicht einfach auf leer setzen. Wenn man also die Einträge sicher haben will, kommt man auf eine Teilweise Kopier nicht herum.
Wenn man andere Anforderungen hat und kann man das ganze natürlich noch weiter optimieren. Aber ich habe gemeint, dass man die Liste dann einfach per Kopie zurückgibt, also auch ohne eine Heap Allokation und Smart Pointer.Also ganz einfach per Kopie zurückgeben und auf RVA hoffen, wie ich immer noch der Meinung bin, dass es hier ziehen müsste.

-
Nexus schrieb:
Gustl schrieb:
Und dann habe ich noch eine list als rückgabewert eingebaut [...] vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
Nein, Zeiger bzw. Referenzen solltest du als Verweise auf etwas Bestehendes verwenden. Zum Beispiel auf Objekte in der Liste, die auch nach dem Funktionsaufruf erhalten bleiben. Lokale Objekte werden am Ende der Funktion zerstört, Verweise darauf sind sehr gefährlich.
Aber zeig doch sonst mal die momentane Funktion...

Hallo,
ja, ich würde dann halt private die liste in der Klasse erstellen und darauf einen pointer zurückgeben.
die momentane Funktion schaut jetzt so aus:
vector<Frage> ASK::search(string &suche_frage) { list_answers.clear(); vector<Frage>::iterator it; for (it = list.begin(), end = list.end(); it != end; ++it) { if((*it).frage.find(suche_frage)!=string::npos) list_answers.push_back(*it); } if(list_answers.empty()) list_answers.push_back(F_nofind); return list_answers; }Frage:
warum soll ich die Schleife so schreiben:for (it = list.begin(), end = list.end(); it != end; ++it)anstatt so:
for(it = list.begin(); it != list.end(); ++it)?
Und warum soll ich den iterator nicht in der klasse selbst definieren, das geschieht ja dann jedes mal bei funktionsaufruf...?Zu der Diskussion SmartPointer kann ich leider nichts sagen, weil ich nicht weiß was das ist!?

Danke
Gruß
GustlEdit:
P.S.: Ich habe die Funktion mit einer Geschwindigkeitsmessfunktion im builder gemessen und bin gerade mal auf 0,0004 sec. gekommen.
-
Ich finde die Funktion gut und recht verständlich, bis auf ein paar Details:
vector<Frage> ASK::search(const string &suche_frage) //const damit der User weiss, dass da sicher nichts verändert wird { list_answers.clear(); vector<Frage>::iterator it; for (it = list.begin(), end = list.end(); it != end; ++it) { if((*it).frage.find(suche_frage)!=string::npos) list_answers.push_back(*it); } if(list_answers.empty()) list_answers.push_back(F_nofind); // das verstehe ich nicht ganz. Wenn die Liste leer ist, heisst das doch, // dass nichts gefunden worden ist, oder? Und wie ist F_nofind definiert? // Könnte es nicht durch einen blöden Zufall genau dieser Wert in die Liste gepusht werden? return list_answers; }Das end machst du dort, damit es nicht in jedem Schleifendurchlauf neu evaluiert werden muss.
Über Smart Pointer findest du sehr viel, wenn du mal danach googelst. Grundsätzlich geht es darum die Ownership von Dynamischen Ressourcen zu verwalten. (Wer ist für das Aufräumen verantwortlich, wann wird kopiert und wann nur auf den gleichen Bereich referenziert usw.)
-
Ok, jetzt verstehe ich das mit dem end. thx
im no_find steht fest als ant1 = "FRAGE NICHT GEFUNDEN"
da er ja woanderst dann die gefunden Antworten ausgibt.
-
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
-
drakon schrieb:
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
find ich allg. nicht.. ich würd ne const-ref zurückgeben(man will ja nix in den antworten rumschreiben und es scheint ein element der klasse zu sein)
und in der fkt würd ich nicht auf empty prüfen sondern halt außerhalb... ist nicht schwerer aber wesentlich übersichtlicher...
bb
-
unskilled schrieb:
drakon schrieb:
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
find ich allg. nicht.. ich würd ne const-ref zurückgeben(man will ja nix in den antworten rumschreiben und es scheint ein element der klasse zu sein)
Das verstehe ich nicht ganz. Was willst du wo zurückgeben?
und in der fkt würd ich nicht auf empty prüfen sondern halt außerhalb... ist nicht schwerer aber wesentlich übersichtlicher...
Darauf wollte ich hinaus und würde es wahrscheinlich auch eher so lösen, aber ich denke mir, dass er da so etwas, wie
output_answers()hat und dann wird halt, wenn nix gefunden wird der Text ausgegeben, ohne auf noch irgendetwas zu prüfen oder so. Ist natürlich nicht sehr sauber, wenn man dann z.B im Code mal prüfen möchte, ob jetzt was gefunden worden ist, oder nicht. Dann muss man das erste Element der Liste nehmen und da mittels String Vergleich etwas überprüfen. Finde ich sehr unschön. Aber naja.. Wenn das andere genau seine Anforderungen erfüllt, warum nicht?
Das ganze schöner zu machen ist nicht direkt schwerer, oder komplizierter, aber sicher ein wenig mehr Aufwand, den man nicht ubedingt haben will, wenn man nur ein paar Anforderungen zu erfüllen hat.