Dlaczego ANSI C nie ma przestrzeni nazw?

93

W większości języków posiadanie przestrzeni nazw wydaje się oczywiste. Ale o ile wiem, ANSI C go nie obsługuje. Dlaczego nie? Jakieś plany włączenia go do przyszłego standardu?

Pulkit Sinha
źródło
13
Użyj C ++ jako języka C z przestrzenią nazw!
AraK
3
Oczywiście mogę, ale nadal chciałbym wiedzieć
Pulkit Sinha,
5
2 rzeczy. Niepotrzebna charakterystyczna składnia: wszystkie inne języki z przestrzeniami nazw używają tylko znaku „.” jako separator, ponieważ nie jest niejednoznaczny z innymi zastosowaniami „.”. Co ważniejsze, c ++ nigdy nie wprowadził dyrektywy w zakresie using. Co oznaczało, że programiści nadużywali używania dyrektyw do importowania przestrzeni nazw do zasięgu globalnego. Co oznaczało, że komisja standardów C ++ nie może teraz dodawać nowych funkcji do std :: ever, ponieważ ilość kodu, który mógłby się zepsuć w wyniku, spowodowała, że ​​partycjonowanie stało się zbędne.
Chris Becke
2
@Chris Becke: Lubię charakterystyczną składnię. Lubię wiedzieć, czy patrzę na klasę w przestrzeni nazw, czy na członka klasy.
JeremyP
6
@ChrisBecke, to jest kilka lat później, ale interesujące jest to, że argumentujesz, że przestrzenie nazw C ++ były słabo zaimplementowane, więc nie powinny być implementowane w C. Potem zauważasz, że inne języki implementują je bez zawieszania się C ++. Jeśli inne języki to potrafią, dlaczego nie wprowadzić ich do C?
weberc2

Odpowiedzi:

68

C ma przestrzenie nazw. Jeden dla znaczników struktur, a drugi dla innych typów. Rozważ następującą definicję:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Pierwsza z nich ma tag foo, a druga jest przekształcana w typ foo z typedef. Nadal nie dochodzi do konfliktu nazw. Dzieje się tak, ponieważ tagi i typy struktur (typy wbudowane i typy zdefiniowane przez typ) znajdują się w oddzielnych przestrzeniach nazw.

To, czego C nie pozwala, to tworzenie nowej przestrzeni nazw według woli. C został ustandaryzowany, zanim uznano to za ważne w języku, a dodanie przestrzeni nazw również zagroziłoby wstecznej kompatybilności, ponieważ wymaga zmiany nazw, aby działała poprawnie. Myślę, że można to przypisać szczegółom technicznym, a nie filozofii.

EDYCJA: JeremyP na szczęście poprawił mnie i wspomniał o przestrzeniach nazw, które przegapiłem. Istnieją przestrzenie nazw dla etykiet i dla członków struktury / unii.


źródło
8
W rzeczywistości jest więcej niż dwie przestrzenie nazw. Oprócz dwóch wymienionych, istnieje przestrzeń nazw dla etykiet i przestrzeni nazw dla członków każdej struktury i unii.
JeremyP
@JeremyP: Wielkie dzięki za korektę. Tylko odpisałem to z pamięci, nie sprawdziłem standardu :-)
2
co z przestrzenią nazw dla funkcji?
themihai
8
Można to nazwać przestrzeniami nazw, ale uważam, że nie są to przestrzenie nazw, o które pytał OP.
avl_sweden
1
@jterm Nope. Nie jestem zwolennikiem hakowania funkcji C, a jedynie przedstawianie faktów. Każda structdefinicja deklaruje nową przestrzeń nazw dla swoich członków. Nie opowiadam się za wykorzystywaniem tego faktu, ani nie znam żadnych sposobów wykorzystania tego faktu, ponieważ structnie mogą one mieć statycznych elementów członkowskich.
JeremyP
101

Aby uzyskać kompletność, istnieje kilka sposobów osiągnięcia „korzyści”, które można uzyskać z przestrzeni nazw, w języku C.

Jedną z moich ulubionych metod jest użycie struktury do przechowywania zestawu wskaźników do metod, które są interfejsem do Twojej biblioteki / itd.

Następnie używasz zewnętrznej instancji tej struktury, którą inicjalizujesz w swojej bibliotece, wskazując na wszystkie funkcje. Pozwala to na zachowanie prostych nazw w bibliotece bez wchodzenia w przestrzeń nazw klienta (inną niż zmienna extern w zakresie globalnym, 1 zmienna a być może setki metod).

Wymaga to dodatkowej konserwacji, ale wydaje mi się, że jest ona minimalna.

Oto przykład:

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

Sposób użycia . syntax tworzy silne powiązanie z klasyczną metodą Library_function () Library_some_value. Istnieją jednak pewne ograniczenia, z jednej strony nie można używać makr jako funkcji.

