Warum hat ANSI C keine Namespaces?

91

Namespaces zu haben, scheint für die meisten Sprachen ein Kinderspiel zu sein. Aber soweit ich das beurteilen kann, unterstützt ANSI C dies nicht. Warum nicht? Gibt es Pläne, es in einen zukünftigen Standard aufzunehmen?

Pulkit Sinha
quelle
13
Verwenden Sie C ++ als C-with-Namespace!
AraK
3
Ich kann natürlich, aber ich würde immer noch gerne wissen
Pulkit Sinha
5
2 Dinge. Eine unnötige Unterscheidungssyntax: Alle anderen Sprachen mit Namespaces verwenden nur '.' als Trennzeichen, da es mit anderen Verwendungen von '.' nicht mehrdeutig ist. Und kritischer ist, dass c ++ niemals einen Gültigkeitsbereich mit Direktive eingeführt hat. Dies bedeutete, dass Programmierer die Verwendung von Anweisungen zum Importieren von Namespaces in den globalen Bereich überbeanspruchten. Dies bedeutete, dass das c ++ - Standardkomitee std :: ever jetzt keine neuen Funktionen hinzufügen kann, da die Menge an Code, die dadurch beschädigt würde, die Partitionierung überflüssig gemacht hat.
Chris Becke
2
@ Chris Becke: Ich mag unverwechselbare Syntax. Ich möchte wissen, ob ich eine Klasse in einem Namensraum oder ein Mitglied in einer Klasse betrachte.
JeremyP
6
@ChrisBecke, dies ist ein paar Jahre zu spät, aber es ist interessant, dass Sie argumentieren, dass C ++ - Namespaces schlecht implementiert wurden, sodass sie nicht in C implementiert werden sollten. Dann stellen Sie fest, dass andere Sprachen sie ohne die Aufhänge von C ++ implementieren. Wenn andere Sprachen dies können, warum nicht C vorstellen?
weberc2

Antworten:

67

C hat Namespaces. Eine für Struktur-Tags und eine für andere Typen. Betrachten Sie die folgende Definition:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Der erste hat das Tag foo und der spätere wird mit einem typedef zum Typ foo gemacht. Es kommt immer noch nicht zu Namenskonflikten. Dies liegt daran, dass Struktur-Tags und -Typen (integrierte Typen und typdefinierte Typen) in separaten Namespaces gespeichert sind.

Was C nicht erlaubt, ist, durch Willen einen neuen Namespace zu erstellen . C wurde standardisiert, bevor dies in einer Sprache als wichtig erachtet wurde, und das Hinzufügen von Namespaces würde auch die Abwärtskompatibilität gefährden, da die Namensveränderung erforderlich ist, um richtig zu funktionieren. Ich denke, dies kann auf technische Aspekte zurückgeführt werden, nicht auf die Philosophie.

EDIT: JeremyP hat mich glücklicherweise korrigiert und die Namespaces erwähnt, die ich verpasst habe. Es gibt Namespaces für Labels und auch für Struktur- / Gewerkschaftsmitglieder.


quelle
8
Es gibt tatsächlich mehr als zwei Namensräume. Zusätzlich zu den beiden von Ihnen erwähnten gibt es einen Namensraum für Beschriftungen und Namensräume für die Mitglieder jeder Struktur und Vereinigung.
JeremyP
@JeremyP: Vielen Dank für die Korrektur. Ich habe das nur aus dem Gedächtnis geschrieben, ich habe den Standard nicht überprüft :-)
2
Was ist mit dem Namespace für Funktionen?
Themihai
8
Dies kann durchaus als Namespaces bezeichnet werden, aber ich glaube, dies sind nicht die Namespaces, nach denen das OP gefragt hat.
avl_sweden
1
@jterm Nein. Ich befürworte nicht das Hacken von C-Funktionen, sondern nur die Fakten. Jede structDefinition deklariert einen neuen Namespace für ihre Mitglieder. Ich befürworte weder die Ausnutzung dieser Tatsache, noch sind mir Mittel zur Ausnutzung dieser Tatsache bekannt, da structs keine statischen Mitglieder haben kann.
JeremyP
98

Der Vollständigkeit halber gibt es in C verschiedene Möglichkeiten, die "Vorteile" von Namespaces zu erzielen.

