Progetto C che evita conflitti di denominazione


13

Faccio fatica a trovare consigli pragmatici nel mondo reale sulle convenzioni di denominazione delle funzioni per un progetto di biblioteca C di medie dimensioni. Il mio progetto di libreria è diviso in alcuni moduli e sottomoduli con le loro intestazioni e segue vagamente uno stile OO (tutte le funzioni assumono una certa struttura come primo argomento, niente globali ecc.). Ha posto il nostro qualcosa come:

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

Generalmente le funzioni sono troppo grandi per (ad esempio) incollare some_foo_actione another_foo_actionin un foo.cfile di implementazione, rendere la maggior parte delle funzioni statiche e chiamarlo un giorno.

Posso occuparmi di rimuovere i miei simboli interni ("modulo privato") quando creo la libreria per evitare conflitti per i miei utenti con i loro programmi client, ma la domanda è come nominare i simboli nella mia biblioteca? Finora ho fatto:

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

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

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

Ma sto finendo con nomi pazzi di simboli lunghi (molto più lunghi degli esempi). Se non prefisso i nomi con uno "spazio dei nomi falso", i nomi dei simboli interni dei moduli si scontrano tutti.

Nota: non mi interessa il caso di camelcase / Pascal ecc., Solo i nomi stessi.

Risposte:


10

Il prefisso (beh, l'apposizione) è davvero l'unica opzione. Alcuni schemi che vedrai sono <library>_<name>(es. OpenGL, ObjC runtime), <module/class>_<name>(es. Parti di Linux), <library>_<module/class>_<name>(es. GTK +). Il tuo schema è perfettamente ragionevole.

I nomi lunghi non sono necessariamente cattivi se sono prevedibili. Il fatto che tu stia finendo con nomi e funzioni lunghi e folli che sono troppo grandi per rimanere attaccati a funzioni correlate in un singolo file sorgente solleva preoccupazioni diverse. Hai qualche esempio più concreto?


Vedo da dove vieni - Mi chiedevo se sono troppo pedante per la divisione in file separati, ma aiuta molto con leggibilità, manutenzione e git mergeing. Ad esempio, ho un modulo per disegnare l'interfaccia utente con OpenGL e ho .cfile separati per ogni elemento di cui ho bisogno ( slider.c, indicator.cecc.). Queste implementazioni di elementi hanno una funzione di disegno principale lunga forse qualche centinaio di righe e un numero equo di statichelper all'interno. Chiamano anche alcune funzioni di pura geometria all'interno del modulo UI. Sembra abbastanza tipico?
Dan Halliday,

Un esempio migliore dei nomi lunghi potrebbe essere il mio modulo audio: ho una gerarchia Audio Module > Engines > Channels > Filtersche significa qualcosa di simile MyLibAudioEngines<EngineName>Channel<ActionName>. O nel mio sottomodulo di filtri: MyLibAudioFilters<FilterName><Type><Action>ad es. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Dan Halliday,

Niente di tutto ciò sembra irragionevole. Alcune centinaia di linee per una funzione sembrano un po 'lunghe ma se ciò che stai disegnando è complicato, può essere difficile da evitare. È un ramo pesante o solo molte istruzioni?
user2313838

Ri: i nomi dei moduli audio, potresti abbreviare AudioFilters / AudioEngines poiché penso che sarebbe facile dire se si tratta di un filtro o di un modulo basato sul nome. Qualificatori di tipi di dati come Float32 potrebbero anche essere abbreviati (ad esempio "d", "f") poiché tali abbreviazioni sono comuni nella programmazione C.
user2313838

Grazie per le risposte - a volte sembra quasi impossibile ottenere buone informazioni sull'architettura del programma C (soprattutto se confrontato con le lingue di livello superiore). Molti libri in C che ho letto considerano a malapena l'idea di avere più moduli o anche più di un file! Nel complesso, non penso che cambierò molto, salvo considerare se convivere con abbreviazioni per alcuni dei nomi più lunghi.
Dan Halliday,

5

La consueta convenzione per le librerie C consiste nell'utilizzare il nome della libreria come prefisso per nomi utilizzabili esternamente, ad es

struct MyLibFoo;
void MyLibAFooAction(...);

Per i nomi interni alla libreria che devono essere ancora accessibili in più unità della libreria, non esiste una convenzione rigorosa, ma utilizzerei un prefisso del nome della libreria e un'indicazione che è una funzione interna. Per esempio:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Concordo sul fatto che ciò può portare a nomi piuttosto lunghi e difficili da digitare, ma sfortunatamente questo è il prezzo che dobbiamo pagare per non avere un meccanismo come gli spazi dei nomi C ++. Per ridurre la lunghezza dei nomi, a scapito della chiarezza, puoi scegliere di utilizzare abbreviazioni nei tuoi nomi, ma devi valutare attentamente i vantaggi e gli svantaggi.


3

Se vuoi evitare prefissi lunghi, puoi abbreviare i nomi delle librerie come quello che Apple fa in iOS e OS X:

  • NSString è una stringa proveniente dalle radici NextStep del sistema operativo
  • CALayer è un livello di animazione principale

2

Che dire di avere una variabile di struttura globale precompilata con i puntatori a funzione?

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};

Harness:

#include "lib.h"

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

La parola chiave statica limita l'ambito all'unità di traduzione in modo che non entri in conflitto con altri.

L'utente può quindi accorciarlo utilizzando un puntatore come YourApi *ya = &yourApi, quindi utilizzando ya->doFoo(...).

Fornisce anche un bel modo per deridere la tua libreria per i test.


Fantastico schema, richiede un po 'di piastra della caldaia, ma questo renderà il completamento automatico molto più utile.
Rick Love,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.