โปรแกรม C++ ที่ใช้ส่วนหัวของไลบรารี C รับรู้ว่าเป็นคีย์เวิร์ด ข้อผิดพลาดภายนอก C?

โปรแกรม C++ ของฉันจำเป็นต้องใช้ไลบรารี C ภายนอก ดังนั้นฉันจึงใช้

extern "C"
{
   #include <library_header.h>
}

ไวยากรณ์สำหรับทุกโมดูลที่ฉันต้องใช้

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

แม้ว่าฉันจะใช้ไวยากรณ์ extern "C" แต่ฉันก็ได้รับข้อผิดพลาดจากโปรแกรม C++ เมื่อฉันรวมไฟล์ส่วนหัวนั้น

หากฉันเปลี่ยนชื่อทุก สิ่งนี้ ในไฟล์ส่วนหัวของไลบรารี C นั้นด้วย _this ทุกอย่างดูเหมือนจะทำงานได้ดี

คำถามคือ:

ไวยากรณ์ extern "C" ไม่ควรเพียงพอสำหรับความเข้ากันได้แบบย้อนหลัง อย่างน้อยก็ในระดับไวยากรณ์สำหรับไฟล์ส่วนหัว นี่เป็นปัญหากับคอมไพเลอร์หรือไม่?


person nyarlathotep108    schedule 03.09.2014    source แหล่งที่มา
comment
สิ่งที่ extern "C" ทำทั้งหมดคือการบอกคอมไพเลอร์ไม่ให้ การรบกวนชื่อ   -  person Some programmer dude    schedule 03.09.2014
comment
stackoverflow.com/q/1041866/1147772   -  person Drax    schedule 03.09.2014
comment
@JoachimPileborg - extern "C" บอกว่าจะใช้ชื่อ C mangling ซึ่งโดยทั่วไปหมายถึงการเติมขีดล่าง   -  person Pete Becker    schedule 03.09.2014
comment
extern "C" ไม่ได้เปลี่ยนภาษาปัจจุบันเป็นภาษา C อย่างน่าอัศจรรย์ สิ่งที่ส่งผลกระทบคือข้อกำหนดการเชื่อมโยงของเอนทิตีภายนอก ไฟล์ส่วนหัวของคุณยังคงคอมไพล์เป็นโค้ด C++ และต้องปฏิบัติตามกฎทั้งหมดของภาษา C++   -  person AnT    schedule 03.09.2014


คำตอบ (4)


ไวยากรณ์ "C" ภายนอกไม่ควรเพียงพอสำหรับความเข้ากันได้แบบย้อนหลัง อย่างน้อยก็ในระดับไวยากรณ์สำหรับไฟล์ส่วนหัว นี่เป็นปัญหากับคอมไพเลอร์หรือไม่?

ไม่ Extern "C" ใช้สำหรับการเชื่อมโยง - โดยเฉพาะนโยบายที่ใช้สำหรับชื่อสัญลักษณ์ที่สร้างขึ้น ("ชื่อ mangling") และรูปแบบการเรียก (แอสเซมบลีใดที่จะถูกสร้างขึ้นเพื่อเรียก API และค่าพารามิเตอร์สแต็ก) - ไม่ใช่การคอมไพล์

ปัญหาที่คุณพบไม่ได้จำกัดอยู่เพียงคำหลัก this ในโค้ดฐานปัจจุบันของเรา เรากำลังย้ายโค้ดบางส่วนไปยัง C++ และเรามีโครงสร้างดังนี้:

struct Something {
    char *value;
    char class[20]; // <-- bad bad code!
};

วิธีนี้ใช้ได้ดีในโค้ด C แต่ (เช่นคุณ) เราถูกบังคับให้เปลี่ยนชื่อเพื่อให้สามารถคอมไพล์เป็น C++ ได้

