Stil der C-API-Funktion

8

Ich arbeite an einer Bibliothek, die mehrere Programmierumgebung wie VB6 und FoxPro unterstützt. Ich muss bei C-Konvention bleiben, da es der kleinste gemeinsame Nenner ist. Jetzt habe ich eine Frage bezüglich des Stils.

Angenommen, die Funktion verarbeitet eine Eingabe und gibt eine Zeichenfolge zurück. Während des Prozesses kann der Fehler auftreten. Der aktuelle vorgeschlagene Stil ist dies:

%Vor%

Das Gute an diesem Stil ist, dass alles im Prototyp inklusive Fehlercode enthalten ist. Und Speicherzuweisung kann vermieden werden. Das Problem ist, dass die Funktion ziemlich ausführlich ist. Da buffer_size beliebig sein kann, ist mehr Code zur Implementierung erforderlich.

Eine andere Option ist, char * zurückzugeben und NULL zurückzugeben, um einen Fehler anzuzeigen:

%Vor%

Bei diesem Stil muss der Aufrufer den Puffer löschen. Die Speicherzuweisung ist erforderlich, damit ein Serverprogramm Speicherfragmentierungsprobleme auftreten kann.

Eine Variante der zweiten Option besteht darin, eine lokale Threadvariable zu verwenden, um den zurückgegebenen Zeiger char * zu speichern, so dass der Benutzer den Puffer nicht löschen muss.

Welchen Stil magst du? Und Grund?

    
Glitch 09.03.2009, 14:57
quelle

9 Antworten

8

Ich bin ein bisschen "beschädigte Ware", wenn es um dieses Thema geht. Ich habe ziemlich große APIs für Embedded Telecom entworfen und gewartet. Ein Kontext, in dem man nichts als selbstverständlich betrachten kann. Nicht einmal Dinge wie globale Variablen oder TLS. Manchmal zeigen sich sogar Heap-Buffer, die eigentlich adressierter ROM-Speicher sind.

Wenn Sie also nach einem "kleinsten gemeinsamen Nenner" suchen, möchten Sie vielleicht auch darüber nachdenken, welche Sprachkonstrukte in Ihrer Zielumgebung verfügbar sind (der Compiler akzeptiert wahrscheinlich alles innerhalb von Standard C, aber wenn es etwas ist) nicht unterstützt wird der Linker Nein sagen.

Nachdem ich das gesagt habe, würde ich immer die Alternative 1 wählen. Teilweise, weil (wie andere darauf hingewiesen haben), sollten Sie niemals Speicher für den Benutzer direkt reservieren (ein indirekter Ansatz wird weiter unten erklärt). Selbst wenn der Benutzer garantiert mit reinem und normalem C arbeitet, kann er zum Beispiel seine eigene angepasste Speicherverwaltungs-API verwenden, um Lecks, Diagnoseprotokollierung usw. zu verfolgen. Die Unterstützung solcher Strategien wird allgemein geschätzt.

Fehler bei der Kommunikation ist einer der wichtigsten Punkte beim Umgang mit einer API. Da der Benutzer wahrscheinlich verschiedene Möglichkeiten hat, Fehler in seinem Code zu behandeln, sollten Sie diese Kommunikation innerhalb der API so konsistent wie möglich halten. Der Benutzer sollte in der Lage sein, die Fehlerbehandlung in konsistenter Weise und mit minimalem Code an Ihre API anzupassen. Ich würde im Allgemeinen immer empfehlen, klare Aufzählungscodes oder defines / typedefs zu verwenden. Ich persönlich bevorzuge typedef: ed enums:

%Vor%

.. weil es Typ- / Zuordnungssicherheit bietet.

Eine get-last-error -Funktion ist auch nett (erfordert einen zentralen Speicher), ich persönlich benutze es nur für zusätzliche Informationen über einen bereits erkannten Fehler.

Die Ausführlichkeit von Alternative 1 kann durch einfache Verbindungen wie folgt eingeschränkt werden:

%Vor%

Dann könnte Ihre API besser aussehen:

%Vor%

Diese Strategie eröffnet auch komplexere Mechanismen. Nehmen wir beispielsweise an, Sie MÜSSEN in der Lage sein, Speicher für den Benutzer zuzuweisen (z. B. wenn Sie die Größe des Puffers ändern müssen), dann können Sie einen indirekten Ansatz dafür angeben:

%Vor%

Natürlich ist der Stil solcher Konstrukte immer offen für ernsthafte Diskussionen.

Viel Glück!

    
sharkin 09.03.2009 15:32
quelle
5

Ich würde die erste Definition bevorzugen, bei der der Puffer und seine Größe übergeben werden. Es gibt Ausnahmen, aber normalerweise erwarten Sie nicht, dass Sie nach den aufgerufenen Funktionen aufräumen müssen. Während, wenn ich Speicher zuteile und es in eine Funktion übergebe, dann weiß ich, dass ich nach mir aufräumen muss.

