Por que ANSI C não tem namespaces?

91

Ter namespaces parece óbvio para a maioria das linguagens. Mas, pelo que eu posso dizer, ANSI C não o suporta. Por que não? Algum plano de incluí-lo em um padrão futuro?

Pulkit Sinha
fonte
13
Use C ++ como C com namespace!
AraK
3
Posso, claro, mas ainda gostaria de saber
Pulkit Sinha
5
2 coisas. Uma sintaxe distinta desnecessária: todas as outras linguagens com namespaces usam apenas '.' como separador, pois não é ambíguo com outros usos de '.'. E, mais criticamente, c ++ nunca introduziu uma diretiva using com escopo. O que significa que os programadores abusaram do uso de diretivas para importar namespaces para o escopo global. O que significava que o comitê de padrões c ++ agora não pode adicionar novos recursos a std :: ever, pois a quantidade de código que quebraria como resultado tornou o particionamento redundante.
Chris Becke
2
@Chris Becke: Gosto de sintaxe distinta. Gosto de saber se estou olhando para uma classe em um espaço de nomes ou para um membro de uma classe.
JeremyP
6
@ChrisBecke, isto está alguns anos atrasado, mas é interessante que você argumente que os namespaces C ++ foram mal implementados, então eles não deveriam ser implementados em C. Então você notou que outras linguagens os implementam sem os problemas do C ++. Se outras linguagens podem fazer isso, por que não apresentá-los ao C?
weberc2

Respostas:

67

C tem namespaces. Um para tags de estrutura e outro para outros tipos. Considere a seguinte definição:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

O primeiro possui tag foo, e o último é transformado no tipo foo com um typedef. Ainda assim, nenhum conflito de nomes acontece. Isso ocorre porque as tags e os tipos de estrutura (tipos integrados e tipos definidos por tipo) vivem em namespaces separados.

O que C não permite é criar um novo namespace por vontade própria. C foi padronizado antes de ser considerado importante em uma linguagem, e adicionar namespaces também ameaçaria a compatibilidade com versões anteriores, porque requer a mutilação de nomes para funcionar corretamente. Acho que isso pode ser atribuído a aspectos técnicos, não à filosofia.

EDIT: JeremyP felizmente me corrigiu e mencionou os namespaces que perdi. Existem namespaces para rótulos e também para membros de estrutura / união.


fonte
8
Na verdade, existem mais de dois espaços de nome. Além dos dois que você mencionou, há um espaço de nomes para rótulos e espaços de nomes para os membros de cada estrutura e união.
JeremyP
@JeremyP: Muito obrigado pela correção. Eu só escrevi isso de memória, não verifiquei o padrão :-)
2
e quanto ao namespace para funções?
themihai
8
Isso pode muito bem ser chamado de namespaces, mas acredito que esses não são o tipo de namespaces que o OP estava perguntando.
avl_sweden
1
@jterm Não. Não estou defendendo a hackeagem de recursos C, apenas afirmando os fatos. Cada structdefinição declara um novo namespace para seus membros. Não estou defendendo a exploração desse fato, nem estou ciente de qualquer meio de explorá-lo, já que structs não podem ter membros estáticos.
JeremyP
98

Para completar, existem várias maneiras de obter os "benefícios" que você pode obter dos namespaces, em C.

Um dos meus métodos favoritos é usar uma estrutura para abrigar vários ponteiros de método que são a interface para sua biblioteca / etc.

Você então usa uma instância externa dessa estrutura que você inicializa dentro de sua biblioteca apontando para todas as suas funções. Isso permite que você mantenha seus nomes simples em sua biblioteca sem pisar no namespace do cliente (além da variável externa no escopo global, 1 variável vs possivelmente centenas de métodos ..)

Há alguma manutenção adicional envolvida, mas sinto que é mínima.

Aqui está um exemplo:

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

O uso de . a sintaxe cria uma associação forte sobre o método clássico Library_function () Library_some_value. Existem algumas limitações, no entanto, para quem você não pode usar macros como funções.

