Mengapa ANSI C tidak memiliki namespace?


93

Memiliki ruang nama sepertinya tidak perlu dipikirkan untuk kebanyakan bahasa. Tapi sejauh yang saya tahu, ANSI C tidak mendukungnya. Kenapa tidak? Adakah rencana untuk memasukkannya ke dalam standar masa depan?


13
Gunakan C ++ sebagai C-with-namespace!
AraK

3
Tentu saja saya bisa, tetapi saya masih ingin tahu
Pulkit Sinha

5
2 hal. Sintaks khusus yang tidak perlu: Semua bahasa lain dengan ruang nama hanya menggunakan '.' sebagai pemisah karena tidak ambigu dengan penggunaan lain dari '.'. Dan, yang lebih penting, c ++ tidak pernah memperkenalkan scoped menggunakan direktif. Yang berarti pemrogram terlalu sering menggunakan arahan untuk mengimpor namespace ke dalam cakupan global. Yang berarti bahwa komite standar c ++ sekarang tidak dapat menambahkan fitur baru ke std :: ever karena jumlah kode yang akan rusak akibatnya membuat partisi menjadi berlebihan.
Chris Becke

2
@ Chris Becke: Saya suka sintaks yang berbeda. Saya ingin tahu apakah saya melihat kelas di ruang nama atau anggota di kelas.
JeremyP

6
@ChrisBecke, ini terlambat beberapa tahun, tetapi menarik bahwa Anda berpendapat bahwa namespace C ++ tidak diimplementasikan dengan baik, sehingga tidak boleh diterapkan di C. Kemudian Anda perhatikan bahwa bahasa lain menerapkannya tanpa hangup C ++. Jika bahasa lain bisa melakukannya, mengapa tidak memperkenalkan mereka ke C?
weberc2

Jawaban:


68

C memang memiliki ruang nama. Satu untuk tag struktur, dan satu untuk jenis lainnya. Perhatikan definisi berikut:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Yang pertama memiliki tag foo, dan yang selanjutnya dibuat menjadi tipe foo dengan typedef. Masih tidak ada bentrokan nama yang terjadi. Ini karena tag dan tipe struktur (tipe bawaan dan tipe yang diketik) tinggal di ruang nama yang terpisah.

Yang tidak diizinkan C adalah membuat namespace baru dengan keinginan. C distandarisasi sebelum ini dianggap penting dalam suatu bahasa, dan menambahkan namespace juga akan mengancam kompatibilitas ke belakang, karena memerlukan name mangling agar berfungsi dengan benar. Saya pikir ini dapat dikaitkan karena teknis, bukan filosofi.

EDIT: Untungnya JeremyP mengoreksi saya dan menyebutkan ruang nama yang saya lewatkan. Ada ruang nama untuk label dan untuk anggota struct / union juga.


8
Sebenarnya ada lebih dari dua ruang nama. Selain dua yang Anda sebutkan, ada ruang nama untuk label dan ruang nama untuk anggota setiap struct dan union.
JeremyP

@JeremyP: Terima kasih banyak atas koreksinya. Saya hanya menghapus ini dari memori, saya tidak memeriksa standar :-)

2
bagaimana dengan namespace untuk fungsi?
themihai

8
Ini mungkin disebut ruang nama, tapi saya yakin ini bukan jenis ruang nama yang ditanyakan OP.
avl_sweden

1
@Tidak. Saya tidak menganjurkan meretas fitur C, hanya menyatakan fakta. Setiap structdefinisi mendeklarasikan namespace baru untuk anggotanya. Saya tidak menganjurkan untuk mengeksploitasi fakta itu, saya juga tidak mengetahui cara apa pun untuk mengeksploitasinya karena structtidak boleh memiliki anggota statis.
JeremyP

101

Untuk kelengkapan ada beberapa cara untuk mencapai "manfaat" yang mungkin Anda peroleh dari namespace, di C.

Salah satu metode favorit saya adalah menggunakan struktur untuk menampung banyak penunjuk metode yang merupakan antarmuka ke perpustakaan Anda / dll ..

Anda kemudian menggunakan contoh eksternal dari struktur ini yang Anda inisialisasi di dalam perpustakaan Anda yang menunjuk ke semua fungsi Anda. Ini memungkinkan Anda untuk membuat nama Anda tetap sederhana di perpustakaan tanpa menginjak ruang nama klien (selain variabel eksternal pada cakupan global, 1 variabel vs mungkin ratusan metode ..)

Ada beberapa perawatan tambahan yang terlibat tetapi saya merasa ini minimal.

Berikut ini contohnya:

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

Penggunaan. sintaks membuat asosiasi yang kuat atas metode Library_function () Library_some_value klasik. Namun ada beberapa batasan, untuk satu Anda tidak dapat menggunakan makro sebagai fungsi.


