ที่อยู่ C ++ 11 เกี่ยวข้องกับการส่งวัตถุ std lib ระหว่างขอบเขตไลบรารีแบบไดนามิก / ใช้ร่วมกันหรือไม่? (เช่นที่กำลังเป็นต้น)?


34

หนึ่งในข้อร้องเรียนหลักของฉันเกี่ยวกับ C ++ คือวิธียากในทางปฏิบัติก็คือการส่งวัตถุไลบรารี std นอกขอบเขตไลบรารีแบบไดนามิก (เช่น dll / ดังนั้น)

ไลบรารี std มักเป็นส่วนหัวเท่านั้น ซึ่งเป็นสิ่งที่ดีสำหรับการเพิ่มประสิทธิภาพที่ยอดเยี่ยม อย่างไรก็ตามสำหรับ dll พวกเขามักจะสร้างด้วยการตั้งค่าคอมไพเลอร์ที่แตกต่างกันซึ่งอาจส่งผลกระทบต่อโครงสร้างภายใน / รหัสของคอนเทนเนอร์ไลบรารี std ตัวอย่างเช่นใน MSVC dll หนึ่งอาจสร้างขึ้นด้วยการดีบัก iterator ในขณะที่คนอื่นสร้างมันออก Dll ทั้งสองนี้อาจพบปัญหาในการส่งผ่านคอนเทนเนอร์มาตรฐาน หากฉันเปิดเผยstd::stringในส่วนต่อประสานของฉันฉันไม่สามารถรับประกันได้ว่ารหัสที่ลูกค้าใช้std::stringอยู่นั้นตรงกับห้องสมุดของฉันstd::stringทั้งหมด

สิ่งนี้นำไปสู่ปัญหาการดีบักปัญหาปวดหัว ฯลฯ คุณควบคุมการตั้งค่าคอมไพเลอร์ในองค์กรของคุณอย่างแน่นหนาเพื่อป้องกันปัญหาเหล่านี้หรือคุณใช้อินเทอร์เฟซ C ที่ง่ายกว่าที่ไม่มีปัญหาเหล่านี้ หรือระบุให้กับลูกค้าของคุณถึงการตั้งค่าคอมไพเลอร์ที่คาดหวังที่พวกเขาควรใช้ (ซึ่งจะดูดถ้าห้องสมุดอื่นระบุการตั้งค่าคอมไพเลอร์อื่น ๆ )

คำถามของฉันคือ C ++ 11 พยายามทำสิ่งใดเพื่อแก้ไขปัญหาเหล่านี้หรือไม่


3
ฉันไม่ทราบคำตอบสำหรับคำถามของคุณ แต่ฉันสามารถพูดได้ว่าความกังวลของคุณจะถูกแบ่งปัน พวกเขาเป็นกุญแจสำคัญในการที่ฉันจะไม่ใช้ C ++ ในโครงการของฉันเนื่องจากเราให้ความสำคัญกับความเสถียรของ ABI ในการกำจัดทุกรอบของประสิทธิภาพที่มีศักยภาพ
Donal Fellows

2
กรุณาแยกแยะ มันยากระหว่างDLLs ระหว่างSOมันใช้ได้ดีเสมอ
Jan Hudec

1
พูดอย่างเคร่งครัดนี่ไม่ใช่ปัญหา C ++ เท่านั้น เป็นไปได้ที่จะมีปัญหากับภาษาอื่น
MrFox

2
@JanHudec ฉันสามารถรับประกันได้ว่าระหว่าง SOs ไม่ทำงานเกือบจะน่าอัศจรรย์อย่างที่คุณเห็น เมื่อมองเห็นสัญลักษณ์และการใช้ชื่อ mangling บ่อยแค่ไหนคุณอาจมีปัญหาจากฉนวนมากขึ้น แต่การคอมไพล์ด้วย. so ด้วยค่าสถานะที่แตกต่างกัน / etc. และสมมติว่าคุณสามารถเชื่อมโยงมันในโปรแกรมที่มีค่าสถานะอื่น ๆ
sdg

3
@sdg: ด้วยการตั้งค่าสถานะเริ่มต้นและการมองเห็นเริ่มต้นมันทำงาน หากคุณเปลี่ยนพวกเขาและมีปัญหามันเป็นปัญหาของคุณและไม่มีใครอื่น
Jan Hudec

คำตอบ:


20

