จาก C# ไปสู่ C++: วิธีการแปลงโครงการอัตโนมัติ – ส่วนที่ 2

การพัฒนา

การออกแบบและการพัฒนาตัวแปลรหัส C# เป็น C++ ได้รับการดำเนินการโดยทีม CodePorting เท่านั้น ซึ่งต้องการการสำรวจหลายครั้ง การใช้วิธีการหลายวิธี และการทดสอบที่แตกต่างกันตามแบบจำลองหน่วยความจำและด้านอื่น ๆ ในที่สุด ได้เลือกสองวิธี หนึ่งในนั้นกำลังใช้งานอยู่ในการเปิดตัว C++ ของผลิตภัณฑ์ Aspose ค่ะ

เทคโนโลยี

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

การวิเคราะห์ไวยากรณ์ถูกดำเนินการโดยไลบรารี NRefactory ในรุ่นที่เก่าของโปรแกรมแปลภาษาและโดย Roslyn ในรุ่นใหม่

โปรแกรมแปลภาษาใช้การวิ่งผ่าน AST tree หลายครั้งเพื่อเก็บข้อมูลและสร้างรหัส C++ ที่เป็นผลลัพธ์ สำหรับรหัส C++ ไม่มีการสร้าง AST representation แทนที่เราจะจัดการรหัสผลลัพธ์ในรูปแบบข้อความเท่านั้น

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

เมื่อแปลจาก C# เป็น C++, คลาสและโครงสร้างจะถูกแปลงเป็นคลาส C++. สมาชิกและรหัสต้นฉบับของพวกเขาจะถูกแปลงเป็นความใกล้เคียงที่สุด ประเภทและเมธอดทั่วไปจะถูกแมปเป็นเทมเพลต C++. การอ้างอิง C# จะถูกแปลงเป็นสมาร์ทพอยน์เตอร์ (shared หรือ weak). คลาสอ้างอิงถูกกำหนดไว้ใน Library. รายละเอียดภายในของตัวแปลรหัสจะถูกอธิบายใน บทความ ที่แยกออกมา

ดังนั้น โปรเจคที่ถูกแปลงจาก C# เป็น C++ ขึ้นอยู่กับ Library ของเราแทนที่จะใช้ .NET libraries:

C# to C++

เพื่อสร้าง Library และโปรเจคที่ถูกแปลงของตัวแปลรหัส ณ จุดนี้เราใช้ Cmake ในการสนับสนุน. ณ ขณะนี้เราสนับสนุนคอมไพล์เลเวล VS 2017 และ 2019 (Windows), GCC, และ Clang (Linux).

เหมือนที่กล่าวไว้แล้ว ส่วนใหญ่ของการประยุกต์ใช้ .NET ของเราเป็นตัวอ่อนที่ปรับใช้กับไลบรารีของบุคคลที่สาม ซึ่งรวมถึง:

  • Skia — สนับสนุนกราฟิก
  • Botan — ฟังก์ชันการเข้ารหัส
  • ICU — สตริง รหัสหน้า และสนับสนุนวัฒนธรรม
  • Libxml2 — การดำเนินการ XML
  • PCRE2 — สนับสนุนการใช้สูตรประจำ
  • zlib — ฟังก์ชันการบีบอัด
  • Boost — ใช้ในวัตถุประสงค์ต่าง ๆ
  • ไลบรารีอื่น ๆ

ทั้ง Translator และ Library ได้รับการทดสอบอย่างละเอียด การทดสอบของ Library ใช้ GoogleTest framework ส่วนการทดสอบของ Translator ส่วนใหญ่เขียนด้วย NUnit/xUnit และแบ่งเป็นหลายหมวดหมู่เพื่อให้แน่ใจว่า:

  • ผลลัพธ์ของตัวแปลตรงกับเป้าหมายของมันในข้อมูลนำเข้าที่เฉพาะเจาะจง
  • ผลลัพธ์ของโปรแกรมที่ถูกแปลงตรงกับเป้าหมายของมัน
  • การทดสอบ NUnit/xUnit จากโปรเจคข้อมูลนำเข้าถูกแปลงเป็น GoogleTest และผ่าน
  • API ของโปรเจคที่ถูกแปลงทำงานได้ดีใน C++
  • ตัวเลือกและแอตทริบิวต์ของตัวแปลทำงานตามที่คาดหวัง

เราใช้ GitLab เป็นระบบควบคุมเวอร์ชัน สำหรับ CI เราใช้ Jenkins ผลิตภัณฑ์ที่ถูกแปลงมีให้ใช้เป็นแพ็คเกจ NuGet และเป็นไฟล์เก็บถาวรที่สามารถดาวน์โหลดได้

