Oceniam bibliotekę, której publiczny interfejs API wygląda obecnie tak:
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);
Zauważ, że enh
jest to uchwyt ogólny, używany jako uchwyt kilku różnych typów danych ( silników i haków ).
Wewnętrznie większość z tych API oczywiście przekazuje „uchwyt” do wewnętrznej struktury, którą malloc
:
silnik. 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); }
Osobiście nie lubię chować rzeczy za typedef
s, szczególnie gdy zagraża to bezpieczeństwu typu. (Biorąc pod uwagę enh
, skąd mam wiedzieć, co to właściwie dotyczy?)
Wysłałem więc żądanie ściągnięcia, sugerując następującą zmianę interfejsu API (po zmodyfikowaniu całej biblioteki, aby była zgodna):
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);
Oczywiście sprawia to, że wewnętrzne implementacje API wyglądają znacznie lepiej, eliminując rzutowania i zachowując bezpieczeństwo typu do / z perspektywy konsumenta.
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);
}
Wolę to z następujących powodów:
- Dodano bezpieczeństwo typu
- Poprawiona przejrzystość rodzajów i ich przeznaczenia
- Usunięto obsady i
typedef
s - Jest zgodny z zalecanym wzorem dla typów nieprzezroczystych w C.
Jednak właściciel projektu bał się na żądanie ściągnięcia (sparafrazował):
Osobiście nie podoba mi się pomysł ujawnienia
struct engine
. Nadal uważam, że obecny sposób jest czystszy i bardziej przyjazny.Początkowo użyłem innego typu danych dla uchwytu haka, ale potem zdecydowałem się przełączyć na użycie
enh
, więc wszystkie rodzaje uchwytów mają ten sam typ danych, aby było to proste. Jeśli jest to mylące, z pewnością możemy użyć innego typu danych.Zobaczmy, co inni myślą o tym PR.
Ta biblioteka jest obecnie w fazie prywatnej wersji beta, więc nie ma zbyt wiele kodu konsumenckiego (jeszcze). Też trochę zaciemniłem nazwy.
W jaki sposób nieprzejrzysty uchwyt jest lepszy od nazwanej, nieprzezroczystej struktury?
Uwaga: zadałem to pytanie w Code Review , gdzie zostało zamknięte.