String einen substring über mehrere Zeilen


  • Mod

    kritticker schrieb:

    SeppJ schrieb:

    Das ist Absicht. Wieso sollten keine leeren Bezeichner erlaubt sein?

    Falls es wirklich gewollt ist, sollte die Sektion "[]" von der globalen Sektion verschieden sein. Wüsste aber ehrlich gesagt nicht, weshalb leere Sections und/oder Keys erlaubt sein sollten (values schon, aber das unterstütze ich ja).

    Doch, das war ganz bewusst Absicht. Wenn dies anders gewünscht wird, dann kann man dies trivial einbauen, da ohnehin bekannt ist, ob man gerade in einer Section oder im globalen Bereich ist. Also ich fand diese Semantik gut. Da das Format nicht genau vorgegeben war, kann der Programmierer tun, was er für richtig hält.

    Sone will hier Geschwindigkeit machen. Wen interessiert Geschwindigkeit bei einem ini-Parser? Mich nicht.

    Für diese Einstellung ist mir dein Code viel zu low-level.

    Es gibt aber keine Methoden auf höherem Level in C++03.

    throw std::logic_error("Error parsing ini-file: Entry without a '='");
    

    Exceptions -- lol? Ich zitiere mal die Doku:

    <a href= schrieb:

    std::logic_error ">It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.

    Und?

    Es ist ganz sicher kein Fehler der Programmlogik, wenn das Ini-File fehlerhaft ist.

    Und?

    Oh, super. #include<iniparserlibrary> hätte ich wohl auch gekonnt und wäre noch kürzer. 🙄

    <iniparserlibrary> wird es wohl nie in absehbarer Zeit in die Standardlibrary schaffen. Boost.Regex hat das schon (ich warte nur noch auf Unterstützung)

    Der Code ist C++03 und es geht darum zu zeigen, wie man so etwas macht. Fertige Bibliotheken gibt es für das Problem haufenweise.



  • @kritticker
    Nette Lösung gegen den Bloat der hier, wie leider üblich, sonst gepostet wurde.



  • std::for_each( std::begin(child_nodes), std::end(child_nodes), static_cast<void(*)(void*)>(operator delete) );
    

    Mein persönlicher WTF des Tages. 👎 👎 👎



  • CreativeLabs schrieb:

    std::for_each( std::begin(child_nodes), std::end(child_nodes), static_cast<void(*)(void*)>(operator delete) );
    

    Mein persönlicher WTF des Tages. 👎 👎 👎

    Ja, das ist wirklich Bullshit. Nicht wahr? Sone hat, anstatt einfach
    for( auto ptr : child_nodes ) delete ptr;
    zu schreiben, alles katastrophal gemacht. Dazu auch noch missachtet, dass möglicherweise der Operator überladen wurde...

    Leutchen, das war eine unvollständige Zwischenversion (!).



  • Sone schrieb:

    Leutchen, das war eine unvollständige Zwischenversion (!).

    Will nur niemand lesen. Warum musst Du jedes deiner tollen Experimente auch hier veröffentlichen?



  • SeppJ schrieb:

    kritticker schrieb:

    Falls es wirklich gewollt ist, sollte die Sektion "[]" von der globalen Sektion verschieden sein.

    Doch, das war ganz bewusst Absicht.

    Na dann...

    Es gibt aber keine Methoden auf höherem Level in C++03.

    Seit wann darf hier nur Code in C++03, der ausschliesslich die Standardbibliothek benutzt gepostet werden? Boost steht explizit in der Forenbeschreibung, wenn es mit der reinen C++03 Standardbibliothek nicht vernünftig geht.

    Es ist ganz sicher kein Fehler der Programmlogik, wenn das Ini-File fehlerhaft ist.

    Und?

    Du verwendest die Klasse völlig falsch. Selbst der C++-Standard hält die Nutzung hier für unangebracht.

    The class logic_error defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants.

    Du könntest genausogut throw (std::valarray*)0; schreiben.

    Der Code ist C++03 und es geht darum zu zeigen, wie man so etwas macht. Fertige Bibliotheken gibt es für das Problem haufenweise.

    "Machen" würde es so niemand.
    Der OP war weder daran interessiert, noch wird er es verstehen (rtrim???)
    Die Beschränkung auf C++03 ist willkürlich zumal der OP C++11 verwendet (std::string-Konstruktor für fstream)
    Stilistisch ist das falscher (logic_error) und hässlicher (8 Einrückungsebenen) Code.

    Naja, vielleicht hat der Code irgendeinen einen Mehrwert (ich seh ich ehrlich gesagt nicht). Aber der std::logic_error muss korrigiert werden.


  • Mod

    kritticker schrieb:

    Aber der std::logic_error muss korrigiert werden.

    Nein. Wenn du das nicht verstehst, dann tust du mir leid. Aber von dem sonstigen Unsinn her nehme ich mal an, du willst bloß absichtlich rumscheißern.



  • SeppJ schrieb:

    kritticker schrieb:

    Aber der std::logic_error muss korrigiert werden.

    Nein. Wenn du das nicht verstehst, dann tust du mir leid.

    Wie soll ich dich verstehen, wenn du nur "Und?" und "Nein." schreibst? Ich begründe wenigstens.


  • Mod

    Ich bin kein Sklave irgendeiner Beschreibung. logic_error hat vom Namen her einen passenden Flavour. Ich nehme ihn. Es ist ein Fehler in der Logik der Datei aufgetreten. Mir doch egal, dass es nicht durch einen internen Fehler geschehen ist (natürlich passiert so etwas nicht durch interne Fehler, meine Programme sind schließlich richtig). In der Doku schreibe ich, dass das Ding einen logic_error schmeißt, wenn die Operation aufgrund eines Fehlers im logischen Aufbau der Datei fehlschlägt. Das allgemeine ios::failure kann diesen Flavour nicht ausdrücken.



  • @kritticker, SeppJ: Ihr solltet wirklich über die Sache streiten, nicht über Euch. Ich habe das Gefühl, dass Ihr einfach eine andere Sichtweise darüber habt, wie ein Fehler aus einer Ini-Datei (falsches Format etc.) zu klassifizieren ist.

    kritticker sieht " logic_error "s explizit als solche an, die aus der Programmlogik kommen; die Ini-Datei dagegen sieht er auf der Ebene von Benutzereingaben, daher kann es (da das Programm selbst perfekt ist) per Definition von "logic_error" (errors presumably detectable before the program executes) kein solcher sein.

    Prinzipiell sehe ich das ähnlich, würde in diesem Fall eher ein " runtime_error " verwenden oder eine eigene Klasse, davon abgeleitet, sowas in der Art "user_input_error" oder "bad_config_error"...



  • An std::logic_error ist technisch nichts auszusetzen, wer für sich allein programmiert, soll den ruhig verwenden, wie er will.

    Sofern allerdings jemand anders den Code anschaut, sollte man sich an gewisse allgemein anerkannte Konventionen halten. Was im Standard steht ist allgemein anerkannt. Wenn der Standard unsinnig ist, soll es nicht verwendet werden.

    SeppJ schrieb:

    Mir doch egal, dass es nicht durch einen internen Fehler geschehen ist (natürlich passiert so etwas nicht durch interne Fehler, meine Programme sind schließlich richtig).

    Es ist nur so, dass jeder andere Programmierer logic_error als internen Fehler ansieht. Die Klasse umzudeuten halte ich für nicht so klug. Wenn dir "logischer Fehler" gefällt, leite dir halt eine Klasse logic_input_error von std::runtime_error ab.

    Ich persönlich meine, dass ios::failure genau dieses Flavour (fehlerhafter User-Input) ausdrückt. Die genaue Fehlernachricht steht für den, der sie wissen möchte in einem separaten Parameter.



  • Noch ein paar allgemeine Fragen:

    @ Helppls1234: Wie fix ist denn Dein Dateiformat? Das Ini-Format, was ich so kenne, braucht eigentlich keine schließenden Tags. Es enthält "Sections" (das in den eckigen Klammern) und dahinter Key-Value-Paare, bis zur nächsten Section oder Dateiende. Deine Datei wäre dann z.B.:

    `[Schurke]

    Str=12

    Dex=17

    Int=4

    [Krieger]

    Str=18

    Dex=10

    Int=2`

    Das würde den Parser etwas vereinfachen...

    @ SeppJ: Warum gibst Du bei den get -Methoden nicht einfach den Wert zurück? Schon klar, Template-Typ muss in die Signatur, aber irgendwie fühlt es sich komisch an, bei einem get() den Wert als Referenz zu übergeben und die Möglichkeit eines Rückgabewertes durch void zu verschenken.

    Bei einer Ini-Datei würde ich aus dem Bauch raus die Values entweder fix als string zurückgeben (und die Interpretation nach int oder double später; der Parser soll ja den Typ der einzelnen Values gar nicht kennen müssen), oder gerne die Templates so wie sie sind und dann speziell einen anderen Typ anfordern.

    Also statt:

    int a;
    sip.get_verbose(std::cout, "Krieger", "Str", a);
    

    etwa so:

    int a = sip.get_verbose<int>(std::cout, "Krieger", "Str");
    

    @Sone: Warum nicht einfach ein vector aus shared_ptr ? Bei Containern aus puren Pointern (und den dazugehörigen selbstgeschriebenen Lösch-Schleifen) krieg ich inzwischen Gänsehaut). Also statt

    std::vector<Node*> child_nodes; // Darf *nicht* verändert werden - Inhalt wird gelöscht
    
        ~Node()
        {
            std::for_each( std::begin(child_nodes), std::end(child_nodes), static_cast<void(*)(void*)>(operator delete) );
        }
    
    std::vector<std::shared_ptr<Node>> child_nodes; // Darf *nicht* verändert werden - Inhalt wird gelöscht
    


  • @SeppJ

    isoiec14882 19.1.1 - 1 schrieb:

    The class logic_error defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants.

    Für mich bedeutet das: man muss ein Programm so schreiben können dass es keine logic_error wirft.
    Im Falle von fehlerhaften Input-Files würde das bedeuten dass man das File erstmal selbst verifizieren müsste bevor man es dem Parser füttern darf.
    Was sinnlos ist.

    => Der Parser sollte keine logic_error werfen.

    Ich kenne übrigens auch keinen Programmierer der das anders interpretiert.


  • Mod

    minastaros schrieb:

    @ SeppJ: Warum gibst Du bei den get -Methoden nicht einfach den Wert zurück? Schon klar, Template-Typ muss in die Signatur, aber irgendwie fühlt es sich komisch an, bei einem get() den Wert als Referenz zu übergeben und die Möglichkeit eines Rückgabewertes durch void zu verschenken.

    Damit man den Typ nicht angeben braucht, sondern die automatische Templateparametererkennung nutzen kann.



  • SeppJ schrieb:

    minastaros schrieb:

    @ SeppJ: Warum gibst Du bei den get -Methoden nicht einfach den Wert zurück? Schon klar, Template-Typ muss in die Signatur, aber irgendwie fühlt es sich komisch an, bei einem get() den Wert als Referenz zu übergeben und die Möglichkeit eines Rückgabewertes durch void zu verschenken.

    Damit man den Typ nicht angeben braucht, sondern die automatische Templateparametererkennung nutzen kann.

    Einerseits das, andererseits funktioniert das auch im pathologischen Fall, wenn der Typ keinen Default-Konstruktor hat. Dieses get() ist richtig.

    Was ich allerdings nicht ok finde, ist die Schwesterfunktion

    template<typename T> void get(std::string const& category, std::string const& name,
                                    T &entry, const T& default_value) const
      {
        try
          {
            get(category, name, entry);
          }
        catch (std::logic_error)
          {
            entry = default_value;
          }
      }
    

    Manche (mich eingeschlossen) würden diese Nutzung von Exceptions als "premature Pessimization" beschreiben.

    Dazu kommt:
    - "Throw by value, catch by reference" -- hier nicht schlimm, aber grundsätzlich schlechter Stil
    - Bei normalen Ini-Dateien gilt: Fehler ausgeben bei fehlerhaftem Wert, Defaultwert bei fehlender Angabe, sonst der angegebene Wert. Für dieses get() wird "kein Wert" und "falscher Wert" gleich behandelt. Womöglich wieder eine bewusste Entscheidung!?

    hustbaer schrieb:

    => Der Parser sollte keine logic_error werfen.

    Schön, dass ich nicht der einzige bin, der das so sieht.


  • Mod

    kritticker schrieb:

    Womöglich wieder eine bewusste Entscheidung!?

    Nein, in allen drei Fällen Faulheit. Eine Defaultwertfunktion war lauf Aufgabenstellung verlangt. Durch Mitnutzung der normalen get-Funktion, konnte ich mir so die Codewiederholung sparen. Das get ist nicht Kernteil dessen gewesen, was ich vorführen wollte.



  • Und der Code-Blah ist einfach überwältigend.

    Supi! Sollte es auch sein. Ein möchtegern-Perfektionist wie ich...

    Hier der optimierte Code.
    http://ideone.com/lNJGDO


  • Mod

    Wow, mein Rechner setzt erst einmal eine Sekunde aus, wenn er den Tab mit dem Quelltext lädt 😮 .



  • SeppJ schrieb:

    Wow, mein Rechner setzt erst einmal eine Sekunde aus, wenn er den Tab mit dem Quelltext lädt 😮 .

    Langsamer Rechner, nicht wahr?

    Hier die Geschwindigkeiten, die ich gemessen habe:

    Kritticker: 3547000 
    Sone:       1210000
    SeppJ:      710000
    

    Verdammt, da hat der SeppJ aber Schwein. Nur weil ich so viel drumherum habe.


  • Mod

    Das ist kein Glück. Was erwartet ihr denn von solch fetten Codes? Gerade die Regex von Kritticker sind Overkill³. Bei dir kann man wenigstens sagen, dass dein Code mehr kann.


Anmelden zum Antworten