11 Mart 2024
Çevirmenimizin sözdizimsel yapıları C# dilinden C++ diline nasıl dönüştürdüğünü tartışalım. Bu süreçte çevirinin ayrıntılarını ve ortaya çıkan sınırlamaları inceleyeceğiz.
Çeviri, proje bazında gerçekleşir. Bir C# projesi, bir veya iki C++ projesine dönüştürülür. İlk proje, C# projesini yansıtırken, ikincisi orijinal projede varsa testleri çalıştırmak için bir googletest uygulaması olarak hizmet eder. Her girdi projesi için bir CMakeLists.txt dosyası oluşturulur, bu da çoğu derleme sistemi için projelerin oluşturulmasına olanak tanır.
Genellikle bir .cs dosyası bir .h dosyasına ve bir .cpp dosyasına karşılık gelir. Tip tanımları genellikle başlık dosyalarına yerleştirilirken, yöntem tanımları kaynak kod dosyalarında bulunur. Ancak, şablon türleri için bu farklıdır; burada tüm kod başlık dosyalarında kalır. En az bir genel tanım içeren başlık dosyaları, bağımlı projeler ve son kullanıcılar tarafından erişilebilen include dizinine yerleştirilir. Yalnızca iç tanımları olan başlık dosyaları source dizinine yerleştirilir.
Orijinal C# kodunu çevirerek elde edilen kod dosyalarının yanı sıra, çevirmen hizmet kodu içeren ek dosyalar da oluşturur. Başlık dosyalarındaki bu projenin türlerini nerede bulacağını belirten giriş dosyaları da çıktı dizinine yerleştirilir. Bu bilgi, bağımlı derlemeleri işlemek için gereklidir. Ayrıca, kapsamlı bir çeviri günlüğü çıktı dizininde saklanır.
using <typename> = ...
sözdizimi kullanılarak çevrilir.enum class
sözdizimi kullanılarak) eşlenir.System::MulticastDelegate
sınıfının özelleştirmeleri için takma adlara dönüştürülür:public delegate int IntIntDlg(int n);
using IntIntDlg = System::MulticastDelegate<int32_t(int32_t)>;
System.Object
'ten gelen zımni kalıtım açık hale getirilir.Tüm bu faktörler bir araya geldiğinde birkaç sınırlama ortaya çıkar:
C# davranışını sıkı bir şekilde taklit etmenin biraz farklı bir yaklaşım gerektireceğini anlıyoruz. Bununla birlikte, dönüştürülen kitaplıkların API'sini C++ paradigmalarına daha yakın hale getiren bu mantığı seçtik. Aşağıdaki örnek bu özellikleri açıklar:
C# kodu:
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++ başlık dosyası:
#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++ kaynak kodu:
#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()
{
}
Her çevrilen sınıfın başlangıcındaki takma adlar ve makrolar, özellikle GetType
, typeof
ve is
gibi belirli C# mekanizmalarını taklit etmek için kullanılır. .cpp dosyasındaki karma kodlar etkili tür karşılaştırmaları için kullanılır. Arayüzleri uygulayan tüm işlevler sanaldır, bu C# davranışından farklıdır.