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 adressierten ROM-Speicher sind. Wenn Sie 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) wird nicht unterstützt, wird der Linker Nein sagen).
Nachdem gesagt, würde ich immer für Alternative 1 gehen. 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 Speichermanagement-API verwenden, um Lecks, Diagnoseprotokollierung usw. zu verfolgen. Unterstützung für solche Strategien wird allgemein geschätzt.
Fehler Kommunikation ist eines der wichtigsten Dinge im 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:
typedef enum {
RESULT_ONE,
RESULT_TWO
} RESULT;
.. weil es Typ/Zuweisung Sicherheit bietet.
Mit einem get-last-Fehler Funktion ist auch schön (erfordert zentralen Speicher jedoch), ich persönlich benutze es nur für zusätzliche Informationen über einen bereits erkannten Fehler.
Die Ausführlichkeit alternativer 1 kann durch einfache Verbindungen wie folgt begrenzt:
struct Buffer
{
unsigned long size;
char* data;
};
Dann könnte Ihr api besser aussehen:
ERROR_CODE func(params... , Buffer* outBuffer);
auch diese Strategie für aufwändigere öffnet Mechanismen. Sagen Sie zum Beispiel, MÜSSEN Sie in der Lage sein, Speicher für den Benutzer zuordnen (zB wenn Sie den Puffer, um die Größe müssen), dann können Sie einen indirekten Ansatz dazu bieten:
struct Buffer
{
unsigned long size;
char* data;
void* (*allocator_callback)(unsigned long size);
void (*free_callback)(void* p);
};
Ofcourse, den Stil solcher Konstrukte ist immer offen für eine ernsthafte Debatte.
Viel Glück!
Sollte der Typ des Puffers nicht char ** sein? Warum brauchen Sie auch eine Puffergröße in Option eins und nicht in Option zwei? – mweerden
Er übergibt einen vorbelegten Puffer als Parameter in und erwartet von der aufgerufenen Funktion, dass er mit Fehlertext gefüllt wird. – sharptooth
Ok, aber dann muss buffer_size kein Zeiger sein, oder? – mweerden