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.