Ich bewerte eine Bibliothek, deren öffentliche API derzeit so aussieht:
libengine.h
/* Handle, used for all APIs */ typedef size_t enh; /* Create new engine instance; result returned in handle */ int en_open(int mode, enh *handle); /* Start an engine */ int en_start(enh handle); /* Add a new hook to the engine; hook handle returned in h2 */ int en_add_hook(enh handle, int hooknum, enh *h2);
Beachten Sie, dass enh
es sich um ein generisches Handle handelt, das als Handle für verschiedene Datentypen ( Engines und Hooks ) verwendet wird.
Intern wandeln die meisten dieser APIs das "Handle" natürlich in eine interne Struktur um, die sie haben malloc
:
engine.c
struct engine { // ... implementation details ... }; int en_open(int mode, *enh handle) { struct engine *en; en = malloc(sizeof(*en)); if (!en) return -1; // ...initialization... *handle = (enh)en; return 0; } int en_start(enh handle) { struct engine *en = (struct engine*)handle; return en->start(en); }
Persönlich hasse ich es, Dinge hinter typedef
s zu verstecken , besonders wenn es die Typensicherheit gefährdet. (Angenommen enh
, woher weiß ich, worauf es sich tatsächlich bezieht?)
Daher habe ich eine Pull-Anfrage gesendet, in der die folgende API-Änderung vorgeschlagen wird (nachdem die gesamte Bibliothek entsprechend geändert wurde ):
libengine.h
struct engine; /* Forward declaration */
typedef size_t hook_h; /* Still a handle, for other reasons */
/* Create new engine instance, result returned in en */
int en_open(int mode, struct engine **en);
/* Start an engine */
int en_start(struct engine *en);
/* Add a new hook to the engine; hook handle returned in hh */
int en_add_hook(struct engine *en, int hooknum, hook_h *hh);
Dadurch sehen die internen API-Implementierungen natürlich viel besser aus, es werden keine Umsetzungen vorgenommen, und die Typensicherheit wird aus Sicht des Verbrauchers gewahrt.
libengine.c
struct engine
{
// ... implementation details ...
};
int en_open(int mode, struct engine **en)
{
struct engine *_e;
_e = malloc(sizeof(*_e));
if (!_e)
return -1;
// ...initialization...
*en = _e;
return 0;
}
int en_start(struct engine *en)
{
return en->start(en);
}
Ich bevorzuge dies aus folgenden Gründen:
- Typensicherheit hinzugefügt
- Verbesserte Klarheit der Typen und ihres Zwecks
- Besetzungen und
typedef
s entfernt - Es folgt dem empfohlenen Muster für opake Typen in C
Der Eigentümer des Projekts wies jedoch die Pull-Anfrage zurück (umschrieben):
Persönlich mag ich die Idee nicht, das zu entlarven
struct engine
. Ich denke immer noch, dass der derzeitige Weg sauberer und freundlicher ist.Ursprünglich habe ich einen anderen Datentyp für das Hook-Handle verwendet, mich dann aber für die Verwendung entschieden
enh
, sodass alle Arten von Handles denselben Datentyp verwenden, um es einfach zu halten. Wenn dies verwirrend ist, können wir sicherlich einen anderen Datentyp verwenden.Mal sehen, was andere über diese PR denken.
Diese Bibliothek befindet sich derzeit in einer privaten Beta-Phase, daher gibt es (noch) nicht viel zu befürchten. Außerdem habe ich die Namen etwas verschleiert.
Wie ist ein undurchsichtiger Griff besser als eine benannte undurchsichtige Struktur?
Hinweis: Ich habe diese Frage bei Code Review gestellt , wo sie geschlossen wurde.