คุณถูกต้องว่าอะไร STL - อันที่จริงอะไรจากห้องสมุดบุคคลที่สามซึ่งเป็น templated - จะหลีกเลี่ยงได้ดีที่สุดใน C ++ API สาธารณะใด ๆ คุณต้องการติดตามรายการกฎที่ยาวที่http://www.ros.org/reps/rep-0009.html#definitionเพื่อยับยั้งการแตก ABI ซึ่งทำให้การเขียนโปรแกรม C ++ API สาธารณะเป็นงานที่น่าสนใจ

และคำตอบเกี่ยวกับ C ++ 11 คือไม่มาตรฐานนี้ไม่ได้สัมผัส ที่น่าสนใจคือทำไมไม่ คำตอบก็คือเพราะ C ++ 17 เป็นสิ่งที่สัมผัสได้อย่างมากและสำหรับโมดูล C ++ ที่จะนำไปใช้เราจำเป็นต้องมีเทมเพลตที่ส่งออกเพื่อทำงานและเพื่อที่เราต้องการคอมไพเลอร์ประเภท LLVM เช่นเสียงดังกราวซึ่งสามารถถ่ายโอน AST เต็มแผ่น ทำการค้นหาที่ขึ้นอยู่กับผู้โทรเพื่อจัดการกรณีละเมิด ODR จำนวนมากในโครงการ C ++ ขนาดใหญ่ - ซึ่งรวมถึงรหัส GCC และ ELF จำนวนมาก

ท้ายสุดฉันเห็นความเกลียดชังและความเห็นของ MSVC จำนวนมากเกี่ยวกับ MSVC สิ่งเหล่านี้ผิดไปมาก - GCC บน ELF นั้นเป็นพื้นฐานและไม่สามารถแก้ไขได้ในการผลิตรหัส C ++ ที่ถูกต้องและแก้ไขไม่ได้ สาเหตุของเรื่องนี้มีมากมายและหลากหลาย แต่ฉันจะอ้างตัวอย่างกรณีหนึ่งอย่างรวดเร็ว: GCC บน ELF ไม่สามารถสร้างส่วนขยาย Python ที่เขียนโดยใช้ Boost.Python อย่างปลอดภัยได้ซึ่งมีการโหลดส่วนขยายมากกว่าหนึ่งรายการจาก Boost.Python ลงใน Python อย่างปลอดภัย นั่นเป็นเพราะเอลฟ์ที่มีตารางสัญลักษณ์ C ทั่วโลกนั้นไม่สามารถทำได้โดยการออกแบบเพื่อป้องกันการละเมิด ODR ที่ก่อให้เกิด segfaults ในขณะที่ PE และ MachO และแน่นอนว่าข้อกำหนด C ++ Modules ที่เสนอทั้งหมดใช้ตารางสัญลักษณ์ต่อโมดูล และยังมีปัญหาอีกมากมาย: ดู StackOverflow ที่ฉันตอบเมื่อเร็ว ๆ นี้ที่https://stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055ตัวอย่างเช่นที่การขว้างข้อยกเว้น C ++ นั้นแตกอย่างไม่สามารถแก้ไขได้ใน ELF

ประเด็นสุดท้าย: เกี่ยวกับการสอดแทรก STL ที่แตกต่างกันนี่เป็นความเจ็บปวดครั้งใหญ่สำหรับผู้ใช้องค์กรขนาดใหญ่จำนวนมากที่พยายามผสมผสานห้องสมุดบุคคลที่สามซึ่งมีการบูรณาการอย่างแน่นหนากับการนำ STL ไปใช้ ทางออกเดียวคือกลไกใหม่สำหรับ C ++ เพื่อจัดการกับ STL interop และในขณะที่พวกมันอยู่ในนั้นคุณอาจแก้ไขคอมไพเลอร์ interop ด้วยดังนั้นคุณสามารถ (ตัวอย่าง) ผสมไฟล์ MSVC, GCC และ clang รวบรวมวัตถุและมันใช้งานได้ . ฉันจะดูความพยายามของ C ++ 17 และดูว่ามีอะไรเกิดขึ้นบ้างในอีกไม่กี่ปีข้างหน้า - ฉันจะแปลกใจถ้าไม่มีอะไรเกิดขึ้น


การตอบสนองที่ยอดเยี่ยม! ฉันเพียงหวังว่าเสียงดังกังวานช่วยปรับปรุงความเข้ากันได้กับ windows และอาจตั้งค่าคอมไพเลอร์มาตรฐานเริ่มต้นที่ดี ระบบการรวมข้อความ / ส่วนหัวของ C ++ นั้นแย่มากฉันรอคอยจนถึงวันที่โมดูลทำให้การจัดระเบียบรหัส C ++ ง่ายขึ้นโดยไม่ จำกัด เวลาเร่งความเร็วในการคอมไพล์และปรับปรุงการทำงานร่วมกันของคอมไพเลอร์ด้วยการจับที่ละเมิด ODR
Alessandro Stamatto

