Projet C évitant les conflits de nommage


13

J'ai du mal à trouver des conseils pragmatiques du monde réel sur les conventions de dénomination des fonctions pour un projet de bibliothèque C de taille moyenne. Mon projet de bibliothèque est séparé en quelques modules et sous-modules avec leurs propres en-têtes, et suit vaguement un style OO (toutes les fonctions prennent une certaine structure comme premier argument, pas de globaux, etc.). Il a posé notre quelque chose comme:

MyLib
  - Foo
    - foo.h
    - foo_internal.h
    - some_foo_action.c
    - another_foo_action.c
    - Baz
      - baz.h
      - some_baz_action.c
  - Bar
    - bar.h
    - bar_internal.h
    - some_bar_action.c

Généralement, les fonctions sont beaucoup trop volumineuses pour (par exemple) rester some_foo_actionet another_foo_actiondans un foo.cfichier d'implémentation, rendre la plupart des fonctions statiques et l'appeler un jour.

Je peux gérer le retrait de mes symboles internes ("module privé") lors de la construction de la bibliothèque pour éviter les conflits pour mes utilisateurs avec leurs programmes clients, mais la question est de savoir comment nommer les symboles dans ma bibliothèque? Jusqu'à présent, je fais:

struct MyLibFoo;
void MyLibFooSomeAction(MyLibFoo *foo, ...);

struct MyLibBar;
void MyLibBarAnAction(MyLibBar *bar, ...);

// Submodule
struct MyLibFooBaz;
void MyLibFooBazAnotherAction(MyLibFooBaz *baz, ...);

Mais je me retrouve avec des noms de symboles longs et fous (beaucoup plus longs que les exemples). Si je ne préfixe pas les noms avec un "faux espace de noms", les symboles internes des modules sont tous en conflit.

Remarque: je ne me soucie pas du cas camelcase / Pascal, etc., juste des noms eux-mêmes.

Réponses:


10

Le préfixe (enfin l'apposition) est vraiment la seule option. Certains modèles que vous verrez sont <library>_<name>(par exemple, OpenGL, runtime ObjC), <module/class>_<name>(par exemple des parties de Linux), <library>_<module/class>_<name>(par exemple, GTK +). Votre plan est parfaitement raisonnable.

Les noms longs ne sont pas nécessairement mauvais s'ils sont prévisibles. Le fait que vous vous retrouvez avec des noms longs et des fonctions folles qui sont trop gros pour rester avec des fonctions connexes dans un seul fichier source soulève des préoccupations différentes. Avez-vous des exemples plus concrets?


Je vois d'où vous venez - je me suis demandé si j'étais trop pédant sur la division en fichiers séparés, mais cela aide beaucoup en termes de lisibilité, de maintenance et d' git mergeing. À titre d'exemple, j'ai un module pour dessiner l'interface utilisateur avec OpenGL, et j'ai des .cfichiers séparés pour chaque élément dont j'ai besoin ( slider.c, indicator.cetc.). Ces implémentations d'éléments ont une fonction de dessin principale de quelques centaines de lignes et un bon nombre d' staticaides. Ils appellent également quelques fonctions de géométrie pure à partir du module d'interface utilisateur. Cela vous semble-t-il assez typique?
Dan Halliday

Un meilleur exemple des noms longs pourrait être mon module audio - j'ai une hiérarchie comme Audio Module > Engines > Channels > Filtersce qui signifie quelque chose comme MyLibAudioEngines<EngineName>Channel<ActionName>. Ou dans mon sous-module de filtres: MyLibAudioFilters<FilterName><Type><Action>par exemple. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Dan Halliday

Rien de tout cela ne semble déraisonnable. Quelques centaines de lignes pour une fonction semblent un peu longues mais si ce que vous dessinez est compliqué, cela peut être difficile à éviter. Est-ce une branche lourde ou juste beaucoup d'instructions?
user2313838

Re: les noms des modules audio, vous pouvez abréger AudioFilters / AudioEngines car je pense qu'il serait facile de dire s'il s'agit d'un filtre ou d'un module basé sur le nom. Les qualificatifs de type de données comme Float32 peuvent également être abrégés (par exemple «d», «f») car ces abréviations sont courantes dans la programmation C.
user2313838

Merci pour vos réponses - il semble parfois impossible d'obtenir de bonnes informations sur l'architecture du programme C (en particulier par rapport aux langages de niveau supérieur). Beaucoup de livres C que j'ai lus considèrent à peine l'idée d'avoir plusieurs modules ou même plus d'un fichier! Dans l'ensemble, je ne pense pas que je changerai beaucoup, sauf si je souhaite vivre avec des abréviations pour certains des noms les plus longs.
Dan Halliday

5

La convention habituelle pour les bibliothèques C est d'utiliser le nom de la bibliothèque comme préfixe pour les noms utilisables en externe, par exemple

struct MyLibFoo;
void MyLibAFooAction(...);

Pour les noms internes à la bibliothèque qui doivent toujours être accessibles dans plusieurs unités de la bibliothèque, il n'y a pas de convention stricte, mais j'utiliserais un préfixe du nom de la bibliothèque et une indication qu'il s'agit d'une fonction interne. Par exemple:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Je suis d'accord que cela peut conduire à des noms assez longs et difficiles à taper, mais malheureusement c'est le prix à payer pour ne pas avoir de mécanisme comme les espaces de noms C ++. Pour réduire la longueur des noms, au prix d'une certaine clarté, vous pouvez choisir d'utiliser des abréviations dans vos noms, mais vous devez peser soigneusement les avantages et les inconvénients.


3

Si vous voulez éviter les longs préfixes, vous pouvez abréger les noms de bibliothèques comme ce que fait Apple dans iOS et OS X:

  • NSString est une chaîne issue des racines NextStep du système d'exploitation
  • CALayer est une couche Core Animation

2

Qu'en est-il d'avoir une variable de structure globale préremplie de pointeurs de fonction?

lib.h

#pragma once

typedef struct
{
    void (*doFoo)(int x);
    const char *(*doBar)(void *p);
} YourApi;

extern const YourApi yourApi;

lib.c:

#include "lib.h"

#include <stdio.h>

static void doFoo(int x)
{
    printf("Doing foo %d\n", x);
}

static const char *doBar(void *p)
{
    printf("Doing bar: %p\n", p);
    return "Hello";
}

const YourApi yourApi = {
    doFoo,
    doBar};

Harnais:

#include "lib.h"

int main()
{
    yourApi.doFoo(42);
    yourApi.doBar("asd");
}

Le mot-clé statique limite la portée à l'unité de traduction afin qu'elle n'entre pas en collision avec d'autres.

L'utilisateur peut ensuite le raccourcir en utilisant un pointeur comme YourApi *ya = &yourApi, puis en utilisant ya->doFoo(...).

Il fournit également un bon moyen de simuler votre bibliothèque pour les tests.


Modèle sympa, il nécessite un peu de passe-partout, mais cela rendra la saisie semi-automatique beaucoup plus utile.
Rick Love
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.