Eine meiner Lieblingsmethoden ist die Verwendung einer Struktur, um eine Reihe von Methodenzeigern zu speichern, die die Schnittstelle zu Ihrer Bibliothek / etc bilden.

Anschließend verwenden Sie eine externe Instanz dieser Struktur, die Sie in Ihrer Bibliothek initialisieren und auf alle Ihre Funktionen verweisen. Auf diese Weise können Sie Ihre Namen in Ihrer Bibliothek einfach halten, ohne auf den Client-Namespace zuzugreifen (außer der externen Variablen im globalen Bereich, 1 Variable gegenüber möglicherweise Hunderten von Methoden.)

Es sind einige zusätzliche Wartungsarbeiten erforderlich, aber ich bin der Meinung, dass diese minimal sind.

Hier ist ein Beispiel:

/* interface.h */

struct library {
    const int some_value;
    void (*method1)(void);
    void (*method2)(int);
    /* ... */
};

extern const struct library Library;
/* interface.h */

/* interface.c */
#include "interface.h"

void method1(void)
{
   ...
}
void method2(int arg)
{
   ...
}

const struct library Library = {
    .method1 = method1,
    .method2 = method2,
    .some_value = 36
};
/* end interface.c */

/* client code */
#include "interface.h"

int main(void)
{
    Library.method1();
    Library.method2(5);
    printf("%d\n", Library.some_value);
    return 0;
}
/* end */

Die Verwendung von . Die Syntax stellt eine starke Assoziation zur klassischen Library_function () Library_some_value-Methode her. Es gibt jedoch einige Einschränkungen, zum einen können Sie keine Makros als Funktionen verwenden.

Gast
quelle
12
... und sind Compiler klug genug, um den Funktionszeiger beim Kompilieren zu "dereferenzieren", wenn Sie dies tun library.method1()?
Einpoklum
1
Das ist so toll. Eine Sache, die ich hinzufügen könnte, ist, dass ich versuche, alle meine Funktionen in meinen .cDateien standardmäßig statisch zu machen. Daher sind die einzigen Funktionen, die verfügbar gemacht werden, diejenigen, die explizit in der const structDefinition in der .cDatei verfügbar gemacht werden.
lastmjs
3
Das ist eine großartige Idee, aber wie gehen Sie mit Konstanten und Aufzählungen um?
Nowox
1
@einpoklum - Entschuldigung für Necro, aber zumindest ab Version 6.3.0 berechnet gcc die tatsächliche Adresse von function1/ method2beim Kompilieren mit -O2und -flto. Wenn Sie solche Bibliotheken nicht zusammen mit Ihrer eigenen Quelle kompilieren, erhöht dieser Ansatz die Funktionsaufrufe um einen gewissen Aufwand.
Alex Reinking
3
@AlexReinking: Nun, das ist schön, aber wir würden diese Funktionen niemals inline bekommen. Und - Nekro'ing ist großartig, keine Entschuldigung nötig.
Einpoklum
24

C hat Namespaces. Die Syntax lautet namespace_name. Sie können sie sogar wie in verschachteln general_specific_name. Wenn Sie auf Namen zugreifen möchten, ohne jedes Mal den Namespace-Namen ausschreiben zu müssen, fügen Sie die entsprechenden Präprozessor-Makros in eine Header-Datei ein, z

#define myfunction mylib_myfunction

