Bagaimana saya menulis library C ++ 11 standar atau mengapa boost sangat menakutkan. Pendahuluan

Ya - ya, dengan moto ini saya bergegas ke medan pertempuran.

Alih-alih kata pengantar


Mungkin dengan gambar ini, cerita apa saja tentang boost , Loki , independen, dan juga implementasi dari pustaka C ++ standar yang disertakan dengan kompiler harus dimulai.

Ya, ya, dan jika Anda berpikir bahwa pengembang perpustakaan standar untuk g ++ yang sama, dentang, Visual Studio atau, Tuhan maafkan saya, C ++ Builder (sebelumnya Borland, tetapi Embarcadero saat ini) adalah guru yang tidak membuat kruk, mereka tidak melanggar standar untuk kompiler mereka dan jangan menulis sepeda, maka kemungkinan besar Anda tidak begitu aktif menggunakan pustaka C ++ standar seperti yang Anda pikirkan.

Artikel ini ditulis sebagai sebuah cerita, dan berisi banyak "air" dan penyimpangan, tetapi saya berharap bahwa pengalaman saya dan kode yang dihasilkan akan bermanfaat bagi mereka yang menghadapi masalah yang sama ketika mengembangkan C ++, terutama pada kompiler yang lebih tua. Tautan ke GitHub dengan hasil untuk hari ini untuk yang tidak sabar dan yang bukan pembaca:

https://github.com/oktonion/stdex (komitmen dan kritik konstruktif dipersilahkan)

Dan sekarang, hal pertama yang pertama.


Daftar isi


Pendahuluan
Bab 1. Viam supervadet vadens
Bab 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endif
Bab 3. Menemukan implementasi nullptr yang sempurna
Bab 4. C ++ Template Magic
.... 4.1 Kita mulai dari yang kecil
.... 4.2 Tentang berapa banyak kesalahan ajaib yang dikompilasi oleh log untuk kita
.... 4.3 Pointer dan semuanya
.... 4.4 Apa lagi yang diperlukan untuk pustaka templat
Bab 5
...

Entri


Saat itu tahun 2017, C ++ 11 telah lama mengalir ke aliran baru kompiler baru dan relatif baru, membawa pekerjaan terstandarisasi dengan aliran, mutex, memperluas pemrograman templat dan pendekatan standardisasi untuk itu, ada โ€œbesarโ€ jenis panjang yang panjang dalam standar , akhirnya menyingkirkan kebutuhan luas untuk menampilkan jenis untuk kompiler menggunakan otomatis (selamat tinggal std :: map <type, type> :: const_iterator it = ... - well, Anda mengerti saya), dan kombinasi fitur ini dengan yang baru untuk masing-masing telah menjadi salah satu yang paling umum menggunakan implementasi loop iterator. Akhirnya, kami (pengembang) dapat memberi tahu pengguna (pengembang) secara manusiawi mengapa kode tersebut tidak dikumpulkan menggunakan static_assert , serta enable_if , yang sekarang memilih overload yang diperlukan seperti sulap.

Di halaman itu tahun 2017! Sudah C + + 17 secara aktif diperkenalkan di GCC, dentang, Visual Studio, di mana-mana ada dectype (karena C + + 11), constexpr (sejak C + + 11, tetapi secara signifikan ditingkatkan), modul hampir di jalan, ada waktu yang baik. Saya sedang bekerja dan dengan beberapa ketidaksetujuan melihat Internal Compiler Error berikutnya di Borland C ++ Builder 6.0 saya, serta banyak kesalahan build dengan versi berikutnya dari boost library. Saya pikir sekarang Anda mengerti dari mana keinginan untuk membangun sepeda ini berasal. Kami menggunakan Borland C ++ Builder 6.0 dan Visual Studio 2010 untuk Windows, g ++ versi 4.4.2 atau lebih rendah untuk QNX dan untuk beberapa sistem unix. Kami terhindar dari MacOS, yang tidak diragukan lagi merupakan nilai tambah. Tidak ada kompiler lain (termasuk C ++ 11) yang dapat dipertimbangkan dengan alasan kami meninggalkan artikel ini.

"Dan apa yang bisa sangat rumit di sana" - sebuah pemikiran merayap ke dalam upaya saya yang lelah untuk meningkatkan di bawah otak pembangun tua yang baik. "Yang saya butuhkan adalah type_traits , utas , mutex , mungkin chrono , nullptr akan menyenangkan." Saya beralasan dan mulai bekerja.

Bab 1. Viam supervadet vadens


