C# den C++'a: Nasıl Otomatik Proje Dönüşümü Gerçekleştirdik – Bölüm 2

Geliştirme

C#'den C++ kod çeviricisinin tasarımı ve geliştirilmesi yalnızca CodePorting tarafından gerçekleştirildi. Bu, bellek modeli ve diğer yönlerden farklı olan birçok araştırma, çoklu yaklaşım ve test gerektirdi. Sonunda iki çözüm seçildi. Bunlardan biri şu anda Aspose ürünlerinin C++ sürümleri için kullanılıyor.

Teknolojiler

Şimdi kod çeviricisinde kullandığımız teknolojileri açıklama zamanı geldi. Çevirici, C# ile yazılmış bir konsol uygulamasıdır, bu da tipik dizileri gerçekleştiren komut dosyalarına gömülmesini kolaylaştırır, örneğin çevir-derle-test. Ayrıca, aynı işlemi düğmelere tıklayarak yapmanıza olanak tanıyan bir GUI bileşeni bulunmaktadır.

Sözdizimi analizi, eski nesil çeviricinin NRefactory kütüphanesi tarafından ve yeni nesil çeviricinin Roslyn tarafından gerçekleştirilmektedir.

Çevirici, bilgi toplamak ve çıktı C++ kodu oluşturmak için birkaç AST ağacı gezinmesi kullanır. C++ kodu için AST temsili oluşturulmaz, bunun yerine çıktı kodunu saf metin biçiminde ele alırız.

Çeviriciyi ayarlamak için ekstra bilgi gerektiren birçok durum vardır. Bu bilgi, seçenekler ve nitelikler aracılığıyla iletilir. Seçenekler, genellikle tüm projeye uygulanır. Genellikle, kodu ayrıştırırken kullanılan sınıf dışa aktarma makro adını veya C# koşullu sembollerini belirtmek için kullanılırlar. Nitelikler, türler ve varlıklara uygulanır ve bunlar için bazı özel bilgiler sağlar, örneğin çevrilen kodda hangi sınıf üyelerinin const veya mutable niteliklerini gerektirdiğini işaretler veya hangi varlıkların çeviriden hariç tutulması gerektiğini belirtir.

C# sınıfları ve yapıları C++ sınıflarına dönüştürülür. Üyeleri ve kaynak kodları en yakın benzerlere dönüştürülür. Genel türler ve yöntemler C++ şablonlarına eşlenir. C# referansları akıllı işaretçilere (paylaşılan veya zayıf) çevrilir. Referans sınıfları Kütüphane'de tanımlanır. Kod çeviricisinin diğer iç ayrıntıları ayrı bir makalede açıklanacaktır.

Bu nedenle, C# den C++'a çevrilen proje, .NET kütüphanelerinin yerine kendi Kütüphanemize dayanır:

C# to C++

C# kod çevirici Kütüphanesini ve çevrilen projeleri oluşturmak için Cmake kullanıyoruz. Şu anda, VS 2017 ve 2019 (Windows), GCC ve Clang (Linux) derleyicilerini destekliyoruz.

Daha önce de belirtildiği gibi, .NET uygulamalarımızın çoğu, şunlar dahil olmak üzere üçüncü taraf kütüphaneler üzerinde ince adaptörlerdir:

  • Skia — grafik desteği.
  • Botan — şifreleme fonksiyonları.
  • ICU — dizeler, kod sayfaları ve kültürler desteği.
  • Libxml2 — XML işlemleri.
  • PCRE2 — düzenli ifadeler desteği.
  • zlib — sıkıştırma fonksiyonları.
  • Boost — farklı amaçlar.
  • Birkaç diğer kütüphane.

Çevirici ve Kütüphane birçok testle kaplıdır. Kütüphane testleri GoogleTest çerçevesini kullanır. Çevirici testleri çoğunlukla NUnit/xUnit ile yazılmıştır ve çeşitli kategorilere ayrılmıştır, bu da şunları sağlar:

  • Çeviricinin çıktısı belirli girdi verilerinde hedefine uyuyor.
  • Çevrilen programların çıktısı hedefine uyuyor.
  • Girdi projelerinden alınan NUnit/xUnit testleri GoogleTest testlerine çevriliyor ve başarılı oluyor.
  • Çevrilen projelerin API'si C++'da düzgün çalışıyor.
  • Çevirici seçenekleri ve nitelikleri beklenildiği gibi çalışıyor.

Sürüm kontrol sistemi olarak GitLab kullanıyoruz. Sürekli entegrasyon için Jenkins kullanıyoruz. Çevrilen ürünler NuGet paketleri olarak ve indirilebilir arşivler olarak sunulmaktadır.

Sorunlar