convidado
fonte
12
... e os compiladores são inteligentes o suficiente para "desreferenciar" o ponteiro de função em tempo de compilação quando você o faz library.method1()?
einpoklum
1
Isso é tão incrível. Uma coisa que devo acrescentar, estou tentando tornar todas as minhas funções em meus .carquivos estáticas por padrão, portanto, as únicas funções expostas são aquelas explicitamente expostas na const structdefinição do .carquivo.
lastmjs 01 de
3
É uma ótima ideia, mas como você lida com constantes e enums?
nowox de
1
@einpoklum - desculpe necro, mas pelo menos a partir da versão 6.3.0, o gcc irá computar o endereço real de function1/ method2ao compilar com -O2e -flto. A menos que você compile essas bibliotecas junto com seu próprio código-fonte, essa abordagem adicionará alguma sobrecarga às chamadas de função.
Alex Reinking
3
@AlexReinking: Bem, isso é bom, mas nunca teríamos essas funções embutidas. E - necro'ing é ótimo, não há necessidade de desculpas.
einpoklum
24

C tem namespaces. A sintaxe é namespace_name. Você pode até aninhá-los como em general_specific_name. E se você quiser ser capaz de acessar nomes sem escrever o nome do namespace todas as vezes, inclua as macros de pré-processador relevantes em um arquivo de cabeçalho, por exemplo

#define myfunction mylib_myfunction

Isso é muito mais limpo do que mutilar nomes e outras atrocidades que certas linguagens cometem para entregar espaços de nomes.

R .. GitHub PARAR DE AJUDAR O GELO
fonte
24
Eu vejo isso de forma diferente. Complicar a gramática, introduzir nomes mutilados em símbolos, etc. para conseguir algo que já era trivial de fazer com o pré-processador é o que eu chamaria de hack sujo e design pobre.
R .. GitHub PARAR DE AJUDAR O ICE
40
Não vejo como você pode realmente apoiar essa posição. Pergunte à comunidade Javascript sobre a integração de projetos quando todos os outros sistemas têm um hack caseiro diferente para implementar namespaces. Eu nunca ouvi ninguém reclamar sobre a palavra-chave 'namespace' ou 'pacote' adicionar muita complexidade à sua linguagem. Por outro lado, tentar depurar código repleto de macros pode ficar complicado rápido!
weberc2
5
Já ouvi muitas pessoas reclamarem sobre a mutilação de nomes C ++ (do ponto de vista de depuração, conjunto de ferramentas, compatibilidade ABI, pesquisa de símbolo dinâmico, ...) e a complexidade de não saber a que um determinado nome está realmente se referindo.
R .. GitHub PARAR DE AJUDAR O ICE
6
@R .. Isso não aconteceria se o nome mutilado em C ++ fosse padronizado. Isso por si só não ajudaria com a compatibilidade de ABI, mas definitivamente resolveria o problema de mapeamento de nomes.
Malcolm
19
Acho incrível que as pessoas C vão realmente argumentar isso com uma cara séria. Existem muitos recursos em C ++ com bordas afiadas que causam tristeza às pessoas. Os namespaces não são um desses recursos. Eles são ótimos, funcionam muito bem. E nada é trivial com o pré-processador, só para constar. Finalmente, remover nomes é trivial, existem vários utilitários de linha de comando que farão isso por você.
Nir Friedman de
12

Historicamente, os compiladores C não alteram os nomes (eles fazem no Windows, mas a alteração da cdeclconvenção de chamada consiste em apenas adicionar um prefixo de sublinhado).

Isso facilita o uso de bibliotecas C de outras linguagens (incluindo assembler) e é um dos motivos pelos quais você costuma ver extern "C"wrappers para APIs C ++.

Christoph
fonte
2
Mas por que isso é um problema tão grande? Quer dizer, suponha que todos os nomes com espaço de nomes comecem com _da13cd6447244ab9a30027d3d0a08903 e depois com o nome (é um UUID v4 que acabei de gerar). Há uma chance de que isso possa quebrar nomes que usam este UUID específico, mas essa chance é essencialmente zero. Portanto, na prática, não haverá problema em mutilar only_namespace_names .
einpoklum
7

