Pourquoi ANSI C n'a-t-il pas d'espaces de noms?

91

Avoir des espaces de noms semble une évidence pour la plupart des langues. Mais pour autant que je sache, ANSI C ne le prend pas en charge. Pourquoi pas? Avez-vous l'intention de l'inclure dans une future norme?

Pulkit Sinha
la source
13
Utilisez C ++ comme C-avec-espace de noms!
AraK
3
Je peux bien sûr, mais j'aimerais toujours savoir
Pulkit Sinha
5
2 choses. Une syntaxe distinctive inutile: toutes les autres langues avec des espaces de noms utilisent simplement '.' comme séparateur car il n'est pas ambigu avec d'autres utilisations de «.». Et, plus important encore, c ++ n'a jamais introduit de directive utilisant une portée. Ce qui signifiait que les programmeurs utilisaient abusivement des directives pour importer des espaces de noms dans la portée globale. Ce qui signifiait que le comité des normes c ++ ne pouvait plus ajouter de nouvelles fonctionnalités à std :: ever car la quantité de code qui serait rompue en conséquence a rendu le partitionnement redondant.
Chris Becke
2
@Chris Becke: J'aime la syntaxe distinctive. J'aime savoir si je regarde une classe dans un espace de nom ou un membre dans une classe.
JeremyP
6
@ChrisBecke, c'est quelques années de retard, mais il est intéressant que vous souteniez que les espaces de noms C ++ étaient mal implémentés, donc ils ne devraient pas être implémentés en C. Ensuite, vous notez que d'autres langages les implémentent sans les blocages de C ++. Si d'autres langages peuvent le faire, pourquoi ne pas les introduire en C?
weberc2

Réponses:

67

C a des espaces de noms. Un pour les balises de structure et un pour les autres types. Considérez la définition suivante:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Le premier a la balise foo, et le dernier est transformé en type foo avec un typedef. Il n'y a toujours pas de conflit de noms. Cela est dû au fait que les balises et les types de structure (types intégrés et types à défaut typé) vivent dans des espaces de noms séparés.

Ce que C ne permet pas, c'est de créer un nouvel espace de noms par volonté. C a été normalisé avant que cela ne soit considéré comme important dans une langue, et l'ajout d'espaces de noms menacerait également la compatibilité ascendante, car cela nécessite une modification des noms pour fonctionner correctement. Je pense que cela peut être attribué à des aspects techniques et non à la philosophie.

EDIT: JeremyP m'a heureusement corrigé et a mentionné les espaces de noms que j'avais manqués. Il existe des espaces de noms pour les étiquettes et pour les membres de struct / union.


la source
8
Il y a en fait plus de deux espaces de nom. En plus des deux que vous mentionnez, il existe un espace de nom pour les étiquettes et les espaces de nom pour les membres de chaque structure et union.
JeremyP
@JeremyP: Merci beaucoup pour la correction. Je n'ai écrit cela que de mémoire, je n'ai pas vérifié la norme :-)
2
qu'en est-il de l'espace de noms pour les fonctions?
themihai
8
Cela peut très bien s'appeler des espaces de noms, mais je pense que ce ne sont pas les types d'espaces de noms que l'OP demandait.
avl_sweden
1
@jterm Non. Je ne préconise pas de pirater les fonctionnalités C, mais simplement d'énoncer les faits. Chaque structdéfinition déclare un nouvel espace de noms pour ses membres. Je ne préconise pas d'exploiter ce fait, et je ne connais aucun moyen de l'exploiter car structs ne peut pas avoir de membres statiques.
JeremyP
98

Par souci d'exhaustivité, il existe plusieurs façons d'obtenir les "avantages" que vous pourriez obtenir des espaces de noms, en C.

Une de mes méthodes préférées consiste à utiliser une structure pour héberger un tas de pointeurs de méthode qui sont l'interface de votre bibliothèque / etc.

Vous utilisez ensuite une instance externe de cette structure que vous initialisez à l'intérieur de votre bibliothèque pointant vers toutes vos fonctions. Cela vous permet de garder vos noms simples dans votre bibliothèque sans marcher sur l'espace de noms des clients (autre que la variable externe à portée globale, 1 variable contre éventuellement des centaines de méthodes ..)

Il y a un peu d'entretien supplémentaire impliqué mais je pense que c'est minime.

Voici un exemple:

/* 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 */

L'utilisation de . La syntaxe crée une forte association avec la méthode classique Library_function () Library_some_value. Il y a cependant quelques limitations, vous ne pouvez pas utiliser de macros comme fonctions.