person utnapistim    schedule 03.09.2014
comment
เคล็ดลับจากมือโปร: เขียนโค้ดที่เข้ากันได้กับชื่อ C++ เมื่อเขียนไลบรารี C ช่วยประหยัดเรื่องปวดหัวสำหรับนักพัฒนารายอื่นได้มาก - person Cole Johnson; 03.09.2014
comment
RE: ปัญหาในชั้นเรียนของคุณ โปรดสังเกตวิธีแก้ปัญหา ในคำตอบของฉัน. แน่นอน อย่าทำแบบนั้น แก้ไขมันสิ! :-) - person HostileFork says dont trust SE; 03.09.2014
comment
ตกลง ตอนนี้ชัดเจนแล้ว อย่างที่คุณพูด แก้ไขปัญหาคีย์เวิร์ดนี้ด้วยการเปลี่ยนชื่อ มันเริ่มบ่นกับการแปลง int/bool และความแตกต่างภาษาหลัก C/C++ หลักอื่นๆ ฉันคิดว่าเนื่องจากเป็น extern C มันจะคอมไพล์โดยใช้ข้อมูลจำเพาะของ C และฉันก็คิดผิดโดยสิ้นเชิง ขอบคุณ! - person nyarlathotep108; 03.09.2014
comment
@ColeJohnson: โดยปกติแล้วจะเป็นคำแนะนำที่ดีสำหรับส่วนหัว คำแนะนำที่ไม่ดีสำหรับการนำไปปฏิบัติ คุณต้องการโค้ด C พังอย่างรวดเร็วและพังแย่หากมีคนพยายามคอมไพล์มันด้วยคอมไพเลอร์ C++ การแนะนำข้อผิดพลาดเล็กๆ น้อยๆ เนื่องจากเป็นภาษาที่แตกต่างกันและความหมายต่างกันนั้นแย่กว่ามาก - person R.. GitHub STOP HELPING ICE; 03.09.2014
comment
@R.. คุณพูดถูก ฉันกำลังอ้างอิงส่วนหัว ฉันแค่คิดว่ามันเป็นนัย เดาว่าไม่ การนำไปปฏิบัติเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง ฉันเห็นด้วย - person Cole Johnson; 03.09.2014
comment
@R.. เอ่อ...ฉันเห็นด้วยกับประเด็นทั่วไปของคุณว่าหากมีพื้นที่สีเทาของความเข้ากันได้ คุณควรพยายามกำหนดเส้นทางให้เร็วและใช้เสียงมากที่สุดในการคอมไพล์ใหม่ แต่นั่นไม่เกี่ยวอะไรกับโปรแกรมเมอร์ภาษา C ที่กระตือรือร้นในการตั้งชื่อฟิลด์ struct class และตัวแปรโกลบอล this - person HostileFork says dont trust SE; 04.09.2014

น่าแปลกที่คอมไพเลอร์จำนวนมาก ไม่บังคับไม่อนุญาต การกำหนดคำสำคัญใหม่ผ่านตัวประมวลผลล่วงหน้า:

#include <iostream>

// temporary redefinition to compile code abusing the "this" keyword
#define cppThis this
#define this thisFunction

int this() {
    return 1020;
}

int that() {
   return this();
}

// put the C++ definition back so you can use it
#undef this
#define this cppThis

struct DumpThat {
    int dump() {
       std::cout << that();
    }
    DumpThat() {
       this->dump();
    }
};

int main ()
{
    DumpThat dt;
}

ดังนั้นหากคุณพิงกำแพง นั่นอาจช่วยให้คุณรวบรวมไฟล์ที่เขียนไปยังสมมติฐาน C ที่คุณไม่สามารถเปลี่ยนแปลงได้

อย่างไรก็ตาม จะไม่อนุญาตให้คุณได้รับชื่อลิงก์เกอร์เป็น "สิ่งนี้" อาจมีลิงก์เกอร์ที่ให้คุณทำการแมปชื่อใหม่เพื่อช่วยหลีกเลี่ยงการชนกัน ผลข้างเคียงอาจทำให้คุณพูด thisFunction -> this ได้ และไม่มีปัญหาที่ด้านขวามือของการแม็ปเป็นคำหลัก

ยังไงก็ตาม...คำตอบที่ดีกว่าถ้าคุณเปลี่ยนได้คือ...เปลี่ยน!

