ควรรายงานข้อผิดพลาดในห้องสมุดวิทยาศาสตร์อย่างไร


11

มีปรัชญามากมายในสาขาวิศวกรรมซอฟต์แวร์ที่แตกต่างกันเกี่ยวกับวิธีที่ห้องสมุดควรจัดการกับข้อผิดพลาดหรือเงื่อนไขพิเศษอื่น ๆ บางอย่างที่ฉันเคยเห็น:

  1. ส่งคืนรหัสข้อผิดพลาดพร้อมผลลัพธ์ที่ส่งคืนโดยอาร์กิวเมนต์ตัวชี้ นี่คือสิ่งที่ PETSc ทำ
  2. ส่งคืนข้อผิดพลาดโดยค่า Sentinel ตัวอย่างเช่น malloc ส่งคืน NULL หากไม่สามารถจัดสรรหน่วยความจำได้sqrtจะส่งคืน NaN หากคุณส่งผ่านจำนวนลบเป็นต้นวิธีการนี้ใช้ในฟังก์ชัน libc จำนวนมาก
  3. โยนข้อยกเว้น ใช้ในข้อตกลง II, Trilinos ฯลฯ
  4. ส่งคืนชนิดของตัวแปร ตัวอย่างเช่นฟังก์ชัน c ++ ที่ผลตอบแทนวัตถุของการพิมพ์Resultถ้ามันจะทำงานได้อย่างถูกต้องและใช้ประเภทเพื่ออธิบายวิธีการที่จะล้มเหลวก็จะกลับมาErrorstd::variant<Error, Result>
  5. ใช้ยืนยันและผิดพลาด ใช้ใน p4est และบางส่วนของการเขียนด้วยลายมือ

ปัญหาของแต่ละวิธี:

  1. การตรวจสอบข้อผิดพลาดทุกครั้งจะแนะนำรหัสพิเศษจำนวนมาก ค่าที่จะเก็บผลลัพธ์จะต้องประกาศก่อนเสมอแนะนำตัวแปรชั่วคราวจำนวนมากที่อาจใช้เพียงครั้งเดียว วิธีการนี้จะอธิบายถึงข้อผิดพลาดที่เกิดขึ้น แต่อาจเป็นเรื่องยากที่จะตัดสินว่าทำไมหรือสำหรับ call call stack ที่ใด
  2. กรณีข้อผิดพลาดง่ายต่อการเพิกเฉย ยิ่งไปกว่านั้นฟังก์ชั่นจำนวนมากไม่สามารถมีค่า Sentinel ที่มีความหมายได้หากช่วงของประเภทเอาต์พุตทั้งหมดเป็นผลลัพธ์ที่เป็นไปได้ ปัญหาเดียวกันจำนวนมากเช่น # 1
  3. ทำได้เฉพาะใน C ++, Python และอื่น ๆ ไม่ใช่ใน C หรือ Fortran สามารถเลียนแบบใน C ใช้ setjmp / เวทมนตร์ longjmp หรือlibunwind
  4. เป็นไปได้เฉพาะใน C ++, Rust, OCaml และอื่น ๆ ไม่ใช่ใน C หรือ Fortran สามารถเลียนแบบใน C โดยใช้เวทมนตร์คาถา
  5. เนื้อหาที่ให้ข้อมูลมากที่สุด แต่ถ้าคุณใช้วิธีการนี้เพื่อบอกว่าไลบรารี C ที่คุณเขียน Piper wrapper สำหรับข้อผิดพลาดโง่ ๆ เช่นการส่งดัชนีนอกขอบเขตไปยังอาร์เรย์จะทำให้ล่าม Python ขัดข้อง

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

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

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

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


ข้อควรพิจารณาอีกประการหนึ่งคือประสิทธิภาพของเครือข่าย วิธีการต่าง ๆ เหล่านี้มีผลต่อความเร็วของซอฟต์แวร์อย่างไร เราควรใช้การจัดการข้อผิดพลาดที่แตกต่างกันในส่วน "ควบคุม" ของรหัส (เช่นการประมวลผลไฟล์อินพุต) กับ "เอ็นจิ้น" ที่มีราคาแพงหรือไม่
LedHead

โปรดทราบว่าคำตอบที่ดีที่สุดจะแตกต่างกันไปตามภาษา
chrylis -on

คำตอบ:


10

ฉันจะให้มุมมองของฉันซึ่งถูกเข้ารหัสในข้อตกลง II โครงการที่คุณอ้างอิง