ปัญหา

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

  1. ความแตกต่างระหว่างระบบประเภทของ .NET และ C++.
    ภาษา C++ ไม่มีการแทนที่สำหรับประเภท Object และคลาสในไลบรารีส่วนใหญ่ไม่มี RTTI ซึ่งทำให้เป็นไปไม่ได้ที่จะทำการแมปประเภทของ .NET ไปยัง STL ได้
  2. อัลกอริทึมการแปลภาษาซับซ้อน.
    มีความซับซ้อนในการค้นหารายละเอียดที่ต้องการในรหัสที่ถูกแปล ตัวอย่างเช่น ใน C# มีลำดับที่กำหนดให้คำนวณอาร์กิวเมนต์ของเมธอด ในขณะที่ใน C++ มี UB ที่นี่
  3. การแก้ปัญหายาก.
    การดีบักรหัสที่ถูกแปลต้องใช้ทักษะเฉพาะ รายละเอียดเช่นที่อธิบายข้างต้นอาจมีผลกระทบต่อการทำงานของโปรแกรมอย่างสำคัญ ทำให้เกิดข้อผิดพลาดที่ยากต่อการอธิบาย อย่างไรก็ตาม มันอาจกลายเป็นข้อผิดพลาดที่ซ่อนอยู่และยังคงอยู่เป็นเวลานาน
  4. ระบบการจัดการหน่วยความจำแตกต่างกัน.
    C++ ไม่มีการรวบรวมขยะ ด้วยเหตุนี้จึงต้องใช้ทรัพยากรมากขึ้นเพื่อให้รหัสที่ถูกแปลมีพฤติกรรมเช่นเดียวกับรหัสต้นฉบับ
  5. ต้องมีวินัยสำหรับนักพัฒนา C#
    นักพัฒนา C# ต้องเริ่มคำจำกัดความที่เกิดจากกระบวนการแปลรหัส สาเหตุของข้อจำกัดดังนี้:
    • เวอร์ชันของภาษาต้องได้รับการสนับสนุนจากตัววิเคราะห์ไวยากรณ์ของตัวแปล
    • โค้ดที่ไม่ได้รับการสนับสนุนจากตัวแปลจะถูกห้าม (เช่น yield)
    • สไตล์ของโค้ดถูกจำกัดโดยโครงสร้างของโค้ดที่ถูกแปล (เช่น ทุกฟิลด์อ้างอิงต้องเป็นฟิลด์อ้างอิงที่ไม่ก่อให้เกิดความกำกวมหรือฟิลด์อ้างอิงที่ใช้ร่วมกัน ในขณะที่สำหรับโค้ด C# ที่ไม่ใช่กรณีนี้)
    • ภาษา C++ กำหนดข้อจำกัดของมัน (เช่น ใน C# ตัวแปรสแตติกจะไม่ถูกลบก่อนที่เธรดหน้าจอทั้งหมดจะเสร็จสิ้น ในขณะที่ใน C++ ไม่ใช่กรณีนี้)
  6. ปริมาณงานที่มาก
    ส่วนหนึ่งของไลบรารี .NET ที่ใช้ในผลิตภัณฑ์ของเรามีขนาดใหญ่พอ และใช้เวลามากในการดำเนินการทั้งหมด
  7. ความต้องการพิเศษสำหรับนักพัฒนา
    ความจำเป็นในการศึกษาลึกลงในภายในแพลตฟอร์มที่ซับซ้อน และการทำงานกับภาษาโปรแกรมอย่างน้อย 2 ภาษา จำกัดจำนวนผู้สมัครที่มีอยู่ ในทางกลับกัน นักพัฒนาที่สนใจในทฤษฎีของคอมไพเลอร์หรือวิชาที่เป็นเอ็กโซติกสามารถหาที่อยู่ในโครงการได้ง่าย
  8. ความบ Fragility ของระบบ.
    แม้ว่าเราจะมีทดสอบหลายพันรายการและล้านบรรทัดของรหัสเพื่อทดสอบตัวแปล แต่บางครั้งเราก็พบปัญหาเมื่อการเปลี่ยนแปลงที่ทำเพื่อแก้ไขการคอมไพล์ของโครงการหนึ่งทำให้มันเสียหายสำหรับโครงการอื่น ตัวอย่างเช่นสิ่งนี้อาจเกิดขึ้นกับโครงสร้างไวยากรณ์ที่หายากและสไตล์รหัสที่เฉพาะเจาะจงในโครงการ
  9. ขีปนาวุธสูง.
    งานในโครงการแปลรหัสส่วนใหญ่ต้องการการวิเคราะห์ลึก ด้วยเหตุผลที่มีจำนวนย่อยระบบและสถานการณ์ที่หลากหลาย งานใหม่ทุกงานต้องใช้เวลาในการเรียนรู้เกี่ยวกับด้านใหม่ของโครงการนาน
  10. ปัญหาการป้องกันทรัพย์สินทางปัญญา.
    ในขณะที่มีส่วนที่พร้อมใช้ในการทำให้รหัส C# ซับซ้อนมาก ใน C++ มีข้อมูลที่ถูกเก็บไว้ในส่วนหัวของคลาส นอกจากนี้ บางคำนิยามไม่สามารถลบออกจากส่วนหัวสาธารณะได้โดยไม่มีผลกระทบ การแมปคลาสและเมธอดทั่วไปเป็นเทมเพลตสร้างความเสี่ยงอีกครั้ง เนื่องจากมันเปิดเผยขั้นตอนวิธีการ

แม้ว่าจะมีปัญหาเหล่านี้ โครงการแปลรหัสยังคงน่าสนใจอย่างมากจากมุมมองทางเทคนิค และความซับซ้อนทางวิชาการของมันทำให้เราเรียนรู้สิ่งใหม่ๆ ตลอดเวลา

ความสรุป

ในระหว่างการทำงานในโครงการแปลรหัส พวกเราได้ประสบความสำเร็จในการสร้างระบบที่แก้ปัญหางานวิชาการที่น่าสนใจเกี่ยวกับการแปลรหัส พวกเราได้จัดการเผยแพร่ Aspose libraries รายเดือนสำหรับภาษาที่ไม่ควรทำงานด้วย

มีแผนที่จะเผยแพร่บทความเพิ่มเติมเกี่ยวกับโครงการแปลรหัส บทความถัดไป จะอธิบายกระบวนการแปลงอย่างละเอียด รวมถึงวิธีที่โครงสร้าง C# ที่เป็นที่แน่นอนถูกแมปไปยัง C++ อีกอย่าง บทความอื่น ๆ จะพูดถึงรูปแบบการจัดการหน่วยความจำ

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

บทความที่เกี่ยวข้อง