Untuk mengekalkan pelaksanaan templat C++ dalam fail sumber berasingan pada macOS, instansiasikan jenis yang disokong secara eksplisit di tempat takrifnya kelihatan, kemudian pautkan fail objek itu ke dalam boleh laksana anda. Panduan ini membina contoh tempatan ringkas menggunakan alat C++ Apple.
Tugas ini khusus: menyusun program C++ yang badan ahli templatnya berada dalam fail .cpp. Contoh ini menyokong dua jenis konkrit. Bahagian kemudian menerangkan keperluan tambahan apabila aplikasi desktop Swift menggunakan antara muka C++ yang sama.
Mengapakah memindahkan kod templat ke dalam fail sumber menyebabkan pemautan gagal?
Templat menerangkan keluarga jenis atau fungsi. Menyusun fail pelaksanaannya tidak secara automatik menghasilkan setiap pengkhususan yang mungkin. Instansiasi kelas juga tidak secara automatik menginstansiasikan setiap badan fungsi ahli; ahli lazimnya diinstansiasikan apabila diperlukan. Perbezaan ini menjelaskan sebab pemanggil boleh disusun dengan jayanya tetapi masih meninggalkan rujukan yang tidak dapat diselesaikan semasa pemautan. Draf kerja C++: Instansiasi tersirat
Pertimbangkan pengepala yang mentakrifkan susun atur templat kelas dan mengisytiharkan pembina serta kaedahnya. Pemanggil boleh memahami pengisytiharan tersebut. Namun, jika badan kaedah hanya wujud dalam fail sumber lain, pemanggil tidak boleh menggunakan badan itu untuk menjana fungsi yang diperlukan. Fail pelaksanaan mesti menyediakan instansiasi konkrit yang diperlukan oleh pemanggil.
Takrif instansiasi eksplisit meminta instansiasi tersebut. Pengisytiharan extern template mengenal pasti instansiasi yang dibekalkan di tempat lain. Takrif pelaksanaan mesti tersedia dalam unit terjemahan yang menyediakan takrif instansiasi eksplisit itu. Draf kerja C++: Instansiasi eksplisit
Pilih susunan berdasarkan pihak yang mengawal argumen jenis:
| Keperluan pembaca | Susunan yang sesuai | Kesan terhadap penyelenggaraan |
|---|---|---|
| Pemanggil boleh membekalkan jenis sesuai yang sewenang-wenangnya | Letakkan takrif dalam pengepala, atau dalam pengepala pelaksanaan yang disertakan daripadanya | Pemanggil boleh menginstansiasikan jenis baharu |
| Pustaka menyokong set jenis yang diketahui | Kekalkan badan dalam fail sumber dan instansiasikan set itu secara eksplisit | Tambahkan instansiasi apabila menambah jenis yang disokong |
| Aplikasi desktop hanya memerlukan operasi konkrit | Dedahkan pembalut C++ biasa di sekeliling pengkhususan yang dipilih | Kekalkan pilihan templat dalam pelaksanaan C++ |
Panduan keputusan ini berasaskan peraturan instansiasi di atas. Untuk panduan ini, set yang disokong sengaja dihadkan kepada int dan double.
Bina contoh lengkap yang terdiri daripada tiga fail
Gunakan folder baharu yang kosong dan simpan tiga fail berikut bersama-sama. Program rekaan ini menambah ofset pada nilai; input kecil memudahkan hasil yang dijangka diperiksa.
Sediakan alat baris perintah Apple
Apple membekalkan pengkompil dan SDK macOS melalui Xcode atau pakej Command Line Tools kendiri. Jika kedua-duanya belum dipasang, buka Terminal dan jalankan perintah berikut, kemudian lengkapkan dialog pemasang. Pemasangan alat berasingan tidak diperlukan jika Xcode sudah dipasang. Memasang alat baris perintah
xcode-select --install
Takrifkan antara muka awam
Simpan pengepala lengkap ini sebagai Offset.hpp. Kekalkan takrif kelas, termasuk ahli datanya, supaya kelihatan kepada pemanggil. Hanya badan pembina dan kaedah dipindahkan ke dalam fail sumber.
#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
Dua baris extern template itu mengisytiharkan instansiasi yang dibekalkan; baris tersebut tidak menyediakan badan ahli. Takrif templat kelas mesti kekal boleh dicapai untuk susunan instansiasi eksplisit pada peringkat kelas ini. Draf kerja C++: Instansiasi eksplisit
Takrifkan badan dan sediakan instansiasi
Simpan pelaksanaan lengkap ini sebagai Offset.cpp. Letakkan takrif instansiasi eksplisit selepas takrif ahli.
#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
Susunan penting: takrif instansiasi eksplisit pada peringkat kelas hanya meliputi ahli yang ditakrifkan pada ketika itu. Ia juga tidak secara automatik menginstansiasikan templat ahli. Kekalkan satu fail sumber pemilik bagi setiap takrif instansiasi eksplisit; bahasa ini membenarkan paling banyak satu takrif bagi setiap set templat dan argumen dalam program. Instansiasi eksplisit, Instansiasi dan pengkhususan templat: Umum
Panggil kedua-dua jenis yang disokong
Simpan pemanggil lengkap ini sebagai main.cpp.
#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 ini menyemak hasilnya melalui status keluarnya selain mencetaknya. Contoh ini diperiksa dari segi konsistensi tetapi tidak disusun semasa penyediaan: persekitaran penyelidikan tidak mempunyai pengkompil C++ atau SDK macOS.
Susun kedua-dua fail sumber, kemudian pautkan kedua-dua fail objek
Dalam Terminal, beralih ke folder yang mengandungi tiga fail tersebut. Jalankan perintah ini mengikut turutan dan teruskan hanya apabila setiap perintah sebelumnya berjaya:
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
Di sini, -c menghasilkan fail objek tanpa pemautan. Seruan pengkompil terakhir menggugurkan -c dan menggabungkan kedua-dua objek menjadi boleh laksana. Perintah ini memilih C++17 dan libc++ secara eksplisit dan bukannya bergantung pada nilai lalai. Panduan perintah pengkompil Clang
Output yang dijangka, berdasarkan input rekaan, ialah:
7
2.5
Kebergantungan penting jelas kelihatan dalam perintah binaan terakhir: main.o meminta fungsi, dan Offset.o membekalkan instansiasinya. Dalam projek yang lebih besar, pastikan sasaran yang mengandungi Offset.cpp dibina dan objek atau pustakanya disertakan dalam langkah pemautan boleh laksana. Penyusunan fail itu sahaja tidak melengkapkan pemautan.
Jangan tambahkan #include "Offset.cpp" pada pemanggil sambil menyusun fail itu secara berasingan. Untuk susunan ini, sertakan pengepala awam dan susun pelaksanaan sebagai unit terjemahan tersendiri. Jika pemanggil memerlukan badannya, pilih secara sengaja susunan pengepala pelaksanaan yang disertakan daripada jadual terdahulu.
Diagnosis instansiasi yang tiada mengikut turutan yang berguna
Mula-mula kenal pasti fungsi tepat yang tidak dapat diselesaikan, termasuk argumen templat dan kelayakannya. Kemudian ikuti urutan ini. Semakan ini menerapkan perbezaan yang didokumenkan antara menjana fail objek dengan memautkannya, bersama-sama keperluan instansiasi eksplisit. Peringkat penyusunan Clang, Peraturan instansiasi eksplisit C++
| Perkara yang ditemui | Perkara yang perlu disemak atau diubah |
|---|---|
| Ahli bagi kedua-dua jenis yang disokong tidak dapat diselesaikan | Sahkan bahawa Offset.o muncul dalam perintah pemautan terakhir |
| Jenis yang baru digunakan gagal | Tambahkan instansiasi sepadan pada fail pelaksanaan pemilik dan pengisytiharan pada pengepala |
| Satu kaedah tiada | Sahkan bahawa badannya mendahului instansiasi kelas dan pengisytiharannya sepadan dengan takrif |
| Templat ahli tiada | Sediakan instansiasi templat ahli yang diperlukan secara berasingan, dengan takrifnya kelihatan |
| Diagnostik takrif pendua muncul | Semak sama ada terdapat takrif instansiasi berulang atau fail pelaksanaan yang disusun dan disertakan |
| Diagnostik menyebut seni bina yang tidak serasi | Semak seni bina setiap objek dan pustaka yang dipautkan |
Contohnya, menukar pemanggil supaya menggunakan demo::Offset<float> memperkenalkan jenis yang tidak dibekalkan oleh pelaksanaan ini. Untuk menyokongnya, tambahkan extern template class Offset<float>; di dalam ruang nama pengepala dan template class Offset<float>; selepas badan dalam Offset.cpp, kemudian bina semula kedua-dua objek. Menambahkan pengisytiharan sahaja tidak menjana fungsi yang tiada.
Jika boleh laksana llvm-nm berasingan LLVM dipasang dan tersedia melalui laluan perintah anda, perintah pilihan ini memeriksa objek daripada contoh tersebut:
llvm-nm --demangle --undefined-only main.o
llvm-nm --demangle --defined-only Offset.o
Cari nama ahli demo::Offset yang sepadan dalam simbol pemanggil yang belum diselesaikan dan simbol pelaksanaan yang ditakrifkan. Entri lain dijangka ada, termasuk rujukan pustaka standard. LLVM mendokumenkan kedua-dua penapis dan penyahkaburan nama; perintah ini secara khusus memerlukan llvm-nm, yang ketersediaannya tidak boleh dianggap pasti daripada pemasangan alat Apple. Panduan perintah llvm-nm
Kekalkan keserasian seni bina dan ABI macOS
Instansiasi eksplisit membekalkan kod templat konkrit; ia tidak menjadikan satu fail objek boleh digunakan untuk setiap seni bina. Aliran kerja binari universal Apple menyusun dan memaut versi khusus seni bina sebelum menggabungkannya. Jika aplikasi menyokong kedua-dua arm64 dan x86_64, sertakan sumber instansiasi dalam kedua-dua binaan. Membina binari macOS universal
Untuk binaan universal bagi sampel ini, periksa boleh laksana yang terhasil dengan perintah berikut. Pastikan seni bina yang dilaporkan sepadan dengan binaan yang dimaksudkan; perintah binaan biasa terdahulu tidak meminta binari universal.
xcrun lipo -archs ./template-demo
Mengasingkan badan kaedah juga memastikan susun atur kelas kelihatan dalam pengepala. Anggap perubahan pada susun atur itu sebagai perubahan antara muka yang memerlukan kod bergantung dibina semula. Jangan membuat kesimpulan bahawa antara muka binari stabil hanya berdasarkan kehadiran instansiasi eksplisit. libc++ mendokumenkan jaminan keserasian tertentu dan had konfigurasi, serta membezakan dengan jelas jaminan tersebut daripada jaminan tentang fungsi yang ditakrifkan pengguna. Jaminan ABI libc++
Apakah yang berubah apabila aplikasi Swift memanggil kod C++?
Pisahkan dua keperluan: Swift mesti mengimport pengisytiharan C++ yang disokong, dan boleh laksana akhir mesti memaut pelaksanaannya. Dokumentasi Swift menunjukkan pengkhususan templat kelas konkrit melalui alias jenis C++; templat yang belum diinstansiasikan itu sendiri tidak tersedia secara langsung sebagai jenis Swift. Menggabungkan Swift dan C++: Menggunakan Templat Kelas
Untuk antara muka sampel ini, fragmen pengepala berikut ialah pilihan untuk disisipkan di dalam namespace demo, selepas takrif kelas. Fragmen ini memberikan nama kepada jenis konkrit supaya dapat diimport:
using IntOffset = Offset<int>;
Alias itu menamakan jenis tersebut. Instansiasi eksplisit dalam Offset.cpp masih membekalkan pembina dan kaedah luar baris. Swift mengimport pengepala C++ menggunakan modul Clang dan pada masa ini tidak boleh mengimport modul C++20. Ikuti persediaan kesalingoperasian berasaskan pengepala untuk projek tersebut. Menggabungkan Swift dan C++
Dalam Xcode, dayakan C++ and Objective-C interoperability dengan nilai C++ / Objective-C++ untuk sasaran Swift. Bagi sasaran aplikasi yang menggunakan C++ daripada sasaran yang sama, sertakan pengepala C++ dalam pengepala jambatannya. Sasaran rangka kerja mendedahkan pengisytiharan yang disokong melalui pengepala awam. Menyediakan Projek Swift dan C++ Bahasa Campuran
Kod Swift dan C++ campuran mesti menggunakan pustaka standard C++ yang sama; platform Apple menggunakan libc++. Kesalingoperasian memerlukan C++14 atau lebih baharu. Padankan mod bahasa pengepala yang diimport dengan binaan C++, dan semak senarai ciri yang disokong sebelum mendedahkan templat yang lebih rumit. Instansiasi eksplisit tidak memperluas pemetaan bahasa yang disokong oleh Swift. Ciri dan Kekangan Kesalingoperasian C++ yang Disokong
Bagi set jenis yang tetap, selenggarakan pengisytiharan awam, senarai instansiasi dan input pemautan akhir bersama-sama. Apabila pemanggil baharu memerlukan jenis lain, sama ada tambahkan jenis itu secara sengaja atau jadikan pelaksanaan tersedia untuk instansiasi pada pihak pemanggil. Pilihan itu memastikan sempadan fail sumber mudah difahami apabila projek desktop berkembang.
Sumber
- Instansiasi tersirat — draf kerja C++
- Instansiasi eksplisit — draf kerja C++
- Instansiasi dan pengkhususan templat: Umum — draf kerja C++
- Memasang alat baris perintah — Apple
- clang — pengkompil C, C++ dan Objective-C Clang — Projek LLVM
- llvm-nm — senaraikan jadual simbol bitkod LLVM dan fail objek — Projek LLVM
- Membina binari macOS universal — Apple
- Jaminan ABI libc++ — Projek LLVM
- Menggabungkan Swift dan C++ — Swift.org
- Menyediakan Projek Swift dan C++ Bahasa Campuran — Swift.org
- Ciri dan Kekangan Kesalingoperasian C++ yang Disokong — Swift.org