เหตุใด ANSI C จึงไม่มีเนมสเปซ


93

การมีเนมสเปซดูเหมือนจะไม่ใช่เรื่องง่ายสำหรับภาษาส่วนใหญ่ แต่เท่าที่บอก ANSI C ไม่รองรับ ทำไมจะไม่ล่ะ? มีแผนที่จะรวมไว้ในมาตรฐานในอนาคตหรือไม่?


13
ใช้ C ++ เป็น C-with-namespace!
AraK

3
แน่นอนฉันทำได้ แต่ฉันก็ยังอยากรู้
Pulkit Sinha

5
2 อย่าง. ไวยากรณ์เฉพาะที่ไม่จำเป็น: ภาษาอื่น ๆ ทั้งหมดที่มีเนมสเปซใช้เพียง "." เป็นตัวคั่นเนื่องจากไม่คลุมเครือกับการใช้ "." อื่น ๆ และที่สำคัญยิ่งกว่านั้น c ++ ไม่เคยแนะนำขอบเขตโดยใช้คำสั่ง ซึ่งหมายความว่าโปรแกรมเมอร์ใช้คำสั่งในการนำเข้าเนมสเปซเข้าสู่ขอบเขตส่วนกลางมากเกินไป ซึ่งหมายความว่าตอนนี้คณะกรรมการมาตรฐาน c ++ ไม่สามารถเพิ่มคุณสมบัติใหม่ให้กับ std :: ได้เลยเนื่องจากจำนวนโค้ดที่จะแตกเป็นผลทำให้การแบ่งพาร์ติชันซ้ำซ้อน
Chris Becke

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
เนมสเปซสำหรับฟังก์ชันล่ะ
ธีม

8
สิ่งนี้อาจเรียกว่าเนมสเปซได้ แต่ฉันเชื่อว่านี่ไม่ใช่ประเภทของเนมสเปซที่ OP ถามถึง
avl_sweden

1
@jterm Nope. ฉันไม่สนับสนุนการแฮ็กฟีเจอร์ C เพียง แต่ระบุข้อเท็จจริงเท่านั้น แต่ละstructคำจำกัดความจะประกาศเนมสเปซใหม่สำหรับสมาชิก ฉันไม่สนับสนุนการใช้ประโยชน์จากข้อเท็จจริงนั้นและฉันไม่ทราบถึงวิธีการใช้ประโยชน์ใด ๆ เนื่องจากstructไม่สามารถมีสมาชิกแบบคงที่ได้
JeremyP

101

เพื่อความสมบูรณ์มีหลายวิธีในการบรรลุ "ประโยชน์" ที่คุณอาจได้รับจากเนมสเปซในภาษา C

หนึ่งในวิธีที่ฉันชอบคือการใช้โครงสร้างเพื่อเก็บตัวชี้วิธีการมากมายซึ่งเป็นส่วนต่อประสานกับห้องสมุดของคุณ / ฯลฯ