Dies ist viel sauberer als das Mangeln von Namen und die anderen Gräueltaten, die bestimmte Sprachen begehen, um Namespaces bereitzustellen.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
24
Ich sehe das anders Die Grammatik zu komplizieren, Namen auf Symbolen zu entstellen usw., um etwas zu erreichen, das mit dem Präprozessor bereits trivial war, würde ich als schmutzigen Hack und schlechtes Design bezeichnen.
R .. GitHub STOP HELPING ICE
40
Ich sehe nicht ein, wie Sie diese Position wirklich unterstützen können. Fragen Sie die Javascript-Community nach der Integration von Projekten, wenn jedes andere System einen anderen selbst entwickelten Hack zum Implementieren von Namespaces hat. Ich habe noch nie jemanden über das Schlüsselwort "Namespace" oder "Paket" beschweren hören, das seiner Sprache zu viel Komplexität verleiht. Auf der anderen Seite kann der Versuch, mit Makros übersäten Code zu debuggen, schnell haarig werden!
weberc2
5
Ich habe viele Leute gehört, die sich über C ++ - Namensmangel (unter den Gesichtspunkten Debugging, Toolchain, ABI-Kompatibilität, dynamische Symbolsuche usw.) und die Komplexität beschwert haben, nicht zu wissen, worauf sich ein bestimmter Name tatsächlich bezieht.
R .. GitHub STOP HELPING ICE
6
@R .. Das würde nicht passieren, wenn der Name Mangling in C ++ standardisiert wäre. Dies allein würde bei der ABI-Kompatibilität nicht helfen, würde aber definitiv das Problem der Namenszuordnung beheben.
Malcolm
19
Ich finde es umwerfend, dass C-Leute dies tatsächlich mit ernstem Gesicht argumentieren. In C ++ gibt es viele Funktionen mit scharfen Kanten, die den Menschen Kummer bereiten. Namespaces gehören nicht zu diesen Funktionen. Sie sind großartig, sie funktionieren sehr gut. Und mit dem Präprozessor ist nichts trivial. Schließlich ist das Entwirren von Namen trivial. Es gibt viele Befehlszeilenprogramme, die dies für Sie erledigen.
Nir Friedman
12

In der Vergangenheit haben C-Compiler keine Namen entstellt (unter Windows, aber das Verfälschen für die cdeclaufrufende Konvention besteht darin, nur ein Unterstrichpräfix hinzuzufügen).

Dies erleichtert die Verwendung von C-Bibliotheken aus anderen Sprachen (einschließlich Assembler) und ist einer der Gründe, warum häufig extern "C"Wrapper für C ++ - APIs angezeigt werden.

Christoph
quelle
2
Aber warum ist das so ein Problem? Angenommen, alle Namen mit Namespaces würden mit _da13cd6447244ab9a30027d3d0a08903 und dann mit dem Namen beginnen (das ist eine UUID v4, die ich gerade generiert habe). Es besteht die Möglichkeit, dass Namen, die diese bestimmte UUID verwenden, beschädigt werden, aber diese Wahrscheinlichkeit ist im Wesentlichen Null. In der Praxis wird es also kein Problem geben, only_namespace_names zu entstellen .
Einpoklum
7

nur historische Gründe. Niemand dachte damals daran, so etwas wie einen Namespace zu haben. Außerdem versuchten sie wirklich, die Sprache einfach zu halten. Sie können es in der Zukunft haben

Lovesh
quelle
2
Gibt es im Standardkomitee eine Bewegung, um C in Zukunft Namespaces hinzuzufügen? Möglich mit der Umstellung auf C / C ++ Modul könnte dies in Zukunft einfacher machen?
Lanoxx
1
@lanoxx Aus Gründen der Abwärtskompatibilität besteht kein Wille, C Namespaces zu C hinzuzufügen.
Themihai
6

Keine Antwort, aber kein Kommentar. C bietet keine Möglichkeit, namespaceexplizit zu definieren . Es hat einen variablen Umfang. Beispielsweise:

int i=10;

struct ex {
  int i;
}

void foo() {
  int i=0;
}

void bar() {
  int i=5;
  foo();
  printf("my i=%d\n", i);
}

void foobar() {
  foo();
  bar();
  printf("my i=%d\n", i);
}

Sie können qualifizierte Namen für Variablen und Funktionen verwenden:

mylib.h

void mylib_init();
void mylib_sayhello();

Der einzige Unterschied zu Namespaces besteht darin, dass Sie usingnicht importiert werden können from mylib.

Khachik
quelle
Sie können auch nicht die letzten beiden Zeilen ersetzen, die namespace mylib { void init(); void say_hello(); }ebenfalls wichtig sind (ish).
Einpoklum
3

ANSI C wurde erfunden, bevor es Namespaces gab.

Crashworks
quelle
10
Es war? Die erste ANSI C-Spezifikation war 1989. Ich bin mir ziemlich sicher, dass Namespaces (in der einen oder anderen Form) zuvor in Programmiersprachen waren. Ada zum Beispiel wurde 1983 standardisiert und hatte Pakete als Namespaces. Diese wiederum basierten im Wesentlichen auf Modula-2-Modulen.
Nur meine richtige Meinung
4
Ich würde die Erfindung von ANSI C nicht auf den Zeitpunkt datieren, an dem die Spezifikation offiziell verabschiedet wurde. Die Sprache existierte vorher und die Spezifikation dokumentierte nur, was bereits da war. Obwohl aus einigen der Antworten auf dieser Site man denken könnte, dass die Spezifikation zuerst und der erste Compiler nachträglich kam.
Crashworks
ANSI C hatte einige signifikante Unterschiede zu vor ANSI C, aber Namespaces gehörten nicht dazu.
Dan04
3

