การเขียนโปรแกรม C ได้รับ Generics แบบ Type-Safe โดยใช้เทคนิค Union ทำให้เกิดการถกเถียงเรื่อง Best Practices

ทีมชุมชน BigGo
การเขียนโปรแกรม C ได้รับ Generics แบบ Type-Safe โดยใช้เทคนิค Union ทำให้เกิดการถกเถียงเรื่อง Best Practices

แนวทางใหม่ในการสร้าง generic data structures แบบ type-safe ใน C ได้เกิดขึ้น โดยใช้ unions เพื่อเชื่อมโยงข้อมูลประเภทกับ generic containers เทคนิคนี้สัญญาว่าจะนำประโยชน์บางส่วนของ generics ในภาษาสมัยใหม่มาสู่การเขียนโปรแกรม C ในขณะที่ยังคงความเข้ากันได้กับ codebase ที่มีอยู่

วิธีการนี้ทำงานโดยการสร้าง union ที่ประกอบด้วยทั้ง data structure จริงและ payload member ที่มีอยู่เพื่อข้อมูลประเภทใน compile-time เท่านั้น โดยการใช้ประโยชน์จาก operator typeof (ซึ่งตอนนี้เป็นส่วนหนึ่งของ C23 ) นักพัฒนาสามารถบังคับใช้ type safety โดยไม่มี runtime overhead ในขณะที่หลีกเลี่ยงข้อผิดพลาดแบบดั้งเดิมของการเขียนโปรแกรม generic ที่ใช้ void pointer

การรองรับ typeof จากคอมไพเลอร์

  • มาตรฐาน C23: typeof ได้กลายเป็นส่วนหนึ่งของมาตรฐาน C อย่างเป็นทางการแล้ว
  • GCC/Clang: รองรับมาอย่างยาวนานผ่านส่วนขยาย __typeof__
  • MSVC: เพิ่มการรองรับในเวอร์ชัน 19.39 (ปลายปี 2023)
  • คอมไพเลอร์รุ่นเก่า: ต้องใช้วิธีแก้ปัญหาชั่วคราวโดยใช้การตรวจสอบประเภทข้อมูลแบบ assignment-based

ชุมชนแบ่งแยกเรื่องแนวทางการใช้งาน

ชุมชนนักเขียนโปรแกรมแสดงปฏิกิริยาแบบผสมผสานต่อแนวทาง union-based นี้ นักพัฒนาบางคนโต้แย้งว่า macro-based generics แบบดั้งเดิม แม้จะมีความซับซ้อน แต่ให้ประสิทธิภาพและความสามารถในการ debug ที่เหนือกว่า การใช้งาน header-based เหล่านี้สร้าง monomorphized code ที่ compiler สามารถ optimize ได้อย่างมีประสิทธิภาพมากขึ้น คล้ายกับ C++ templates

อย่างไรก็ตาม คนอื่นๆ ชี้ให้เห็นข้อเสียที่สำคัญของแนวทาง macro-heavy เครื่องมือ code completion มีปัญหากับ macro-generated functions ขนาด binary อาจพองโตด้วย instantiations หลายตัว และการ debug กลายเป็นเรื่องท้าทายมากขึ้นเมื่อต้อง step through macro-expanded code

แนวทางการเขียนโปรแกรมแบบ Generic ใน C

วิธีการ ความปลอดภัยของชนิดข้อมูล ประสิทธิภาพ ขนาดไฟล์ Binary การ Debug
Macro Headers สูง ยอดเยี่ยม ใหญ่ ยาก
void* Pointers ไม่มี ดี เล็ก ง่าย
Union + typeof สูง ดี ปานกลาง ปานกลาง
Transpilation สูง ยอดเยี่ยม ใหญ่ ง่าย

ข้อกังวลทางเทคนิคและข้อจำกัด

ปัญหาทางเทคนิคหลายประการได้ถูกยกขึ้นเกี่ยวกับวิธี union-based แนวทางนี้อาศัย function pointer casting ซึ่งบางคนโต้แย้งว่าเป็น undefined behavior ตามมาตรฐาน C ปัญหา memory alignment ยังสามารถเกิดขึ้นได้เมื่อใช้ flexible array members กับประเภทที่มีความต้องการ alignment ที่แตกต่างจาก base structure

เทคนิคนี้ยังเผชิญกับข้อจำกัดเกี่ยวกับ intrusive data structures ซึ่ง container node ถูกฝังอยู่ภายในข้อมูลแทนที่จะบรรจุข้อมูลไว้ รูปแบบนี้ซึ่งใช้กันทั่วไปในการเขียนโปรแกรมระบบเช่น Linux kernel ไม่สามารถแปลงไปสู่แนวทาง union-based ได้ดี

วิธีแก้ไขทางเลือกและ Workarounds

สำหรับนักพัฒนาที่ทำงานกับ compiler รุ่นเก่าที่ไม่มีการสนับสนุน typeof มีการใช้งานทางเลือกโดยใช้ assignment-based type checking อย่างไรก็ตาม workarounds เหล่านี้นำความซับซ้อนของตัวเองมาด้วย รวมถึงการประเมิน argument สองครั้งและข้อจำกัดเกี่ยวกับ const-qualified containers

เทคนิคที่คุณกล่าวถึงคือวิธีที่ผมสร้าง C dialect ทั้งหมด syntax ค่อนข้างหนัก แต่ข้อได้เปรียบใหญ่คือ: คุณได้ C structs ปกติในท้ายที่สุด เรียบง่ายมาก คาดเดาได้มาก optimize ได้มาก

นักพัฒนาบางคนสนับสนุนให้ละทิ้งเทคนิค C ที่ซับซ้อนทั้งหมดเพื่อใช้แนวทาง transpilation แทน ซึ่ง compiler ขนาดเล็กสร้าง C code จาก syntax ระดับสูงกว่า วิธีการนี้สามารถให้ abstraction ที่สะอาดกว่าในขณะที่ยังคงมี C เป็น output สุดท้าย

ข้อพิจารณาในทางปฏิบัติ

การถกเถียงในท้ายที่สุดมุ่งเน้นไปที่ trade-offs ในทางปฏิบัติ ในขณะที่เทคนิค union นำเสนอวิธีแก้ไขที่สง่างามสำหรับ container ง่ายๆ เช่น linked lists แต่อาจไม่สามารถขยายได้ดีกับ data structures ที่ซับซ้อนกว่าที่ต้องดำเนินการกับข้อมูลที่บรรจุอยู่ เช่น binary heaps หรือ hash tables ที่ต้องการการเปรียบเทียบหאו hashing operations

สำหรับกรณีการใช้งานหลายๆ แบบ โดยเฉพาะใน embedded systems หรือเมื่อต้องเชื่อมต่อกับ C libraries ที่มีอยู่ เทคนิคการเขียนโปรแกรม generic เหล่านี้ให้การปรับปรุง type safety ที่มีค่าเหนือแนวทาง void pointer แบบดั้งเดิม อย่างไรก็ตาม การเลือกระหว่างวิธีการต่างๆ มักขึ้นอยู่กับความต้องการของโครงการเฉพาะ การสนับสนุนของ compiler และความชอบของทีม

การอภิปรายนี้เน้นย้ำถึงความตึงเครียดที่ดำเนินต่อไปในการเขียนโปรแกรม C ระหว่างการรักษาความเรียบง่ายของภาษาและการเพิ่มความสะดวกในการเขียนโปรแกรมสมัยใหม่ที่นักพัฒนาคาดหวังจากภาษาใหม่ๆ

อ้างอิง: I Write Type Safe Generic Data Structures in C