12
... dan apakah kompiler cukup pintar untuk "mendereferensi" penunjuk fungsi pada waktu kompilasi saat Anda melakukannya library.method1()?
einpoklum

1
Ini luar biasa. Satu hal yang dapat saya tambahkan, saya mencoba membuat semua fungsi saya di .cfile saya statis secara default, jadi satu-satunya fungsi yang diekspos adalah yang secara eksplisit diekspos dalam const structdefinisi di .cfile.
lastmjs

3
Itu ide yang bagus, tapi bagaimana Anda menangani konstanta dan enum?
nowox

1
@einpoklum - maaf untuk necro, tetapi setidaknya pada versi 6.3.0, gcc akan menghitung alamat sebenarnya dari function1/ method2saat mengkompilasi dengan -O2dan -flto. Kecuali jika Anda mengompilasi pustaka tersebut bersama dengan sumber Anda sendiri, pendekatan ini akan menambahkan beberapa overhead ke pemanggilan fungsinya.
Alex Reinking

3
@AlexReinking: Ya, itu bagus, tapi kami tidak akan pernah mendapatkan fungsi ini sebaris. Dan - necro'ing itu bagus, tidak perlu meminta maaf.
einpoklum

25

C memiliki ruang nama. Sintaksnya adalah namespace_name. Anda bahkan dapat menyarangkannya seperti di general_specific_name. Dan jika Anda ingin dapat mengakses nama tanpa menulis nama namespace setiap saat, sertakan makro preprocessor yang relevan di file header, mis.

#define myfunction mylib_myfunction

Ini jauh lebih bersih daripada merusak nama dan kekejaman lain yang dilakukan bahasa tertentu untuk memberikan ruang nama.


24
Saya melihatnya secara berbeda. Mempersulit tata bahasa, memperkenalkan nama mangling pada simbol, dll. Untuk mencapai sesuatu yang sudah sepele untuk dilakukan dengan preprocessor adalah apa yang saya sebut sebagai hack kotor dan desain yang buruk.
R .. GitHub STOP HELPING ICE

41
Saya tidak melihat bagaimana Anda dapat benar-benar mendukung posisi itu. Tanyakan komunitas Javascript tentang mengintegrasikan proyek ketika setiap sistem lain memiliki peretasan yang tumbuh di dalam negeri untuk mengimplementasikan ruang nama. Saya tidak pernah mendengar ada orang yang mengeluh tentang kata kunci 'namespace' atau 'paket' yang menambahkan terlalu banyak kerumitan pada bahasa mereka. Di sisi lain, mencoba men-debug kode yang dipenuhi dengan makro bisa menjadi sangat cepat!
weberc2

5
Saya pernah mendengar banyak orang mengeluh tentang nama C ++ mangling (dari sudut pandang debugging, toolchain, kompatibilitas ABI, pencarian simbol dinamis, ...) dan kompleksitas karena tidak mengetahui apa nama tertentu sebenarnya merujuk.
R .. GitHub STOP HELPING ICE

6
@R .. Itu tidak akan terjadi jika nama mangling di C ++ distandarisasi. Ini saja tidak akan membantu kompatibilitas ABI, tetapi pasti akan memperbaiki masalah pemetaan nama.
Malcolm

20
Saya merasa heran bahwa orang C benar-benar akan mendebat ini dengan wajah lurus. Ada banyak fitur di C ++ dengan ujung tajam yang membuat orang sedih. Namespaces bukan salah satu fitur tersebut. Mereka hebat, mereka bekerja dengan sangat baik. Dan tidak ada yang sepele dengan preprocessor, sebagai catatan. Akhirnya, menghapus nama-nama itu sepele, ada banyak utilitas baris perintah yang akan melakukannya untuk Anda.
Nir Friedman

12

Secara historis, kompiler C tidak mengotak-atik nama (mereka melakukannya di Windows, tetapi penguraian untuk cdeclkonvensi pemanggilan hanya terdiri dari penambahan awalan garis bawah).

Ini membuatnya mudah untuk menggunakan pustaka C dari bahasa lain (termasuk assembler) dan merupakan salah satu alasan mengapa Anda sering melihat extern "C"pembungkus untuk C ++ API.


2
Tapi kenapa itu jadi masalah? Maksud saya, misalkan semua nama dengan namespace akan dimulai dengan _da13cd6447244ab9a30027d3d0a08903 dan kemudian nama (Itu adalah UUID v4 yang baru saja saya buat)? Ada kemungkinan ini dapat merusak nama yang menggunakan UUID khusus ini, tetapi peluang itu pada dasarnya nol. Jadi dalam prakteknya tidak akan ada masalah mangling only_namespace_names .
einpoklum

7

hanya alasan sejarah. tidak ada yang berpikir untuk memiliki sesuatu seperti namespace pada saat itu. Juga mereka benar-benar berusaha menjaga bahasanya tetap sederhana. Mereka mungkin memilikinya di masa depan