3
โดยส่วนตัวแล้วฉันคาดหวังว่าคอมไพเลอร์จะเพิ่มขึ้นอย่างมาก การข้าม AST โมดูลภายในอย่างรวดเร็วนั้นยากมากและเราอาจต้องใช้แคชหน่วยความจำที่แชร์ในหน่วยความจำ อย่างไรก็ตามเกือบทุกอย่างอื่นที่ไม่ดีก็จะดีขึ้น BTW, ไฟล์ส่วนหัวจะอยู่รอบ ๆ แน่นอนโมดูล C ++ ปัจจุบันมีไฟล์อินเตอร์เฟสที่แมป 1 ต่อ 1 กับไฟล์ส่วนหัว นอกจากนี้ไฟล์อินเทอร์เฟซที่สร้างขึ้นโดยอัตโนมัติจะเป็นภาษา C ++ ที่ถูกต้องดังนั้นส่วนหัวดั้งเดิมจะได้รับมาโคร C ที่ถูกกรองออกและพ่นออกมาเป็นไฟล์ส่วนต่อประสาน มีความสุขใช่มั้ย
Niall Douglas

เย็น! ฉันมีข้อสงสัยมากมายเกี่ยวกับโมดูล ระบบโมดูลจะคำนึงถึงการรวมข้อความและการรวมสัญลักษณ์หรือไม่ ด้วยคำสั่งในปัจจุบันรวมถึงคอมไพเลอร์จะต้องคอมไพล์โค้ดซ้ำอีกหลายหมื่นบรรทัดสำหรับไฟล์ต้นฉบับทุกไฟล์ ระบบโมดูลจะอนุญาตให้ใช้รหัสในสักวันหนึ่งโดยไม่มีการประกาศล่วงหน้าหรือไม่ มันจะปรับปรุง / ทำให้ง่ายขึ้นในการสร้างเครื่องมือ?
Alessandro Stamatto

2
-1 สำหรับการแนะนำว่าสงสัยว่าแม่แบบของบุคคลที่สามทั้งหมด การเปลี่ยนแปลงการกำหนดค่าไม่ขึ้นอยู่กับว่าสิ่งที่กำหนดค่านั้นเป็นเทมเพลตหรือไม่
DeadMG

1
@Alessandro: โมดูล C ++ ที่เสนอจะปิดใช้งานแมโคร C อย่างชัดเจน คุณสามารถใช้เทมเพลตหรือตอนนี้ อินเทอร์เฟซที่นำเสนอนั้นถูกต้องตามกฎหมาย C ++ เพียงสร้างอัตโนมัติและสามารถเลือกคอมไพล์ล่วงหน้าเพื่อเพิ่มความเร็วในการแยกวิเคราะห์เช่นไม่คาดว่าจะมีการเร่งความเร็วเหนือส่วนหัวพรีคอมไพล์ที่มีอยู่ สองคำถามสุดท้ายที่จริงผมไม่ทราบว่ามันขึ้นอยู่ :)
เนียลดักลาส

8

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

