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

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

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

พื้นหลัง

ความสำเร็จของตัวแปลโค้ดจาก C# ไปยัง C++ ขึ้นอยู่กับประสบการณ์ที่ประสบความสำเร็จของทีม CodePorting ในการตั้งค่าการแปลโค้ดจาก C# ไปยัง Java อัตโนมัติ โครงสร้างที่สร้างขึ้นสามารถแปลงคลาส C# เป็นคลาส Java โดยที่มีการแทนที่เรียกใช้งานไลบรารีระบบอย่างถูกต้อง

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

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

แผนการแก้ปัญหาถูกแบ่งออกเป็นสองส่วน:

  • Translator — แอปพลิเคชันที่ใช้ในการแปลภาษา C# เป็นภาษา Java โดยแทนที่ประเภทและเมธอดของ .NET ด้วยการแทนที่ที่เหมาะสมจากไลบรารีของภาษาเป้าหมาย
  • Library — ส่วนประกอบที่จำลองส่วนของไลบรารี .NET ที่ไม่สามารถแมปไปยังภาษา Java ได้อย่างถูกต้อง ในการทำงานง่ายขึ้น สามารถใช้คอมโพเนนต์จากบุคคลที่สามได้

ข้อความต่อไปนี้ยืนยันว่าแผนการเป็นไปได้ทางเทคนิค:

  1. ภาษา C# และ Java มีแนวคิดที่คล้ายกัน อย่างน้อยก็เมื่อเกี่ยวกับโครงสร้างของประเภทและรูปแบบการจัดการหน่วยความจำ
  2. เราต้องแปลไลบรารีเท่านั้น ดังนั้นการย้าย GUI ไปยังแพลตฟอร์มอื่นไม่เป็นปัญหา
  3. ไลบรารีที่ถูกแปลมีเนื้อหาทางธุรกิจและการดำเนินการระดับต่ำ ๆ โดยมีความซับซ้อนสูงสุดคือ System.Net และ System.Drawing
  4. ตั้งแต่ต้นเราได้พัฒนาไลบรารีให้ทำงานกับรุ่น .NET ที่หลากหลาย (รวมถึง Framework, Standard และ Xamarin) ดังนั้นความแตกต่างขนานเล็กของแพลตฟอร์มสามารถถูกละเว้นได้

เราจะไม่ลงรายละเอียดเพิ่มเติมเกี่ยวกับตัวแปลภาษา C# เป็น Java นี้ ซึ่งต้องการบทความที่เป็นส่วน Dedicated ในสรุป การแปลผลิตภัณฑ์ C# เป็น Java ได้กลายเป็นปฏิบัติการประจำของบริษัท ด้วยความขอบคุณต่อตัวแปลรหัสที่สร้างขึ้น ตัวแปลได้เติบโตจากตัวแปลข้อกำหนดข้อกำหนดที่ใช้กฎเพียงครั้งเดียวเป็นตัวสร้างรหัสที่ซับซ้อนที่ทำงานกับ AST representation ของรหัสต้นฉบับ

ความสำเร็จของตัวแปล C# เป็น Java ช่วยให้เราเข้าสู่ตลาด Java และเกิดความสนใจในการเริ่มต้นการเปิดตัวสำหรับ C++ โดยใช้สถานการณ์เดียวกัน

ข้อกำหนด

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

  1. จำลองสภาพแวดล้อม .NET สำหรับรหัสที่ถูกแปล
  2. ปรับรหัสที่ถูกแปลให้เหมาะสมสำหรับ C++: โครงสร้างประเภท การจัดการหน่วยความจำ ฯลฯ
  3. ย้ายจากสไตล์รหัส C# ที่ถูกแปลไปสู่สไตล์ C++ เพื่อทำให้ง่ายต่อการใช้รหัสสำหรับนักพัฒนาที่ไม่คุ้นเคยกับแนวคิดของ .NET

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

  1. ไม่สามารถครอบคลุมความต้องการที่สองและสามได้
  2. Mono ถูกสร้างขึ้นบนภาษา C# และขึ้นอยู่กับรันไทม์ของ C#
  3. การปรับปรุงรหัสจากโค้ดของบุคคลที่สามให้เข้ากับความต้องการของเรา (API, ระบบประเภท, ระบบจัดการหน่วยความจำ, การปรับปรุง ฯลฯ) จำเป็นต้องใช้เวลาเท่ากับการสร้างโซลูชันของเรา
  4. ผลิตภัณฑ์ของเราไม่ต้องการการประมวลผล .NET ทั้งหมด แต่ถ้าเรามีการประมวลผล .NET ทั้งหมด จะยากต่อการแยกว่าเราต้องการเมธอดและคลาสใด และคลาสใดไม่ต้องการ ทำให้เราต้องใช้เวลามากในการแก้ไขคุณสมบัติที่เราไม่เคยใช้

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

เรามากลับมาที่ความต้องการของเราสำหรับตัวแปลโค้ดกันบ้างครับ ด้วยเหตุผลที่ไม่สามารถแมปประเภทของ .NET ไปยัง STL ได้ เราตัดสินใจใช้ประเภทของ Library ที่กำหนดเองแทน ไลบรารีถูกพัฒนาขึ้นเป็นชุดของอะแดปเตอร์ที่อนุญาตให้ใช้คุณสมบัติของไลบรารีของบุคคลที่สามผ่าน API ที่คล้ายกับ .NET (เหมือนในภาษา Java)

เมื่อเราแปลไลบรารีด้วย API ที่มีอยู่แล้ว ความต้องการสำคัญสำหรับโค้ดที่ถูกแปลคือ โค้ดที่ถูกแปลควรทำงานภายในแอปพลิเคชันของลูกค้าใด ๆ ดังนั้นเราไม่สามารถใช้การเก็บขยะสำหรับโค้ดที่ถูกแปลได้ เนื่องจากมันจะครอบคลุมทั้งแอปพลิเคชัน แทนที่นั้น รูปแบบการจัดการหน่วยความจำของเราต้องเป็นชัดเจนสำหรับนักพัฒนา C++ การใช้ smart pointers ถูกเลือกเป็นการตัดสินใจที่สอดคล้องกัน ในบทความที่แตกต่างกันเราจะอธิบายว่าเราได้ประสบความสำเร็จในการเปลี่ยนรูปแบบการจัดการหน่วยความจำ

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

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

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