Почему в ANSI C нет пространств имен?


93

Для большинства языков наличие пространств имен кажется легкой задачей. Но насколько я могу судить, ANSI C его не поддерживает. Почему нет? Есть ли планы включить его в будущий стандарт?


13
Используйте C ++ как C-with-namespace!
AraK

3
Я могу, конечно, но мне все равно хотелось бы знать
Pulkit Sinha

5
2 вещи. Излишний отличительный синтаксис: все остальные языки с пространствами имен просто используют '.' как разделитель, поскольку он не противоречит другим видам использования '.'. И, что более важно, в C ++ никогда не вводилась директива scoped using. Это означало, что программисты злоупотребляли директивами using для импорта пространств имен в глобальную область видимости. Это означало, что комитет по стандартам C ++ теперь не может добавлять новые функции в std :: ever, поскольку объем кода, который в результате может сломаться, сделал разделение ненужным.
Крис Бек,

2
@Chris Becke: Мне нравится отличительный синтаксис. Мне нравится знать, смотрю ли я на класс в пространстве имен или на член в классе.
JeremyP

6
@ChrisBecke, это опоздание на несколько лет, но интересно, что вы утверждаете, что пространства имен C ++ были плохо реализованы, поэтому их не следует реализовывать на C. Затем вы замечаете, что другие языки реализуют их без зависаний C ++. Если другие языки могут это сделать, почему бы не познакомить их с C?
weberc2

Ответы:


68

У C есть пространства имен. Один для структурных тегов и один для других типов. Рассмотрим следующее определение:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Первый имеет тег foo, а второй преобразован в тип foo с typedef. По-прежнему не происходит конфликта имен. Это связано с тем, что структурные теги и типы (встроенные типы и типы с определением типа) находятся в отдельных пространствах имен.

C не позволяет создавать новое пространство имен по своему желанию. C был стандартизирован до того, как это стало считаться важным для языка, и добавление пространств имен также угрожало бы обратной совместимости, потому что для правильной работы требуется искажение имен. Я думаю, это можно объяснить техническими особенностями, а не философией.

РЕДАКТИРОВАТЬ: JeremyP, к счастью, поправил меня и упомянул пропущенные мной пространства имен. Существуют пространства имен для меток, а также для членов структуры / объединения.


8
На самом деле существует более двух пространств имен. В дополнение к двум, которые вы упомянули, существует пространство имен для меток и пространства имен для членов каждой структуры и объединения.
JeremyP

@JeremyP: Большое спасибо за исправление. Я только списал это на память, стандарт не проверял :-)

2
как насчет пространства имен для функций?
themihai

8
Это вполне можно назвать пространствами имен, но я считаю, что это не те пространства имен, о которых спрашивал OP.
avl_sweden

1
@jterm Нет. Я не защищаю взлом C-функций, просто констатирую факты. Каждое structопределение объявляет новое пространство имен для своих членов. Я не защищаю использование этого факта, и я не знаю никаких средств его использования, поскольку structs не может иметь статических членов.
JeremyP

101

Для полноты картины существует несколько способов добиться «преимуществ», которые вы можете получить от пространств имен в C.

Один из моих любимых методов - это использование структуры для размещения множества указателей на методы, которые являются интерфейсом к вашей библиотеке и т. Д.

Затем вы используете внешний экземпляр этой структуры, который вы инициализируете внутри своей библиотеки, указывая на все ваши функции. Это позволяет вам сохранять ваши имена простыми в вашей библиотеке, не наступая на пространство имен клиентов (кроме переменной extern в глобальной области видимости, 1 переменной и, возможно, сотен методов ..)

Требуется дополнительное обслуживание, но я считаю, что оно минимальное.

Вот пример:

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

Использование . синтаксис создает сильную ассоциацию с классическим методом Library_function () Library_some_value. Однако есть некоторые ограничения, например, вы не можете использовать макросы как функции.


