Estoy evaluando una biblioteca cuya API pública actualmente se ve así:
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);
Tenga en cuenta que enh
es un identificador genérico, que se utiliza como un identificador para varios tipos de datos diferentes ( motores y ganchos ).
Internamente, la mayoría de estas API, por supuesto, arrojan el "identificador" a una estructura interna que han hecho malloc
:
motor.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); }
Personalmente, odio esconder cosas detrás de typedef
s, especialmente cuando compromete la seguridad del tipo. (Dado un enh
, ¿cómo sé a qué se refiere realmente?)
Así que envié una solicitud de extracción, sugiriendo el siguiente cambio de API (después de modificar toda la biblioteca para que se ajuste):
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);
Por supuesto, esto hace que las implementaciones internas de la API se vean mucho mejor, eliminando los moldes y manteniendo la seguridad del tipo desde / para la perspectiva del consumidor.
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);
}
Prefiero esto por las siguientes razones:
- Se agregó seguridad de tipo
- Mayor claridad de los tipos y su finalidad.
- Se eliminaron los moldes y
typedef
s - Sigue el patrón recomendado para tipos opacos en C
Sin embargo, el propietario del proyecto rechazó la solicitud de extracción (parafraseado):
Personalmente no me gusta la idea de exponer el
struct engine
. Sigo pensando que la forma actual es más limpia y amigable.Inicialmente, utilicé otro tipo de datos para el identificador de enlace, pero luego decidí cambiar para usar
enh
, por lo que todo tipo de identificadores comparten el mismo tipo de datos para simplificarlo. Si esto es confuso, ciertamente podemos usar otro tipo de datos.Veamos qué piensan los demás sobre este PR.
Esta biblioteca se encuentra actualmente en una etapa beta privada, por lo que no hay mucho código de consumidor de qué preocuparse (todavía). Además, he ofuscado un poco los nombres.
¿Cómo es mejor un mango opaco que una estructura opaca con nombre?
Nota: Hice esta pregunta en Code Review , donde se cerró.