Itu perlu untuk memulai dari mana, dan untuk memulai dari mana - tentu saja saya memiliki sejumlah file header dan kode sumber yang tersebar di seluruh proyek dengan implementasi fungsionalitas yang serupa atau identik dari pustaka standar C ++ 11 standar perkembangan saya, serta secara jujur โ€‹โ€‹meminjam atau memproses dari kode-kode itu. gcc dan boost yang sama. Menggabungkan semua ini bersama-sama, saya punya beberapa fungsi, kelas, makro yang seharusnya berubah menjadi perpustakaan standar yang elegan dan ramping. Setelah memperkirakan jumlah pekerjaan, saya segera memutuskan untuk meninggalkan implementasi segalanya dan segalanya, membatasi diri pada pengembangan "add-on" di atas pustaka C ++ 98 standar yang disertakan dengan kompiler.

Dalam versi awal tidak ada kepatuhan khusus terhadap standar, terutama masalah yang diterapkan diselesaikan. Misalnya, nullptr terlihat seperti ini:

#define nullptr 0 

static_assert juga diselesaikan dengan sederhana:

  #define STATIC_ASSERT(expr) typedef int test##__LINE__##[expr ? 1 : -1]; 

std :: to_string diimplementasikan melalui std :: stringstream , yang digantikan oleh std :: strstream dalam implementasi tanpa file header sstream , dan semua ini segera dimasukkan ke namespace std :

  #ifndef NO_STD_SSTREAM_HEADER #include <sstream> #else #include <strstream> namespace std {typedef std::strstream stringstream;} #endif namespace std { template<class T> string to_string(const T &t) { stringstream ss; ss << t; return ss.str(); } } 

Ada juga "trik" yang tidak termasuk dalam standar, tetapi tetap berguna dalam pekerjaan sehari-hari, seperti makro selamanya atau hitungan :

  #define forever for(;;) //     #define countof(arr) sizeof(arr) / sizeof(arr[0]) //        C 

countof kemudian diubah menjadi lebih banyak opsi C ++:

  template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N]; //        C++ (       ): #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x)) 

Bekerja dengan utas ( utas file header dari std) diimplementasikan melalui beberapa pustaka Tiny, ditulis ulang dengan mempertimbangkan fitur-fitur seluruh kebun binatang kompiler dan OS. Dan mungkin type_traits sampai taraf tertentu sudah mirip dengan apa yang diperlukan standar C ++ 11. Ada std :: enable_if , std :: integral_constant , std :: is_const dan templat sejenisnya yang sudah digunakan dalam pengembangan.

  namespace std { template<bool Cond, class Iftrue, class Iffalse> struct conditional { typedef Iftrue type; }; // Partial specialization for false. template<class Iftrue, class Iffalse> struct conditional<false, Iftrue, Iffalse> { typedef Iffalse type; }; template <bool, class T = void> struct enable_if { }; template <class T> struct enable_if<true, T> { typedef T type; }; template<class Tp, Tp Val> struct integral_constant { // convenient template for integral constant types static const Tp value = Val; typedef const Tp value_type; typedef integral_constant<Tp, Val> type; }; typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type; template<bool Val> struct bool_constant : public integral_constant<bool, Val> { }; template<class, class> struct is_same : public false_type { }; template<class Tp> struct is_same<Tp, Tp> : public true_type // specialization { }; } // ...     

Diputuskan untuk memisahkan semua makro, fungsi, tipe non-standar dan "kompiler" menjadi file header.h. Dan, bertentangan dengan praktik boost, di mana implementasi "switching" menggunakan makro banyak digunakan, untuk mengabaikan macro terkait dengan hal-hal yang bergantung pada compiler di semua file library, kecuali core.h. Juga, fungsionalitas yang tidak dapat diimplementasikan tanpa menggunakan "peretasan" (pelanggaran standar, bergantung pada Perilaku Tidak Terdefinisi untuk Ditentukan secara Agak), atau diterapkan secara individual untuk setiap kompiler (misalnya dengan makro bawaannya), diputuskan untuk tidak menambah perpustakaan, agar tidak menghasilkan dorongan mengerikan lain (tapi indah). Akibatnya, satu-satunya hal utama dan praktis yang digunakan core.h adalah untuk menentukan apakah ada dukungan untuk nullptr bawaan (karena kompiler bersumpah jika mengganti kata yang dipesan), dukungan untuk static_assert bawaan (sekali lagi, untuk menghindari memotong kata yang dipesan) dan dukungan untuk tipe bawaan C ++ 11 char16_t dan char32_t .

Ke depan saya dapat mengatakan bahwa idenya hampir berhasil, karena sebagian besar dari apa yang didefinisikan dalam boost oleh macro keras tergantung pada kompiler tertentu, dalam implementasi ini ditentukan oleh kompiler itu sendiri pada tahap kompilasi.

Akhir bab pertama. Pada bab kedua, saya akan melanjutkan narasi tentang kesulitan berurusan dengan kompiler, tentang kruk yang ditemukan dan solusi elegan di perut gcc, boost dan Visual Studio, serta deskripsi kesan saya tentang apa yang saya lihat dan dapatkan pengalaman dengan contoh kode.

Terima kasih atas perhatian anda

Source: https://habr.com/ru/post/id417027/


All Articles