J'évalue une bibliothèque dont l'API publique ressemble actuellement à ceci:
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);
Notez qu'il enh
s'agit d'un descripteur générique, utilisé comme descripteur pour plusieurs types de données différents ( moteurs et hooks ).
En interne, la plupart de ces API jettent bien entendu le "handle" sur une structure interne qu'elles auraient 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); }
Personnellement, je déteste cacher des choses derrière typedef
s, surtout quand cela compromet la sécurité du type. (Étant donné un enh
, comment puis-je savoir de quoi il s'agit réellement?)
J'ai donc soumis une demande d'extraction, suggérant le changement d'API suivant (après avoir modifié la bibliothèque entière pour qu'elle soit conforme):
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);
Bien sûr, cela améliore considérablement la mise en œuvre des API internes, en éliminant les transtypages et en maintenant la sécurité des types vers / du point de vue du consommateur.
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);
}
Je préfère cela pour les raisons suivantes:
- Sécurité de type supplémentaire
- Amélioration de la clarté des types et de leur objectif
- Moulages et
typedef
s supprimés - Il suit le modèle recommandé pour les types opaques en C
Cependant, le propriétaire du projet a reculé à la demande de retrait (paraphrasé):
Personnellement, je n'aime pas l'idée d'exposer le
struct engine
. Je pense toujours que la voie actuelle est plus propre et plus conviviale.Au départ, j'ai utilisé un autre type de données pour le hook hook, mais j'ai ensuite décidé de passer à l'utilisation
enh
, donc toutes sortes de poignées partagent le même type de données pour rester simple. Si cela prête à confusion, nous pouvons certainement utiliser un autre type de données.Voyons ce que les autres pensent de ce PR.
Cette bibliothèque est actuellement en phase bêta privée, il n'y a donc pas (encore) beaucoup de code consommateur. De plus, j'ai un peu obscurci les noms.
Comment une poignée opaque est-elle meilleure qu'une structure opaque nommée?
Remarque: J'ai posé cette question lors de la révision du code , où elle a été fermée.