client
la source
12
... et les compilateurs sont-ils assez intelligents pour "déréférencer" le pointeur de fonction au moment de la compilation quand vous le faites library.method1()?
einpoklum
1
C'est tellement génial. Une chose que je pourrais ajouter, j'essaie de rendre toutes mes fonctions dans mes .cfichiers statiques par défaut, donc les seules fonctions exposées sont celles explicitement exposées dans la const structdéfinition du .cfichier.
lastmjs
3
C'est une excellente idée, mais comment gérez-vous les constantes et les énumérations?
nowox
1
@einpoklum - désolé pour necro, mais au moins à partir de la version 6.3.0, gcc calculera l'adresse réelle de function1/ method2lors de la compilation avec -O2et -flto. À moins que vous ne compiliez de telles bibliothèques avec votre propre source, cette approche ajoutera une surcharge à ses appels de fonction.
Alex Reinking
3
@AlexReinking: Eh bien, c'est bien, mais nous n'allions jamais intégrer ces fonctions. Et - nécroser est génial, aucune excuse n'est nécessaire.
einpoklum
24

C a des espaces de noms. La syntaxe est namespace_name. Vous pouvez même les imbriquer comme dans general_specific_name. Et si vous voulez pouvoir accéder aux noms sans écrire le nom de l'espace de noms à chaque fois, incluez les macros de préprocesseur appropriées dans un fichier d'en-tête, par exemple

#define myfunction mylib_myfunction

C'est beaucoup plus propre que la manipulation des noms et les autres atrocités que certaines langues s'engagent à fournir des espaces de noms.

R .. GitHub STOP AIDING ICE
la source
24
Je vois les choses différemment. Compliquer la grammaire, introduire la manipulation des noms sur les symboles, etc. pour réaliser quelque chose qui était déjà trivial à faire avec le préprocesseur, c'est ce que j'appellerais un sale hack et une mauvaise conception.
R .. GitHub STOP HELPING ICE
40
Je ne vois pas comment vous pouvez vraiment soutenir cette position. Demandez à la communauté Javascript d'intégrer des projets lorsque tous les autres systèmes ont un hack local différent pour l'implémentation des espaces de noms. Je n'ai jamais entendu personne se plaindre du mot clé «namespace» ou «package» ajoutant trop de complexité à leur langage. D'un autre côté, essayer de déboguer du code jonché de macros peut devenir rapide!
weberc2
5
J'ai entendu beaucoup de gens se plaindre de la déformation des noms C ++ (du point de vue du débogage, de la chaîne d'outils, de la compatibilité ABI, de la recherche de symboles dynamiques, ...) et de la complexité de ne pas savoir à quoi un nom particulier fait référence.
R .. GitHub STOP HELPING ICE
6
@R .. Cela ne se produirait pas si le nom déformé en C ++ était normalisé. Cela seul n'aiderait pas avec la compatibilité ABI, mais résoudrait définitivement le problème de mappage de noms.
Malcolm
19
Je trouve époustouflant que les gens du C argumentent cela avec un visage impassible. Il existe de nombreuses fonctionnalités en C ++ avec des arêtes vives qui donnent du chagrin aux gens. Les espaces de noms ne font pas partie de ces fonctionnalités. Ils sont super, ils fonctionnent très bien. Et rien n'est anodin avec le préprocesseur, pour mémoire. Enfin, démêler les noms est trivial, il existe de nombreux utilitaires de ligne de commande qui le feront pour vous.
Nir Friedman
12

