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

Müşteriler, popüler formatların protokollerini ve dosyalarını manipüle etmeyi sağlayan Aspose ürünlerini değerlendiriyor. Bunların çoğu başlangıçta .NET için geliştirilmişti. Aynı zamanda, dosya formatları için iş uygulamaları farklı ortamlarda çalışır. Bu makale, Aspose ürünlerini C++ için yayınlamayı başardığımızı, C# kodunu çevirmek için bir çerçeve oluşturarak açıklayacaktır. Bu ürünlerin .NET sürümlerinin işlevselliğini korumak teknik olarak zorlu bir süreçti.

Gerekli altyapıyı kendimiz geliştirdik ve diller arasında kod çevirisi ve .NET kütüphane işlevlerinin emülasyonunu mümkün kıldık. Böylece genellikle akademik olarak kabul edilen bir sorunu çözdük. Bu, her bir sürüm için kodu C# koduyla eşleşen C++ diline çevirerek aylık .NET ürünlerini C++ diline yayınlamamıza olanak tanıdı. Ayrıca, orijinal C# kodunu kapsayan testler de yanı sıra C++ dilinde özel olarak yazılmış testlerle birlikte çevrildi, böylece sonuç çözümün işlevselliği izlendi.

Arka Plan

C# den C++ kod çevirmeninin başarısı, CodePorting ekibinin otomatik C# den Java kod çevirisi kurulumunu yaparken sahip olduğu başarılı deneyime dayanmaktadır. Oluşturulan çerçeve, C# sınıflarını Java sınıflarına dönüştürürken sistem kütüphane çağrılarını düzgün bir şekilde değiştiriyordu.

Farklı yaklaşımlar çerçevesinde düşünüldü. Sıfırdan saf Java sürümlerinin geliştirilmesi çok fazla kaynak gerektirirdi. Bir seçenek, Java kodundan .NET ortamına çağrıları taşımaktı, ancak bu gelecekte destekleyebileceğimiz programlama platformlarının kümesini sınırlardı. O zamanlar .NET yalnızca Windows'ta mevcuttu. Çağrı taşıma, nadiren gerçekleşen ve yaygın olarak kullanılan veri türlerini taşıyan çağrılar için uygundur. Ancak, çok sayıda nesne ve özel veri türü ile çalışırken bunaltıcı hale gelir.

Bunun yerine, mevcut kodu yeni bir platforma tamamen nasıl çevireceğimizi merak ettik. Bu, kod göçünün aylık olarak ve tüm ürünler için yapılması gereken bir konu olduğundan, benzer özelliklere sahip sürümlerin senkronize bir akışını üretir.

Çözüm iki bölüme ayrıldı:

  • Çevirmen — C# sözdizimini Java sözdizimine dönüştüren, .NET türlerini ve yöntemlerini hedef dil kütüphanelerinden uygun yerine koymalarla değiştiren bir uygulama.
  • Kütüphane — Java'da düzgün bir şekilde eşleştirilemeyen .NET kütüphanesinin bölümlerini taklit eden bir bileşen. Görevi basitleştirmek için mevcut üçüncü taraf bileşenler kullanılabilir.

Aşağıdaki argümanlar, planın teknik olarak uygulanabilir olduğunu doğruladı:

  1. C# ve Java dillerinin benzer bir ideolojisi vardır. En azından, tür yapısı ve bellek yönetimi modeli konusunda böyledir.
  2. Sadece kütüphaneleri çevirmemiz gerekiyordu, bu nedenle GUI'ları farklı bir platforma taşımak gerekmiyordu.
  3. Çevrilen kütüphaneler çoğunlukla iş mantığı ve düşük seviyeli dosya işlemleri içeriyordu, en karmaşık bağımlılıklar ise System.Net ve System.Drawing idi.
  4. Kütüphaneler başlangıçtan itibaren .NET sürümlerinin geniş bir yelpazede çalışması için geliştirilmişti (Framework, Standard ve hatta Xamarin dahil). Bu nedenle küçük platform farkları göz ardı edilebilirdi.

C# den Java çevirmeninin daha fazla ayrıntısına girmeyeceğiz, bu ayrı bir makale gerektirir. Özetlemek gerekirse, C# ürünlerini Java'ya dönüştürmek, kod çevirmeni sayesinde şirketin düzenli bir uygulaması haline gelmişti. Çevirmen, basit bir kural tabanlı metin dönüştürücüden kaynak kodun AST temsiliyle çalışan karmaşık bir kod üreteci haline gelmişti.

