C-Projekt zur Vermeidung von Namenskonflikten

12

Ich kämpfe darum, pragmatische Ratschläge zu Funktionsnamenkonventionen für ein mittelgroßes C-Bibliotheksprojekt zu finden. Mein Bibliotheksprojekt ist in einige Module und Submodule mit eigenen Kopfzeilen unterteilt und folgt lose einem OO-Stil (alle Funktionen haben eine bestimmte Struktur als erstes Argument, keine globalen Elemente usw.). Es ist unser etwas gelegt wie:

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

Im Allgemeinen sind die Funktionen viel zu groß , um (zum Beispiel) Stick some_foo_actionund another_foo_actionin einer foo.cImplementierungsdatei, machen die meisten Funktionen statisch, und nennen ihn einen Tag.

Ich kann beim Erstellen der Bibliothek die internen ("privaten") Symbole entfernen, um Konflikte für meine Benutzer mit ihren Client-Programmen zu vermeiden. Die Frage ist jedoch, wie Symbole in meiner Bibliothek benannt werden sollen. Bisher habe ich getan:

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

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

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

Aber am Ende habe ich verrückte lange Symbolnamen (viel länger als die Beispiele). Wenn ich den Namen keinen "falschen Namespace" voranstelle, stoßen die internen Symbolnamen der Module aufeinander.

Hinweis: Ich interessiere mich nicht für Camelcase / Pascal usw., nur für die Namen selbst.

Dan Halliday
quelle

Antworten:

10

Das Präfixieren (na ja, das Anbringen) ist wirklich die einzige Option. Einige Muster, die Sie sehen werden, sind <library>_<name>(z. B. OpenGL, ObjC-Laufzeit) <module/class>_<name>(z. B. Teile von Linux) <library>_<module/class>_<name>(z. B. GTK +). Ihr Schema ist vollkommen vernünftig.

Lange Namen sind nicht unbedingt schlecht, wenn sie vorhersehbar sind. Die Tatsache, dass Sie verrückte lange Namen und Funktionen haben, die zu groß sind, um sich auf verwandte Funktionen in einer einzigen Quelldatei zu beschränken, wirft unterschiedliche Bedenken auf. Haben Sie noch weitere konkrete Beispiele?

user2313838
quelle
Ich verstehe, woher Sie kommen - ich habe mich gefragt, ob ich zu umständlich beim Aufteilen in separate Dateien bin, aber es hilft sehr bei der Lesbarkeit, Pflege und git mergeVerwaltung. Als Beispiel habe ich ein Modul zum Zeichnen von UI mit OpenGL und ich habe separate .cDateien für jedes Element, das ich brauche ( slider.c, indicator.cetc). Diese Elementimplementierungen haben eine Hauptzeichnungsfunktion, die möglicherweise einige hundert Zeilen lang ist, und eine angemessene Anzahl von staticHelfern. Sie rufen auch einige reine Geometriefunktionen innerhalb des UI-Moduls auf. Klingt das ziemlich typisch?
Dan Halliday
Ein besseres Beispiel für die langen Namen könnte mein Audiomodul sein - ich habe eine Hierarchie, Audio Module > Engines > Channels > Filtersdie so etwas wie bedeutet MyLibAudioEngines<EngineName>Channel<ActionName>. Oder in meinem Filtersubmodul: MyLibAudioFilters<FilterName><Type><Action>zB. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Dan Halliday
Nichts davon scheint unvernünftig. Ein paar hundert Zeilen für eine Funktion scheinen ein bisschen lang zu sein, aber wenn das, was Sie zeichnen, kompliziert ist, kann es schwierig sein, dies zu vermeiden. Ist es astlastig oder nur eine Menge Anweisungen?
user2313838
Betreff: Die Namen der Audiomodule können mit AudioFilters / AudioEngines abgekürzt werden, da ich denke, dass es leicht zu erkennen ist, ob es sich um einen Filter oder ein Modul handelt, der auf dem Namen basiert. Datentypqualifizierer wie Float32 können auch abgekürzt werden (z. B. 'd', 'f'), da solche Abkürzungen in der C-Programmierung üblich sind.
user2313838
Vielen Dank für Ihre Antworten - manchmal ist es fast unmöglich, gute Informationen über die C-Programmarchitektur zu erhalten (insbesondere im Vergleich zu den höheren Sprachen). Viele C-Bücher, die ich gelesen habe, denken kaum darüber nach, mehrere Module oder sogar mehr als eine Datei zu haben! Insgesamt denke ich nicht, dass ich viel ändern werde, außer darüber nachzudenken, ob ich mit Abkürzungen für einige der längeren Namen leben soll.
Dan Halliday
5

Die übliche Konvention für C-Bibliotheken besteht darin, den Bibliotheksnamen als Präfix für extern verwendbare Namen zu verwenden, z

struct MyLibFoo;
void MyLibAFooAction(...);

Für bibliotheksinterne Namen, auf die in mehreren Einheiten der Bibliothek noch zugegriffen werden muss, gibt es keine strenge Konvention, aber ich würde ein Präfix des Bibliotheksnamens und einen Hinweis darauf verwenden, dass es sich um eine interne Funktion handelt. Beispielsweise:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Ich stimme zu, dass dies zu Namen führen kann, die ziemlich lang und schwer zu tippen sind, aber leider ist dies der Preis, den wir dafür zahlen müssen, dass wir keinen Mechanismus wie C ++ - Namespaces haben. Um die Länge der Namen zu verringern, können Sie auf Kosten der Klarheit Abkürzungen in Ihren Namen verwenden, aber Sie müssen die Vor- und Nachteile sorgfältig abwägen.

Bart van Ingen Schenau
quelle
3

Wenn Sie lange Präfixe vermeiden möchten, können Sie Bibliotheksnamen wie die von Apple in iOS und OS X abkürzen:

  • NSString ist eine Zeichenfolge aus den NextStep-Stammverzeichnissen des Betriebssystems
  • CALayer ist eine Core Animationsebene
mouviciel
quelle
1

Was ist mit einer globalen Strukturvariablen, die vorab mit Funktionszeigern gefüllt ist?

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

Geschirr:

#include "lib.h"

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

Das statische Schlüsselwort begrenzt den Bereich auf die Übersetzungseinheit, damit es nicht mit anderen kollidiert.

Der Benutzer kann es dann verkürzen, indem er einen Zeiger wie YourApi *ya = &yourApifolgt verwendet ya->doFoo(...).

Es bietet auch eine gute Möglichkeit, Ihre Bibliothek zum Testen zu verspotten.

Calmarius
quelle
Coole Muster, es erfordert ein bisschen Boilerplate, aber das macht Autocomplete viel nützlicher.
Rick Love