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 enhs'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 typedefs, 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
typedefs 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.