12
... и достаточно ли умен компиляторы, чтобы "разыменовать" указатель функции во время компиляции, когда вы это делаете library.method1()?
einpoklum 01

1
Это так здорово. Могу добавить, что я пытаюсь сделать все свои функции в моих .cфайлах статическими по умолчанию, поэтому единственными открытыми функциями являются те, которые явно указаны в const structопределении в .cфайле.
lastmjs 01

3
Это отличная идея, но как вы работаете с константами и перечислениями?
nowox

1
@einpoklum - извините за necro, но, по крайней мере, с версии 6.3.0, gcc будет вычислять фактический адрес function1/ method2при компиляции с обоими -O2и -flto. Если вы не скомпилируете такие библиотеки вместе со своим собственным источником, этот подход добавит некоторые накладные расходы на его вызовы функций.
Alex Reinking

3
@AlexReinking: Что ж, это хорошо, но у нас никогда не будет встроенных функций. И - некро'инг великолепен, никаких извинений не требуется.
einpoklum

25

В C есть пространства имен. Синтаксис такой namespace_name. Вы даже можете вложить их как в general_specific_name. И если вы хотите иметь возможность доступа к именам, не записывая каждый раз имя пространства имен, включите соответствующие макросы препроцессора в файл заголовка, например

#define myfunction mylib_myfunction

Это намного чище, чем искажение имен и другие злодеяния, которые некоторые языки совершают для доставки пространств имен.


24
Я вижу это иначе. Усложнение грамматики, введение искажения имен в символы и т. Д. Для достижения чего-то, что уже было тривиально для препроцессора, - это то, что я бы назвал грязным приемом и плохим дизайном.
R .. GitHub ПЕРЕСТАНОВИТЬ ПОМОЩЬ ICE

41
Я не понимаю, как вы действительно можете поддержать эту позицию. Спросите сообщество Javascript об интеграции проектов, когда в каждой другой системе есть свой собственный хакерский метод для реализации пространств имен. Я никогда не слышал, чтобы кто-то жаловался на ключевое слово «пространство имен» или «пакет», усложняющее их язык. С другой стороны, попытки отладить код, заваленный макросами, могут быстро стать непосильными!
weberc2 04

5
Я слышал, как многие люди жалуются на искажение имен C ++ (с точки зрения отладки, инструментальной цепочки, совместимости с ABI, динамический поиск символов и т. Д.) И сложность незнания того, к чему на самом деле относится конкретное имя.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE 04 июл.13,

6
@R .. Этого бы не случилось, если бы искажение имен в C ++ было стандартизировано. Само по себе это не поможет с совместимостью с ABI, но определенно решит проблему сопоставления имен.
Малькольм

20
Мне кажется невероятным, что люди Си на самом деле спорили с этим с невозмутимым видом. В C ++ есть множество особенностей с острыми гранями, которые вызывают у людей горе. Пространства имен не являются одной из этих функций. Они молодцы, очень хорошо работают. И с препроцессором, к сведению, нет ничего тривиального. Наконец, разобрать имена тривиально, существует множество утилит командной строки, которые сделают это за вас.
Нир Фридман

12

Исторически сложилось так, что компиляторы C не искажают имена (они это делают в Windows, но искажают cdecl соглашения о вызовах состоит только в добавлении префикса подчеркивания).

Это упрощает использование библиотек C из других языков (включая ассемблер) и является одной из причин, по которой вы часто видите extern "C"оболочки для API C ++.


2
Но почему это такая проблема? Я имею в виду, предположим, что все имена в пространстве имен будут начинаться с _da13cd6447244ab9a30027d3d0a08903, а затем с имени (это UUID v4, который я только что сгенерировал)? Есть вероятность, что это может сломать имена, которые используют этот конкретный UUID, но этот шанс практически равен нулю. Так что на практике проблем с изменением only_namespace_names не возникнет .
einpoklum 01

7

просто исторические причины. в то время никто не думал о существовании чего-то вроде пространства имен. Кроме того, они действительно пытались сделать язык простым. У них может быть это в будущем