C# den Java çevirmeninin başarısı, Java pazarına girmemize yardımcı oldu ve aynı senaryoyu kullanarak C++ için yayınlamaya başlama konusu gündeme geldi.

Gereksinimler

Ürünlerimizin C++ sürümünü yayınlamayı mümkün kılmak için, C# kodunu C++'a çevirmemizi, derlememizi, test etmemizi ve müşteriye göndermemizi sağlayacak bir çerçeve oluşturmak gerekiyordu. Kod, her biri birkaç milyon satıra kadar olan bir dizi kütüphaneden oluşuyordu. Kod çevirmeninin Kütüphane bileşeni aşağıdaki konuları kapsamalıydı:

  1. Çevrilen kod için .NET ortamını taklit etmek.
  2. Çevrilen kodu C++ için uyarlamak: tür yapısı, bellek yönetimi vb.
  3. Çevrilen C# kod stilinden C++ stiline geçmek, .NET paradigmalarına aşina olmayan geliştiricilerin kodu kullanmasını kolaylaştırmak için.

Birçok okur, neden Mono projesi gibi mevcut çözümleri kullanmadığımızı soracaktır. Bunu yapmamak için birkaç neden vardı:

  1. Bu, ikinci ve üçüncü gereksinimleri kapsamazdı.
  2. Mono, C# üzerinde uygulanmış ve çalışma zamanına bağlı olan bir projedir.
  3. Üçüncü taraf kodunu ihtiyaçlarımıza (API, tür sistemi, bellek yönetimi modeli, optimizasyon vb.) uyarlamak, kendi çözümümüzü oluşturmak kadar zaman gerektirirdi.
  4. Ürünlerimiz tam .NET uygulamasını gerektirmez. Ancak tam bir uygulamamız olsaydı, hangi yöntemlerin ve sınıfların ihtiyacımız olduğunu ve hangilerinin olmadığını ayırt etmek zor olurdu. Kullanmadığımız özellikleri düzeltmek için çok zaman harcardık.

Teorik olarak, mevcut bir çözümü C++'a dönüştürmek için çevirmenimizi kullanabilirdik. Ancak bu, bir sistem kütüphanesi olmadan çevrilen herhangi bir kodu hata ayıklamanın imkansız olduğu anlamına gelir. Ayrıca, optimizasyon sorunları, sistem kütüphane çağrılarının genellikle darboğaz haline gelmesi nedeniyle, çevrilen ürünlerin kodu için daha da önemli hale gelirdi.

Çevirmen için gereksinimlerimize geri dönelim. .NET türlerini STL türlerine eşleştiremememiz nedeniyle özel Kütüphane türlerini kullanmaya karar verdik. Kütüphane, üçüncü taraf kütüphanelerinin özelliklerini bir .NET benzeri API (Java'daki gibi) aracılığıyla kullanmaya izin veren adaptörler kümesi olarak geliştirildi.

Varolan API ile kütüphaneleri çevirirken, çevrilen kodun önemli bir gereksinimi, herhangi bir müşteri uygulamasında çalışabilmesiydi. Bu nedenle, çevrilen kod için çöp toplama kullanamazdık, çünkü bu tüm uygulamayı kapsardı. Bunun yerine, bellek yönetim modelimiz C++ geliştiricileri için net olmalıydı. Akıllı işaretçiler kullanmak bir uzlaşma olarak seçildi. Bellek modelini nasıl değiştirdiğimizi ayrı bir makalede açıklayacağız.

CodePorting'in güçlü bir test kapsama kültürü vardır ve C# kodu için yazılan testleri C++ ürünlerine uygulama yeteneği, sorun gidermeyi önemli ölçüde basitleştirecektir. Kod çevirmeninin testleri de çevirebilmesi gerekiyordu.

Başlangıçta, çevrilen Java kodunun manuel düzeltilmesi, geliştirmeyi hızlandırmaya ve ürün sürümlerini yayınlamaya yardımcı oldu. Ancak uzun vadede, her çeviri hatasının her seferinde düzeltilmesi gerektiği için her sürümü hazırlamak için gereken masrafları önemli ölçüde artırdı. Bu, sonuçta elde edilen Java kodunu, her seferinde sıfırdan dönüştürmek yerine iki ardışık C# kodu revizyonu için çevirmenin çıktıları arasındaki fark olarak hesaplanan yamalarla beslemekle yönetilebilirdi. Yine de, sonuç kod düzeltmesi yerine C++ çerçevesini düzeltmeye öncelik verilmesine karar verildi, böylece her çeviri hatası sadece bir kez düzeltilmiş oldu.

İlgili makaleler