11 มีนาคม 2567
เรามาพูดคุยกันว่านักแปลของเราแปลงโครงสร้างวากยสัมพันธ์จากภาษา C# ไปเป็น C++ ได้อย่างไร เราจะสำรวจข้อมูลเฉพาะของการแปลและข้อจำกัดที่เกิดขึ้นระหว่างกระบวนการนี้
การแปลเกิดขึ้นเป็นรายโครงการ หนึ่งโปรเจ็กต์ C# จะถูกแปลงเป็นหนึ่งหรือสองโปรเจ็กต์ C++ โปรเจ็กต์แรกสะท้อนโปรเจ็กต์ C# ในขณะที่โปรเจ็กต์ที่สองทำหน้าที่เป็นแอปพลิเคชัน googletest สำหรับเรียกใช้การทดสอบหากมีอยู่ในโปรเจ็กต์ดั้งเดิม ไฟล์ CMakeLists.txt ถูกสร้างขึ้นสำหรับแต่ละโปรเจ็กต์อินพุต ซึ่งช่วยให้สามารถสร้างโปรเจ็กต์สำหรับระบบ build ส่วนใหญ่ได้
โดยปกติแล้ว ไฟล์ .cs หนึ่งไฟล์จะสอดคล้องกับไฟล์ .h หนึ่งไฟล์ และไฟล์ .cpp หนึ่งไฟล์ โดยทั่วไปแล้ว คำจำกัดความของประเภทจะอยู่ในไฟล์ส่วนหัว ในขณะที่คำจำกัดความของวิธีการจะอยู่ในไฟล์ซอร์สโค้ด อย่างไรก็ตาม สิ่งนี้จะแตกต่างออกไปสำหรับประเภทเทมเพลต โดยที่โค้ดทั้งหมดจะยังคงอยู่ในไฟล์ส่วนหัว ไฟล์ส่วนหัวที่มีคำจำกัดความสาธารณะอย่างน้อยหนึ่งรายการจะจบลงในไดเร็กทอรีรวม ซึ่งสามารถเข้าถึงได้โดยโปรเจ็กต์และผู้ใช้ปลายทาง ไฟล์ส่วนหัวที่มีคำจำกัดความภายในเท่านั้นจะเข้าสู่ไดเร็กทอรีต้นทาง
นอกจากไฟล์โค้ดที่ได้รับจากการแปลโค้ด C# ต้นฉบับแล้ว นักแปลยังสร้างไฟล์เพิ่มเติมที่มีโค้ดบริการอีกด้วย ไฟล์การกำหนดค่าที่มีรายการระบุตำแหน่งที่จะค้นหาประเภทจากโปรเจ็กต์นี้ในไฟล์ส่วนหัวจะถูกวางไว้ในไดเร็กทอรีเอาต์พุตด้วย ข้อมูลนี้จำเป็นสำหรับการจัดการแอสเซมบลีที่ต้องพึ่งพา นอกจากนี้ บันทึกการแปลที่ครอบคลุมจะถูกจัดเก็บไว้ในไดเรกทอรีผลลัพธ์
using <typename> = ...
enum class
)System::MulticastDelegate
:public delegate int IntIntDlg(int n);
using IntIntDlg = System::MulticastDelegate<int32_t(int32_t)>;
System.Object
จะมีความชัดเจนปัจจัยทั้งหมดเหล่านี้รวมกันทำให้เกิดข้อจำกัดหลายประการ:
เราเข้าใจดีว่าการเลียนแบบพฤติกรรม C# อย่างเคร่งครัดจะต้องมีแนวทางที่แตกต่างออกไปบ้าง อย่างไรก็ตาม เราเลือกตรรกะนี้เนื่องจากจะปรับ API ของไลบรารีที่แปลงให้สอดคล้องกับกระบวนทัศน์ C++ มากขึ้น ตัวอย่างด้านล่างแสดงให้เห็นถึงคุณลักษณะเหล่านี้:
รหัส C#:
using System;
public class Base
{
public virtual void Foo1()
{ }
public void Bar()
{ }
}
public interface IFoo
{
void Foo1();
void Foo2();
void Foo3();
}
public interface IBar
{
void Bar();
}
public class Child : Base, IFoo, IBar
{
public void Foo2()
{ }
public virtual void Foo3()
{ }
public T Bazz<T>(object o) where T : class
{
if (o is T)
return (T)o;
else
return default(T);
}
}
ไฟล์ส่วนหัว C++:
#pragma once
#include <system/object_ext.h>
#include <system/exceptions.h>
#include <system/default.h>
#include <system/constraints.h>
class Base : public virtual System::Object
{
typedef Base ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Foo1();
void Bar();
};
class IFoo : public virtual System::Object
{
typedef IFoo ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Foo1() = 0;
virtual void Foo2() = 0;
virtual void Foo3() = 0;
};
class IBar : public virtual System::Object
{
typedef IBar ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Bar() = 0;
};
class Child : public Base, public IFoo, public IBar
{
typedef Child ThisType;
typedef Base BaseType;
typedef IFoo BaseType1;
typedef IBar BaseType2;
typedef ::System::BaseTypesInfo<BaseType, BaseType1, BaseType2> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
void Foo1() override;
void Bar() override;
void Foo2() override;
void Foo3() override;
template <typename T>
T Bazz(System::SharedPtr<System::Object> o)
{
assert_is_cs_class(T);
if (System::ObjectExt::Is<T>(o))
{
return System::StaticCast<typename T::Pointee_>(o);
}
else
{
return System::Default<T>();
}
}
};
ซอร์สโค้ด C++:
#include "Class1.h"
RTTI_INFO_IMPL_HASH(788057553u, ::Base, ThisTypeBaseTypesInfo);
void Base::Foo1()
{
}
void Base::Bar()
{
}
RTTI_INFO_IMPL_HASH(1733877629u, ::IFoo, ThisTypeBaseTypesInfo);
RTTI_INFO_IMPL_HASH(1699913226u, ::IBar, ThisTypeBaseTypesInfo);
RTTI_INFO_IMPL_HASH(3787596220u, ::Child, ThisTypeBaseTypesInfo);
void Child::Foo1()
{
Base::Foo1();
}
void Child::Bar()
{
Base::Bar();
}
void Child::Foo2()
{
}
void Child::Foo3()
{
}
ชุดของนามแฝงและมาโครที่จุดเริ่มต้นของคลาสที่แปลแต่ละคลาสใช้เพื่อจำลองกลไก C# บางอย่าง โดยหลักๆ คือ GetType
, typeof
และ is
รหัสแฮชจากไฟล์ .cpp ใช้สำหรับการเปรียบเทียบประเภทที่มีประสิทธิภาพ ฟังก์ชันทั้งหมดที่ใช้อินเทอร์เฟซเป็นแบบเสมือน แม้ว่าจะแตกต่างจากลักษณะการทำงานของ C# ก็ตาม