ควรมีการยืนยันในการสร้างงาน


20

พฤติกรรมเริ่มต้นของassertใน C ++ คือการไม่ทำอะไรเลยใน build build ฉันเข้าใจว่ามันทำด้วยเหตุผลด้านประสิทธิภาพและอาจป้องกันไม่ให้ผู้ใช้เห็นข้อความแสดงข้อผิดพลาดที่น่ารังเกียจ

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

นอกจากนี้อาร์กิวเมนต์ประสิทธิภาพสำหรับฉันนับเฉพาะเมื่อมันเป็นปัญหาที่วัดได้ ส่วนใหญ่assertในรหัสของฉันไม่ซับซ้อนกว่า

assert(ptr != nullptr);

ซึ่งจะมีผลกระทบเล็กน้อยกับรหัสส่วนใหญ่

สิ่งนี้นำไปสู่คำถาม: ควรยืนยัน (หมายถึงแนวคิดไม่ใช่การนำไปปฏิบัติเฉพาะ) ควรมีการใช้งานใน build build หรือไม่? ทำไมจะไม่ล่ะ)?

โปรดทราบว่าคำถามนี้ไม่ได้เกี่ยวกับวิธีเปิดใช้งาน asserts ใน build builds (เช่น#undef _NDEBUGหรือใช้การยืนยัน assert แบบกำหนดเอง) นอกจากนี้มันไม่เกี่ยวกับการเปิดใช้งาน asserts ในรหัสห้องสมุดของบุคคลที่สาม / มาตรฐาน แต่ในรหัสที่ควบคุมโดยฉัน


ฉันรู้จักผู้เล่นระดับโลกในตลาดด้านการดูแลสุขภาพที่ติดตั้งระบบแก้ไขข้อมูลของโรงพยาบาลในเว็บไซต์ของลูกค้า พวกเขามีข้อตกลงพิเศษกับ Microsoft เพื่อติดตั้งไลบรารีดีบัก C ++ ที่นั่นเช่นกัน ดีที่มีคุณภาพของผลิตภัณฑ์ของตนคือ ...
เบอร์นาร์ดฮิลเลอร์

2
มีคำพูดเก่า ๆ ที่บอกว่า"การเอาการยืนยันออกเป็นเหมือนการสวมเสื้อชูชีพเพื่อฝึกซ้อมที่ท่าเรือ แต่จากนั้นทิ้งเสื้อชูชีพไว้ข้างหลังเมื่อเรือของคุณออกจากมหาสมุทรเปิด" ( wiki.c2.com/?AssertionsAsDefensiveProgramming ) . โดยส่วนตัวฉันจะเปิดใช้งานในรุ่นต่อโดยค่าเริ่มต้น น่าเสียดายที่การฝึกฝนนี้ไม่ได้เกิดขึ้นบ่อยนักในโลก C ++ แต่อย่างน้อยทหารผ่านศึก C ++ ที่มีชื่อเสียงอย่าง James Kanze มักจะใช้ในการโต้แย้งเพื่อรักษาคำยืนยันในการสร้างรุ่น: stackoverflow.com/a/12162195/3313064
Christian Hackl

คำตอบ:


20

คลาสสิกassertเป็นเครื่องมือจากไลบรารี C มาตรฐานเก่าไม่ใช่จาก C ++ มันยังคงมีอยู่ใน C ++ อย่างน้อยก็ด้วยเหตุผลของความเข้ากันได้ย้อนหลัง

ฉันไม่มีไทม์ไลน์ที่แม่นยำของ libs มาตรฐาน C ที่อยู่ในมือ แต่ฉันค่อนข้างแน่ใจว่าassertจะมีให้บริการในเวลาไม่นานหลังจากที่ K&R C เข้าสู่การถ่ายทอดสด (ประมาณปี 1978) ในแบบคลาสสิก C สำหรับการเขียนโปรแกรมที่มีประสิทธิภาพการเพิ่มการทดสอบตัวชี้ NULL และการตรวจสอบขอบเขตอาเรย์จะต้องทำบ่อยกว่าใน C ++ การทดสอบตัวชี้ NULL รวมสามารถหลีกเลี่ยงได้โดยใช้การอ้างอิงและ / หรือตัวชี้สมาร์ทแทนตัวชี้และโดยการใช้การstd::vectorตรวจสอบขอบเขตอาร์เรย์มักไม่จำเป็น ยิ่งกว่านั้นการแสดงที่ยอดเยี่ยมในปี 1980 นั้นสำคัญกว่าวันนี้อย่างแน่นอน ดังนั้นฉันคิดว่าน่าจะเป็นเหตุผลว่า "assert" ถูกออกแบบมาให้ใช้งานได้เฉพาะใน debug builds โดยค่าเริ่มต้น

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

  • ทดสอบเงื่อนไขบางอย่าง (และหยุดการทำงานที่ขอบเขตซึ่งเงื่อนไขล้มเหลว)

  • แสดงข้อความแสดงข้อผิดพลาดที่ชัดเจนในกรณีที่ไม่มีเงื่อนไข

  • อนุญาตให้ขอบเขตภายนอกใช้ข้อความแสดงข้อผิดพลาดและส่งออกไปยังช่องทางการสื่อสารเฉพาะ ช่องทางนี้อาจเป็น stderr ไฟล์บันทึกมาตรฐานกล่องข้อความในโปรแกรม GUI ข้อผิดพลาดทั่วไปในการจัดการการโทรกลับช่องข้อผิดพลาดที่เปิดใช้งานเครือข่ายหรือสิ่งที่เหมาะกับซอฟต์แวร์เฉพาะที่สุด

  • อนุญาตให้ขอบเขตด้านนอกบนฐานต่อกรณีตัดสินใจได้ว่าโปรแกรมควรจบอย่างสง่างามหรือควรดำเนินการต่อ

(แน่นอนว่ายังมีสถานการณ์ที่การสิ้นสุดโปรแกรมทันทีในกรณีที่เงื่อนไขที่ไม่ได้ผลเป็นเพียงตัวเลือกที่สมเหตุสมผล แต่ในกรณีเช่นนั้นมันควรเกิดขึ้นในการสร้างการปล่อยเช่นกันไม่ใช่เฉพาะในการสร้างการดีบัก)

เนื่องจากคลาสสิกassertไม่ได้มีคุณสมบัตินี้จึงไม่เหมาะสำหรับการสร้างการเปิดตัวสันนิษฐานว่าการสร้างการเปิดตัวเป็นสิ่งที่ปรับใช้ในการผลิต

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

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


อ่าลืมเกี่ยวกับมรดก C ทั้งหมด (หลังจากนั้นทั้งหมด#include <cassert>) ตอนนี้มันสมเหตุสมผลแล้ว
ไม่มีใคร

1
ฉันไม่เห็นด้วยกับคำตอบทั้งหมดนี้อย่างสมบูรณ์ การโยนข้อยกเว้นเมื่อแอปพลิเคชันของคุณค้นพบว่าซอร์สโค้ดของมันนั้นเป็นบั๊กกี้เป็นทางเลือกสุดท้ายในภาษาเช่น Java ซึ่งไม่สามารถทำได้ดีกว่า แต่ใน C ++ โดยทั้งหมดหมายความว่าจะยุติโปรแกรมทันทีในสถานการณ์ดังกล่าว คุณไม่ต้องการให้กองคลี่คลายเพื่อทำการดำเนินการเพิ่มเติมใด ๆที่จะดำเนินการ ณ จุดนั้น โปรแกรมบั๊กกี้อาจเขียนข้อมูลที่เสียหายไปยังไฟล์ฐานข้อมูลหรือซ็อกเก็ตเครือข่าย เพิ่งพัง ASAP (และอาจมีการรีสตาร์ทโดยกลไกภายนอก)
Christian Hackl

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

15

หากคุณพบว่าคุณต้องการเปิดใช้งานการยืนยันในการเปิดตัวคุณได้ขอให้การยืนยันทำผลงานผิด

ประเด็นของการยืนยันคือพวกเขาไม่ได้เปิดใช้งานในการเปิดตัว สิ่งนี้ช่วยให้สามารถทดสอบค่าคงที่ในระหว่างการพัฒนาด้วยรหัสซึ่งอาจต้องเป็นรหัสนั่งร้าน รหัสที่จะต้องลบออกก่อนปล่อย

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

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

ฉันไม่ได้พูดถึงการใช้งานเฉพาะของการยืนยัน แต่แนวคิดของการยืนยัน ฉันไม่ต้องการยืนยันหรือทำให้ผู้อ่านสับสน ฉันตั้งใจถามว่าทำไมมันเป็นแบบนี้ตั้งแต่แรก เหตุใดจึงไม่มี release_assert เพิ่มเติม ไม่จำเป็นต้องใช้หรือไม่ เหตุผลที่อยู่เบื้องหลังการยืนยันถูกปิดใช้งานในการเปิดตัวคืออะไร? - ไม่มีใครเลย

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

ทำไม asserts ปิดการใช้งานในการเปิดตัว? การยืนยันถูกสร้างขึ้นที่ความสูงของอายุของรหัสนั่งร้าน รหัสที่เราต้องลบเพราะรู้ว่าเราไม่ต้องการในการผลิต แต่รู้ว่าเราต้องการที่จะทำงานในการพัฒนาเพื่อช่วยให้เราพบข้อบกพร่อง Asserts เป็นทางเลือกที่สะอาดกว่าสำหรับif (DEBUG)รูปแบบที่ให้เราปล่อยโค้ดไว้ แต่ปิดการใช้งาน นี่คือก่อนที่การทดสอบหน่วยจะเริ่มต้นขึ้นเป็นวิธีหลักในการแยกรหัสการทดสอบออกจากรหัสการผลิต การทดสอบยังคงใช้อยู่ในปัจจุบันแม้โดยผู้ทดสอบหน่วยผู้เชี่ยวชาญทั้งเพื่อคาดหวังให้ชัดเจนและครอบคลุมกรณีที่พวกเขายังคงทำได้ดีกว่าการทดสอบหน่วย

ทำไมไม่ปล่อยรหัสการดีบักในการผลิต? เนื่องจากรหัสการผลิตจะต้องไม่ทำให้เกิดปัญหากับ บริษัท ไม่ต้องฟอร์แมตฮาร์ดไดรฟ์ไม่ทำให้ฐานข้อมูลเสียหายและไม่ส่งอีเมลที่คุกคามประธานาธิบดี ในระยะสั้นมันเป็นเรื่องดีที่จะสามารถเขียนรหัสการดีบักในที่ปลอดภัยที่คุณไม่ต้องกังวลกับมัน


2
ฉันเดาคำถามของฉันคือ: ทำไมรหัสนี้จะถูกลบออกก่อนที่จะปล่อย? การตรวจสอบไม่มากประสิทธิภาพท่อระบายน้ำและหากพวกเขาล้มเหลวมีปัญหาแน่นอนว่าฉันต้องการข้อความแสดงข้อผิดพลาดโดยตรงมากขึ้นเกี่ยวกับ มีข้อได้เปรียบอะไรบ้างในการใช้ข้อยกเว้นแทนการยืนยันให้ฉัน ผมก็อาจจะไม่ต้องการรหัสที่ไม่เกี่ยวข้องจะสามารถที่จะcatch(...)มัน
ไม่มีใคร

2
@Nobody ข้อได้เปรียบที่ใหญ่ที่สุดในการไม่ใช้ asserts สำหรับความต้องการที่ไม่เป็นการยืนยันไม่ใช่ความสับสนของผู้อ่าน หากคุณไม่ต้องการให้รหัสถูกปิดใช้งานอย่าใช้สำนวนที่บ่งบอกว่ามันเป็น มันไม่ดีเท่ากับการใช้if (DEBUG)เพื่อควบคุมสิ่งอื่นนอกจากการดีบั๊กโค้ด การเพิ่มประสิทธิภาพขนาดเล็กอาจไม่มีความหมายในกรณีของคุณ แต่สำนวนไม่ควรล้มล้างเพียงเพราะคุณไม่ต้องการมัน
candied_orange

ฉันคิดว่าฉันไม่ได้ตั้งใจให้ชัดเจน ฉันไม่ได้พูดถึงการใช้งานเฉพาะassertแต่แนวคิดของการยืนยัน ฉันไม่ต้องการใช้ในทางที่ผิดassertหรือทำให้ผู้อ่านสับสน ฉันตั้งใจถามว่าทำไมมันเป็นแบบนี้ตั้งแต่แรก ทำไมไม่มีrelease_assertอะไรเพิ่มเติม? ไม่จำเป็นต้องใช้หรือไม่ เหตุผลที่อยู่เบื้องหลังการยืนยันถูกปิดใช้งานในการเปิดตัวคืออะไร?
ไม่มีใคร

2
You're asking for a good abstraction.ฉันไม่แน่ใจเกี่ยวกับเรื่องนี้ ฉันต้องการจัดการกับปัญหาที่ไม่มีการกู้คืนและส่วนใหญ่ไม่ควรเกิดขึ้น ใช้สิ่งนี้ร่วมกับBecause production code needs to [...] not format the hard drive [...]และฉันจะบอกว่าในกรณีที่ค่าคงที่ไม่ถูกต้องฉันจะขอสิทธิ์ในการปล่อยผ่าน UB ทุกเวลา
ไม่มีใคร

1
@Nobody "ปัญหาที่ไม่มีการกู้คืน" สามารถจัดการได้โดยการโยนข้อยกเว้นและไม่จับพวกเขา คุณสามารถทราบว่าพวกเขาไม่ควรเกิดขึ้นในข้อความของข้อยกเว้น
candied_orange

4

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


4

assertเป็นรูปแบบของเอกสารเช่นความคิดเห็น เช่นเดียวกับความคิดเห็นคุณจะไม่จัดส่งให้กับลูกค้าตามปกติ - พวกเขาไม่ได้อยู่ในรหัสการเปิดตัว

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


1
ดังนั้นการยกเลิกค่าคงที่ที่ล้มเหลวก่อนที่จะเกิดสิ่งที่ไม่เหมาะสมคือไม่มีกรณีการใช้ที่ถูกต้อง?
ไม่มีใคร

3

หากคุณไม่ต้องการให้ปิด "ยืนยัน" คุณสามารถเขียนฟังก์ชันง่าย ๆ ที่มีผลคล้ายกันได้

void fail_if(bool b) {if(!b) std::abort();}

นั่นคือassertเป็นสำหรับการทดสอบที่คุณไม่ต้องการให้พวกเขาหายไปในผลิตภัณฑ์จัดส่ง หากคุณต้องการให้การทดสอบนั้นเป็นส่วนหนึ่งของพฤติกรรมที่กำหนดของโปรแกรมassertเป็นเครื่องมือที่ผิด


1
ฉันตระหนักดีถึงสิ่งนี้ คำถามมีมากขึ้นตามเส้นที่ว่าทำไมค่าเริ่มต้นเป็นแบบ
ไม่มีใคร

3

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

สำหรับปัญหาที่ไม่คาดคิดคุณต้องตัดสินใจว่าอะไรคือวิธีที่ดีที่สุดสำหรับทั้งผู้พัฒนาและผู้ใช้ในการจัดการ


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

หืมมverify(cond)วิธีปกติในการยืนยันและส่งคืนผลลัพธ์หรือไม่
Deduplicator

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

2

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

นอกจากนี้ยังได้รับการออกแบบมาให้ละเว้นจากการสร้างรีลีสที่เสถียรไม่ว่าด้วยเหตุผลใดก็ตามที่นักพัฒนาอาจพบว่ามีประโยชน์: ความสวยงาม, ประสิทธิภาพและทุกสิ่งที่พวกเขาต้องการ มันเป็นส่วนหนึ่งของสิ่งที่แยกการสร้างการดีบักออกจากการสร้างการวางจำหน่ายและตามคำจำกัดความการสร้างการเปิดตัวนั้นจะปราศจากการยืนยันดังกล่าว ดังนั้นจึงมีการโค่นล้มของการออกแบบหากคุณต้องการปล่อย"การสร้างการปล่อยพร้อมกับยืนยันในสถานที่" แบบอะนาล็อกซึ่งจะเป็นความพยายามในการสร้างการปล่อยด้วย_DEBUGคำจำกัดความของตัวประมวลผลล่วงหน้าและไม่มีการNDEBUGกำหนดไว้ มันไม่ใช่งานสร้างที่แท้จริงอีกต่อไป

การออกแบบขยายไปถึงไลบรารีมาตรฐาน ในฐานะที่เป็นตัวอย่างพื้นฐานมากในหมู่คนจำนวนมากการใช้งานจำนวนมากstd::vector::operator[]จะassertเป็นการตรวจสุขภาพจิตเพื่อให้แน่ใจว่าคุณไม่ได้ตรวจสอบเวกเตอร์จากขอบเขต และไลบรารี่มาตรฐานจะเริ่มทำงานมากยิ่งแย่กว่านี้หากคุณเปิดใช้งานการตรวจสอบดังกล่าวในบิลด์รีลีส มาตรฐานของvectorใช้งานoperator[]และการเติม ctor ด้วยการยืนยันดังกล่าวที่รวมอยู่กับอาเรย์ไดนามิกแบบเก่ามักจะแสดงอาเรย์แบบไดนามิกเร็วขึ้นมากจนกว่าคุณจะปิดการใช้งานการตรวจสอบดังกล่าวดังนั้นพวกเขาจึงมักจะส่งผลกระทบต่อประสิทธิภาพในระยะไกล การตรวจสอบตัวชี้ที่ว่างเปล่าที่นี่และการตรวจสอบนอกขอบเขตนั้นอาจกลายเป็นค่าใช้จ่ายมหาศาลหากการตรวจสอบดังกล่าวถูกนำไปใช้นับล้านครั้งในทุกเฟรมในลูปที่สำคัญก่อนหน้ารหัสง่าย ๆ เช่นเดียวกับการยกเลิก

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

แต่ในที่สุดและค่อนข้างเห็นด้วยกับคุณฉันเห็นกรณีที่สมเหตุสมผลซึ่งคุณอาจต้องการทดสอบสิ่งที่คล้ายกับ debug build ระหว่างการทดสอบอัลฟาตัวอย่างกับกลุ่มผู้ทดสอบ alpha กลุ่มเล็ก ๆ ที่พูดลงนาม NDA . อาจมีการปรับปรุงการทดสอบอัลฟาถ้าคุณมอบเครื่องทดสอบของคุณนอกเหนือจากการสร้างรุ่นเต็มพร้อมข้อมูลการดีบักที่แนบมาพร้อมกับคุณสมบัติการดีบัก / พัฒนาบางอย่างเช่นการทดสอบที่พวกเขาสามารถเรียกใช้และเอาท์พุท verbose มากขึ้นในขณะที่ใช้ซอฟต์แวร์ อย่างน้อยฉันก็เคยเห็น บริษัท เกมยักษ์ใหญ่บางแห่งทำสิ่งต่าง ๆ เช่นนั้นสำหรับอัลฟ่า แต่สำหรับบางอย่างเช่นการทดสอบอัลฟาหรือการทดสอบภายในที่คุณกำลังพยายามให้ผู้ทดสอบอย่างอื่นนอกเหนือจากการสร้างรีลีส หากคุณกำลังพยายามสร้างการวางจำหน่ายจริงตามนิยามแล้วไม่ควรมี_DEBUG กำหนดไว้หรืออย่างอื่นที่สร้างความสับสนให้กับความแตกต่างระหว่างการสร้าง "debug" และ "release"

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

ดังที่ได้กล่าวไว้ข้างต้นการตรวจสอบนั้นไม่จำเป็นต้องเป็นเรื่องเล็กน้อยจากมุมมองด้านประสิทธิภาพ หลายคนมีแนวโน้มเล็กน้อย แต่อีกครั้งแม้แต่ lib มาตรฐานก็ใช้และอาจส่งผลกระทบต่อประสิทธิภาพการทำงานในรูปแบบที่ยอมรับไม่ได้สำหรับคนจำนวนมากในหลายกรณีถ้าพูดการเข้าถึงแบบสุ่มโดยstd::vectorใช้เวลานานถึง 4 เท่าในสิ่งที่ควรจะเป็น เนื่องจากการตรวจสอบขอบเขตที่ไม่ควรจะล้มเหลว

ในอดีตทีมเราต้องทำให้เมทริกซ์และเวกเตอร์ไลบรารี่ของเราไม่รวมการยืนยันในเส้นทางที่สำคัญบางอย่างเพื่อให้ debug builds ทำงานได้เร็วขึ้นเพราะ asserts เหล่านั้นทำให้การคำนวณทางคณิตศาสตร์ช้าลงโดยเรียงตามลำดับความสำคัญ เริ่มต้นที่จะต้องให้เรารอ 15 นาทีก่อนที่เราจะสามารถสืบค้นรหัสดอกเบี้ยได้ เพื่อนร่วมงานของฉันต้องการลบจริงๆassertsทันทีเพราะพวกเขาพบว่าการทำเช่นนั้นสร้างความแตกต่างอย่างมหันต์ แต่เราตัดสินที่จะทำให้เส้นทางการดีบักที่สำคัญหลีกเลี่ยงพวกเขา เมื่อเราทำให้เส้นทางที่สำคัญเหล่านั้นใช้ข้อมูลเวกเตอร์ / เมทริกซ์โดยตรงโดยไม่ผ่านการตรวจสอบขอบเขตเวลาที่ต้องใช้ในการดำเนินการอย่างเต็มรูปแบบ (ซึ่งรวมมากกว่าแค่เวกเตอร์ / เมทริกซ์คณิตศาสตร์) ลดลงจากนาทีเป็นวินาที นั่นเป็นกรณีที่รุนแรง แต่แน่นอนว่าการยืนยันนั้นไม่ได้มีเพียงเล็กน้อยจากมุมมองด้านประสิทธิภาพเสมอไป

แต่ยังเป็นเพียงวิธีการassertsออกแบบ หากพวกเขาไม่ได้มีผลกระทบอย่างมากต่อบอร์ดฉันก็อาจจะชอบมันถ้าพวกเขาได้รับการออกแบบให้เป็นมากกว่าคุณลักษณะการสร้าง debug หรือเราอาจใช้vector::atซึ่งรวมถึงการตรวจสอบขอบเขตแม้กระทั่งในการสร้างและปล่อยขอบเขต การเข้าถึงเช่น (ยังมีผลงานยอดเยี่ยมจำนวนมาก) แต่ในปัจจุบันฉันพบว่าการออกแบบของพวกเขามีประโยชน์มากขึ้นเนื่องจากได้รับผลกระทบอย่างมากในกรณีของฉันในฐานะคุณลักษณะการดีบักแบบบิลด์เท่านั้นซึ่งไม่ได้ระบุไว้เมื่อNDEBUGถูกกำหนด สำหรับกรณีที่ฉันทำงานด้วยอย่างน้อยก็สร้างความแตกต่างอย่างมากสำหรับงานสร้างรุ่นยกเว้นการตรวจสอบสติที่ไม่ควรล้มเหลวตั้งแต่แรก

vector::at เมื่อเทียบกับ vector::operator[]

ฉันคิดว่าความแตกต่างของสองวิธีนี้เป็นหัวใจของสิ่งนี้รวมถึงทางเลือก: ข้อยกเว้น vector::operator[]โดยทั่วไปการใช้งานassertเพื่อให้แน่ใจว่าการเข้าถึงนอกขอบเขตจะทำให้เกิดข้อผิดพลาดที่ทำซ้ำได้ง่ายเมื่อพยายามเข้าถึงเวกเตอร์จากขอบเขต แต่ผู้ใช้งานไลบรารี่ทำเช่นนี้โดยสันนิษฐานว่าจะไม่มีค่าใช้จ่ายเล็กน้อยในการสร้างรีลีสที่ดีที่สุด

ในขณะเดียวกันvector::atมีให้ซึ่งมักจะไม่ออกจากขอบเขตการตรวจสอบและพ่นแม้ในรุ่นสร้าง แต่ก็มีโทษประสิทธิภาพการทำงานให้มันถึงจุดที่ฉันมักจะเห็นรหัสที่ไกลมากขึ้นโดยใช้กว่าvector::operator[] vector::atการออกแบบจำนวนมากของ C ++ สะท้อนถึงแนวคิดของ "จ่ายสำหรับสิ่งที่คุณใช้ / ต้องการ" และผู้คนจำนวนมากมักชอบoperator[]ซึ่งไม่ได้กังวลกับขอบเขตการตรวจสอบในรุ่นสร้างตามความคิดที่พวกเขาสวม ไม่จำเป็นต้องตรวจสอบขอบเขตในงานสร้างที่ปรับปรุงแล้ว ทันใดนั้นหากการยืนยันถูกเปิดใช้งานในรุ่นบิลด์ประสิทธิภาพของทั้งสองนี้จะเหมือนกันและการใช้เวกเตอร์จะสิ้นสุดลงช้ากว่าอาร์เรย์แบบไดนามิกเสมอ ดังนั้นส่วนใหญ่ของการออกแบบและประโยชน์ของการยืนยันจึงขึ้นอยู่กับความคิดที่ว่าพวกเขามีอิสระในการสร้างรุ่น

release_assert

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

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

จริงๆแล้วมีบางกรณีที่ฉันจะพบความผิดพลาดอย่างหนักที่มีหมายเลขบรรทัดและข้อความแสดงข้อผิดพลาดที่ดีกว่าที่จะกู้คืนอย่างสง่างามจากข้อยกเว้นโยนซึ่งอาจมีราคาถูกพอที่จะเก็บไว้ในรุ่น และมีบางกรณีที่ไม่สามารถกู้คืนจากข้อยกเว้นได้เช่นข้อผิดพลาดที่พบเมื่อพยายามกู้คืนจากข้อยกเว้นที่มีอยู่ ที่นั่นฉันจะพบว่าเหมาะสมอย่างสมบูรณ์แบบสำหรับ a release_assert(!"This should never, ever happen! The software failed to fail!");และโดยธรรมชาติที่จะสกปรกราคาถูกเนื่องจากการตรวจสอบจะดำเนินการภายในเส้นทางที่พิเศษในตอนแรกและจะไม่เสียค่าใช้จ่ายใด ๆ ในเส้นทางการดำเนินการตามปกติ


ความตั้งใจของฉันคือไม่เปิดใช้งานการยืนยันในโค้ดของบุคคลที่สามดูการแก้ไขที่ชัดเจน คำพูดจากความคิดเห็นของฉันละเว้นบริบทจากคำถามของฉัน: Additionally, the performance argument for me only counts when it is a measurable problem.ดังนั้นความคิดคือการตัดสินใจในแต่ละกรณีเมื่อมีการยืนยันออกจากการเปิดตัว
ไม่มีใคร

ปัญหาอย่างหนึ่งของความคิดเห็นล่าสุดคือไลบรารีมาตรฐานใช้สิ่งเดียวกันassertซึ่งอาศัย_DEBUG/NDEBUGตัวประมวลผลก่อนกำหนดเหมือนกับสิ่งที่คุณใช้เมื่อคุณใช้assertแมโคร ดังนั้นจึงไม่มีวิธีการเลือกเปิดใช้งานการยืนยันเพียงรหัสเดียวโดยไม่เปิดใช้งานสำหรับ lib มาตรฐานเช่นกันสมมติว่ามันใช้ lib มาตรฐาน

มีการตรวจสอบตัววนซ้ำ STL ซึ่งคุณสามารถปิดใช้งานแยกต่างหากซึ่งปิดใช้งานการยืนยันบางอย่าง แต่ไม่ใช่ทั้งหมด

โดยพื้นฐานแล้วมันเป็นเครื่องมือที่หยาบไม่ละเอียดและมักจะมีความตั้งใจในการออกแบบเพื่อให้เพื่อนร่วมทีมของคุณอาจใช้มันในพื้นที่ที่สำคัญเมื่อเทียบกับการสันนิษฐานว่ามันจะไม่ส่งผลต่อประสิทธิภาพของการปล่อยห้องสมุดมาตรฐานใช้ วิธีการในพื้นที่ที่สำคัญ libs บุคคลที่สามอื่น ๆ ที่ใช้มันเป็นอย่างนั้น ฯลฯ และเมื่อคุณต้องการสิ่งที่คุณสามารถปิด / เปิดมากขึ้นโดยเฉพาะหนึ่งชิ้นส่วนของรหัสและไม่อื่น ๆ assertเรากลับไปมองอีกครั้งที่สิ่งอื่นที่ไม่ใช่

vector::operator[]และvector::atตัวอย่างเช่นจะมีประสิทธิภาพที่เหมือนกันหากเปิดใช้งานการยืนยันในรุ่นบิลด์และจะไม่มีประโยชน์ในการใช้operator[]อีกต่อไปจากจุดยืนประสิทธิภาพเนื่องจากจะทำการตรวจสอบขอบเขตอยู่แล้ว

2

ฉันเขียนโปรแกรมในทับทิมไม่ C / C ++ และดังนั้นฉันจะไม่พูดเกี่ยวกับความแตกต่างระหว่างอ้างและข้อยกเว้น แต่ผมอยากจะพูดคุยเกี่ยวกับเรื่องนี้เป็นสิ่งที่จะหยุดรันไทม์ ฉันไม่เห็นด้วยกับคำตอบส่วนใหญ่เนื่องจากการหยุดโปรแกรมด้วยการพิมพ์ backtrace ทำงานได้ดีสำหรับฉันเป็นเทคนิคการเขียนโปรแกรมป้องกัน
หากมีวิธีการยืนยันรูทีน (แน่นอนไม่ว่ามันจะเขียน syntaxically และถ้าคำว่า "ยืนยัน" มีการใช้หรือมีอยู่ในภาษาการเขียนโปรแกรมหรือ dsl เลย) ที่จะเรียกว่าหมายถึงงานบางอย่างควรจะทำและผลิตภัณฑ์ กลับไปทันทีจาก "ปล่อยพร้อมใช้งาน" เป็น "ต้องการแพตช์" - ตอนนี้ก็เขียนมันไปจัดการข้อยกเว้นจริงหรือแก้ไขข้อผิดพลาดที่ทำให้ข้อมูลที่ไม่ถูกต้องปรากฏขึ้น

ฉันหมายความว่า Assert ไม่ใช่สิ่งที่คุณควรอยู่ด้วยและโทรบ่อย ๆ - เป็นสัญญาณหยุดที่บ่งบอกว่าคุณควรทำ smth เพื่อไม่ให้มันเกิดขึ้นอีก และบอกว่า "ปล่อยรุ่นไม่ควรมีการยืนยัน" ก็เหมือนพูดว่า "ปล่อยสร้างไม่ควรมีข้อบกพร่อง" - เพื่อนมันเป็นไปไม่ได้เกือบจัดการกับมัน
หรือคิดเกี่ยวกับพวกเขาเกี่ยวกับ "ความล้มเหลว unittests ทำและเรียกใช้โดยผู้ใช้ปลายทาง" คุณไม่สามารถคาดการณ์ทุกสิ่งที่ผู้ใช้ทำกับโปรแกรมของคุณ แต่ถ้า smth ร้ายแรงเกินไปผิดพลาดมันควรจะหยุด - นี่คล้ายกับวิธีที่คุณสร้าง build pipelines - คุณหยุดกระบวนการและไม่เผยแพร่คุณ ? การยืนยันบังคับให้ผู้ใช้หยุดรายงานและรอความช่วยเหลือจากคุณ

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