ก่อนมีเงื่อนไขข้อผิดพลาดสองประเภท: ข้อผิดพลาดที่สามารถกู้คืนได้และข้อผิดพลาดที่ไม่สามารถกู้คืนได้

  • ตัวอย่างแรกคือถ้าไม่สามารถอ่านไฟล์อินพุตตัวอย่างเช่นหากคุณกำลังอ่านข้อมูลจากไฟล์เช่น $HOME/.dealiiนั้นอาจมีหรือไม่มีอยู่ ฟังก์ชั่นการอ่านควรกลับไปที่ฟังก์ชั่นการโทรสำหรับคนหลังเพื่อหาว่าจะทำอย่างไร อาจเป็นไปได้ว่าทรัพยากรไม่สามารถใช้ได้ในขณะนี้ แต่อาจจะกลับมาอีกครั้งในหนึ่งนาที (ระบบไฟล์ที่ติดตั้งแบบรีโมต)

  • ตัวอย่างหลังคือถ้าคุณพยายามที่จะเพิ่มเวกเตอร์ขนาด 10 เป็นเวกเตอร์ขนาด 20: ลองทำตามที่คุณคิดไม่มีอะไรที่สามารถทำได้เกี่ยวกับเรื่องนี้ - มีจุดบกพร่องในรหัสที่นำไปสู่ จุดที่เราพยายามทำการเพิ่ม

เงื่อนไขสองข้อนี้ควรได้รับการปฏิบัติแตกต่างกันโดยไม่คำนึงถึงภาษาการเขียนโปรแกรมที่คุณใช้:

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

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

กล่าวอีกนัยหนึ่งสิ่งที่ฉันเรียกร้องให้ที่นี่ (และสิ่งที่คุณทำข้อตกลง II) เป็นส่วนผสมของกลยุทธ์ของคุณ 3 และ 5 ขึ้นอยู่กับบริบท มันเป็นความจริงที่ 3 ไม่ทำงานในภาษาเช่น C หรือ Fortran - ในกรณีที่เราอาจโต้แย้งว่าเป็นเหตุผลที่ดีที่จะไม่ใช้ภาษาที่ทำให้ยากต่อการแสดงสิ่งที่คุณต้องการ

x) แต่เนื่องจากผู้ประเมินจำเป็นต้องถูกเรียกซ้ำ ๆ มันไม่ควรผิดพลาด แต่เป็นข้อยกเว้น ในกรณีเช่นนี้แม้ว่าการส่งผ่านค่าลบจะไม่สามารถกู้คืนได้ แต่ก็ควรมีข้อยกเว้นมากกว่ายกเลิกโปรแกรม ฉันไม่เห็นด้วยกับท่าทางนี้เมื่อสองสามปีก่อน แต่เปลี่ยนใจหลังจากแนวทางซอฟต์แวร์ชุมชน xSDK เข้ารหัสข้อกำหนดที่โปรแกรมไม่ควรผิดพลาด (หรืออย่างน้อยควรมีวิธีเปลี่ยนจากความผิดพลาดเป็นข้อยกเว้น - ดังนั้นข้อตกลง ตอนนี้ II มีตัวเลือกให้ทำการAssertยกเว้นยกเว้นการโทรabort())


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

@cdalitz: มันเป็นข้อบกพร่องในการออกแบบของ C ++ ที่คุณสามารถโยนวัตถุประเภทใดก็ได้ แต่ซอฟต์แวร์ที่สมเหตุสมผลใด ๆ (ยกเว้น Trilinos) จะส่งข้อยกเว้นที่ได้รับมาstd::exceptionเท่านั้นและสามารถอ้างอิงได้โดยไม่ต้องทราบประเภทที่ได้รับมา
Wolfgang Bangerth

1
แต่ฉันไม่เห็นด้วยอย่างยิ่งกับการส่งคืนรหัสข้อผิดพลาดด้วยเหตุผลที่ระบุไว้ในคำถามเดิม: (i) รหัสข้อผิดพลาดจะถูกละเว้นบ่อยเกินไปและเนื่องจากข้อผิดพลาดที่ตามมาไม่ได้รับการจัดการเลย (ii) ในหลายกรณีไม่มีค่าพิเศษที่สามารถส่งคืนได้อย่างสมเหตุสมผลเนื่องจากประเภทการส่งคืนของฟังก์ชันได้รับการแก้ไข (iii) ฟังก์ชั่นมีประเภทผลตอบแทนที่แตกต่างกันและคุณจะต้องกำหนดในแต่ละกรณีแยกกันว่าค่า "พิเศษ" จะเป็นอย่างไรซึ่งแสดงถึงข้อผิดพลาด
Wolfgang Bangerth

WB เขียน (ขออภัยเคล็ดลับ '@' ไม่สามารถใช้งานได้ด้วยเหตุผลบางอย่างและ StackExchage ถูกลบชื่อผู้ใช้ด้วยเหตุผลบางประการ): "รหัสข้อผิดพลาดถูกละเว้นบ่อยเกินไป" สิ่งนี้มีข้อยกเว้นมากขึ้นในการจับข้อผิดพลาด: มีนักพัฒนาซอฟต์แวร์ไม่มากนักที่ใช้ปัญหาการถ่ายคร่อมการเรียกใช้ฟังก์ชันทั้งหมดในบล็อกลอง / catch แต่ส่วนใหญ่มันเป็นเรื่องของรสนิยม: ตราบใดที่เอกสารระบุอย่างชัดเจนว่ามีข้อยกเว้นใดที่ฟังก์ชั่นพ่นฉันสามารถจัดการมันได้ แต่อีกครั้งอาจกล่าวได้ว่าหน้าที่ในการเขียนเอกสารถูกเพิกเฉยบ่อยเกินไป ;-)
cdalitz

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