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?
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?
Odpowiedzi:
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.
struct
definicja 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ż struct
nie mogą one mieć statycznych elementów członkowskich.
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.
library.method1()
?
.c
domyślnie ustawić wszystkie moje funkcje w moich plikach jako statyczne, dlatego jedynymi ujawnionymi funkcjami są te, które są jawnie ujawnione w const struct
definicji w .c
pliku.
function1
/ method2
podczas kompilacji zarówno z, jak -O2
i -flto
. O ile nie skompilujesz takich bibliotek wraz z własnym źródłem, takie podejście doda trochę narzutu do wywołań funkcji.
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.
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 ++.
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
Nie odpowiedź, ale nie komentarz. C nie umożliwia namespace
jawnego 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 using
i nie możesz importować from mylib
.
namespace mylib { void init(); void say_hello(); }
które też są ważne (ish).
ANSI C zostało wynalezione zanim powstały przestrzenie nazw.
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.
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!