จากนั้นคุณใช้อินสแตนซ์ภายนอกของโครงสร้างนี้ซึ่งคุณเริ่มต้นภายในไลบรารีของคุณโดยชี้ไปที่ฟังก์ชันทั้งหมดของคุณ สิ่งนี้ช่วยให้คุณสามารถทำให้ชื่อของคุณเรียบง่ายในไลบรารีของคุณโดยไม่ต้องเหยียบเนมสเปซของไคลเอนต์ (นอกเหนือจากตัวแปรภายนอกที่ขอบเขตส่วนกลางตัวแปร 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
... และคอมไพเลอร์ฉลาดพอที่จะ "dereference" ตัวชี้ฟังก์ชันในเวลาคอมไพล์เมื่อคุณทำlibrary.method1()หรือไม่?
einpoklum

1
นี่มันสุดยอดมาก สิ่งหนึ่งที่ฉันอาจเพิ่มฉันกำลังพยายามทำให้ฟังก์ชั่นทั้งหมดของฉันใน.cไฟล์ของฉันคงที่โดยค่าเริ่มต้นดังนั้นฟังก์ชันเดียวที่เปิดเผยคือฟังก์ชันที่เปิดเผยอย่างชัดเจนในconst structคำจำกัดความใน.cไฟล์
lastmjs

3
นั่นเป็นความคิดที่ดี แต่คุณจะจัดการกับค่าคงที่และ enums ได้อย่างไร?
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 STOP HELPING ICE

41
ฉันไม่เห็นว่าคุณจะสนับสนุนตำแหน่งนั้นได้อย่างไร ถามชุมชน Javascript เกี่ยวกับการรวมโปรเจ็กต์เมื่อระบบอื่น ๆ ทุกระบบมีแฮ็กพื้นบ้านที่แตกต่างกันสำหรับการใช้งานเนมสเปซ ฉันไม่เคยได้ยินใครบ่นเกี่ยวกับคีย์เวิร์ด 'เนมสเปซ' หรือ 'แพ็กเกจ' ที่เพิ่มความซับซ้อนให้กับภาษาของพวกเขามากเกินไป ในทางกลับกันการพยายามดีบักโค้ดที่เกลื่อนไปด้วยมาโครอาจทำให้เกิดปัญหาได้อย่างรวดเร็ว!
weberc2

5
ฉันเคยได้ยินหลายคนบ่นเกี่ยวกับชื่อ C ++ ที่ยุ่งเหยิง (จากมุมมองของการดีบัก, toolchain, ความเข้ากันได้ของ ABI, การค้นหาสัญลักษณ์แบบไดนามิก, ... ) และความซับซ้อนของการไม่รู้ว่าชื่อใดอ้างถึงจริงๆ
R .. GitHub STOP HELPING ICE

6
@R .. นั่นจะไม่เกิดขึ้นถ้าการโกงชื่อใน C ++ เป็นมาตรฐาน สิ่งนี้เพียงอย่างเดียวจะไม่ช่วยเรื่องความเข้ากันได้ของ ABI แต่จะแก้ไขปัญหาการแมปชื่อได้อย่างแน่นอน
Malcolm

20
ฉันคิดว่าคน C จะเถียงกันแบบหน้าตรง มีคุณสมบัติมากมายใน C ++ พร้อมขอบคมที่ทำให้ผู้คนเศร้าโศก Namespaces ไม่ใช่หนึ่งในคุณสมบัติเหล่านั้น พวกเขายอดเยี่ยมพวกเขาทำงานได้ดีมาก และไม่มีอะไรสำคัญกับพรีโปรเซสเซอร์สำหรับเร็กคอร์ด ในที่สุดชื่อ demangling ก็ไม่สำคัญมียูทิลิตี้บรรทัดคำสั่งมากมายที่จะทำเพื่อคุณ
Nir Friedman

12

ในอดีตคอมไพเลอร์ C ไม่ได้ยุ่งเกี่ยวกับชื่อ (ทำบน Windows แต่การโกงสำหรับหลักการcdeclเรียกประกอบด้วยการเพิ่มคำนำหน้าขีดล่างเท่านั้น)

สิ่งนี้ทำให้ง่ายต่อการใช้ไลบรารี C จากภาษาอื่น ๆ (รวมถึงแอสเซมเบลอร์) และเป็นสาเหตุหนึ่งที่คุณมักเห็นextern "C"Wrapper สำหรับ C ++ API


2
แต่ทำไมถึงเป็นปัญหา? ฉันหมายถึงสมมติว่าชื่อเนมสเปซทั้งหมดจะขึ้นต้นด้วย_da13cd6447244ab9a30027d3d0a08903แล้วชื่อ (นั่นคือ UUID v4 ที่ฉันเพิ่งสร้างขึ้น)? มีโอกาสที่อาจทำลายชื่อที่ใช้ UUID เฉพาะนี้ได้ แต่โอกาสนั้นเป็นศูนย์ ดังนั้นจะมีในทางปฏิบัติไม่เป็นปัญหา mangling only_namespace_names
einpoklum

7

เหตุผลทางประวัติศาสตร์เท่านั้น ไม่มีใครคิดว่าจะมีเนมสเปซในเวลานั้น นอกจากนี้พวกเขาพยายามทำให้ภาษาเรียบง่าย พวกเขาอาจจะมีในอนาคต


2
มีการเคลื่อนไหวใด ๆ ในคณะกรรมการมาตรฐานในการเพิ่มเนมสเปซให้กับ C ในอนาคตหรือไม่? เป็นไปได้ด้วยการย้ายไปที่โมดูล C / C ++ สิ่งนี้จะทำให้ง่ายขึ้นในอนาคตหรือไม่?
lanoxx

1
@lanoxx ไม่มีความประสงค์ที่จะเพิ่มเนมสเปซให้กับ C เนื่องจากเหตุผลด้านความเข้ากันได้ย้อนหลัง
ธีม

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 ฉันค่อนข้างมั่นใจว่าเนมสเปซ (ในบางรูปแบบหรืออื่น ๆ ) เป็นภาษาโปรแกรม ตัวอย่างเช่น Ada ได้รับการสร้างมาตรฐานในปี 1983 และมีแพ็คเกจเป็นเนมสเปซ สิ่งเหล่านั้นขึ้นอยู่กับโมดูล Modula-2 เป็นหลัก
แค่ความคิดเห็นที่ถูกต้องของฉัน

4
ฉันจะไม่วันที่สิ่งประดิษฐ์ของ ANSI C เมื่อข้อกำหนดของมันถูกนำมาใช้อย่างเป็นทางการ มีภาษาอยู่ก่อนแล้วและข้อมูลจำเพาะก็บันทึกสิ่งที่มีอยู่แล้ว แม้ว่าจากคำตอบบางส่วนในไซต์นี้เราอาจคิดว่าสเป็คมาก่อนและคอมไพเลอร์ตัวแรกเป็นสิ่งที่คิดภายหลัง
Crashworks

ANSI C มีความแตกต่างอย่างมีนัยสำคัญจากก่อน ANSI C แต่เนมสเปซไม่ใช่หนึ่งในนั้น
dan04

ในขณะเดียวกันฉันกำลังเขียนมันในปี 2020 หลังจากที่เนมสเปซเริ่มมีอยู่จริง มาตรฐาน C ล่าสุดยังไม่มี เท่าที่ C สมเหตุสมผลนี่เป็นคุณสมบัติหนึ่งที่ขาดหายไปอย่างมาก

3

เนื่องจากผู้ที่ต้องการเพิ่มความสามารถนี้ให้กับ C ไม่ได้รวมตัวกันและจัดระบบเพื่อสร้างแรงกดดันให้กับทีมผู้เขียนคอมไพเลอร์และในหน่วยงาน ISO


1
ฉันคิดว่าเราจะเห็นเนมสเปซใน C ก็ต่อเมื่อคนเหล่านี้จะจัดระเบียบตัวเองและสร้างส่วนขยายที่รองรับเนมสเปซ จากนั้นหน่วยงาน ISO จะไม่มีทางเลือกอื่นนอกจากนำเผยแพร่เป็นมาตรฐาน (โดยมีการเปลี่ยนแปลงมากหรือน้อย) นั่นคือวิธีที่จาวาสคริปต์ (ซึ่งมีความคล้ายคลึงกับ C ในแง่นี้) ทำ
themihai

3
@themihai: "create an extension" = รับ gcc และ clang people เพื่อคอมไพล์เนมสเปซ
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 }

โดยใช้ไฟล์เทมเพลตดังนี้

คูณแม่แบบ h

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);

คูณ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.