Gość
źródło
12
... i czy kompilatory są wystarczająco inteligentne, aby „wyłuskiwać” wskaźnik funkcji w czasie kompilacji, kiedy to robisz library.method1()?
einpoklum
1
To jest niesamowite. Jedną rzecz, którą mógłbym dodać, jest to, że próbuję .cdomyślnie ustawić wszystkie moje funkcje w moich plikach jako statyczne, dlatego jedynymi ujawnionymi funkcjami są te, które są jawnie ujawnione w const structdefinicji w .cpliku.
lastmjs
3
To świetny pomysł, ale jak radzisz sobie ze stałymi i wyliczeniami?
nowox
1
@einpoklum - przepraszam za necro, ale przynajmniej od wersji 6.3.0, gcc obliczy rzeczywisty adres function1/ method2podczas kompilacji zarówno z, jak -O2i -flto. O ile nie skompilujesz takich bibliotek wraz z własnym źródłem, takie podejście doda trochę narzutu do wywołań funkcji.
Alex Reinking
3
@AlexReinking: Cóż, to miłe, ale nigdy nie uzyskalibyśmy wbudowanych funkcji. I - nekro jest świetne, przeprosiny nie są konieczne.
einpoklum
25

C ma przestrzenie nazw. Składnia to namespace_name. Możesz nawet zagnieździć je jak w general_specific_name. A jeśli chcesz mieć dostęp do nazw bez każdorazowego wpisywania nazwy przestrzeni nazw, umieść odpowiednie makra preprocesora w pliku nagłówkowym, np.

#define myfunction mylib_myfunction

Jest to o wiele czystsze niż zniekształcanie nazw i inne okrucieństwa, które niektóre języki dostarczają przestrzeni nazw.

R .. GitHub PRZESTAŃ POMÓC LODOWI
źródło
24
Ja to widzę inaczej. Komplikowanie gramatyki, wprowadzanie zniekształcania nazw na symbolach itp. W celu osiągnięcia czegoś, co było już trywialne w przypadku preprocesora, to coś, co nazwałbym brudnym hackiem i kiepskim projektem.
R .. GitHub STOP HELPING ICE
41
Nie wiem, jak naprawdę możesz poprzeć to stanowisko. Zapytaj społeczność Javascript o integrację projektów, gdy każdy inny system ma inny własny sposób na implementację przestrzeni nazw. Nigdy nie słyszałem, aby ktoś narzekał, że słowo kluczowe „przestrzeń nazw” lub „pakiet” powoduje zbyt dużą złożoność języka. Z drugiej strony, próby debugowania kodu zaśmieconego makrami mogą szybko się skomplikować!
weberc2,
5
Słyszałem wiele osób narzekających na zniekształcanie nazw w C ++ (z punktu widzenia debugowania, łańcucha narzędzi, kompatybilności ABI, dynamicznego wyszukiwania symboli, ...) i złożoności braku wiedzy, do czego właściwie odnosi się konkretna nazwa.
R .. GitHub PRZESTAŃ POMÓC NA LODZIE
6
@R .. To by się nie stało, gdyby nazwa zniekształcona w C ++ była znormalizowana. Samo to nie pomogłoby w kompatybilności ABI, ale zdecydowanie rozwiązałoby problem z mapowaniem nazw.
Malcolm,
20
Uważam, że to niewiarygodne, że ludzie z C będą się spierać o to z powagą. W C ++ jest wiele funkcji z ostrymi krawędziami, które przyprawiają ludzi o smutek. Przestrzenie nazw nie są jedną z tych funkcji. Są świetne, działają bardzo dobrze. I dla przypomnienia, nic nie jest trywialne w przypadku preprocesora. Wreszcie, demangling nazw jest trywialny, istnieje wiele narzędzi wiersza poleceń, które zrobią to za Ciebie.
Nir Friedman
12

Historycznie kompilatory C nie zmieniają nazw (robią to w systemie Windows, ale zniekształcają pliki cdecl konwencji wywoływania polega tylko na dodaniu przedrostka podkreślenia).

Ułatwia to korzystanie z bibliotek C z innych języków (w tym asemblera) i jest jednym z powodów, dla których często widzisz extern "C"opakowania dla interfejsów API C ++.

Christoph
źródło
2
Ale dlaczego to taki problem? To znaczy, załóżmy, że wszystkie nazwy w przestrzeni nazw zaczęłyby się od _da13cd6447244ab9a30027d3d0a08903, a następnie nazwa (to właśnie wygenerowany UUID v4)? Jest szansa, że ​​może to zepsuć nazwy, które używają tego konkretnego identyfikatora UUID, ale szansa ta wynosi zasadniczo zero. Więc w praktyce nie będzie problemu z manipulowaniem only_namespace_names .
einpoklum
7

tylko z powodów historycznych. w tamtym czasie nikt nie myślał o istnieniu czegoś w rodzaju przestrzeni nazw. Poza tym naprawdę starali się, aby język był prosty. Mogą to mieć w przyszłości

