Conventions de nommage des bibliothèques C

introduction

Bonjour les gars, j’ai récemment appris à programmer en C! (C’était un grand pas pour moi, puisque le C ++ était la première langue avec laquelle j’ai été en contact et qui m’a effrayé pendant près de 10 ans.) Venant d’un milieu essentiellement OO (Java + C #), c’était un très beau changement de paradigme.

J’adore C. C’est une si belle langue. Ce qui m’a le plus surpris, c’est le haut degré de modularité et de réutilisation du code que C prend en charge – bien sûr, il n’est pas aussi élevé que dans un langage OO, mais dépasse de loin mes attentes pour un langage impératif.

Question

Comment éviter les conflits de noms entre le code client et le code de ma bibliothèque C? En Java, il existe des packages, en C #, des espaces de noms. Imaginez que j’écris une bibliothèque C, qui offre l’opération “add”. Il est très probable que le client utilise déjà une opération appelée comme ça – que dois-je faire?

Je cherche surtout une solution conviviale pour le client. Par exemple, je ne voudrais pas du tout préfixer toutes mes opérations api comme “myuniquelibname_add”. Quelles sont les solutions communes à cela dans le monde C? Mettez-vous toutes les opérations api dans une structure, afin que le client puisse choisir son propre préfixe?

Je suis très impatient de connaître vos idées à travers vos réponses!

EDIT (question modifiée)

Chers répondeurs, merci pour vos réponses! Je vois maintenant que les préfixes sont le seul moyen d’éviter en toute sécurité les conflits de noms. Je voudrais donc modifier ma question: quelles possibilités ai-je de laisser le client choisir son propre préfixe?

La réponse Détendez-vous posté, est un moyen. Il n’utilise pas de préfixes dans le sens normal, mais il faut préfixer chaque appel d’api par “api->”. Quelles autres solutions y a-t-il (comme utiliser un #define par exemple)?

EDIT 2 (mise à jour de statut)

Tout se résume à l’une des deux approches suivantes:

  • Utiliser une structure
  • Utiliser #define (note: il y a plusieurs façons d’utiliser #define pour atteindre ce que je désire)

Je n’accepterai aucune réponse, car je pense qu’il n’y a pas de réponse correcte. La solution que l’on choisit dépend plutôt du cas particulier et de ses propres préférences. Moi-même, je vais essayer toutes les approches que vous avez mentionnées pour déterminer laquelle me convient le mieux dans quelle situation. N’hésitez pas à poster des arguments pour ou contre certaines approches dans les commentaires des réponses correspondantes.

Enfin, je voudrais remercier tout particulièrement:

  • Unwind – pour sa réponse sophistiquée incluant une implémentation complète de la “struct-method”
  • Christoph – pour sa bonne réponse et m’indiquant les espaces de noms en C
  • Tous les autres – pour votre grande consortingbution

Si quelqu’un trouve qu’il est approprié de clore cette question (aucune autre idée à attendre), il / elle devrait se sentir libre de le faire – je ne peux pas en décider, car je ne suis pas un gourou du C.

    Je ne suis pas un gourou du C, mais dans les bibliothèques que j’ai utilisées, il est assez courant d’utiliser un préfixe pour séparer les fonctions.

    Par exemple, SDL utilisera SDL, OpenGL utilisera gl, etc …

    La manière dont Ken mentionne la structure ressemble à ceci:

    struct MyCoolApi { int (*add)(int x, int y); }; MyCoolApi * my_cool_api_initialize(void); 

    Ensuite, les clients feraient:

     #include  #include  #include "mycoolapi.h" int main(void) { struct MyCoolApi *api; if((api = my_cool_api_initialize()) != NULL) { int sum = api->add(3, 39); printf("The cool API considers 3 + 39 to be %d\n", sum); } return EXIT_SUCCESS; } 

    Cela a toujours “des problèmes d’espace de noms”; le nom de la struct (appelé “balise de structure”) doit être unique et vous ne pouvez pas déclarer des structures nestedes qui sont utiles par elles-mêmes. Cela fonctionne bien pour la collecte de fonctions, et est une technique que vous voyez assez souvent en C.

    UPDATE: Voici à quoi la mise en œuvre pourrait ressembler, cela a été demandé dans un commentaire:

     #include "mycoolapi.h" /* Note: This does **not** pollute the global namespace, * since the function is static. */ static int add(int x, int y) { return x + y; } struct MyCoolApi * my_cool_api_initialize(void) { /* Since we don't need to do anything at initialize, * just keep a const struct ready and return it. */ static const struct MyCoolApi the_api = { add }; return &the_api; } 

    C’est dommage que le C ++ vous ait effrayé, car il a des espaces de noms pour traiter précisément ce problème. En C, vous êtes quasiment limité à l’utilisation de préfixes – vous ne pouvez certainement pas “mettre des opérations api dans une structure”.

    Edit: En réponse à votre deuxième question concernant l’autorisation des utilisateurs de spécifier leur propre préfixe, je l’éviterais comme un fléau. 99,9% des utilisateurs seront satisfaits du préfixe que vous fournirez (en supposant que ce ne soit pas trop idiot) et seront très désemparés face aux cerceaux (macros, structures, etc.) qu’ils devront parcourir pour satisfaire les 0,1% restants.

    En tant qu’utilisateur de bibliothèque, vous pouvez facilement définir vos propres espaces de noms raccourcis via le préprocesseur; le résultat sera un peu étrange, mais ça marche:

     #define ns(NAME) my_cool_namespace_ ## NAME 

    permet d’écrire

     ns(foo)(42) 

    au lieu de

     my_cool_namespace_foo(42) 

    En tant qu’auteur de bibliothèque, vous pouvez fournir des noms abrégés tels que décrits ici .

    Si vous suivez les conseils de unfinds et créez une structure d’API, vous devez rendre les constantes de compilation des pointeurs de fonction afin de rendre possible l’inscription inlinig, c’est-à-dire que dans votre fichier .h , utilisez le code suivant:

     // canonical name extern int my_cool_api_add(int x, int y); // API structure struct my_cool_api { int (*add)(int x, int y); }; typedef const struct my_cool_api *MyCoolApi; // define in header to make inlining possible static MyCoolApi my_cool_api_initialize(void) { static const struct my_cool_api the_api = { my_cool_api_add }; return &the_api; } 

    Malheureusement, il n’y a pas de moyen sûr d’éviter les conflits de noms en C. Puisqu’il manque d’espaces de noms, il vous rest à préfixer les noms des fonctions globales et des variables. La plupart des bibliothèques choisissent un préfixe court et “unique” ( unique entre guillemets pour des raisons évidentes) et espèrent qu’aucun conflit ne se produira.

    Une chose à noter est que la plupart du code d’une bibliothèque peut être déclaré de manière statique, ce qui signifie qu’il ne sera pas en conflit avec des fonctions similaires dans d’autres fichiers. Mais les fonctions exscopes doivent en effet être soigneusement préfixées.

    Étant donné que vous exposez des fonctions portant le même nom, le client ne peut pas inclure les fichiers d’en-tête de votre bibliothèque avec d’autres fichiers d’en-tête ayant une collision de noms. Dans ce cas, vous ajoutez le texte suivant dans le fichier d’en-tête avant le prototype de la fonction, ce qui n’aurait pas d’incidence sur l’utilisation du client.

     #define add myuniquelibname_add 

    S’il vous plaît noter que ceci est une solution rapide et devrait être la dernière option.

    Pour un exemple vraiment énorme de la méthode struct, jetez un œil au kernel Linux; 30-odd million de lignes de C dans ce style.

    Les préfixes ne sont qu’un choix au niveau C.

    Sur certaines plates-formes (qui prennent en charge des espaces de noms distincts pour les lieurs, tels que Windows, OS X et certaines unités commerciales, mais pas Linux et FreeBSD), vous pouvez résoudre les conflits en introduisant du code dans une bibliothèque et en exportant uniquement les symboles de la bibliothèque dont vous avez réellement besoin. (et par exemple aliasing dans l’importLib au cas où il y aurait des conflits dans les symboles exportés)