C++ şablon uygulamalarını macOS’ta ayrı bir kaynak dosyasında tutmak için, desteklenen türleri tanımlarının görünür olduğu yerde açıkça örnekleyin ve ardından bu nesne dosyasını yürütülebilir dosyanıza bağlayın. Bu anlatımda Apple’ın C++ araçlarıyla küçük bir yerel örnek oluşturuluyor.
Görev açıkça tanımlı: şablon üye gövdeleri bir .cpp dosyasında bulunan bir C++ programı derlemek. Örnek iki somut türü destekliyor. İlerleyen bir bölümde, aynı C++ arayüzünü kullanan bir Swift masaüstü uygulaması için gereken ek koşullar açıklanıyor.
Şablon kodunu kaynak dosyasına taşımak bağlamayı neden bozar?
Bir şablon, türler veya işlevlerden oluşan bir aileyi tanımlar. Uygulama dosyasını derlemek, olası her özelleştirmeyi kendiliğinden üretmez. Sınıfın örneklenmesi de her üye işlev gövdesini otomatik olarak örneklemez; üyeler genellikle gerektiğinde örneklenir. Bu ayrımlar, çağıran kodun başarıyla derlenmesine karşın bağlama sırasında çözümlenmemiş başvurular kalabilmesini açıklar. C++ çalışma taslağı: Örtük örnekleme
Bir sınıf şablonunun düzenini tanımlayan ve kurucusuyla yöntemlerini bildiren bir başlık düşünün. Çağıran kod bu bildirimleri anlayabilir. Ancak yöntem gövdeleri yalnızca başka bir kaynak dosyasındaysa çağıran kod, gerekli işlevleri oluşturmak için bu gövdeleri kullanamaz. Uygulama dosyası, çağıran kodun ihtiyaç duyduğu somut örneklemeleri sağlamalıdır.
Açık örnekleme tanımı bu örneklemeleri ister. extern template bildirimi, bir örneklemenin başka bir yerde sağlandığını belirtir. Açık örnekleme tanımını sağlayan çeviri biriminde uygulama tanımları görünür olmalıdır. C++ çalışma taslağı: Açık örnekleme
Tür argümanlarını kimin belirlediğine göre düzeni seçin:
| Okuyucunun gereksinimi | Uygun düzen | Bakım açısından sonucu |
|---|---|---|
| Çağıranlar uygun olan her türü sağlayabilir | Tanımları başlığa veya başlıktan eklenen bir uygulama başlığına koyun | Çağıranlar yeni türleri örnekleyebilir |
| Kitaplık bilinen bir tür kümesini destekler | Gövdeleri bir kaynak dosyasında tutup bu kümeyi açıkça örnekleyin | Desteklenen bir tür eklerken örnekleme de ekleyin |
| Masaüstü uygulaması yalnızca somut bir işleme ihtiyaç duyar | Seçili bir özelleştirmenin çevresinde sıradan bir C++ sarmalayıcı sunun | Şablon seçimlerini C++ uygulamasının içinde tutun |
Bu karar kılavuzu yukarıdaki örnekleme kurallarını temel alır. Bu anlatımda desteklenen küme bilerek int ve double ile sınırlandırılmıştır.
Üç dosyalı eksiksiz bir örnek oluşturun
Yeni ve boş bir klasör kullanın; aşağıdaki üç dosyayı bu klasöre kaydedin. Örnek program bir değere ofset ekler; küçük girdiler beklenen sonuçların kolayca incelenmesini sağlar.
Apple’ın komut satırı araçlarını hazırlayın
Apple, derleyiciyi ve macOS SDK’sını Xcode veya bağımsız Command Line Tools paketiyle sağlar. Bunların hiçbiri yüklü değilse Terminal’i açıp aşağıdaki komutu çalıştırın ve ardından yükleyici iletişim kutusundaki adımları tamamlayın. Xcode zaten yüklüyse ayrıca araç yüklemeniz gerekmez. Komut satırı araçlarını yükleme
xcode-select --install
Genel arayüzü tanımlayın
Bu eksiksiz başlığı Offset.hpp adıyla kaydedin. Veri üyesi de dahil olmak üzere sınıf tanımını çağıran kodun görebileceği biçimde tutun. Yalnızca kurucu ve yöntem gövdelerini kaynak dosyasına taşıyın.
#ifndef OFFSET_HPP
#define OFFSET_HPP
namespace demo {
template <typename T>
class Offset {
public:
explicit Offset(T amount);
T apply(T value) const;
private:
T amount_;
};
extern template class Offset<int>;
extern template class Offset<double>;
} // namespace demo
#endif
İki extern template satırı sağlanan örneklemeleri bildirir; üye gövdelerini sağlamaz. Bu sınıf düzeyindeki açık örnekleme düzeninde sınıf şablonunun tanımı erişilebilir kalmalıdır. C++ çalışma taslağı: Açık örnekleme
Gövdeleri tanımlayın ve örneklemeleri sağlayın
Bu eksiksiz uygulamayı Offset.cpp adıyla kaydedin. Açık örnekleme tanımlarını üye tanımlarının sonuna yerleştirin.
#include "Offset.hpp"
namespace demo {
template <typename T>
Offset<T>::Offset(T amount) : amount_(amount) {}
template <typename T>
T Offset<T>::apply(T value) const {
return value + amount_;
}
template class Offset<int>;
template class Offset<double>;
} // namespace demo
Sıralama önemlidir: sınıf düzeyindeki açık örnekleme tanımı yalnızca o noktada tanımlanmış üyeleri kapsar. Üye şablonlarını da otomatik olarak örneklemez. Her açık örnekleme tanımı için tek bir kaynak dosyası belirleyin; dil, programda aynı şablon ve argüman kümesi için en fazla bir tanıma izin verir. Açık örnekleme, Şablon örneklemesi ve özelleştirme: Genel
Desteklenen iki türü de çağırın
Bu eksiksiz çağıran kodu main.cpp adıyla kaydedin.
#include "Offset.hpp"
#include <iostream>
int main() {
const demo::Offset<int> whole(3);
const demo::Offset<double> fractional(0.5);
const int first = whole.apply(4);
const double second = fractional.apply(2.0);
std::cout << first << '\n' << second << '\n';
return (first == 7 && second == 2.5) ? 0 : 1;
}
Program sonuçlarını yazdırmanın yanı sıra çıkış durumuyla da denetler. Örnek tutarlılık açısından incelenmiştir ancak hazırlık sırasında derlenmemiştir: araştırma ortamında C++ derleyicisi veya macOS SDK’sı yoktu.
Önce iki kaynak dosyasını derleyin, ardından iki nesne dosyasını bağlayın
Terminal’de üç dosyanın bulunduğu klasöre geçin. Aşağıdaki komutları sırayla çalıştırın ve yalnızca her önceki komut başarıyla tamamlandığında devam edin:
xcrun clang++ -std=c++17 -stdlib=libc++ -Wall -Wextra -pedantic -c Offset.cpp -o Offset.o
xcrun clang++ -std=c++17 -stdlib=libc++ -Wall -Wextra -pedantic -c main.cpp -o main.o
xcrun clang++ -std=c++17 -stdlib=libc++ main.o Offset.o -o template-demo
./template-demo
Burada -c, bağlama yapmadan bir nesne dosyası üretir. Son derleyici çağrısında -c kullanılmaz; iki nesne dosyası birleştirilerek yürütülebilir dosya oluşturulur. Komutlar varsayılan ayarlara güvenmek yerine C++17’yi ve libc++’ı açıkça seçer. Clang derleyici komut kılavuzu
Örnek girdilerden hesaplanan beklenen çıktı şöyledir:
7
2.5
Temel bağımlılık son derleme komutunda görülür: main.o işlevleri ister, Offset.o ise bunların örneklemelerini sağlar. Daha büyük bir projede Offset.cpp dosyasını içeren hedefin derlendiğinden ve nesne dosyasının ya da kitaplığının yürütülebilir dosyanın bağlama adımına ulaştığından emin olun. Bu dosyanın tek başına başarıyla derlenmesi bağlama işlemini tamamlamaz.
Bu düzeni kullanırken çağıran koda #include "Offset.cpp" eklemeyin ve dosyayı ayrıca derlemeyin. Genel başlığı ekleyin ve uygulamayı ayrı bir çeviri birimi olarak derleyin. Çağıran kodun gövdelere ihtiyacı varsa önceki tablodaki, uygulama başlığını ekleme düzenini bilinçli olarak seçin.
Eksik örneklemeleri yararlı bir sırayla teşhis edin
Önce şablon argümanları ve niteleyicileri de dahil olmak üzere çözümlenmemiş işlevi tam olarak belirleyin. Ardından şu sırayı izleyin. Bu denetimler, nesne dosyası oluşturma ile bağlama arasındaki belgelenmiş ayrımı ve açık örnekleme gereksinimlerini temel alır. Clang derleme aşamaları, C++ açık örnekleme kuralları
| Bulduğunuz durum | Denetlenecek veya değiştirilecekler |
|---|---|
| Desteklenen her iki türün de üyeleri çözümlenmemiş | Son bağlama komutunda Offset.o bulunduğunu doğrulayın |
| Yeni kullanılan bir tür başarısız oluyor | Bu tür için eşleşen örneklemeyi uygulamanın sahibi olan dosyaya, bildirimini de başlığa ekleyin |
| Tek bir yöntem eksik | Gövdesinin sınıf örneklemesinden önce geldiğini ve bildiriminin tanımla eşleştiğini doğrulayın |
| Üye şablonu eksik | Tanımı görünür olacak şekilde gerekli üye şablonu örneklemesini ayrıca sağlayın |
| Yinelenen tanım tanısı görünüyor | Tekrarlanan örnekleme tanımlarını veya hem derlenen hem de eklenen bir uygulama dosyasını denetleyin |
| Tanı uyumsuz bir mimariden söz ediyor | Bağlanan her nesne dosyasının ve kitaplığın mimarisini denetleyin |
Örneğin çağıran kodu demo::Offset<float> kullanacak şekilde değiştirmek, bu uygulamanın sağlamadığı bir tür ekler. Bu türü desteklemek için ad alanı içindeki başlığa extern template class Offset<float>;, ardından gövdelerden sonra Offset.cpp dosyasına template class Offset<float>; ekleyin ve iki nesne dosyasını yeniden derleyin. Yalnızca bildirimi eklemek eksik işlevleri üretmez.
LLVM’nin ayrı llvm-nm yürütülebilir dosyası yüklüyse ve komut yolunuzda bulunuyorsa, aşağıdaki isteğe bağlı komutlar örnekteki nesne dosyalarını inceler:
llvm-nm --demangle --undefined-only main.o
llvm-nm --demangle --defined-only Offset.o
Çağıran kodun çözümlenmemiş simgelerinde ve uygulamanın tanımlı simgelerinde eşleşen demo::Offset üye adlarını arayın. Standart kitaplık başvuruları da dahil olmak üzere başka girdilerin bulunması beklenir. LLVM her iki filtreyi ve adların çözümlenmesini belgeler; ancak bu komutlar özellikle Apple araçlarının yüklü olmasından hareketle varlığı varsayılamayacak olan llvm-nm gerektirir. llvm-nm komut kılavuzu
macOS mimarisi ve ABI uyumluluğunu koruyun
Açık örnekleme somut şablon kodu sağlar; bir nesne dosyasını tüm mimarilerde kullanılabilir hâle getirmez. Apple’ın evrensel ikili dosya iş akışı, mimariye özgü sürümleri birleştirmeden önce ayrı ayrı derleyip bağlar. Uygulama hem arm64 hem de x86_64 mimarisini destekliyorsa örnekleme kaynağını her iki derlemeye de ekleyin. Evrensel bir macOS ikili dosyası oluşturma
Örnekten evrensel bir derleme oluşturduktan sonra elde edilen yürütülebilir dosyayı aşağıdaki komutla inceleyin. Bildirilen mimarilerin hedeflenen derlemeyle eşleştiğini denetleyin; önceki sıradan derleme komutları evrensel ikili dosya istemez.
xcrun lipo -archs ./template-demo
Yöntem gövdelerinin ayrı tutulması, sınıf düzeninin başlıkta görünür kalmasını da sağlar. Bu düzende yapılacak değişiklikleri, bağımlı kodun yeniden derlenmesini gerektiren arayüz değişiklikleri olarak ele alın. Açık örneklemelerin varlığından kararlı bir ikili arayüz olduğu sonucunu çıkarmayın. libc++ belirli uyumluluk güvencelerini ve yapılandırma sınırlarını belgeler; ayrıca bu güvenceleri kullanıcı tanımlı işlevlere ilişkin güvencelerden açıkça ayırır. libc++’ın ABI güvenceleri
Swift uygulaması C++ kodunu çağırdığında ne değişir?
İki gereksinimi ayrı değerlendirin: Swift’in desteklenen bir C++ bildirimini içe aktarması ve son yürütülebilir dosyanın uygulamayı bağlaması gerekir. Swift belgeleri, somut sınıf şablonu özelleştirmelerinin C++ tür takma adları aracılığıyla sunulmasını açıklar; örneklenmemiş şablon Swift türü olarak doğrudan kullanılamaz. Swift ile C++’ı bir arada kullanma: Sınıf şablonlarını kullanma
Örnek arayüz için aşağıdaki isteğe bağlı başlık parçasını namespace demo içine, sınıf tanımından sonra ekleyin. Böylece somut türe içe aktarılabilmesi için bir ad verilir:
using IntOffset = Offset<int>;
Bu takma ad türe ad verir. Offset.cpp dosyasındaki açık örnekleme yine de dışarıda tanımlanan kurucuyu ve yöntemi sağlar. Swift, C++ başlıklarını Clang modüllerini kullanarak içe aktarır ve şu anda C++20 modüllerini içe aktaramaz. Proje için başlık tabanlı birlikte çalışabilirlik kurulumunu izleyin. Swift ile C++’ı bir arada kullanma
Xcode’da Swift hedefi için C++ and Objective-C interoperability ayarını C++ / Objective-C++ olarak etkinleştirin. Aynı hedeften C++ kullanan bir uygulama hedefinde C++ başlığını köprüleme başlığına ekleyin. Çerçeve hedefleri desteklenen bildirimleri genel başlıklar aracılığıyla sunar. Swift ve C++ ile karma dil projeleri ayarlama
Swift ve C++ kodları aynı C++ standart kitaplığını kullanmalıdır; Apple platformlarında bu kitaplık libc++’tır. Birlikte çalışabilirlik için C++14 veya daha yeni bir sürüm gerekir. İçe aktarılan başlıkların dil kipini C++ derlemesiyle eşleştirin ve daha karmaşık şablonları kullanıma sunmadan önce desteklenen özellikler listesini inceleyin. Açık örnekleme, Swift’in desteklenen dil eşlemelerini genişletmez. C++ birlikte çalışabilirliğinin desteklenen özellikleri ve kısıtları
Sabit bir tür kümesi için genel bildirimleri, örnekleme listesini ve son bağlama girdilerini birlikte yönetin. Yeni bir çağıran kod başka bir türe ihtiyaç duyduğunda ya bu türü bilinçli olarak ekleyin ya da uygulamayı çağıran kodun örneklemesine açın. Bu seçim, masaüstü projesi büyüdükçe kaynak dosyası sınırının anlaşılır kalmasını sağlar.
Kaynaklar
- Örtük örnekleme — C++ çalışma taslağı
- Açık örnekleme — C++ çalışma taslağı
- Şablon örneklemesi ve özelleştirme: Genel — C++ çalışma taslağı
- Komut satırı araçlarını yükleme — Apple
- clang — Clang C, C++ ve Objective-C derleyicisi — LLVM Projesi
- llvm-nm — LLVM bit kodu ve nesne dosyası simge tablosunu listeleme — LLVM Projesi
- Evrensel bir macOS ikili dosyası oluşturma — Apple
- libc++’ın ABI güvenceleri — LLVM Projesi
- Swift ile C++’ı bir arada kullanma — Swift.org
- Swift ve C++ ile karma dil projeleri ayarlama — Swift.org
- C++ birlikte çalışabilirliğinin desteklenen özellikleri ve kısıtları — Swift.org