apenas razões históricas. ninguém pensou em ter algo como um namespace naquela época. Além disso, eles estavam realmente tentando manter a linguagem simples. Eles podem ter isso no futuro

amor
fonte
2
Existe algum movimento no comitê padrão para adicionar namespaces a C no futuro? Possível com a mudança para o módulo C / C ++, isso pode facilitar no futuro?
lanoxx de
1
@lanoxx Não há vontade de adicionar namespaces a C por motivos de compatibilidade com versões anteriores.
themihai
6

Não é uma resposta, mas não um comentário. C não fornece uma maneira de definir namespaceexplicitamente. Ele tem escopo variável. Por exemplo:

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

Você pode usar nomes qualificados para variáveis ​​e funções:

mylib.h

void mylib_init();
void mylib_sayhello();

A única diferença dos namespaces é que você não pode ser usinge não pode importar from mylib.

Khachik
fonte
Você também não pode substituir as duas últimas linhas pelas namespace mylib { void init(); void say_hello(); }quais também é importante (ish).
einpoklum
3

ANSI C foi inventado antes dos namespaces.

Crashworks
fonte
10
Isso foi? A primeira especificação ANSI C foi 1989. Tenho certeza de que os namespaces (de uma forma ou de outra) existiam em linguagens de programação antes disso. Ada, por exemplo, foi padronizado em 1983 e tinha pacotes como namespaces. Esses, por sua vez, eram essencialmente baseados em módulos Modula-2.
APENAS MINHA OPINIÃO correta
4
Eu não daria a invenção do ANSI C quando sua especificação foi oficialmente adotada; a linguagem existia de antemão e a especificação apenas documentava o que já existia. Embora a partir de algumas das respostas neste site, alguém possa pensar que a especificação veio primeiro e o primeiro compilador foi uma reflexão tardia.
Crashworks
ANSI C tinha algumas diferenças significativas do pré-ANSI C, mas os namespaces não eram um deles.
dan04 de
3

Porque as pessoas que desejam adicionar esse recurso ao C não se reuniram e se organizaram para colocar alguma pressão nas equipes de autores de compiladores e nos órgãos ISO.

einpoklum
fonte
1
Acho que veremos namespace em C apenas se essas pessoas se organizassem e criassem uma extensão com suporte a namespace. Então os órgãos ISO não terão escolha a não ser publicá-los como padrão (com mais ou menos alterações). É assim que o javascript (que tem algumas semelhanças com C nesse aspecto) fez isso.
themihai,
3
@themihai: "criar uma extensão" = obter o gcc e clang people para compilar namespaces.
einpoklum
1

C não oferece suporte a namespaces como C ++. A implementação de namespaces C ++ destrói os nomes. A abordagem descrita a seguir permite que você obtenha o benefício de namespaces em C ++ enquanto tem nomes que não são mutilados. Eu percebo que a natureza da questão é por que C não suporta namespaces (e uma resposta trivial seria que não, porque não foi implementado :)). Só pensei que poderia ajudar alguém a ver como implementei a funcionalidade de modelos e namespaces.

Eu escrevi um tutorial sobre como obter a vantagem de namespaces e / ou modelos usando C.

Namespaces e modelos em C

Namespaces e modelos em C (usando listas vinculadas)

Para o namespace básico, pode-se simplesmente prefixar o nome do namespace como uma convenção.

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

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

pode ser escrito como

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

Uma segunda abordagem necessária que usa o conceito de namespacing e modelos é usar a concatenação de macro e incluir. Por exemplo, posso criar um

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

usando arquivos de modelo como segue

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

Agora podemos definir int_multiply da seguinte maneira. Neste exemplo, vou criar um arquivo 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"

No final de tudo isso, você terá uma função e um arquivo de cabeçalho para.

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

Criei um tutorial muito mais detalhado nos links fornecidos, que mostram como funciona com listas vinculadas. Espero que isso ajude alguém!

Andy Curtis
fonte
3
Seus links explicam como adicionar namespaces. No entanto, a questão era por que os namespaces não são suportados. Portanto, essa resposta não é uma resposta e deve ser um comentário.
Thomas Weller