Bibliotheken/wiederverwendbaren Code schreiben + Design / Streams vs. Strings



  • Hallo,

    Wenn man Bibliotheken/wiederverwendbaren Code schreibt, gehört es ganz oft dazu, dass man dann beispielsweise ne struct hat die sich http::response nennt und diese geparst werden muss.

    Gut, bei dem Beispiel einer HTTP-Antwort wäre wohl eher ein Stream die bessere Wahl.

    Wie siehts mit ner URL aus? Eher Streams oder eher Strings?

    Ich hab mal beides implementiert, so gut es ging, und kann mich immer noch nicht entscheiden, was denn eigentlich besser gewesen wäre.

    Ein Zwang meinerseits sagt mir irgendwie, dass es ein Stream sein sollte. Deshalb hab ich das nachimplementiert.

    Andererseits findet man URL's meistens in String-Form.

    Aber irgendwie siehts jetzt furchtbar aus, weil ich für die beiden Typen spezielle Funktionen geschrieben habe, die mir die Dinge halt parsen. Und ich seh auch sonst nie irgendwo ne Lib, die einem beides anbietet. Fällt das also unter die Kategorie Design-Fehler? Oder eher Overdesign? Ist Overdesign strikt genommen ein Designfehler? Wenn man overdesignt, damit fertig ist, und erst später merkt, dass man overdesignt hat, was tut man dann mit dem Code (außer wegwerfen)?

    Was meine Parse-Regeln angeht: Mit den Streams hab ich das ganze extrem strikt gemacht, bei den Strings ist das etwas lockerer.

    Ich hab das jetzt mal provisorisch so gemacht, dass die Strings-Funktionen nun non-member functions sind und die Stream-Methoden halt Methoden der Klasse.



  • Was ist ein Stream?



  • manni66 schrieb:

    Was ist ein Stream?

    Deine Muddi ist ein Stream.



  • Sowas würde ich niemals in C++ machen. Nicht, weil C++ das nicht könnte. Sondern weil sich bei dem schlussendlichen Code jeder verstecken geht.

    getsocial schrieb:

    Wenn man Bibliotheken/wiederverwendbaren Code schreibt, gehört es ganz oft dazu, dass man dann beispielsweise ne struct hat die sich http::response nennt und diese geparst werden muss.

    Eigentlich nö, zumindest ich nicht. Ich habe ein State-Objekt, welches mir angibt, wo bestimmte Sektionen des HTTP-Strings anfangen und wo aufhören.

    Warum? Weil Relokation. HTTP ist ein unglaublich schlecht designtes Protokoll, mit Inband-Signalling noch und nöcher. Wenn du Glück hast, erbarmt sich ein Server, "Content-Length" auszugeben. Wenn nicht, dann müssen Chunks eingelesen werden.

    Am Ende hast du dann eine Menge Reallokationen mit Relokationen deiner Speicherbereiche. Allein schon die Relokation an sich ist beschissen. Zero-Copy da reinzubekommen ist da relativ schwierig, relative Offsets ein Muss - mit absoluten Zeigern die Hölle.

    Und wenn ich dann einen HTTP-String im Speicher habe, alle Sektionen geparst habe, und ich endlich eine schlichte Struktur auf alle Daten zeigen lassen kann, dann will ich nicht unbedingt automatisch davon ausgehen müssen, dass das schon immer ein Response sein wird. Ja, klar, DU erwartest einen Response. Aber nicht alle. Automatisch zu sagen, dass bei einem HTTP-String das entweder ein Response ist, oder wir haben halt verloren, ist doof, die Entscheidung soll der Programmierer außerhalb deiner Bibliothek vornehmen können. Indem er bspw. das Resource-Feld prüft. Wenn das keine Länge hat, dann hast du einen Response. Ganz einfach.

    Deswegen habe ich bei mir:

    struct http_state{/*Alle Infos relevant für das Parsen*/};
    struct http_stack{/*Aufbereitete Daten für einfachen Zugriff, fast immer Zero-Copy*/};
    

    Zero-Copy ist schwierig, weil du das mit Standardallokatoren nur schwer hinbekommst. Windows kann kein VirtualReAlloc , und mremap auf Linux kann nur Seitenweise arbeiten. realloc kann nur in Ausnahmefällen seitenweise arbeiten. Gibt es einen resize -Operator für C++? Ansonsten hast du nämlich wieder malloc , memcpy , free . Intern jetzt. Weggekabselt hinter irgendwelchen STL-Containern.

    getsocial schrieb:

    Gut, bei dem Beispiel einer HTTP-Antwort wäre wohl eher ein Stream die bessere Wahl.

    Siehe oben. Das ist nicht Zero-Copy.

    getsocial schrieb:

    Wie siehts mit ner URL aus? Eher Streams oder eher Strings?

    Pointer mit Längenfeld? Nullterminierte Strings sind doof.

    getsocial schrieb:

    Aber irgendwie siehts jetzt furchtbar aus, weil ich für die beiden Typen spezielle Funktionen geschrieben habe, die mir die Dinge halt parsen.

    Genau deswegen. Pointer mit Längenfeld. Das ist noch so das, was am Besten gelesen werden kann. Hier der Anfang, da das Ende, mach mal.

    EDIT:

    Wenn ich sage "Zero-Copy ist schwierig", dann vor allem unter dem Aspekt, keine weiteren Allokationen auf dich zu nehmen. Das willst du nur dann, wenn du wirklich, wirklich, wirklich keine andere Wahl hast. Und die hast du, wenn dein Design nicht stark kaputt ist.



  • Dude, du vergisst, wir sind hier nichtsdestrotz bei C++ grad.

    Eine Reallokation - und das auch nur wenn du sie brauchst um ein schönes Ding nachher zu haben - soll man dürfen.

    Weißte, meine Klassen bauen auf CURL aus und meine Basisklasse sieht wie folgt aus:

    [cpptemplate<typename T, typename Cleaner = default_cleaner<T>, typename Writer = default_writer<T>>
    struct curl_klasse{
    T& buffer;
    ...
    [/cpp]

    Schlussendlich musst du nur noch ne kleine Spezialisation machen, um direkt in einen Standard-Container oder einen Stream zu schreiben.

    Wenn du dann unbedingt ne http::response brauchst, dann nimmst du vorher nen stringstream und danach stream >> response und voilà.

    Da hast du dann eine Reallokation, aber naja. Sieht schlussendlich wunderbar aus.

    Wie hättet ihr denn die URL geparst, außer State-Objekt?

    Eher String, eher Stream oder beides?



  • getsocial schrieb:

    Dude, du vergisst, wir sind hier nichtsdestrotz bei C++ grad.

    Wenn du das ordentlich machen willst, dann ist C++ einfach nicht das Mittel der Wahl.

    getsocial schrieb:

    Eine Reallokation - und das auch nur wenn du sie brauchst um ein schönes Ding nachher zu haben - soll man dürfen.

    Reden wir hier nicht von einer Bibliothek, wo du alles einmal sauber implementiert haben willst? Da leistest du dir dann so eine Eiterbeule? Um ein schönes Ding zu haben, was du auch so haben kannst?
    Alles klar.

    getsocial schrieb:

    Weißte, meine Klassen bauen auf CURL aus

    Toll. Die haben direkt einen ganzen Haufen Eiterbeulen, meistens in - wen überrascht es - der Speicherverwaltung. Vielleicht machst du es auch tatsächlich besser als die. Aber nicht so gut, wie du könntest.

    getsocial schrieb:

    Da hast du dann eine Reallokation, aber naja. Sieht schlussendlich wunderbar aus.

    Du hast also sonst keine Relokation (!), die eine Kopie auslöst? Beim Erstellen der HTTP-Anfrage? Beim Lesen der Daten vom Socket?

    Ich nämlich nicht. Aber ich kann auch zwischen Reallokation und Relokation unterscheiden.

    getsocial schrieb:

    Wie hättet ihr denn die URL geparst, außer State-Objekt?

    State-Objekt. Zero-Copy. Nullterminierung ist doof.



  • Ja doch ich hab schon verstanden.

    Aber das parsen eines Response Headers ist nicht kostenschwierig und darum ist es mir egal, ob ich gleich alles parse oder nur eben etwas bestimmtes.


Anmelden zum Antworten