2
Есть ли какие-нибудь движения в стандартном комитете по добавлению пространств имен в C в будущем? Возможно ли, что с переходом на модуль C / C ++ это может упростить задачу в будущем?
lanoxx

1
@lanoxx Нет желания добавлять пространства имен в C по причинам обратной совместимости.
themihai 06

6

Не ответ, но не комментарий. C не предоставляет возможности namespaceявного определения . Он имеет переменную область видимости. Например:

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

Вы можете использовать полные имена для переменных и функций:

mylib.h

void mylib_init();
void mylib_sayhello();

Единственное отличие от пространств имен в том, что вы не usingможете и не можете импортировать from mylib.


Вы также не можете заменить последние две строки, namespace mylib { void init(); void say_hello(); }что тоже важно (иш).
einpoklum 01

3

ANSI C был изобретен до появления пространств имен.


10
Это было? Первая спецификация ANSI C была выпущена в 1989 году. Я почти уверен, что пространства имен (в той или иной форме) были в языках программирования до этого. Например, Ada была стандартизирована в 1983 году и имела пакеты в качестве пространств имен. Те, в свою очередь, были в основном основаны на модулях Модула-2.
ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ

4
Я бы не стал датировать изобретение ANSI C моментом официального принятия его спецификации; язык существовал заранее, а спецификация просто документировала то, что уже было. Хотя из некоторых ответов на этом сайте можно подумать, что спецификация появилась первой, а первый компилятор - второстепенным.
Crashworks

ANSI C действительно имел некоторые существенные отличия от предшествующего ANSI C, но пространства имен не входили в их число.
dan04

Между тем, я пишу его в 2020 году, намного позже появления пространств имен. В последних стандартах C их до сих пор нет. Насколько C имеет смысл, это одна функция, которой очень не хватает.

3

Потому что люди, которые хотят добавить эту возможность в C, не собрались вместе и не организовались, чтобы оказать некоторое давление на команды разработчиков компиляторов и тела ISO.


1
Я думаю, что мы увидим пространство имен в C, только если эти люди организуются и создадут расширение (я) с поддержкой пространства имен. Тогда органам ISO не останется ничего иного, кроме как опубликовать их как стандарт (с более или менее изменениями). Вот как это сделал javascript (который в этом отношении имеет некоторое сходство с C).
themihai

3
@themihai: "create an extension" = заставить gcc и clang людей скомпилировать пространства имен.
einpoklum

1

C не поддерживает пространства имен, такие как C ++. Реализация пространств имен C ++ искажает имена. Подход, описанный ниже, позволяет вам получить преимущества пространств имен в C ++, имея при этом имена, которые не искажаются. Я понимаю, что суть вопроса в том, почему C не поддерживает пространства имен (и тривиальный ответ будет, что нет, потому что он не был реализован :)). Я просто подумал, что это может помочь кому-то увидеть, как я реализовал функциональность шаблонов и пространств имен.

Я написал учебник о том, как получить преимущество пространств имен и / или шаблонов с помощью C.

Пространства имен и шаблоны в C

Пространства имен и шаблоны в C (с использованием связанных списков)

Для основного пространства имен можно просто указать префикс имени пространства имен в качестве соглашения.

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

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

можно записать как

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

Второй подход, который мне понадобился, использующий концепцию пространств имен и шаблонов, - это использование конкатенации макросов и включения. Например, я могу создать

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

используя файлы шаблонов следующим образом

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

Теперь мы можем определить int_multiply следующим образом. В этом примере я создам файл 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"

В конце всего этого у вас будет функция и файл заголовка для.

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

Я создал гораздо более подробное руководство по предоставленным ссылкам, которое показывает, как это работает со связанными списками. Надеюсь, это кому-то поможет!


3
Ваши ссылки объясняют, как добавлять пространства имен. Однако возник вопрос, почему не поддерживаются пространства имен. Так что этот ответ не является ответом и должен быть комментарием.
Thomas Weller
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.