26 Mayıs 2025
C++ ekosistemi, kodun nasıl organize edildiğini ve derlendiğini temelden yeniden tasarlayarak son on yılların en derin dönüşümünü yaşıyor. Yıllardır geliştiriciler, #include sisteminin sınırlamalarıyla - yavaş derleme süreleri, yaygın makro kirliliği ve uygun kapsülleme eksikliği ile - mücadele ettiler. Önişlemci tabanlı yaklaşımın bu doğal kusurları, C++'ın büyük ölçekli geliştirmedeki potansiyelini kısıtlamış, mühendisleri karmaşık geçici çözümler ve derleme sistemleri benimsemeye zorlamıştır.
C++ 20 modülleri, dilin başlangıcından bu yana C++ kod organizasyonundaki ilk büyük paradigma değişimini temsil ederek, bu uzun süredir devam eden zorluklara kapsamlı bir çözüm olarak ortaya çıkıyor. Metinsel dahil etmeyi yapılandırılmış ikili bir arayüzle değiştirerek, modüller derleme hızında, kod izolasyonunda ve arayüz netliğinde dönüştürücü iyileştirmeler sunar. Bu sadece artımlı bir iyileşme değil, C++ program yapısının temel mimarisini ele alan köklü bir değişikliktir.
Geleneksel başlık dosyası modeli, modern C++ geliştirme üzerinde dört temel kısıtlama getirir:
C++ modülleri bu zorluklarla doğrudan yüzleşir. Bir kez ikili gösterime derlenirler, bu da derleyicinin metin tabanlı başlık dosyalarından önemli ölçüde daha hızlı içe aktarmasını ve işlemesini sağlar. Bu kod dönüştürme, derleme sırasında gereksiz işi büyük ölçüde azaltır. Modüller ayrıca görünürlüğü sıkı bir şekilde kontrol eder, yalnızca açıkça işaretleneni dışa aktarır, böylece makro kirliliğini önler ve daha güçlü kapsülleme uygular. Açıkça dışa aktarılmayan adlar ve makrolar modüle özel kalır, bu da kod izolasyonunu iyileştirir.
Temel sözdizimini anlamak, C++ modülleriyle etkili programlama için temel oluşturur. Bir modül tanımı genellikle, modülün neyi dışa aktardığını bildiren bir arayüz birimi ve tanımları sağlayan uygulama birimleri içerir.
Bir matematik modülü için basit bir C++ modülü örneğini ele alalım:
1. Modül Arayüz Birimi (math.ixx)
// math.ixx - 'math' için birincil modül arayüz birimi
export module math;
// Dışa aktarılan fonksiyon bildirimleri
export int add(int a, int b);
export int multiply(int a, int b);
// Dahili yardımcı fonksiyon, dışa aktarılmamış
int subtract_internal(int a, int b);
Burada, export module math;
bu dosyayı math
adlı isimlendirilmiş bir modülün birincil arayüzü olarak bildirir. add
ve multiply
önündeki export
anahtar kelimesi, bu fonksiyonların modülün genel arayüzünün bir parçası olduğunu ve math
modülünü içe aktaran diğer çeviri birimleri tarafından erişilebilir olduğunu gösterir. export
içermeyen subtract_internal
fonksiyonu modüle özel kalır.
2. Modül Uygulama Birimi (math.cpp)
// math.cpp - 'math' için modül uygulama birimi
module math; // Bu dosyayı 'math' modülü ile ilişkilendir
// Dışa aktarılan fonksiyonlar için tanımlar
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
// Dahili yardımcı fonksiyon için tanım
int subtract_internal(int a, int b) {
return a - b;
}
Bu dosya, onu math
modülüne bağlayan module math;
ile başlar. math.ixx
içinde bildirilen fonksiyonların tanımlarını içerir. Uygulama dosyalarının modül bildirimlerinde export
kullanmadığına dikkat edin.
3. Modülü Kullanma (main.cpp)
// main.cpp - 'math' modülünü kullanma
import math; // 'math' modülünü içe aktar
#include <iostream> // Konsol çıktısı için
int main() {
int sum = add(10, 5); // Dışa aktarılan fonksiyonu çağır
int product = multiply(10, 5); // Dışa aktarılan fonksiyonu çağır
std::cout << "Toplam: " << sum << std::endl;
std::cout << "Çarpım: " << product << std::endl;
// int difference = subtract_internal(10, 5); // HATA: subtract_internal dışa aktarılmadı
return 0;
}
main.cpp
'de, import math;
ifadesi math
modülünden dışa aktarılan bildirimleri kullanılabilir hale getirir. add
ve multiply
doğrudan kullanılabilirken, subtract_internal
erişilemez kalır, bu da modüllerin güçlü kapsüllemesini gösterir.
Bu C++ modülü örneği, temel kalıbı gösterir. Modül arayüz birimleri, modülün adını ve genel arayüzünü bildirmek için export module
kullanırken, modül uygulama birimleri, modülün dahili uygulamasına katkıda bulunmak için module
kullanır.
Temel export
ve import
kavramlarının ötesinde, C++ 20 modülleri, karmaşık proje yapılarını ve eski kod dönüştürmeyi ele almak için çeşitli gelişmiş özellikler sunar. C++ 23 modülleri, daha fazla iyileştirme ve geliştirilmiş derleyici desteği ile bu evrimi sürdürüyor.
İsimlendirilmiş bir modül, aynı modül adını paylaşan modül birimlerinin (kaynak dosyalar) bir koleksiyonudur.
export module MyModule;
). Bu birimin dışa aktarılan içeriği, import MyModule
yaptığınızda görünür hale gelir.export module
ile bildirilen herhangi bir birim bir arayüz birimidir. Buna birincil arayüz birimi ve bölüm arayüz birimleri dahildir.module MyModule;
( export
olmadan) ile bildirilen bir birim bir uygulama birimidir. Modülün dahili mantığına katkıda bulunur ancak arayüzü doğrudan içe aktaranlara açığa çıkarmaz.Kodu modüllere taşırken önemli bir zorluk, yapılandırma için önişlemci makrolarına dayanan mevcut başlıklarla entegrasyonu içerir. Global Modül Parçası bunu ele alır.
// mymodule_with_legacy.ixx
module; // Global modül parçasının başlangıcı
#define USE_ADVANCED_FEATURE 1 // Global parçada tanımlanmış makro
#include <legacy_header.h> // Eski başlığı dahil et (varsayımsal)
export module mymodule_with_legacy; // Global modül parçasının sonu, modülün başlangıcı
import <iostream>; // Standart kütüphane modülünü içe aktar
export void perform_action() {
#if USE_ADVANCED_FEATURE
std::cout << "Gelişmiş eylem gerçekleştirildi!" << std::endl;
#else
std::cout << "Temel eylem gerçekleştirildi!" << std::endl;
#endif
// Gerekirse legacy_header.h'den bildirimleri kullan
}
module;
bildirimi global modül parçasını başlatır. Bu parça içindeki tüm #include
yönergeleri veya makro tanımları, modülün kendisi tanımlanmadan önce işlenir. Bu, modüllerin önişlemciye bağımlı eski kodla, makro kirliliği olmadan modülün arayüzü içinde etkileşim kurmasına olanak tanır. Burada tanımlanan makrolar, bu parçada dahil edilen başlıkların işlenmesini etkiler, ancak modülde daha sonra başlık birimi olarak içe aktarılan başlıkları etkilemez.
Geliştiricilerin uygulama detaylarını birincil arayüz birimi içinde tutmayı ancak içe aktaranlardan tamamen gizlemeyi tercih ettiği büyük modüller için, C++ 20 modülleri Özel Modül Parçası sunar.
// main_module.ixx
export module main_module;
export int public_function();
module :private; // Özel modül parçasının başlangıcı
// Bu fonksiyon sadece main_module.ixx'in kendisi içinde görünürdür,
// 'main_module'ü içe aktaran hiçbir kod için değil.
int internal_helper_function() {
return 36;
}
int public_function() {
return internal_helper_function();
}
module :private;
bildirimi, modülün genel arayüzü ile özel uygulama detayları arasındaki sınırı işaretler. Bu bildirimden sonra birincil arayüz birimi içindeki kod, yalnızca o birim ve aynı isimlendirilmiş modüle ait diğer birimler tarafından erişilebilir, ancak harici içe aktaranlar tarafından değil. Bu, tek bir .ixx
dosyasının bir modülü temsil etmesine izin verirken, genel ve özel bölümleri açıkça ayırarak kapsüllemeyi geliştirir. Özel bir modül parçası içeren bir modül birimi genellikle modülünün tek birimi olacaktır.
Çok büyük modüller için, onları daha küçük, yönetilebilir birimlere ayırmak faydalıdır. Modül bölümleri (C++ 20 modülleri) bu dahili modülerliği sağlar. Bir bölüm, daha büyük bir isimlendirilmiş modülün bir alt modülüdür.
// large_module_part1.ixx - Bölüm arayüz birimi
export module large_module:part1; // 'large_module' için 'part1' adlı bir bölümü bildirir
export void do_something_in_part1();
// large_module_part1.cpp - Bölüm uygulama birimi
module large_module:part1; // Bu dosyayı 'part1' bölümüne bağlar
void do_something_in_part1() {
// Uygulama
}
// large_module_main.ixx - 'large_module' için birincil modül arayüz birimi
export module large_module;
// Bölüm arayüzünü dışa aktar-içe aktar. Bu, `large_module` içe aktarıldığında
// `do_something_in_part1`'in görünür olmasını sağlar.
export import :part1;
// Bir uygulama bölümünü (eğer varsa) içe aktarabilir, ancak dışa aktaramaz.
// import :internal_part;
export void do_main_thing() {
do_something_in_part1(); // Bölüm fonksiyonlarını doğrudan çağırabilir
}
Bölümler sadece isimlendirilmiş modülün kendisi içinde görünürdür. Harici çeviri birimleri doğrudan large_module:part1;
içe aktaramaz. Bunun yerine, birincil modül arayüz birimi (large_module_main.ixx
), large_module
içe aktaranlara part1
'in dışa aktarılan varlıklarını görünür kılmak için export import :part1;
yapmalıdır. Bu hiyerarşik yapı, önemli yazılım geliştirme projeleri içinde karmaşıklığı yönetmek için sağlam bir yol sağlar.
C++ 20 modülleri ayrıca, geleneksel bir başlık dosyasını import <header_name>;
veya import "header_name";
kullanarak içe aktarmanıza olanak tanıyan Başlık Birimlerini de tanıtır. Bir başlık bir başlık birimi olarak içe aktarıldığında, bir kez ayrıştırılır ve bildirimleri verimli bir şekilde kullanılabilir hale getirilir, modüllere benzer şekilde. Çok önemli olarak, içe aktaran çeviri birimindeki import
ifadesinden önce tanımlanan önişlemci makroları, #include
'un aksine, başlık biriminin kendisinin işlenmesini etkilemez. Bu, daha tutarlı davranış sağlar ve büyük başlıkların derlenmesini önemli ölçüde hızlandırabilir.
// my_app.cpp
#define CUSTOM_SETTING 10 // Bu makro <vector> veya "my_utility.h" dosyasını ETKİLEMEZ
import <vector>; // Standart vektör başlığını bir başlık birimi olarak içe aktarır
import "my_utility.h"; // Özel bir başlığı bir başlık birimi olarak içe aktarır (varsayımsal)
int main() {
std::vector<int> numbers = {1, 2, 3};
// ...
return 0;
}
Bu ayrım, kod taşıma çabaları için önemlidir. Bir başlık, içe aktaran çeviri biriminin önişlemci durumu tarafından etkilenmesi gereken yapılandırma için önişlemci yönergelerine dayanıyorsa, Global Modül Parçası uygun çözümdür. Aksi takdirde, bir başlık birimi olarak içe aktarma, #include
'a göre daha hızlı, daha temiz bir alternatiftir.
Büyük, mevcut C++ projelerini tam bir modül sistemine kod dönüştürme ve kod taşıma aşamalı bir süreç olabilir. Geliştiricilerin tüm kod tabanlarını aynı anda dönüştürmeleri gerekmez. C++ modülleri, geleneksel başlık dosyalarıyla sorunsuz bir şekilde bir arada var olacak şekilde tasarlanmıştır.
import
edebilir hem de başlık dosyalarını #include
edebilir. Bu esneklik, her seferinde bir bileşeni veya kütüphaneyi dönüştürerek aşamalı benimsemeye olanak tanır.Anahtar, denemek ve ölçmektir. Geliştiriciler, benimseme kararlarını derleme sürelerinde anlamlı bir azalma ve kod organizasyonunda bir iyileşme elde edip etmediklerine göre vermelidir.
C++ 20 modüllerine destek, büyük derleyiciler arasında sürekli olarak gelişmektedir.
C++ 23 modülleri daha yaygın hale geldikçe, daha fazla iyileştirme ve geliştirilmiş derleyici olgunluğu, genel geliştirici deneyimini artıracaktır. Tam ve optimize edilmiş modül desteğine doğru yolculuk devam etmektedir, ancak C++ 20'de atılan temel, C++ yazılım geliştirmenin nasıl yapıldığı konusunda derin bir değişimi temsil etmektedir. Bu devam eden evrim, dil için daha da sağlam ve verimli bir gelecek vaat etmektedir.
C++ modülleri, C++'ta programlama pratiklerini temelden yeniden şekillendiren dönüştürücü bir özelliktir. Yavaş derleme, makro paraziti ve zayıf kapsülleme gibi başlık tabanlı sistemle ilişkili uzun süredir devam eden sorunları doğrudan ele alırlar. Açık arayüz tanımı ve verimli derleme için sağlam bir sistem sağlayarak, C++ modülleri kod organizasyonunu geliştirir, derleme sürelerini iyileştirir ve daha net sorumluluk ayrımını teşvik eder. C++ 20 modüllerini benimsemek ve C++ 23 modülleriyle gelecekteki geliştirmelere hazırlanmak, geliştiricileri daha ölçeklenebilir, sürdürülebilir ve verimli yazılım geliştirme projeleri oluşturmaya hazırlar ve dil için yeni bir çağa öncülük eder.