person HostileFork says dont trust SE    schedule 03.09.2014
comment
ฉันไม่คิดว่ามันแปลกหรือคอมไพเลอร์มีทางเลือกอะไร ขั้นตอนตัวประมวลผลล่วงหน้าถูกกำหนดไว้ที่ระดับโทเค็น ในขั้นตอนนั้น โทเค็นยังไม่ได้แยกวิเคราะห์เป็นคำหลัก ดังนั้นคุณจึงไม่สามารถโทเค็นคำหลักแตกต่างจากโทเค็นที่ไม่ใช่คำหลักได้ - person MSalters; 03.09.2014
comment
@MSalters ดู คำจำกัดความใหม่ของคำหลักใน C / C++...ใคร ๆ ก็สามารถขึ้นบัญชีดำคำจากการใช้ตัวประมวลผลล่วงหน้าได้อย่างแน่นอน - person HostileFork says dont trust SE; 03.09.2014
comment
โอ้ใช่แล้ว - C ห้ามเฉพาะในเฟส 7/8 หลังจากประมวลผลล่วงหน้าเฟส 4 (ดูคำตอบของ md5) แต่ใน C ++ มันถูกแบนทันที (ตามคำถามที่เชื่อมโยง) แต่ this จะไม่เป็นคำหลักใน C อยู่แล้ว ดังนั้นแทนที่จะกำหนดเงื่อนไขใหม่สำหรับ C++ คุณควรกำหนดเงื่อนไขใหม่สำหรับ C - person MSalters; 03.09.2014
comment
นี่เป็นวิธีแก้ปัญหาที่ดีที่สามารถแก้ปัญหาคำหลักทั่วไปได้ แต่จริงๆ แล้วไม่สามารถแก้ปัญหาความแตกต่าง C/C++ ทั้งหมดได้ เดาว่าเป็นสิ่งที่ดีที่เราสามารถแก้ไขไฟล์ส่วนหัวได้ รวมถึงไฟล์จากไลบรารีภายนอกด้วย - person nyarlathotep108; 04.09.2014

หาก extern "C" อนุญาตให้คุณใช้คำสำคัญ C++ เป็นสัญลักษณ์ คอมไพลเลอร์จะต้องแก้ไขคำเหล่านั้นนอกส่วน extern "C" ตัวอย่างเช่น:

extern "C" {
    int * this;  //global variable
    typedef int class;
}


int MyClass::MyFunction() { return *this; }  //what does this mean?
                                             //MyClass could have a cast operator
class MyOtherClass;  //forward declaration or a typedef'ed int?
person IronMensan    schedule 03.09.2014

คุณช่วยอธิบายให้ชัดเจนกว่านี้เกี่ยวกับ "การใช้ชื่อนี้กับตัวแปรบางตัวในไฟล์ส่วนหัวไฟล์ใดไฟล์หนึ่ง" ได้ไหม

มันเป็นตัวแปรจริงๆ หรือเป็นพารามิเตอร์ในฟังก์ชันต้นแบบ?

หากเป็นอย่างหลัง คุณจะไม่มีปัญหาที่แท้จริง เนื่องจากต้นแบบ C (และ C++) ระบุพารามิเตอร์ตามตำแหน่ง (และประเภท) และชื่อเป็นทางเลือก คุณสามารถมีเวอร์ชันอื่นของต้นแบบได้ เช่น:

#ifdef __cplusplus
  extern "C" {
   void aFunc(int);
  }
#else
 void aFunc(int this);
#endif

โปรดจำไว้ว่าไม่มีอะไรมหัศจรรย์เกี่ยวกับไฟล์ส่วนหัว - พวกเขาเพียงแค่จัดเตรียมโค้ดที่รวมคำศัพท์ไว้ที่จุด #include - ราวกับว่าคุณคัดลอกและวางมันลงไป

ดังนั้นคุณจึงสามารถมีสำเนาส่วนหัวของไลบรารีของคุณเองซึ่งทำหน้าที่เหมือนกับที่กล่าวมาข้างต้น เพียงแต่กลายเป็นปัญหาในการบำรุงรักษาเพื่อให้แน่ใจว่าคุณจะติดตามสิ่งที่เกิดขึ้นในส่วนหัวดั้งเดิมได้ หากสิ่งนี้มีแนวโน้มที่จะกลายเป็นปัญหา ให้เพิ่มสคริปต์เป็นขั้นตอนการสร้างซึ่งจะแตกต่างไปจากต้นฉบับ และตรวจสอบให้แน่ใจว่าจุดเดียวที่แตกต่างคือโค้ดวิธีแก้ปัญหาของคุณ

person Andy Dent    schedule 03.09.2014