Masquer la définition du type en C

J’ai un fichier .c (une bibliothèque de fonctions) avec une fonction et une définition comme celle-ci:

 typedef long double big; big foo(int x) { ... } 

Je veux créer une interface de cette bibliothèque, un .h . Moi aussi:

 typedef long double big; big foo(int); 

et retirez le typedef long double big; à partir du fichier .c . Mais en faisant cela, je donne la définition du type big dans mon interface, donc ce n’est pas vraiment une interface épurée. Des idées pour réparer celà?

Je sais que je pourrais le faire dans mon fichier .c :

 struct foo { long double; }; 

puis dans le fichier .h , faites:

 typedef struct foo big; big foo(int); 

Mais il semble inutile de créer une struct pour un seul champ, plus je devrais utiliser le . opérateur chaque fois que je veux lire un big .

Si le type ne va jamais devenir plus complexe qu’un long double , il ne vaut probablement pas la peine de savoir comment le cacher. Si cela doit devenir plus complexe, vous pouvez envisager d’utiliser un type opaque. Dans votre en-tête public, big.h , vous utilisez:

 #ifndef BIG_H_INCLUDED #define BIG_H_INCLUDED typedef struct big big_t; extern big_t *foo(int); ... #endif 

Toutes les fonctions prendront et big_t pointeurs dans le type big_t . C’est tout ce que vous pouvez faire avec des types incomplets comme celui-là. Notez que vos clients ne peuvent pas atsortingbuer de valeur big_t pour eux-mêmes; ils ne savent pas quelle est la taille du type. Cela signifie que vous allez probablement vous retrouver avec des fonctions telles que:

 extern big_t *big_create(void); extern void big_destroy(big_t *value); 

pour créer et détruire des valeurs big_t . Ensuite, ils pourront faire de l’arithmétique avec:

 extern big_errno_t big_add(const big_t *lhs, const big_t *rhs, big_t *result); 

Etc. Mais comme ils n’ont qu’un type opaque et incomplet, ils ne peuvent pas big_t manière fiable à l’intérieur d’une structure big_t . Mais notez que vous êtes obligé d’utiliser des pointeurs dans l’interface. Transmettre ou renvoyer des valeurs nécessite des types complets. Si le type est complet, les utilisateurs peuvent en examiner le fonctionnement interne.

Dans l’en-tête d’implémentation, bigimpl.h , vous aurez:

 #ifndef BIGIMPL_H_INCLUDED #define BIGIMPL_H_INCLUDED #include "big.h" struct big { ...whatever the details actually are... }; #endif 

Et votre code d’implémentation n’inclura que bigimpl.h , mais cela inclut big.h Le principal problème ici est de vous assurer que vous savez comment les allocations de mémoire sont gérées.

Parfois, cette technique en vaut la peine. Souvent, ce n’est pas vraiment nécessaire. Vous devrez faire l’évaluation par vous-même.

et retirez le typedef long double big; à partir du fichier .c. Mais en faisant cela, je donne la définition du type big dans mon interface, donc ce n’est pas vraiment une interface épurée.

Comment? Qui se soucie de savoir si les utilisateurs de votre code peuvent voir le typedef? Comment ça fait mal … quelque chose? Ils ont besoin d’un moyen d’utiliser votre typedef et c’est comme ça que ça se passe. Cela me semble être un problème théorique qui n’a pas d’effets pervers appréciables. Vous vous inquiétez de la mauvaise chose (s).

Mais il semble inutile de créer une structure pour un seul champ, plus je devrais utiliser le. opérateur chaque fois que je veux lire un gros.

Ouais, c’est idiot. En outre, maintenant vous avez donné la définition de votre structure! Oh non! En quoi est-ce différent d’exposer le typedef (ou tout autre type)?