Weil Leute, die diese Funktion zu C hinzufügen möchten, sich nicht zusammengetan und organisiert haben, um Druck auf Compiler-Autorenteams und ISO-Gremien auszuüben.

einpoklum
quelle
1
Ich denke, wir werden einen Namespace in C nur sehen, wenn diese Leute sich selbst organisieren und eine oder mehrere Erweiterungen mit Namespace-Unterstützung erstellen würden. Dann haben die ISO-Gremien keine andere Wahl, als sie als Standard zu veröffentlichen (mit mehr oder weniger Änderungen). So hat es Javascript (das in dieser Hinsicht einige Ähnlichkeiten mit C hat) gemacht.
Themihai
3
@themihai: "create a extension" = Bringe die gcc und clang people dazu, Namespaces zu kompilieren.
Einpoklum
1

C unterstützt keine Namespaces wie C ++. Die Implementierung von C ++ - Namespaces entstellt die Namen. Mit dem unten beschriebenen Ansatz können Sie die Vorteile von Namespaces in C ++ nutzen, während Sie Namen haben, die nicht entstellt sind. Mir ist klar, dass die Art der Frage ist, warum C keine Namespaces unterstützt (und eine triviale Antwort wäre, dass dies nicht der Fall ist, weil es nicht implementiert wurde :)). Ich dachte nur, dass es jemandem helfen könnte zu sehen, wie ich die Funktionalität von Vorlagen und Namespaces implementiert habe.

Ich habe ein Tutorial geschrieben, wie man mit C Namespaces und / oder Vorlagen nutzen kann.

Namespaces und Vorlagen in C.

Namespaces und Vorlagen in C (mithilfe verknüpfter Listen)

Für den Basis-Namespace kann man dem Namespace-Namen einfach eine Konvention voranstellen.

namespace MY_OBJECT {
  struct HANDLE;
  HANDLE *init();
  void destroy(HANDLE * & h);

  void do_something(HANDLE *h, ... );
}

kann geschrieben werden als

struct MY_OBJECT_HANDLE;
struct MY_OBJECT_HANDLE *my_object_init();
void my_object_destroy( MY_OBJECT_HANDLE * & h );

void my_object_do_something(MY_OBJECT_HANDLE *h, ... );

Ein zweiter Ansatz, den ich benötigt habe und der das Konzept von Namespace und Vorlagen verwendet, ist die Verwendung der Makroverkettung und des Einschlusses. Zum Beispiel kann ich eine erstellen

template<T> T multiply<T>( T x, T y ) { return x*y }

Verwenden von Vorlagendateien wie folgt

multiplizieren-template.h

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);

multiplizieren-template.c

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
  return x*y;
}

Wir können int_multiply nun wie folgt definieren. In diesem Beispiel erstelle ich eine int_multiply.h / .c-Datei.

int_multiply.h

#ifndef _INT_MULTIPLY_H
#define _INT_MULTIPLY_H

#ifdef _multiply_
#undef _multiply_
#endif
#define _multiply_(NAME) int ## _ ## NAME 

#ifdef _multiply_type_
#undef _multiply_type_
#endif
#define _multiply_type_ int 

#include "multiply-template.h" 
#endif

int_multiply.c

#include "int_multiply.h"
#include "multiply-template.c"

Am Ende all dessen haben Sie eine Funktions- und Header-Datei für.

int int_multiply( int x, int y ) { return x * y }

Ich habe ein viel detaillierteres Tutorial zu den bereitgestellten Links erstellt, das zeigt, wie es mit verknüpften Listen funktioniert. Hoffentlich hilft das jemandem!

Andy Curtis
quelle
3
Ihre Links erklären, wie Sie Namespaces hinzufügen. Die Frage war jedoch, warum Namespaces nicht unterstützt werden. Diese Antwort ist also keine Antwort und sollte stattdessen ein Kommentar sein.
Thomas Weller