2
Apakah ada gerakan dalam komite standar untuk menambahkan namespace ke C di masa mendatang? Mungkinkah dengan pindah ke modul C / C ++ ini bisa membuatnya lebih mudah di masa depan?
lanoxx

1
@lanoxx Tidak ada keinginan untuk menambahkan namespace ke C karena alasan kompatibilitas ke belakang.
themihai

6

Bukan jawaban, tapi bukan komentar. C tidak menyediakan cara untuk mendefinisikan namespacesecara eksplisit. Ini memiliki ruang lingkup variabel. Sebagai contoh:

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

Anda dapat menggunakan nama yang memenuhi syarat untuk variabel dan fungsi:

mylib.h

void mylib_init();
void mylib_sayhello();

Satu-satunya perbedaan dari namespace adalah bahwa Anda tidak dapat usingdan tidak dapat mengimpor from mylib.


Anda juga tidak dapat mengganti dua baris terakhir namespace mylib { void init(); void say_hello(); }yang juga penting (ish).
einpoklum

3

ANSI C ditemukan sebelum namespace ada.


10
Dulu? Spesifikasi ANSI C pertama adalah tahun 1989. Saya cukup yakin bahwa namespace (dalam beberapa bentuk atau lainnya) ada dalam bahasa pemrograman sebelumnya. Ada, misalnya, distandarisasi pada tahun 1983 dan memiliki paket sebagai namespace. Mereka pada dasarnya didasarkan pada modul Modula-2.
HANYA PENDAPAT SAYA yang benar

4
Saya tidak akan memberi tanggal penemuan ANSI C ketika spesifikasinya diadopsi secara resmi; bahasanya sudah ada sebelumnya, dan spesifikasi hanya mendokumentasikan apa yang sudah ada. Meskipun dari beberapa jawaban di situs ini orang mungkin berpikir bahwa spesifikasi datang lebih dulu dan kompiler pertama sebagai renungan.
Crashworks

ANSI C memang memiliki beberapa perbedaan yang signifikan dari pra-ANSI C, tetapi namespace bukan salah satunya.
dan04

Sementara itu, saya menulisnya pada tahun 2020, jauh setelah namespace muncul. Standar C terbaru masih belum memilikinya. Sejauh C masuk akal, ini adalah salah satu fitur yang sangat hilang.

3

Karena orang yang ingin menambahkan kemampuan ini ke C belum berkumpul dan mengatur untuk memberikan tekanan pada tim pembuat kompiler dan badan ISO.


1
Saya pikir kita akan melihat namespace di C hanya jika orang-orang ini akan mengatur diri mereka sendiri dan membuat ekstensi dengan dukungan namespace. Maka badan ISO tidak akan punya pilihan selain menampilkannya sebagai standar (dengan lebih banyak atau lebih sedikit perubahan). Begitulah cara javascript (yang memiliki beberapa kesamaan dengan C dalam hal ini) melakukannya.
themihai

3
@themihai: "create an extension" = minta gcc dan clang people untuk mengompilasi namespace.
einpoklum

1

C tidak mendukung namespace seperti C ++. Implementasi namespaces C ++ merusak nama-nama. Pendekatan yang diuraikan di bawah ini memungkinkan Anda untuk mendapatkan manfaat dari namespace di C ++ sambil memiliki nama yang tidak rusak. Saya menyadari bahwa sifat dari pertanyaannya adalah mengapa C tidak mendukung namespace (dan jawaban sepele adalah bahwa itu tidak karena tidak diterapkan :)). Saya hanya berpikir bahwa ini dapat membantu seseorang untuk melihat bagaimana saya telah mengimplementasikan fungsionalitas template dan namespace.

Saya menulis tutorial tentang cara mendapatkan keuntungan dari namespace dan / atau template menggunakan C.

Namespaces dan template di C

Ruang nama dan templat di C (menggunakan Daftar Tertaut)

Untuk namespace dasar, cukup awali nama namespace sebagai konvensi.

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

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

dapat ditulis sebagai

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

Pendekatan kedua yang saya perlukan yang menggunakan konsep namespacing dan template adalah menggunakan penggabungan makro dan menyertakannya. Misalnya, saya dapat membuat file

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

menggunakan file template sebagai berikut

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

Sekarang kita dapat mendefinisikan int_multiply sebagai berikut. Dalam contoh ini, saya akan membuat file 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"

Di akhir semua ini, Anda akan memiliki file fungsi dan header untuk.

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

Saya membuat tutorial yang jauh lebih rinci tentang tautan yang disediakan yang menunjukkan cara kerjanya dengan daftar tertaut. Semoga ini membantu seseorang!


3
Tautan Anda menjelaskan cara menambahkan ruang nama. Namun, pertanyaannya adalah mengapa ruang nama tidak didukung. Jadi jawaban ini bukanlah jawaban dan seharusnya menjadi komentar.
Thomas Weller
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.