Bu projede çalışırken birçok farklı sorunla karşılaştık. Bunlardan bazıları bekleniyordu, bazıları ise yolda ortaya çıktı:

  1. .NET ve C++ arasındaki tür sistemi farkları.
    C++, Object türü için herhangi bir yerine koyma yapısına sahip değil ve çoğu kütüphane sınıfı RTTI'ye sahip değil. Bu, .NET türlerini STL türlerine eşlemeyi imkansız hale getiriyor.
  2. Çeviri algoritmaları karmaşık.
    Çevrilen kodun içinde çözülmesi gereken birçok ince ayrıntı var. Örneğin, C#'ın bir yöntemin argümanlarını hesaplama sırası tanımlıdır, ancak C++ burada UB'ye sahiptir.
  3. Hata ayıklama zordur.
    Çevrilen kodun hata ayıklanması belirli beceriler gerektirir. Yukarıda açıklanan gibi ince ayrıntılar, bir programın işleyişini önemli ölçüde etkileyebilir ve açıklanması zor hatalara neden olabilir. Öte yandan, bu hatalar kolayca gizli hatalara dönüşebilir ve uzun süre kalabilir.
  4. Bellek yönetim sistemleri farklıdır.
    C++'da çöp toplama yoktur. Bu nedenle, çevrilen kodun orijinali gibi davranması için daha fazla kaynak gereklidir.
  5. C# geliştiricileri için disiplin gereklidir.
    C# geliştiricileri, kod çeviri sürecinin neden olduğu kısıtlamalara alışmalıdır. Bu kısıtlamaların nedenleri:
    • Dil sürümü, çevirici sözdizim analizörü tarafından desteklenmelidir.
    • Çevirici tarafından desteklenmeyen kod yapıları yasaktır (örneğin yield).
    • Kod stili, çevrilen kod yapısı tarafından sınırlıdır (örneğin her referans alanı, keyfi C# kodu için böyle olmasa da zayıf bir referans veya paylaşılan bir referans olmalıdır).
    • C++ dili kendi kısıtlamalarını uygular (örneğin C#'da statik değişkenler, tüm ön plan iş parçacıkları bitmeden silinmezken, C++'da durum böyle değildir).
  6. Büyük bir iş yükü.
    Ürünlerimiz tarafından kullanılan .NET kütüphanesinin alt kümesi yeterince büyük ve tüm sınıfları ve yöntemleri uygulamak çok zaman alıyor.
  7. Geliştiriciler için özel gereksinimler.
    Karmaşık platform iç yapılarına derinlemesine inme gerekliliği ve iki veya daha fazla programlama diliyle çalışma, mevcut aday sayısını sınırlar. Öte yandan, derleyici teorisi veya diğer egzotik disiplinlere ilgi duyan geliştiriciler, projede kolayca yer bulurlar.
  8. Sistemin kırılganlığı.
    Çeviriciyi test etmek için binlerce test ve milyonlarca kod satırımız olsa da, bazen bir projenin derlemesini düzeltmek için yapılan değişiklikler, diğer projeyi bozabilir. Örneğin, bu nadir sözdizimi yapıları ve belirli kod stilleri ile olabilir.
  9. Yüksek giriş engelleri.
    Kod çevirici projesindeki çoğu görev derinlemesine analiz gerektirir. Geniş alt sistem ve senaryo sayısı nedeniyle her yeni görev, projenin yeni yönleriyle tanışmayı uzun süre gerektirir.
  10. Fikri mülkiyet koruma sorunları.
    C# kodunu etkili bir şekilde karartmak için birçok hazır çözüm olsa da, C++'da sınıf başlıklarında çok fazla bilgi korunur. Dahası, bazı tanımlar sonuçları olmadan genel başlıklardan kaldırılamaz. Genel sınıfları ve yöntemleri şablonlara eşlemek, başka bir güvenlik açığı yaratır, çünkü algoritmaları ortaya çıkarır.

Tüm bunlara rağmen, kod çevirici projesi teknik açıdan çok ilginçtir ve akademik karmaşıklığı bizi sürekli olarak yeni şeyler öğrenmeye zorlar.

Sonuç

Kod çevirici projesi üzerinde çalışırken, ilginç bir akademik kod çevirme görevini çözen bir sistem başarıyla uyguladık. Aspose kütüphanelerini, çalışması beklenmeyen diller için aylık olarak yayınladık.

Kod çevirici hakkında daha fazla makale yayınlamayı planlıyoruz. Bir sonraki makale, C# yapılarının nasıl C++ yapılarına eşlendiği dahil olmak üzere dönüşüm sürecini ayrıntılı olarak açıklayacaktır. Bir diğeri bellek yönetim modelini ele alacaktır.

Sorulan sorulara en iyi şekilde yanıt vermeye çalışacağız. Okuyucular kod çevirici geliştirmenin diğer yönleriyle ilgilenirse, daha fazla makale yazmayı düşünebiliriz.

İlgili makaleler