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 enhjest 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 typedefs, 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
typedefs - 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.