bool throw_exceptions = true;



  • Hallo wieder einmal,

    Im Gefühl wohl schlechtes Design / Overdesign oder ähnliches aber nach mittlerweile jahrelangen Gedankenspielen immer noch drin.

    Der einzige der das auch so macht ist std::ios::exceptions() und das wird ja schließlich viel gebraucht...

    Es handelt sich um einen umfangreichen C++ HTTP Library Wrapper, basierend auf curl.

    Schlicht benutzt, sollte es einfach (einer der verschiedenen) Exceptions werfen (connect error, http error, ...).

    Andererseits gibts für jeden Fall auch entsprechende Callbacks (Callbacks werden wenn definiert aufgerufen und dann geworfen, wenn throw_exceptions auf true).

    Weiterhin benutze ich in echten Programmen, die darauf basieren, eher die Callbacks (um Fehlermeldungen auszugeben) und setze throw_exceptions immer wieder manuell auf false, was ich mittlerweile sogar lästig finde.

    Kann das jemand nachvollziehen? Hirngespinnst? Sich für eines entscheiden (was voraussetzt, dass ich keine Exceptions benutze)? Oder hat jemand noch eine bessere Idee?



  • Dein Beitrag ist ein wenig wirr, ich bin nicht sicher was du jetzt von uns wissen willst. Also schreib ich einfach ein paar allgemeine Gedanken zu dem Thema Exceptions/Callbacks und HTTP Libraries.

    MMn. sollte eine HTTP Library Exceptions werfen für Dinge wie:

    • Speicheranforderung fehlgeschlagen
    • Initialisierung einer verwendeten Library (z.B. curl, OpenSSL) fehlgeschlagen

    Per Default sollten mMn. keine Exceptions geworfen werden für Dinge wie:

    • Hostname konnte nicht aufgelöst werden
    • Connection mit Server fehlgeschlagen
    • SSL Handshake mit Server fehlgeschlagen
    • Timeout
    • Server hat HTTP Fehlercode zurückgeliefert

    Es konfigurierbar zu machen dass in solchen Fällen Exceptions geworfen werden finde ich OK. Wenn es konfigurierbar ist, würde ich dazu tendieren dass der Default sein sollte dass in diesen Fällen keine Exception geworfen werden.

    Und auf jeden Fall sollte dann genau dokumentiert sein welche Exception Typen in welchen Fällen geworfen werden, und wie man ggf. an den genauen Fehlercode kommt (z.B. HTTP Statuscode).

    Callbacks für die verschiedenen Fehlerfällt finde ich auch durchaus OK. Beides, also Exceptions und Callbacks anzubieten -- ja, keine Ahnung, wieso nicht? Falls es Callbacks gibt, sollte dabei eines explizit erlaubt sein: eine eigene Exception aus dem Callback zu werfen, wobei diese dann sauber an den Aufrufer "propagiert" werden sollte - also ohne dass dabei irgendwelche Probleme (Leaks, inkonsistente Zustände etc.) entstehen.

    Falls das alles Dinge sind die du nicht wissen wolltest stell bitte konkrete, unmisverständliche Fragen 🙂



  • @hustbaer

    Tut mir Leid für die Verwirrung, aber doch, du hast mich nicht missverstanden 🙂

    Es handelt sich halt um eine mittlerweile etwas komplexere Lib die ich seit ein paar Jahren pflege, wollte jetzt auch keinen Roman dazu schreiben.

    @hustbaer sagte in bool throw_exceptions = true;:

    MMn. sollte eine HTTP Library Exceptions werfen für Dinge wie:

    Speicheranforderung fehlgeschlagen
    Initialisierung einer verwendeten Library (z.B. curl, OpenSSL) fehlgeschlagen

    Ist drin.

    @hustbaer sagte in bool throw_exceptions = true;:

    Per Default sollten mMn. keine Exceptions geworfen werden für Dinge wie:

    Hostname konnte nicht aufgelöst werden
    Connection mit Server fehlgeschlagen
    SSL Handshake mit Server fehlgeschlagen
    Timeout
    Server hat HTTP Fehlercode zurückgeliefert

    Dem stimme ich nicht ganz zu. Und ich weiß auch nicht ob du mich vom Gegenteil überzeugen kannst. Ich meine, wenn du beispielsweise blind eine get request abfeuerst und zB der Hostname nicht aufgelöst werden kann, dann stehst du am Ende da mit einer leeren Programmausgabe... Da habe ich lieber, du wirfst eine Exception.

    Zu letztem Punkt gebe ich dir teilweise Recht (HTTP Fehlercode), weil bei einer bestimmten Funktion schalte ich diese aus. Auch hier gibt es ein bool throw_http_errors = true; in meiner Basisklasse. Mit der Lib gibt es verschiedene Möglichkeiten Requests zu erzeugen und habe mich auch darauf fokussiert, dass sie mit den Standard Container und Stream Klassen kompatibel sind.

    Ich will Mal etwas Code zeigen. Die einfachste Variante eine get request zu machen:

    int main(){
        status_code code = get(std::cout, "https://example.net");
        std::cout << status_code_string(code) << '\n';
    }
    

    Hier schalte ich intern in der get() Funktion das werfen von HTTP Exceptions aus, weil ich es für sinnvoll halte den Status Code in diesem Fall zurückzugeben. Andere Exceptions (wie zum Beispiel connect errors) werden allerdings geworfen.

    Dann gibt es noch weitere Varianten, zum Beispiel diese:

    int main(){
        std::string source;
        get_request<std::string> get(source, "https://example.net");
        get.perform();
    }
    

    Hier werden effektiv HTTP Errors geworfen, was man wie gesagt auch ausschalten kann. Ich finde das für die Variante besser, weil das Programm bei einem Fehler direkt um die Ohren fliegt und der Programmierer nicht manuell Fallunterscheidungen muss, was er aber schließlich auch selber beeinflussen kann.

    @hustbaer sagte in bool throw_exceptions = true;:

    Es konfigurierbar zu machen dass in solchen Fällen Exceptions geworfen werden finde ich OK. Wenn es konfigurierbar ist, würde ich dazu tendieren dass der Default sein sollte dass in diesen Fällen keine Exception geworfen werden.

    Wie oben bereits erwähnt, ich weiß nicht ob du mich dazu überzeugen kannst das Ding von true auf false zu setzen... 🙂

    @hustbaer sagte in bool throw_exceptions = true;:

    Und auf jeden Fall sollte dann genau dokumentiert sein welche Exception Typen in welchen Fällen geworfen werden, und wie man ggf. an den genauen Fehlercode kommt (z.B. HTTP Statuscode).

    Meine Request Klassen haben natürlich eine status_code get_status_code() const; Methode 🙂

    @hustbaer sagte in bool throw_exceptions = true;:

    Callbacks für die verschiedenen Fehlerfällt finde ich auch durchaus OK. Beides, also Exceptions und Callbacks anzubieten -- ja, keine Ahnung, wieso nicht? Falls es Callbacks gibt, sollte dabei eines explizit erlaubt sein: eine eigene Exception aus dem Callback zu werfen

    Und hier die Antwort auf das, worauf ich eigentlich eingehen wollte 🙂 Für dich also ok Callbacks und ein- und ausschaltbare Exceptions zu benutzen. Das war mein jahrelanger Zweifel, weil ich ein bisschen Overdesign gerochen habe aber mich nie davon trennen wollte, weil ich beides irgendwie sinnvoll halte. Die Antwort wirkt beruhigend und das habe ich mir erhofft. Amen.

    @hustbaer sagte in bool throw_exceptions = true;:

    wobei diese dann sauber an den Aufrufer "propagiert" werden sollte - also ohne dass dabei irgendwelche Probleme (Leaks, inkonsistente Zustände etc.) entstehen.

    Danke für den Hinweis aber darauf habe ich auch geachtet 🙂

    @hustbaer sagte in bool throw_exceptions = true;:

    Falls das alles Dinge sind die du nicht wissen wolltest stell bitte konkrete, unmisverständliche Fragen

    Du hast zweifelnderweise verstanden worauf ich hinaus war 🙂

    Danke.



  • @Luks-Nuke
    Also je nach verwendeter API Exceptions zu werfen ... weiss nicht. Wenn es durch die Parameter oder Funktionsnamen klar erkennbar ist, wie es z.B. bei std::filesystem gemacht wurde: OK. Ist aber bei deiner API nicht so. Ich kann zumindest hier keinen roten Faden finden - also nichts an dem ich mich orientieren könnte, woraus ich ableiten kann dass get(std::cout, "https://example.net"); per Default nen Fehlercode zurückgibt aber aber get_request<std::string> get(...); get.perform(); per Default ne Exception wirft.

    Der roten Faden bei meinem Vorschlag wäre: Exception bei unerwarteten Fehlern (Speicheranforderung oder Library Initialisierung fehlgeschlagen), keine Exception bei erwarteten Fehlern (alles was mit Netzwerk/dem Server/der Reply zu tun hat). Und das für alle Funktionen/Klassen. Das ist einfach zu merken. Was als erwartete Fehler gelten und was nicht kann zentral an einer Stelle in der Doku definiert werden. Das muss man 1x lesen und es gilt dann für die ganze Library.

    Wenn der Anwender etwas anderes benötigt, kann er sich auch einfach eine kleine Hilfsfunktion schreiben die in den vom Anwender gewünschten Fällen dann eine Exception wirft. Finde ich eine schöne, saubere Lösung. Umgekehrt ist das mMn. deutlich hässlicher, also Exceptions fangen und dann in Fehlercodes zu verwandeln. Und was die Performance angeht ist es auch nicht gut.

    Eine andere Möglichkeit es sehr klar zu machen wäre: alle Funktionen die bei erwarteten Fehlern keine Exception werfen beginnen mit dem Prefix try_. Also try_get, try_perform etc. Wenn man das konsequent durchzieht, ist das IMO super einfach zu verstehen.

    Meine Request Klassen haben natürlich eine status_code get_status_code() const; Methode

    Idealerweise sollte in der Exception selbst alles drin sein was man braucht. Also zumindest der Statuscode, ggf. auch die wichtigsten Daten des Requests (z.B. URL & HTTP-method).

    Für dich also ok Callbacks und ein- und ausschaltbare Exceptions zu benutzen.

    OK schon. MMn. Overkill, aber es wäre kein Grund für mich eine Library schlecht zu reden die es so macht. Ich würde mich vielleicht ein bisschen wundern. Aber wenn der Rest der API sauber designed ist, ist das etwas womit ich leben kann. Wichtiger wäre mir was ich schon erwähnt habe: idealerweise Exceptions nur für "unerwartete" Fehler. Und ansonsten sollte zumindest anhand des Aufrufes klar sein ob bei erwarteten Fehlern Exceptions fliegen oder nicht.

    Soll heissen: ich würde vermutlich Support zum Werfen von Exceptions für erwartete Probleme weglassen. Selbst was die Callbacks angeht könnte man argumentieren dass man diese nicht braucht, bzw. zumindest keine wo es nur um das Behandeln von "endgültigen" Fehlern geht. Denn genaugenommen ist das alles etwas, was der Benutzer der Library sich einfach selbst stricken kann, und zwar genau so wie er es braucht/gerne hätte.

    Achja: natürlich dürfen all diese Dinge (Flags die Exceptions konfigurieren, Callbacks, sonstige Settings die das Verhalten steuern) nicht global/statisch sein. Es muss immer möglich sein dass zwei Programmteile die nichts voneinander wissen und unabhängig voneinander entwickelt worden sind, beide deine Library verwenden, und beide gleichzeitig im selben Programm laufen - ohne dass es dabei zu Problemen kommt.

    Falls da also irgendwas statisch/global ist, würde ich sehr die Nase rümpfen und die Library für unbrauchbar erklären.


    Drehen wir das ganze mal um: Was ist deiner Meinung nach der Benefit von Callbacks? Und was ist der Benefit davon per Default Exceptions zu werfen? Und denk dabei daran, dass es für den Benutzer der Library vermutlich kein grosses Problem ist sich selbst eine 5-Zeilen Wrapperfunktion für deine Library zu schreiben die z.B. eine Log-Meldung ausgibt und/oder eine Exception wirft wenn der Request fehlschlägt.



  • @hustbaer sagte in bool throw_exceptions = true;:

    Drehen wir das ganze mal um: Was ist deiner Meinung nach der Benefit von Callbacks?

    Die ganze Lib ist eigentlich Callback-basiert. Es gibt eigentlich für alles ein Callback, man kann so ziemlich überall Code einschleusen. Beispielsweise bei Fehlern, wenn der Status Code geparst wurde, wenn der response header geparst wurde, wenn die Response angekommen ist, usw.

    @hustbaer sagte in bool throw_exceptions = true;:

    Und was ist der Benefit davon per Default Exceptions zu werfen?

    Ich finde das etwas verbaler und wenn eine Exception fliegt sieht man auch direkt, dass irgendwo ein Fehler aufgetaucht ist. Ansonsten müsste man ja eben zwangsweise Callbacks definieren um überhaupt zu erfahren, was passiert ist. Auch wenn das nur ein paar Zeilen sind, irgendwie habe ich da ein ungutes Gefühl bei der Sache.

    @hustbaer sagte in bool throw_exceptions = true;:

    Achja: natürlich dürfen all diese Dinge (Flags die Exceptions konfigurieren, Callbacks, sonstige Settings die das Verhalten steuern) nicht global/statisch sein

    Nein, die Callbacks sind alle Member. Habe mir Mühe gemacht alles objektorientiert und gut zu verkapseln.

    Naja, ich glaube ich muss mir noch weiterhin ein paar Gedanken darüber machen... Die Lib ist ja eh eher für den persönlichen Gebrauch. Aber ja, irgendwie schwebt da immer noch dieser leichte Geruch in der Luft...

    Danke jedenfalls für die Vorschläge.



  • @Luks-Nuke sagte in bool throw_exceptions = true;:

    @hustbaer sagte in bool throw_exceptions = true;:

    Drehen wir das ganze mal um: Was ist deiner Meinung nach der Benefit von Callbacks?

    Die ganze Lib ist eigentlich Callback-basiert. Es gibt eigentlich für alles ein Callback, man kann so ziemlich überall Code einschleusen. Beispielsweise bei Fehlern, wenn der Status Code geparst wurde, wenn der response header geparst wurde, wenn die Response angekommen ist, usw.

    Ein Callback wenn z.B. der Status-Code geparsed wurde kann schon Sinn machen. Für Fälle wo man z.B. den Transfer bei bestimmten Statuscodes abbrechen möchte, weil man mit den Daten die noch kommen würden sowieso nichts anfangen könnte. Dazu muss der Callback aber natürlich auch kommen bevor die ganze Reply gelesen wurde.

    @hustbaer sagte in bool throw_exceptions = true;:

    Und was ist der Benefit davon per Default Exceptions zu werfen?

    Ich finde das etwas verbaler

    Ich verstehe nicht was "verbal(er)" in diesem Zusammenhang heissen soll.

    und wenn eine Exception fliegt sieht man auch direkt, dass irgendwo ein Fehler aufgetaucht ist. Ansonsten müsste man ja eben zwangsweise Callbacks definieren um überhaupt zu erfahren, was passiert ist.

    Nö, wieso? Man kann ja alles über diverse Status-/Fehlerwerte zurückgeben. Muss ja nicht ein einziger numerischer Code sein. Der Rückgabewert kann ja auch eine Instanz einer Klasse sein die aus mehreren Teilen besteht. Bzw. kann man auch Status + Reply im "Request" Objekt speichern (das dann vermutlich eher "Transfer" oder so heissen sollte). Die "perform" Methode müsste dann gar nichts zurückgeben, bzw. maximal nen bool der angibt ob der HTTP Transfer erfolgreich war (egal welcher Statuscode zurückgekommen ist).

    Auch wenn das nur ein paar Zeilen sind, irgendwie habe ich da ein ungutes Gefühl bei der Sache.

    Ich nicht 🙂
    Das mit den Callbacks für Dinge wie z.B. "status code parsed" kann wie oben erwähnt schon Sinn machen. Aber das Argument dass man sonst nicht rausbekommen könnte was schief gegangen ist kann ich so nicht akzeptieren 🙂

    @hustbaer sagte in bool throw_exceptions = true;:

    Achja: natürlich dürfen all diese Dinge (Flags die Exceptions konfigurieren, Callbacks, sonstige Settings die das Verhalten steuern) nicht global/statisch sein

    Nein, die Callbacks sind alle Member. Habe mir Mühe gemacht alles objektorientiert und gut zu verkapseln.

    Gut 🙂

    Naja, ich glaube ich muss mir noch weiterhin ein paar Gedanken darüber machen... Die Lib ist ja eh eher für den persönlichen Gebrauch. Aber ja, irgendwie schwebt da immer noch dieser leichte Geruch in der Luft...

    Danke jedenfalls für die Vorschläge.

    Bitte.

    Achja: Eine Möglichkeit wäre auch noch das alles in eine High-Level und eine Low-Level API zu teilen. In der Low-Level API würde ich nur Callbacks anbieten (wo sinnvoll) + Returnwerte -- also abgesehen von Sachen wie "out of memory" & Co wo man immer ne Exception wirft. Die Low-Level API darf dann auch gern etwas komplizierter zu verwenden sein.

    Darauf aufbauen kann man dann eine High-Level API für Fälle wo man nur daran interessiert ist erfolgreiche Ergebnisse zu bekommen - und alle Fehler gleich behandeln. Diese kann dann Exceptions für alles werfen was kein erfolgreicher 2xx Transfer war. Wenn man dann z.B. das Dokument das bei einem 404 mitkommt haben möchte, dann muss man halt die Low-Level API verwenden und ein paar Zeilen mehr Code schreiben.



  • @hustbaer sagte in bool throw_exceptions = true;:

    Ein Callback wenn z.B. der Status-Code geparsed wurde kann schon Sinn machen. Für Fälle wo man z.B. den Transfer bei bestimmten Statuscodes abbrechen möchte, weil man mit den Daten die noch kommen würden sowieso nichts anfangen könnte. Dazu muss der Callback aber natürlich auch kommen bevor die ganze Reply gelesen wurde.

    Exakt. Deswegen auch:

    using status_code_callback_t = std::function<bool(status_code )>;
    

    Wo bei negativen Rückgabewert der Rest nicht mehr ausgeführt wird.

    @hustbaer sagte in bool throw_exceptions = true;:

    Ich verstehe nicht was "verbal(er)" in diesem Zusammenhang heissen soll.

    Naja, sagen wir einfach Mal, der unwissende Laie sieht direkt was schief läuft...

    @hustbaer sagte in bool throw_exceptions = true;:

    Nö, wieso? Man kann ja alles über diverse Status-/Fehlerwerte zurückgeben. Muss ja nicht ein einziger numerischer Code sein. Der Rückgabewert kann ja auch eine Instanz einer Klasse sein die aus mehreren Teilen besteht. Bzw. kann man auch Status + Reply im "Request" Objekt speichern (das dann vermutlich eher "Transfer" oder so heissen sollte). Die "perform" Methode müsste dann gar nichts zurückgeben, bzw. maximal nen bool der angibt ob der HTTP Transfer erfolgreich war (egal welcher Statuscode zurückgekommen ist).

    Ich weiß nicht ob ich dich da richtig verstanden habe. Ich biete eigentlich allerlei Methoden an um an die verschiedenen States zu kommen. Auch würde ich, wenn dann, bei perform() nicht nur ein bool zurückgeben, sondern eher den status_code Enumerator. Ich biete ja schließlich auch folgende freie Funktionen an:

        inline bool is_informational(status_code code){
            return (int)code < 200;
        }
    
        inline bool is_success(status_code code){
            return (int)code > 199 && (int)code < 300;
        }
    
        inline bool is_redirection(status_code code){
            return (int)code > 299 && (int)code < 400;
        }
    
        inline bool is_client_error(status_code code){
            return (int)code > 399 && (int)code < 500;
        }
    
        inline bool is_server_error(status_code code){
            return (int)code > 499;
        }
    

    (Bitte jetzt nicht auf C-Casts abschweifen). Und dann sind andere Fehlersorten (zB connect error) immer noch unbehandelt. Und alle Fehlerklassen in eine einzige Klasse packen finde ich recht unschön.

    @hustbaer sagte in bool throw_exceptions = true;:

    Aber das Argument dass man sonst nicht rausbekommen könnte was schief gegangen ist kann ich so nicht akzeptieren

    Das Callback nimmt den status_code Enumerator entgegen. Ich glaube mehr braucht man nicht.

    @hustbaer sagte in bool throw_exceptions = true;:

    Achja: Eine Möglichkeit wäre auch noch das alles in eine High-Level und eine Low-Level API zu teilen. In der Low-Level API würde ich nur Callbacks anbieten (wo sinnvoll) + Returnwerte -- also abgesehen von Sachen wie "out of memory" & Co wo man immer ne Exception wirft. Die Low-Level API darf dann auch gern etwas komplizierter zu verwenden sein.
    Darauf aufbauen kann man dann eine High-Level API für Fälle wo man nur daran interessiert ist erfolgreiche Ergebnisse zu bekommen - und alle Fehler gleich behandeln. Diese kann dann Exceptions für alles werfen was kein erfolgreicher 2xx Transfer war. Wenn man dann z.B. das Dokument das bei einem 404 mitkommt haben möchte, dann muss man halt die Low-Level API verwenden und ein paar Zeilen mehr Code schreiben.

    Ja... Deswegen mein bool throw_exceptions; Dilemma...



  • Der Versuch einen HTTP Transfer durchzuführen kann auf viele verschiedene Arten schief gehen. Wenn du eine API willst die ohne Exceptions und ohne Callbacks sinnvoll zu verwenden ist, dann muss die API das natürlich auch berücksichtigen.

    Ich sehe zwei Möglichkeiten das zu strukturieren: "Phasen" bzw. "Operations" und "Layer".
    Phasen bzw. Operations könnten z.B. sein:

    • Name Resolution
    • Transport-Connection Herstellen (z.B. TCP connect)
    • Secure-Connection Herstellen (z.B. SSL Handshake + Certificate Check)
    • Request-Headers Senden
    • Request-Body Senden
    • Response-Headers Empfangen
    • Response-Body Empfangen

    Die Sache mit Umleitungen und "continue" bedeutet dass hier die Phasen u.U. mehrfach durchlaufen werden. Was ein wenig doof ist. Aber das Problem hast du bei Verwendung von Exceptions genau so.

    "Layer" könnten sein:

    • Name resolution
    • Transport (z.B. TCP)
    • Transport-Security (z.B. SSL)
    • Application (in deinem Fall)

    Um sehr detailiert zu beschreiben wie ein Transfer fehlgeschlagen ist könntest du z.B. folgende Werte zurückgeben:

    • Phase/Operation wo der Fehler passiert ist
    • Layer+Protokoll wo der Fehler aufgetreten ist
    • Nativer Fehlercode des Protokolls

    Also angenommen die TLS Library hat einen CRC- oder Protokollfehler gemeldet während du dabei warst die Response-Headers zu lesen, dann hättest du

    • Phase = Read Response Headers
    • Layer = Transport-Security
    • Protocol = TLS
    • Nativer Fehlercode = Code von z.B. OpenSSL

    Bzw. wenn ein Format-Fehler beim Lesen der HTTP-Resonse-Headers passiert ist:

    • Phase = Read Response Headers
    • Layer = Application
    • Protocol = HTTP
    • Nativer Fehlercode = Von dir definierter Code für "HTTP Header Format Error"

    usw.

    @hustbaer sagte in bool throw_exceptions = true;:

    Achja: Eine Möglichkeit wäre auch noch das alles in eine High-Level und eine Low-Level API zu teilen. In der Low-Level API würde ich nur Callbacks anbieten (wo sinnvoll) + Returnwerte -- also abgesehen von Sachen wie "out of memory" & Co wo man immer ne Exception wirft. Die Low-Level API darf dann auch gern etwas komplizierter zu verwenden sein.
    Darauf aufbauen kann man dann eine High-Level API für Fälle wo man nur daran interessiert ist erfolgreiche Ergebnisse zu bekommen - und alle Fehler gleich behandeln. Diese kann dann Exceptions für alles werfen was kein erfolgreicher 2xx Transfer war. Wenn man dann z.B. das Dokument das bei einem 404 mitkommt haben möchte, dann muss man halt die Low-Level API verwenden und ein paar Zeilen mehr Code schreiben.

    Ja... Deswegen mein bool throw_exceptions; Dilemma...

    Verstehe ich nicht. Was ich meine ist: du baust erstmal eine Low-Level API. Diese wirft wie beschrieben nur in Ausnahmefällen (out of memory etc.) Exceptions. Alles andere wird über Returnwerte, Werte die man danach vom Transport Objekt abfragen kann und/oder Callbacks abgehandelt.

    Auf dieser Low-Level API aufbauend machst du dann zusätzlich eine High-Level API. Diese ist einfach und wirft für alles Exceptions. Damit meine ich nicht dass du die Low-Level API jetzt irgendwie modifizierst und der auch beibringst für Dinge wie "host not found" oder 404 Exceptions zu werfen. Die Low-Level API bleibt so wie sie ist. Und zusätzlich machst du halt z.B. eine einfach Funktion perform_http_request oder http_get/http_put etc.

    Ein bool throw_exceptions; brauchst du da nirgends.



  • ps: Du kannst davon ausgehen dass die Listen oben nicht vollständig sind. z.B. sollte vermutlich irgendwo Authentication auftauchen.



  • @hustbaer

    Ok, wieder einmal, was du genau mit Phasen, Operations und Layer meinst muss ich mir erstmal ein Bild davon machen. Momentan wrappe ich einfach alle CURLE_xxx Error Codes in eine entsprechende Exception Klasse.

    Aber das mit der High- und Low-Level API glaube ich verstanden zu haben. Bei Low-Level wird halt einfach verlangt, dass die Callbacks definiert sind. Die High-Level API kann werfen. Irgendwie seh ich da Licht dieses bool throw_exceptions; los zu werden. Allerdings riecht das auch nach größerem Umdesignen, da muss ich mir wiederum Gedanken darüber machen.

    Nur so als Randnote, die Lib, so wie sie zur Zeit steht, ich komme sehr gut klar damit. Habe sie auch schon viel angewendet und sie scheint gut zu funktionieren. Vielleicht fokussiere ich mich zu sehr auf Perfektion, wollte allerdings auch ein paar Erfahrungen mit anderen Programmierern austauschen. Deshalb vielen lieben Dank für die zahlreichen Antworten, ich werde wohl noch etwas weiter tüfteln müssen.


Anmelden zum Antworten