Windows DLLs ละเมิดข้อกำหนดนี้ นั่นเป็นเหตุผลที่มีปัญหาเหล่านี้ทั้งหมด ดังนั้นมันขึ้นอยู่กับ Microsoft ที่จะแก้ไขไม่ใช่คณะกรรมการมาตรฐาน C ++ Unix ไม่เคยมีปัญหานี้เพราะไลบรารีที่ใช้งานร่วมกันมีความแตกต่างกันและโดยค่าเริ่มต้นจะเป็นไปตามกฎความหมายเดียว (คุณสามารถทำลายมันได้อย่างชัดเจน แต่คุณจะทำอย่างชัดเจนถ้าคุณรู้ว่าคุณสามารถจ่ายได้

Windows DLLs ละเมิดกฎข้อกำหนดหนึ่งข้อเนื่องจาก:

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

Unix ที่ใช้รูปแบบการส่งออกของ ELF โดยปริยายจะนำเข้าสัญลักษณ์ที่ส่งออกทั้งหมดเพื่อหลีกเลี่ยงปัญหาแรกและไม่แยกความแตกต่างระหว่างสัญลักษณ์ที่ได้รับการแก้ไขแบบสแตติกและแบบไดนามิกจนกระทั่งเวลาลิงก์แบบสแตติกเพื่อหลีกเลี่ยงวินาที


ปัญหาอื่นเป็นของธงคอมไพเลอร์ ปัญหานั้นมีอยู่สำหรับโปรแกรมใด ๆ ที่ประกอบด้วยหน่วยการคอมไพล์หลายหน่วยไลบรารีแบบไดนามิกไม่จำเป็นต้องเกี่ยวข้อง อย่างไรก็ตามมันแย่กว่านั้นใน Windows บน Unix มันไม่สำคัญว่าคุณจะเชื่อมโยงแบบคงที่หรือแบบไดนามิกไม่มีใครเชื่อมโยงรันไทม์มาตรฐานแบบคงที่อยู่แล้ว (ใน Linux มันอาจผิดกฎหมาย) และไม่มีการดีบักพิเศษเป็นพิเศษดังนั้นการสร้างหนึ่งจึงดีพอ แต่วิธีที่ Microsoft ใช้การเชื่อมโยงแบบสแตติกและไดนามิกการดีบักและรีลีสรันไทม์และตัวเลือกอื่น ๆ หมายถึงพวกมันทำให้เกิดการระเบิดแบบ combinatorial ของไลบรารีที่ต้องการ ปัญหาแพลตฟอร์มอีกครั้งแทนที่จะเป็นปัญหาภาษา C ++


2
@DougT .: GCC ไม่มีส่วนเกี่ยวข้องกับเรื่องนี้ ABI แพลตฟอร์มมี ใน ELF รูปแบบวัตถุที่ใช้โดย Unices ส่วนใหญ่ไลบรารีที่ใช้ร่วมกันจะส่งออกสัญลักษณ์ที่มองเห็นได้ทั้งหมดและนำเข้าสัญลักษณ์ทั้งหมดที่ส่งออก ดังนั้นหากมีสิ่งใดสร้างขึ้นในหลาย ๆ ไลบรารีตัวเชื่อมโยงแบบไดนามิกจะใช้คำนิยามแรกสำหรับทุกคน เรียบง่ายสง่างามและทำงานได้
Jan Hudec

1
@MartinBa: ไม่มีอะไรที่จะผสาน แต่มันไม่สำคัญว่ามันจะเหมือนกันและตราบใดที่มันไม่ควรถูกรวมเข้าด้วยกันในตอนแรก ใช่ถ้าคุณใช้การตั้งค่าคอมไพเลอร์ที่เข้ากันไม่ได้บนแพลตฟอร์ม ELF คุณจะได้รับความยุ่งเหยิงเช่นเดียวกับทุกที่และทุกที่ แม้ว่าจะไม่ได้ใช้ไลบรารีที่ใช้ร่วมกันดังนั้นจึงค่อนข้างนอกหัวข้อที่นี่
Jan Hudec

1
@Jan - มันเกี่ยวข้องกับคำตอบของคุณ คุณเขียน: "... กฎข้อกำหนดหนึ่งข้อ ... DLLs ของ Windows ละเมิดข้อกำหนดนี้ ... ไลบรารีที่ใช้ร่วมกันทำงานแตกต่างกัน [บน UNix] ... " แต่คำถามที่ถามเกี่ยวข้องกับปัญหาเกี่ยวกับ std-lib (กำหนดไว้ในส่วนหัว) และเหตุผลที่ไม่มีปัญหาเกี่ยวกับ Unix นั้นไม่เกี่ยวกับ SO กับ DLL แต่ด้วยข้อเท็จจริงที่ว่าบน Unix (เห็นได้ชัด) มีไลบรารีมาตรฐานรุ่นที่เข้ากันได้เพียงรุ่นเดียวในขณะที่บน Windows MS เลือกที่จะมีรุ่นที่เข้ากันไม่ได้ (พร้อมการตรวจสอบเพิ่มเติม ฯลฯ )
Martin Ba

1
@MartinBa: ไม่เหตุผลหลักที่มีปัญหาใน Windows คือกลไกการส่งออก / นำเข้าที่ใช้บน Windows ไม่สามารถรวมสมาชิกแบบคงที่และความต้านทานคลาสของเทมเพลตคลาสในทุกกรณีและไม่สามารถผสานสัญลักษณ์ที่เชื่อมโยงแบบไดนามิกและแบบไดนามิก กว่ามันจะเลวร้ายลงมากโดยหลายตัวแปรของไลบรารี แต่ปัญหาหลักคือ C ++ ต้องการความยืดหยุ่นจากตัวเชื่อมโยงที่ตัวเชื่อมโยงแบบไดนามิกของ Windows ไม่มี
Jan Hudec

4
ฉันคิดว่าสิ่งนี้มีความหมายว่าข้อมูลจำเพาะ DLL ไม่ทำงานและความต้องการที่เกี่ยวข้องสำหรับ Msft เพื่อ 'แก้ไข' จะถูกใส่ผิดที่ ข้อเท็จจริงที่ว่า DLLs ไม่สนับสนุนคุณลักษณะบางอย่างของ C ++ นั้นไม่ใช่ข้อบกพร่องของข้อกำหนดคุณสมบัติ DLL Dll เป็นกลไกการบรรจุภัณฑ์ที่เป็นกลางทางภาษาผู้จำหน่ายและ ABI เพื่อแสดงจุดเข้าใช้งานกับรหัสเครื่อง ('ฟังก์ชั่นการโทร') และ blobs ข้อมูล พวกเขาไม่เคยตั้งใจจะสนับสนุนคุณสมบัติขั้นสูงของภาษาเฉพาะใด ๆ ไม่ใช่ข้อผิดพลาดของ Msft หรือข้อกำหนดของ DLL ที่บางคนต้องการให้พวกเขาเป็นอย่างอื่น
Euro Micelli

6

เลขที่

มีงานจำนวนมากกำลังดำเนินการเพื่อแทนที่ระบบส่วนหัวคุณสมบัติที่เรียกว่าโมดูลและซึ่งอาจส่งผลกระทบต่อสิ่งนี้ แต่ก็ไม่ใช่เรื่องใหญ่


2
ฉันไม่คิดว่าระบบส่วนหัวจะมีผลกระทบกับสิ่งนี้ ปัญหาคือ Windows DLLs ละเมิดกฎข้อกำหนดหนึ่งข้อ (ซึ่งหมายความว่าพวกเขาไม่ปฏิบัติตามข้อกำหนด C ++ ดังนั้นคณะกรรมการ C ++ ไม่สามารถทำอะไรกับมันได้) และมีตัวแปรมากมายของ runtime มาตรฐานใน Windows ซึ่งคณะกรรมการ C ++ สามารถ ' ไม่ทำอะไรเลยอย่างใดอย่างหนึ่ง
Jan Hudec

1
ไม่พวกเขาทำไม่ได้ วิธีการที่พวกเขาได้สเปคไม่ได้พูดถึงสิ่งที่ชนิด นอกเหนือจากนั้นเมื่อโปรแกรม (Windows) เชื่อมโยงกับ Windows DLLs แล้ว ODR ก็พอใจ: สัญลักษณ์ทั้งหมด (ส่งออก) ที่มองเห็นได้จะต้องเป็นไปตาม ODR
Paul Michalik

@PaulMichalik C ++ ครอบคลุมการเชื่อมโยง (เฟส 9) และดูเหมือนว่าสำหรับฉันอย่างน้อยการโหลดลิงก์ของ DLLs / SOs นั้นอยู่ในระยะที่ 9 นั่นหมายความว่าสัญลักษณ์ที่มีลิงก์ภายนอก (ไม่ว่าจะส่งออกหรือไม่) ควรเชื่อมโยงและสอดคล้องกับ ODR การเชื่อมโยงแบบไดนามิกกับ LoadLibrary / dlopen ชัดไม่ตกอยู่ภายใต้ข้อกำหนดเหล่านั้น
bames53

@ bames53: IMHO รายละเอียดอ่อนแอเกินไปที่จะอนุญาตข้อความประเภทนั้น .dll /ดังนั้นอาจจะเห็นว่าเป็น "โปรแกรม" ในตัวของมันเอง กว่านั้นกฎก็มีความพึงพอใจ สิ่งที่ต้องการโหลด "โปรแกรม" อื่น ๆ ณ เวลารันไทม์นั้นเป็นไปตามมาตรฐานที่ข้อความใด ๆ
Paul Michalik

@PaulMichalik หากการปฏิบัติการต้องการการเชื่อมโยงโหลดโหลดดังนั้นก่อนการเชื่อมโยงโหลดมีหน่วยงานภายนอกที่ไม่ได้รับการแก้ไขและข้อมูลที่จำเป็นสำหรับการดำเนินการหายไป LoadLibrary และ dlopen อยู่นอกสเป็ค แต่การเชื่อมโยงโหลดโหลดค่อนข้างชัดเจนต้องเป็นส่วนหนึ่งของเฟส 9
bames53
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.