Die Handhabung unterschiedlich großer Puffer sollte kein Problem sein.

    
Evan Shaw 09.03.2009 15:10
quelle
2

Wenn ich zwischen den beiden gezeigten Stilen wählen muss, gehe ich jedes Mal auf die erste. Der zweite Stil gibt den Benutzern Ihrer Bibliothek etwas anderes zum Nachdenken, eine Erinnerungszuweisung, und jemand muss vergessen, die Erinnerung freizugeben.

    
Jackson 09.03.2009 15:07
quelle
2

Ein weiteres Problem mit dem zweiten Stil ist, dass die Kontexte, denen der Speicher zugewiesen ist, unterschiedlich sein können. Zum Beispiel:

%Vor%

Und das ist nur ein kleiner Teil des Problems. Man kann sagen, dass beide Seiten malloc & amp; frei, aber was ist, wenn sie verschiedene Compiler-Implementierungen verwenden? Es ist besser, dass alle Zuweisungen und Deallocations am selben Ort stattfinden. ob dies die Bibliothek ist, steht Ihnen der Client-Code frei.

    
anon 09.03.2009 15:13
quelle
1

Die zweite Variante ist sauberer.

COM IErrorInfo ist eine Implementierung des zweiten Ansatzes. Der Server ruft SetErrorInfo auf, um Details zu den Fehlern einzustellen, und gibt einen Fehlercode zurück. Der Aufrufer untersucht den Code und kann GetErrorInfo aufrufen, um die Details abzurufen. Der Aufrufer ist verantwortlich für die Freigabe der IErrorInfo, aber die Übergabe der Parameter jedes Aufrufs in der ersten Variante ist auch nicht schön.

Der Server konnte beim Start ausreichend Speicher vorgeben, so dass er sicher genug Speicher hat, um Fehlerdetails zurückzugeben.

    
sharptooth 09.03.2009 15:02
quelle
1

Wenige Dinge zum Nachdenken;

  • Die Zuweisung und Freigabe sollte im gleichen Umfang (idealerweise) erfolgen. Es ist am besten, einen vorab zugewiesenen Puffer vom Anrufer zu übergeben. Der Anrufer kann dies später sicher freigeben. Dies wirft die Frage auf - wie groß der Puffer sein sollte? Ein Ansatz, den ich in Win32 ziemlich häufig verwendet habe, ist die Übergabe von NULL als Eingabepuffer und der Parameter size sagt Ihnen, wie viel Sie benötigen.

  • Wie viele mögliche Fehlerbedingungen überwachen Sie? Die Rückgabe eines char* kann das Ausmaß der Fehlerberichterstattung einschränken.

  • Welche Vor- und Nachbedingungen möchten Sie erfüllen? Spiegelt Ihr Prototyp das wider?

  • Machen Sie eine Fehlerüberprüfung beim Anrufer oder beim Angerufenen?

Ich kann dir nicht wirklich sagen, dass einer besser ist als der andere, da ich nicht das große Bild habe. Aber ich bin mir sicher, dass diese Dinge dein Denken und die anderen Beiträge zum Nachdenken anregen können.

    
dirkgently 09.03.2009 15:19
quelle
1

Wie wäre es mit beiden Methoden? Ich stimme dem Konsens der Antworten zu, die Stil 1 gegenüber den Fallstricken von Stil 2 bevorzugen. Ich glaube, Stil 2 könnte verwendet werden, wenn alle Ihre APIs einem einheitlichen Benennungs-Idiom folgen:

%Vor%

/ D

    
thompsongunner 09.03.2009 17:24
quelle
1

Die erste Ausgabe wäre weniger fehleranfällig, wenn andere Programmierer sie benutzen.

Wenn Programmierer Speicher selbst reservieren müssen, erinnern sie sich eher daran, ihn freizugeben. Wenn eine Bibliothek ihnen Speicher zuweist, ist das eine weitere Abstraktion und kann / wird zu Komplikationen führen.

    
Steve Lazaridis 09.03.2009 15:16
quelle
0

Ich würde es ähnlich wie beim ersten Weg machen, aber nur subtil anders, nach dem Modell von snprintf und ähnlichen Funktionen:

%Vor%

Auf diese Weise können Sie, wenn Sie viele davon haben, ähnlich aussehen, und Sie können, wo immer sinnvoll, eine variable Anzahl von Argumenten verwenden.

Ich stimme den bereits genannten Gründen für die Verwendung von Version 1 und nicht von Version 2 sehr zu - Speicherprobleme treten bei Version 2 sehr viel häufiger auf.

    
Kim Reece 09.03.2009 15:40
quelle

Tags und Links