28 ธันวาคม 2567
กรอบการทำงานของเรา CodePorting.Translator Cs2Cpp ช่วยให้การเผยแพร่ไลบรารีที่พัฒนาสำหรับแพลตฟอร์ม .NET เป็นภาษา C++ ได้ ในบทความนี้ เราจะพูดถึงวิธีที่เราสามารถปรับให้เข้ากับโมเดลหน่วยความจำของสองภาษานี้ และรับประกันการทำงานที่ถูกต้องของโค้ดที่แปลแล้วในสภาพแวดล้อมที่ไม่มีการจัดการ
คุณจะได้เรียนรู้เกี่ยวกับ สมาร์ทพอยน์เตอร์ ที่เราใช้ และเหตุผลที่เราต้องพัฒนาการใช้งานของเราเองสำหรับพวกเขา นอกจากนี้เราจะครอบคลุมกระบวนการเตรียมโค้ด C# สำหรับการพอร์ตจากมุมมองของการจัดการวงจรชีวิตของวัตถุ อุปสรรคที่เราเผชิญ และวิธีการวินิจฉัยเฉพาะทางที่เราต้องใช้ในงานของเรา
โค้ด C# ถูกเรียกใช้งานใน สภาพแวดล้อมที่มีการจัดการ พร้อม การเก็บขยะ เพื่อวัตถุประสงค์ในการแปล หมายความว่าผู้เขียนโปรแกรม C# ซึ่งแตกต่างจากนักพัฒนา C++ ไม่ต้องกังวลเกี่ยวกับการคืนหน่วยความจำที่ไม่ใช้แล้วให้กับระบบ สิ่งนี้ดำเนินการโดยการเก็บขยะ (GC) ซึ่งเป็นส่วนประกอบของสภาพแวดล้อม CLR ที่จะตรวจสอบอย่างสม่ำเสมอว่า วัตถุใดในโปรแกรมที่ยังคงใช้งานอยู่ และล้างข้อมูลที่ไม่มีการอ้างอิงที่ใช้งานอีกต่อไป
การอ้างอิงที่ใช้งานอยู่ถือเป็น:
อัลกอริธึมของการเก็บขยะมักจะถูกอธิบายว่าเป็นการเดินผ่านกราฟของวัตถุ โดยเริ่มจากการอ้างอิงที่ตั้งอยู่บนสแต็คและในฟิลด์สแตติก จากนั้นลบทุกอย่างที่ไม่ถึงในขั้นตอนก่อนหน้า เพื่อป้องกันการทำให้กราฟการอ้างอิงเป็นโมฆะในระหว่างการดำเนินการ GC มีการใช้กลไก หยุดโลก คำอธิบายนี้อาจถูกทำให้เรียบง่าย แต่ก็เพียงพอสำหรับวัตถุประสงค์ของเรา
ต่างจากสมาร์ทพอยน์เตอร์ การเก็บขยะไม่มีปัญหาการอ้างอิงข้ามหรืออ้างอิงเป็นวง หากมีวัตถุสองตัวอ้างอิงซึ่งกันและกัน อาจผ่านวัตถุกลางหลายตัว นั่นจะไม่ขัดขวาง GC จากการลบพวกเขาเมื่อกลุ่มทั้งหมด ซึ่งรู้จักกันในชื่อเกาะแห่งการแยกตัว ไม่มีการอ้างอิงที่ใช้งานอยู่อีกต่อไป ดังนั้นนักเขียนโปรแกรม C# ไม่มีความลำเอียงต่อการเชื่อมโยงวัตถุเข้าด้วยกันในเวลาใดก็ตามและในรูปแบบใดก็ตาม
ประเภทข้อมูลใน C# ถูกแบ่งออกเป็น อ้างอิง และ ค่า อินสแตนซ์ของประเภทค่าอยู่เสมอในสแต็ค ในหน่วยความจำสแตติก หรือโดยตรงในฟิลด์ของโครงสร้างและวัตถุ ในทางตรงกันข้าม อินสแตนซ์ของประเภทอ้างอิงจะถูกสร้างขึ้นบนฮีปเสมอ ในขณะที่ในสแต็ค หน่วยความจำสแตติก และฟิลด์จะมีเพียงการอ้างอิง (ที่อยู่) เท่านั้น ประเภทค่ารวมถึงโครงสร้าง ค่าพีชคณิตดั้งเดิม และการอ้างอิง ประเภทอ้างอิงรวมถึงคลาสและ ทางประวัติศาสตร์ เดลิเกต ข้อยกเว้นเพียงอย่างเดียวสำหรับกฎนี้คือกรณีของ การบรรจุ ซึ่งโครงสร้างหรือประเภทค่าอื่น ๆ จะถูกคัดลอกไปยังฮีปเพื่อใช้ในบริบทเฉพาะ
ช่วงเวลาของการทำลายวัตถุไม่ได้ถูกกำหนด – ภาษารับประกันเพียงว่ามันจะไม่เกิดขึ้นก่อนที่การอ้างอิงที่ใช้งานล่าสุดจะถูกลบออก หากในโค้ดที่แปลงแล้ว วัตถุจะถูกทำลายทันทีเมื่อการอ้างอิงที่ใช้งานล่าสุดถูกลบออก สิ่งนี้จะไม่ขัดขวางการทำงานของโปรแกรม
จุดสำคัญอีกประการหนึ่งเกี่ยวข้องกับการสนับสนุนประเภทและวิธีการทั่วไปใน C# C# อนุญาตให้เขียนเจนเนอริคหนึ่งครั้ง และใช้มันกับทั้งประเภทอ้างอิงและประเภทค่า ตามที่จะแสดงให้เห็นในภายหลัง ประเด็นนี้พิสูจน์แล้วว่าสำคัญ
คำไม่กี่คำเกี่ยวกับวิธีที่เราจับคู่ประเภท C# กับประเภท C++ เนื่องจากเฟรมเวิร์ก CodePorting.Translator Cs2Cpp มีจุดประสงค์เพื่อการพอร์ตไลบรารีมากกว่าการใช้งาน ข้อกำหนดที่สำคัญสำหรับเราคือการสร้าง API ของโครงการ .NET ดั้งเดิมให้แม่นยำที่สุด ดังนั้นเราจึงแปลงคลาส อินเตอร์เฟส และโครงสร้างของ C# เป็นคลาส C++ ที่สืบทอดประเภทฐานที่สอดคล้องกัน
ตัวอย่างเช่น พิจารณารหัสต่อไปนี้:
interface I1 {}
interface I2 {}
interface I3 : I2 {}
class A {}
class B : A, I1 {}
class C : B, I2 {}
class D : C, I3 {}
class Generic<T> { public T value; }
struct S {}
มันจะถูกแปลดังนี้:
class I1 : public virtual System::Object {};
class I2 : public virtual System::Object {};
class I3 : public virtual I2 {};
class A : public virtual System::Object {};
class B : public A, public virtual I1 {};
class C : public B, public virtual I2 {};
class D : public C, public virtual I3 {};
template <typename T> class Generic { public: T value; };
class S : public System::Object {};
คลาส System::Object
เป็นคลาสระบบที่ประกาศในไลบรารีการสนับสนุนของนักแปล ซึ่งอยู่นอกโครงการที่แปลงแล้ว คลาสและอินเทอร์เฟซสืบทอดจากมันโดยใช้การสืบทอดเสมือนเพื่อหลีกเลี่ยง ปัญหาของเพชร โครงสร้างในโค้ด C++ ที่แปลงแล้วจะสืบทอดจาก System::Object
ในขณะที่ใน C# จะสืบทอดจากมันผ่าน System.ValueType
แต่การสืบทอดที่เกินมาจะถูกลบเพื่อจุดประสงค์ในการเพิ่มประสิทธิภาพ ประเภทและวิธีการทั่วไปจะแปลเป็นคลาสและวิธีการแม่แบบตามลำดับ
อินสแตนซ์ของประเภทที่แปลต้องปฏิบัติตามการรับประกันเดียวกันกับที่ C# มอบให้ อินสแตนซ์ของคลาสจะต้องถูกสร้างขึ้นในฮีปและอายุการใช้งานของพวกมันจะต้องถูกกำหนดโดยอายุการใช้งานของการอ้างอิงที่ใช้งานอยู่ อินสแตนซ์ของโครงสร้างจะต้องถูกสร้างในสแต็ค ยกเว้นในกรณีของการบรรจุ เดลิเกตเป็นกรณีพิเศษและค่อนข้างไม่ซับซ้อน ซึ่งอยู่นอกขอบเขตของบทความนี้
เราได้พิจารณาแล้วว่าโมเดลการจัดการหน่วยความจำใน C# ส่งผลต่อกระบวนการแปลงโค้ดเป็น C++ อย่างไร ในส่วนถัดไปของบทความ เราจะพูดคุยเกี่ยวกับวิธีการตัดสินใจเกี่ยวกับวิธีการจัดการอายุการใช้งานของวัตถุที่ถูกจัดสรรในฮีป และวิธีการนำวิธีการนี้ไปใช้