Historiquement, les compilateurs C ne modifient pas les noms (ils le font sous Windows, mais le cdecl convention d'appel consiste uniquement à ajouter un préfixe de soulignement).

Cela facilite l'utilisation des bibliothèques C d'autres langages (y compris l'assembleur) et est l'une des raisons pour lesquelles vous voyez souvent des extern "C"wrappers pour les API C ++.

Christoph
la source
2
Mais pourquoi est-ce un tel problème? Je veux dire, supposons que tous les noms d'espaces de noms commenceraient par _da13cd6447244ab9a30027d3d0a08903 et ensuite le nom (c'est un UUID v4 que je viens de générer)? Il y a une chance que cela puisse casser les noms qui utilisent cet UUID particulier, mais cette chance est essentiellement nulle. Donc, en pratique, il n'y aura pas de problème de manipulation de only_namespace_names .
einpoklum
7

juste des raisons historiques. personne n'a pensé à avoir quelque chose comme un espace de noms à ce moment-là. De plus, ils essayaient vraiment de garder le langage simple. Ils peuvent l'avoir dans le futur

amour
la source
2
Y a-t-il un mouvement dans le comité standard pour ajouter des espaces de noms à C à l'avenir? Possible avec le passage au module C / C ++, cela pourrait faciliter les choses à l'avenir?
lanoxx
1
@lanoxx Il n'y a aucune volonté d'ajouter des espaces de noms à C pour des raisons de rétrocompatibilité.
themihai
6

Pas une réponse, mais pas un commentaire. C ne fournit pas un moyen de définir namespaceexplicitement. Il a une portée variable. Par exemple:

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

Vous pouvez utiliser des noms qualifiés pour les variables et les fonctions:

mylib.h

void mylib_init();
void mylib_sayhello();

La seule différence avec les espaces de noms est que vous ne pouvez pas être usinget ne pouvez pas importer from mylib.

Khachik
la source
Vous ne pouvez pas non plus remplacer les deux dernières lignes par namespace mylib { void init(); void say_hello(); }ce qui est également important (ish).
einpoklum
3

ANSI C a été inventé avant les espaces de noms.

Crashworks
la source
10
C'était? La première spécification ANSI C date de 1989. Je suis presque sûr que les espaces de noms (sous une forme ou une autre) étaient auparavant dans des langages de programmation. Ada, par exemple, a été normalisé en 1983 et avait des packages comme espaces de noms. Ceux-ci à leur tour étaient essentiellement basés sur des modules Modula-2.
JUSTE MON OPINION correcte
4
Je ne daterais pas l'invention de l'ANSI C au moment où sa spécification a été officiellement adoptée; le langage existait auparavant et la spécification documentait simplement ce qui était déjà là. Bien qu'à partir de certaines des réponses sur ce site, on pourrait penser que la spécification est venue en premier et le premier compilateur après coup.
Crashworks
ANSI C avait des différences significatives par rapport au pré-ANSI C, mais les espaces de noms n'en faisaient pas partie.
dan04
3

Parce que les gens qui veulent ajouter cette capacité à C ne se sont pas réunis et ne se sont pas organisés pour faire pression sur les équipes de compilateurs et sur les organismes ISO.

einpoklum
la source
1
Je pense que nous verrons l'espace de noms en C uniquement si ces personnes s'organisent et créent une ou plusieurs extensions avec prise en charge de l'espace de noms. Ensuite, les organismes ISO n'auront d'autre choix que de les publier en standard (avec plus ou moins de modifications). C'est ainsi que javascript (qui a quelques similitudes avec C à cet égard) a fait.
themihai
3
@themihai: "create an extension" = demande aux personnes gcc et clang de compiler les espaces de noms.
einpoklum
1

C ne prend pas en charge les espaces de noms comme C ++. L'implémentation des espaces de noms C ++ modifie les noms. L'approche décrite ci-dessous vous permet de tirer parti des espaces de noms en C ++ tout en ayant des noms qui ne sont pas mutilés. Je me rends compte que la nature de la question est de savoir pourquoi C ne prend pas en charge les espaces de noms (et une réponse triviale serait que ce n'est pas le cas parce qu'il n'a pas été implémenté :)) J'ai juste pensé que cela pourrait aider quelqu'un à voir comment j'ai implémenté la fonctionnalité des modèles et des espaces de noms.

J'ai rédigé un tutoriel sur la façon de tirer parti des espaces de noms et / ou des modèles en utilisant C.

Espaces de noms et modèles en C

Espaces de noms et modèles en C (à l'aide de listes liées)

Pour l'espace de noms de base, on peut simplement préfixer le nom de l'espace de noms comme convention.

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

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

peut être écrit comme

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, ... );

Une deuxième approche dont j'avais besoin et qui utilise le concept d'espacement de noms et de modèles consiste à utiliser la concaténation de macros et à inclure. Par exemple, je peux créer un

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

en utilisant des fichiers modèles comme suit

multiply-template.h

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

multiply-template.c

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

Nous pouvons maintenant définir int_multiply comme suit. Dans cet exemple, je vais créer un fichier int_multiply.h / .c.

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"

À la fin de tout cela, vous aurez une fonction et un fichier d'en-tête pour.

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

J'ai créé un tutoriel beaucoup plus détaillé sur les liens fournis qui montrent comment cela fonctionne avec les listes liées. Espérons que cela aide quelqu'un!

Andy Curtis
la source
3
Vos liens expliquent comment ajouter des espaces de noms. Cependant, la question était de savoir pourquoi les espaces de noms ne sont pas pris en charge. Cette réponse n'est donc pas une réponse et devrait plutôt être un commentaire.
Thomas Weller