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?
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?
Respostas:
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.
struct
definiçã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 struct
s não podem ter membros estáticos.
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.
library.method1()
?
.c
arquivos estáticas por padrão, portanto, as únicas funções expostas são aquelas explicitamente expostas na const struct
definição do .c
arquivo.
function1
/ method2
ao compilar com -O2
e -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.
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.
Historicamente, os compiladores C não alteram os nomes (eles fazem no Windows, mas a alteração da cdecl
convençã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 ++.
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
Não é uma resposta, mas não um comentário. C não fornece uma maneira de definir namespace
explicitamente. 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 using
e não pode importar from mylib
.
namespace mylib { void init(); void say_hello(); }
quais também é importante (ish).
ANSI C foi inventado antes dos namespaces.
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.
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 (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!