miłość
źródło
2
Czy w komitecie normalizacyjnym jest jakiś ruch dotyczący dodawania przestrzeni nazw do języka C w przyszłości? Czy przejście na moduł C / C ++ mogłoby to ułatwić w przyszłości?
lanoxx
1
@lanoxx Nie ma woli dodawania przestrzeni nazw do C z powodu wstecznej kompatybilności.
themihai
6

Nie odpowiedź, ale nie komentarz. C nie umożliwia namespacejawnego definiowania . Ma zmienny zakres. Na przykład:

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

Możesz używać nazw kwalifikowanych dla zmiennych i funkcji:

mylib.h

void mylib_init();
void mylib_sayhello();

Jedyna różnica w stosunku do przestrzeni nazw polega na tym, że nie możesz usingi nie możesz importować from mylib.

chaczik
źródło
Nie można też zamienić dwóch ostatnich wierszy, namespace mylib { void init(); void say_hello(); }które też są ważne (ish).
einpoklum
3

ANSI C zostało wynalezione zanim powstały przestrzenie nazw.

Crashworks
źródło
10
To było? Pierwsza specyfikacja ANSI C to rok 1989. Jestem prawie pewien, że przestrzenie nazw (w takiej czy innej formie) były wcześniej w językach programowania. Na przykład Ada została znormalizowana w 1983 roku i miała pakiety jako przestrzenie nazw. Te z kolei były zasadniczo oparte na modułach Modula-2.
TYLKO MOJA poprawna OPINIA
4
Nie datowałbym wynalazku ANSI C na datę oficjalnego przyjęcia jego specyfikacji; język istniał wcześniej, a specyfikacja po prostu dokumentowała to, co już tam było. Chociaż z niektórych odpowiedzi na tej stronie można by pomyśleć, że specyfikacja była pierwsza, a pierwszy kompilator po namyśle.
Crashworks
ANSI C rzeczywiście różniło się znacznie od wersji przed ANSI C, ale przestrzenie nazw nie były jedną z nich.
dan04
Tymczasem piszę go w 2020 roku, długo po powstaniu przestrzeni nazw. Najnowsze standardy C nadal ich nie mają. O ile C ma sens, jest to jedna cecha, której bardzo brakuje.
3

Ponieważ ludzie, którzy chcą dodać tę możliwość do C, nie spotkali się i nie zorganizowali, aby wywierać presję na zespoły autorów kompilatorów i treści ISO.

einpoklum
źródło
1
Myślę, że zobaczymy przestrzeń nazw w C tylko wtedy, gdy ci ludzie zorganizują się i utworzą rozszerzenie (a) z obsługą przestrzeni nazw. Wtedy organy ISO nie będą miały innego wyboru i będą publikować je w standardzie (z mniejszymi lub większymi zmianami). Tak właśnie zrobił javascript (który ma pod tym względem pewne podobieństwa z C).
themihai
3
@themihai: "utwórz rozszerzenie" = pobierz gcc i klanguj ludzi w celu skompilowania przestrzeni nazw.
einpoklum
1

C nie obsługuje przestrzeni nazw, takich jak C ++. Implementacja przestrzeni nazw C ++ zmienia nazwy. Podejście opisane poniżej umożliwia korzystanie z przestrzeni nazw w C ++ przy zachowaniu nazw, które nie są zniekształcone. Zdaję sobie sprawę, że natura pytania jest taka, dlaczego C nie obsługuje przestrzeni nazw (a trywialną odpowiedzią byłoby to, że nie, ponieważ nie zostało zaimplementowane :)). Pomyślałem, że ktoś może zobaczyć, jak zaimplementowałem funkcjonalność szablonów i przestrzeni nazw.

Napisałem samouczek na temat korzystania z przestrzeni nazw i / lub szablonów przy użyciu C.

Przestrzenie nazw i szablony w C

Przestrzenie nazw i szablony w C (przy użyciu połączonych list)

W przypadku podstawowej przestrzeni nazw można po prostu poprzedzić nazwę przestrzeni nazw jako konwencję.

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

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

można zapisać jako

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

Drugie podejście, którego potrzebowałem, wykorzystujące koncepcję przestrzeni nazw i szablonów, polega na użyciu konkatenacji makr i dołączania. Na przykład mogę utworzyć plik

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

używając plików szablonów w następujący sposób

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

Możemy teraz zdefiniować int_multiply w następujący sposób. W tym przykładzie utworzę plik 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"

Na końcu tego wszystkiego będziesz mieć funkcję i plik nagłówkowy dla.

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

Stworzyłem znacznie bardziej szczegółowy samouczek dotyczący podanych linków, który pokazuje, jak to działa z połączonymi listami. Mam nadzieję, że to komuś pomoże!

Andy Curtis
źródło
3
Twoje linki wyjaśniają, jak dodawać przestrzenie nazw. Pojawiło się jednak pytanie, dlaczego przestrzenie nazw nie są obsługiwane. Więc ta odpowiedź nie jest odpowiedzią i powinna być zamiast tego komentarzem.
Thomas Weller