Чому у ANSI C немає просторів імен?


92

Наявність простору імен здається незрозумілим для більшості мов. Але, наскільки я можу зрозуміти, ANSI C цього не підтримує. Чому ні? Будь-які плани включити його до майбутнього стандарту?


13
Використовуйте C ++ як C-with-namespace!
AraK

3
Звичайно, можу, але все одно хотів би знати
Pulkit Sinha

5
2 речі. Непотрібний відмітний синтаксис: Усі інші мови з просторами імен просто використовують '.' як роздільник, оскільки не є двозначним з іншими способами використання.. І, що є більш критичним, c ++ ніколи не вводив сферу застосування за допомогою директиви. Це означало, що програмісти зловживали використанням директив для імпорту простору імен у глобальну сферу. Це означало, що комітет стандартів c ++ тепер не може додавати нові функції до std :: ніколи, оскільки кількість коду, яка в результаті порушиться, зробила розділення зайвим.
Кріс Бекке,

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

6
@ChrisBecke, це запізнилося на кілька років, але цікаво, що ви стверджуєте, що простори імен C ++ були погано реалізовані, тому їх не слід реалізовувати в C. Тоді ви зазначаєте, що інші мови реалізують їх без зависань C ++. Якщо інші мови можуть це зробити, чому б не познайомити їх із С?
weberc2

Відповіді:


68

C має простори імен. Один для тегів структури, а другий для інших типів. Розглянемо наступне визначення:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Перший має тег foo, а пізній перетворюється на type foo за допомогою typedef. Досі не відбувається конфлікту імен. Це тому, що теги та типи структури (вбудовані типи та типи, визначені типом) живуть в окремих просторах імен.

Те, що C не дозволяє, - це створювати новий простір імен за заповітом. C був стандартизований до того, як це було визнано важливим у мові, і додавання просторів імен також загрожувало б зворотній сумісності, оскільки для правильної роботи потрібно керувати іменами. Думаю, це можна пояснити технічними особливостями, а не філософією.

EDIT: JeremyP, на щастя, виправив мене і згадав простори імен, які я пропустив. Існують простори імен для етикеток і для членів структури / об’єднання.


8
Насправді існує більше двох просторів імен. На додаток до двох, про які ви згадали, є простір імен для міток та простір імен для членів кожної структури та об'єднання.
JeremyP

@JeremyP: Велике спасибі за виправлення. Я лише списав цю пам'ять, я не перевірив стандарт :-)

2
як щодо простору імен для функцій?
themihai

8
Це цілком можна назвати просторами імен, але я вважаю, що це не такі простори імен, про які ОП запитував.
avl_sweden

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

99

Для повноти є кілька способів досягти "переваг", які ви можете отримати від просторів імен, в 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

1
Це так приголомшливо. Одне, що я міг би додати, я намагаюся зробити всі мої функції у моїх .cфайлах статичними за замовчуванням, отже, виставляються лише ті функції, які явно виставляються у const structвизначенні у .cфайлі.
lastmjs

3
Це чудова ідея, але як ви працюєте з константами та переліками?
nowox

1
@einpoklum - вибачте за некро, але принаймні станом на версію 6.3.0, gcc буде обчислювати фактичну адресу function1/ method2при компіляції з обома -O2та -flto. Якщо ви не скомпілюєте такі бібліотеки разом із власним джерелом, цей підхід додасть певних накладних витрат на виклики своїх функцій.
Алекс Рейнкінг,

3
@AlexReinking: Ну, це приємно, але ми ніколи б не вбудували ці функції. І - некро'нг - це чудово, ніяких вибачень не потрібно.
einpoklum

24

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

#define myfunction mylib_myfunction

Це набагато чистіше, ніж маніпулювання іменами та інші жорстокості, які певні мови здійснюють для надання просторів імен.


24
Я бачу це по-іншому. Ускладнення граматики, введення маніпуляції іменами на символах тощо для досягнення чогось, що вже було тривіально робити з препроцесором, - це те, що я б назвав брудним хаком і поганим дизайном.
R .. GitHub СТОП ДОПОМОГАЙ ЛЕД

41
Я не розумію, як ви справді можете підтримати цю позицію. Запитайте спільноту Javascript про інтеграцію проектів, коли кожна інша система має інший власний хак для впровадження просторів імен. Я ніколи не чув, щоб хтось скаржився на ключове слово "простір імен" або "пакет", що додає занадто багато складності їх мові. З іншого боку, спроба налагодження коду, заповненого макросами, може швидко стати волохатою!
weberc2

5
Я чув, як багато людей скаржиться на маніпуляції іменами на C ++ (з точки зору налагодження, набору інструментів, сумісності ABI, динамічного пошуку символів, ...) та складності невідомості, на що конкретно позначається ім'я.
R .. GitHub СТОП ДОПОМОГАЙ ЛЕДІ

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

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

12

Історично склалося, що компілятори C не спотворюють імена (вони роблять це у Windows, але керування умовами cdeclвиклику складається лише з додавання префікса підкреслення).

Це полегшує використання бібліотек C з інших мов (включаючи асемблер) і є однією з причин, чому ви часто бачите extern "C"обгортки для API C ++.


2
Але чому це така проблема? Я маю на увазі, припустимо, що всі імена з простором імен починатимуться з _da13cd6447244ab9a30027d3d0a08903, а потім імені (Це UUID v4, який я щойно створив)? Існує ймовірність, що це може зламати імена, які використовують цей конкретний UUID, але цей шанс по суті дорівнює нулю. Отже, на практиці не виникне проблем із керуванням простором_назви_назви_назви .
einpoklum

7

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


2
Чи існує якийсь рух у стандартному комітеті щодо додавання просторів імен до C у майбутньому? Можливо, з переходом на модуль C / C ++ це може спростити його в майбутньому?
lanoxx

1
@lanoxx Немає бажання додавати простори імен до C через причини зворотної сумісності.
themihai

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(); }що також важливо (ish).
einpoklum

3

ANSI C був винайдений раніше просторів імен.


10
Це було? Першою специфікацією ANSI C був 1989 рік. Я майже впевнений, що простори імен (в тій чи іншій формі) раніше були мовами програмування. Наприклад, Ада була стандартизована в 1983 році і мала пакети як простори імен. Вони, в свою чергу, по суті базувались на модулях Modula-2.
ТІЛЬКИ МОЙ правильний ДУМКА

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

ANSI C мав деякі суттєві відмінності від попередньої ANSI C, але простори імен не були одними з них.
dan04

Тим часом, я пишу це у 2020 році, значно після того, як з’явилися простори імен. Останні стандарти С їх досі не мають. Наскільки С має сенс, це одна особливість, яка дуже відсутня.

3

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


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

3
@themihai: "створити розширення" = отримати 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
Ваші посилання пояснюють, як додати простори імен. Однак питання полягало в тому, чому простори імен не підтримуються. Отже, ця відповідь не є відповіддю, і вона повинна бути коментарем.
і я впевнений, що мені не вистачає
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.