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

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

Ringkasan bagian sebelumnya


Karena pembatasan pada kemampuan untuk menggunakan kompiler C ++ 11, dan dari kurangnya alternatif, dorongan ingin menulis implementasi sendiri dari pustaka C ++ 11 standar di atas pustaka C ++ 98 / C ++ 03 yang disertakan dengan kompiler.

Selain file header standar type_traits , utas , mutex , chrono , nullptr.h ditambahkan yang mengimplementasikan std :: nullptr_t dan core.h di mana makro yang terkait dengan fungsionalitas yang bergantung pada kompiler ditambahkan, serta memperluas pustaka standar.

Tautan ke GitHub dengan hasil untuk hari ini untuk yang tidak sabar dan yang bukan pembaca:

Komitmen dan kritik yang membangun dipersilahkan

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
...

Bab 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endif


Setelah semua kode disisir sedikit dan dibagi dengan header "standar" menjadi stdex namespace yang terpisah , saya melanjutkan untuk mengisi type_traits , nullptr.h dan sepanjang core.h yang sama, yang berisi makro untuk menentukan versi standar yang digunakan oleh kompiler dan mendukungnya Nullptr asli , char16_t , char32_t dan static_assert .

Secara teori, semuanya sederhana - menurut standar C ++ (klausa 14.8), makro __cplusplus harus ditentukan oleh kompiler dan sesuai dengan versi standar yang didukung:

C++ pre-C++98: #define __cplusplus 1 C++98: #define __cplusplus 199711L C++98 + TR1: #define __cplusplus 199711L // ??? C++11: #define __cplusplus 201103L C++14: #define __cplusplus 201402L C++17: #define __cplusplus 201703L 

oleh karena itu, kode untuk menentukan ketersediaan dukungan adalah sepele:

 #if (__cplusplus >= 201103L) //  C++ 11   #define _STDEX_NATIVE_CPP11_SUPPORT //   11  (nullptr, static_assert) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT //    char16_t, char32_t #endif 

gambar Bahkan, tidak semuanya begitu sederhana dan sekarang kruk yang menarik dengan menyapu dimulai.

Pertama, tidak semua, atau lebih tepatnya tidak ada, dari penyusun tidak menerapkan standar berikutnya sepenuhnya dan segera. Misalnya, dalam Visual Studio 2013 constexpr tidak ada untuk waktu yang sangat lama, sementara itu diklaim mendukung C ++ 11 - dengan peringatan bahwa implementasinya tidak lengkap. Yaitu, otomatis - tolong, static_assert - sama mudahnya (bahkan dari MS VS sebelumnya), tetapi constexpr tidak . Kedua, tidak semua kompiler (dan ini bahkan lebih mengejutkan) dengan benar mengekspos definisi ini dan memperbaruinya tepat waktu. Tiba-tiba, dalam kompiler yang sama, Visual Studio tidak mengubah versi definisi __cplusplus dari versi pertama kompiler, meskipun dukungan penuh untuk C ++ 11 telah lama diumumkan (yang juga tidak benar, yang mana ada sinar ketidakpuasan yang terpisah - segera setelah percakapan tiba pada fungsi spesifik dari "baru" "11 pengembang standar segera mengatakan bahwa tidak ada preprosesor C99, tidak ada" fitur "lainnya. Dan situasinya diperburuk oleh fakta bahwa oleh penyusun standar diizinkan untuk menetapkan definisi ini berbeda dari nilai-nilai di atas, jika mereka tidak sepenuhnya memenuhi standar yang dinyatakan. Akan logis untuk mengasumsikan, misalnya, pengembangan definisi untuk makro yang diberikan (dengan pengenalan fungsi baru, menambah angka yang tersembunyi di balik definisi ini):

 standart C++98: #define __cplusplus 199711L // C++98 standart C++98 + TR1: #define __cplusplus 200311L // C++03 nonstandart C++11: #define __cplusplus 200411L // C++03 + auto and dectype nonstandart C++11: #define __cplusplus 200511L // C++03 + auto, dectype and constexpr(partly) ... standart C++11: #define __cplusplus 201103L // C++11 

Tetapi pada saat yang sama, tidak ada kompiler populer utama yang "usang" dengan fitur ini.

Karena semua ini (saya tidak takut dengan kata ini), sekarang untuk setiap kompiler non-standar Anda harus menulis cek khusus Anda sendiri untuk mengetahui standar C ++ dan sejauh mana mendukungnya. Berita baiknya adalah kita perlu mempelajari beberapa fungsi kompiler agar berfungsi dengan benar. Pertama, kita sekarang menambahkan versi memeriksa Visual Studio melalui makro _MSC_VER , unik untuk kompiler ini. Karena di gudang kompiler yang didukung saya ada juga C ++ Borland Builder 6.0, yang pengembangnya, pada gilirannya, sangat tertarik untuk menjaga kompatibilitas dengan Visual Studio (termasuk "fitur" dan bug), maka tiba-tiba ada makro ini juga. Untuk kompiler yang kompatibel dengan dentang, ada __has_feature makro non-standar ( fitur_name ) , yang memungkinkan Anda untuk mengetahui apakah kompiler mendukung fungsi ini atau itu. Akibatnya, kode ini meningkat ke:

 #ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif 

Ingin menjangkau lebih banyak kompiler? Kami menambahkan cek untuk Codegear C ++ Builder, yang merupakan pewaris Borland (dalam manifestasi terburuknya, tetapi lebih lanjut tentang itu nanti):

 #ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif 

Perlu juga dicatat bahwa karena Visual Studio telah mengimplementasikan dukungan nullptr dari versi kompiler _MSC_VER 1600 , serta tipe bawaan char16_t dan char32_t , kita perlu menangani ini dengan benar. Beberapa cek lagi ditambahkan:

 #ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if ((!defined(_MSC_VER) || _MSC_VER < 1600) && !defined(_STDEX_NATIVE_CPP11_SUPPORT)) #define _STDEX_IMPLEMENTS_NULLPTR_SUPPORT #else #define _STDEX_NATIVE_NULLPTR_SUPPORT #endif #if (_MSC_VER >= 1600) #ifndef _STDEX_NATIVE_CPP11_TYPES_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif 

Pada saat yang sama, kami akan memeriksa dukungan C ++ 98, karena untuk kompiler tanpa itu tidak akan ada beberapa file header dari perpustakaan standar, dan kami tidak dapat memverifikasi tidak adanya mereka menggunakan kompiler.

Opsi lengkap
 #ifndef __has_feature #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif // Any compiler claiming C++11 supports, Visual C++ 2015 and Clang version supporting constexpr #if ((__cplusplus >= 201103L) || (_MSC_VER >= 1900) || (__has_feature(cxx_constexpr))) // C++ 11 implementation #define _STDEX_NATIVE_CPP11_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #if !defined(_STDEX_NATIVE_CPP11_TYPES_SUPPORT) #if ((__cplusplus > 199711L) || defined(__CODEGEARC__)) #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if ((!defined(_MSC_VER) || _MSC_VER < 1600) && !defined(_STDEX_NATIVE_CPP11_SUPPORT)) #define _STDEX_IMPLEMENTS_NULLPTR_SUPPORT #else #define _STDEX_NATIVE_NULLPTR_SUPPORT #endif #if (_MSC_VER >= 1600) #ifndef _STDEX_NATIVE_CPP11_TYPES_SUPPORT #define _STDEX_NATIVE_CPP11_TYPES_SUPPORT #endif #endif #if _MSC_VER // Visual C++ fallback #define _STDEX_NATIVE_MICROSOFT_COMPILER_EXTENSIONS_SUPPORT #define _STDEX_CDECL __cdecl #if (__cplusplus >= 199711L) #define _STDEX_NATIVE_CPP_98_SUPPORT #endif #endif // C++ 98 check: #if ((__cplusplus >= 199711L) && ((defined(__INTEL_COMPILER) || defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)))))) #ifndef _STDEX_NATIVE_CPP_98_SUPPORT #define _STDEX_NATIVE_CPP_98_SUPPORT #endif #endif 


Dan sekarang konfigurasi yang beraneka ragam dari boost mulai muncul dalam ingatan saya di mana banyak pengembang pekerja keras menulis semua makro yang bergantung pada kompiler ini dan membuat peta tentang apa yang didukung dan apa yang bukan oleh kompiler spesifik dari versi tertentu, dari mana saya pribadi merasa tidak nyaman, Saya ingin tidak pernah melihatnya atau menyentuhnya lagi. Tetapi kabar baiknya adalah Anda bisa berhenti di situ. Setidaknya ini cukup bagi saya untuk mendukung kompiler paling populer, tetapi jika Anda menemukan ketidaktepatan atau ingin menambahkan kompiler lain, saya akan dengan senang hati menerima permintaan tarik.

Sebuah pencapaian hebat dibandingkan dengan peningkatan, saya percaya bahwa memungkinkan untuk menjaga penyebaran makro yang bergantung pada kompiler dalam kode, yang membuat kode lebih bersih dan lebih mudah untuk dipahami, dan juga tidak menumpuk puluhan file konfigurasi untuk setiap OS dan untuk setiap kompiler. Kami akan berbicara tentang kelemahan dari pendekatan ini sedikit kemudian.

Pada tahap ini, kita sudah dapat mulai menghubungkan fungsionalitas yang hilang dari 11 standar, dan hal pertama yang kami perkenalkan adalah static_assert .

static_assert


Kami mendefinisikan struktur StaticAssertion , yang akan mengambil nilai Boolean sebagai parameter templat - akan ada kondisi kami, jika tidak terpenuhi (ekspresi salah ), kesalahan akan terjadi dalam kompilasi templat yang tidak ditentukan. Dan struktur boneka lain untuk menerima sizeof ( StaticAssertion ) .

 namespace stdex { namespace detail { template <bool> struct StaticAssertion; template <> struct StaticAssertion<true> { }; // StaticAssertion<true> template<int i> struct StaticAssertionTest { }; // StaticAssertionTest<int> } } 

dan sihir makro lebih lanjut

 #ifdef _STDEX_NATIVE_CPP11_SUPPORT #define STATIC_ASSERT(expression, message) static_assert((expression), #message) #else // no C++11 support #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 #define STATIC_ASSERT(expression, message)\ struct CONCATENATE(__static_assertion_at_line_, __LINE__)\ {\ stdex::detail::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _WITH__), message);\ };\ typedef stdex::detail::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__) #ifndef _STDEX_NATIVE_NULLPTR_SUPPORT #define static_assert(expression, message) STATIC_ASSERT(expression, ERROR_MESSAGE_STRING) #endif #endif 

penggunaan:

 STATIC_ASSERT(sizeof(void*) == 4, non_x32_platform_is_unsupported); 

Perbedaan penting antara implementasi saya dan standar adalah bahwa tidak ada kata kunci yang berlebihan tanpa memberi tahu pengguna. Hal ini disebabkan oleh fakta bahwa dalam C ++ tidak mungkin untuk mendefinisikan beberapa definisi dengan jumlah argumen yang berbeda tetapi satu nama, dan implementasi tanpa pesan jauh lebih tidak berguna daripada opsi yang dipilih. Fitur ini mengarah pada fakta bahwa pada dasarnya STATIC_ASSERT dalam implementasi saya adalah versi yang sudah ditambahkan di C ++ 11.
Mari kita lihat apa yang terjadi. Sebagai hasil dari memeriksa versi __cplusplus dan makro kompiler non-standar, kami memiliki informasi yang cukup tentang dukungan C ++ 11 (dan karenanya static_assert ), diungkapkan oleh definisi _STDEX_NATIVE_CPP11_SUPPORT. Oleh karena itu, jika makro ini didefinisikan, kita cukup menggunakan static_assert standar:

 #ifdef _STDEX_NATIVE_CPP11_SUPPORT #define STATIC_ASSERT(expression, message) static_assert((expression), #message) 

Harap perhatikan bahwa parameter kedua makro STATIC_ASSERT sama sekali bukan string literal, dan karenanya menggunakan operator preprocessor # kami akan mengonversi parameter pesan menjadi string untuk transmisi ke static_assert standar.
Jika kami tidak memiliki dukungan dari kompiler, maka kami melanjutkan ke implementasi kami. Untuk memulainya, kami akan mendeklarasikan macro tambahan untuk string "gluing" (operator preprocessor ## hanya bertanggung jawab untuk ini).

 #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 

Saya secara khusus tidak menggunakan secara sederhana #define CONCATENATE ( arg1 , arg2 ) arg1 ## arg2 agar dapat meneruskan hasil dari makro CONCATENATE yang sama sebagai argumen ke arg1 dan arg2 .
Selanjutnya, kami mendeklarasikan struktur dengan nama cantik __static_assertion_at_line_ {nomor baris} (makro __LINE__ juga ditentukan oleh standar dan harus diperluas ke nomor baris yang dipanggil), dan di dalam struktur ini kami menambahkan bidang jenis StaticAssertion kami dengan nama STATIC_ASSERTION_FAILED_AT_LINE _ {_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ pesan kesalahan dari makro panggilan}.

 #define STATIC_ASSERT(expression, message)\ struct CONCATENATE(__static_assertion_at_line_, __LINE__)\ {\ stdex::detail::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _WITH__), message);\ };\ typedef stdex::detail::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__) 

Dengan parameter templat di StaticAssertion, kami meneruskan ekspresi yang dicentang di STATIC_ASSERT , yang mengarahkannya ke bool . Akhirnya, untuk menghindari membuat variabel lokal dan nol-overhead memeriksa kondisi pengguna, alias dideklarasikan untuk tipe StaticAssertionTest <sizeof ({nama struktur yang dinyatakan di atas}) dengan nama __static_assertion_test_at_line_ {nomor baris}.

Semua keindahan dengan penamaan diperlukan hanya untuk membuatnya jelas dari kesalahan kompilasi bahwa ini adalah hasil yang tegas, dan bukan hanya kesalahan, tetapi juga untuk menampilkan pesan kesalahan yang ditetapkan untuk pernyataan ini. Trik sizeof diperlukan untuk memaksa kompiler untuk membuat instance kelas template StaticAssertion , yang ada di dalam struktur yang baru saja dinyatakan, dan dengan demikian memeriksa kondisi yang dilewati untuk menegaskan.

STATIC_ASSERT hasil
GCC:
30: 103: kesalahan: bidang 'STATIC_ASSERTION_FAILED_AT_LINE_36_WITH__non_x32_platform_is_unsupported' memiliki jenis 'stdex :: detail :: StaticAssertion <false>' yang tidak lengkap
25:36: note: dalam definisi makro 'CONCATENATE2'
23:36: catatan: dalam ekspansi makro 'CONCATENATE1'
30:67: note: dalam ekspansi makro 'CONCATENATE'
24:36: note: dalam ekspansi makro 'CONCATENATE2'
23:36: catatan: dalam ekspansi makro 'CONCATENATE1'
30:79: note: dalam ekspansi makro 'CONCATENATE'
24:36: note: dalam ekspansi makro 'CONCATENATE2'
23:36: catatan: dalam ekspansi makro 'CONCATENATE1'
30:91: note: dalam ekspansi makro 'CONCATENATE'
36: 3: note: dalam perluasan makro 'STATIC_ASSERT'

Borland C ++ Builder:
[C ++ Error] stdex_test.cpp (36): E2450 Struktur tidak terdefinisi 'stdex :: detail :: StaticAssertion <0>'
[C ++ Kesalahan] stdex_test.cpp (36): E2449 Ukuran 'STATIC_ASSERTION_FAILED_AT_LINE_36_WITH__non_x32_platform_is_unsupported' tidak diketahui atau nol
[C ++ Error] stdex_test.cpp (36): E2450 Struktur tidak terdefinisi 'stdex :: detail :: StaticAssertion <0>'

Visual Studio:
Kesalahan c2079


"Trik" kedua yang ingin saya miliki, sementara hilang dari standar adalah countof - menghitung jumlah elemen dalam array. Sishers sangat suka mendeklarasikan makro ini melalui sizeof (arr) / sizeof (arr [0]), tetapi kita akan melangkah lebih jauh.

hitungan


 #ifdef _STDEX_NATIVE_CPP11_SUPPORT #include <cstddef> namespace stdex { namespace detail { template <class T, std::size_t N> constexpr std::size_t _my_countof(T const (&)[N]) noexcept { return N; } } // namespace detail } #define countof(arr) stdex::detail::_my_countof(arr) #else //no C++11 support #ifdef _STDEX_NATIVE_MICROSOFT_COMPILER_EXTENSIONS_SUPPORT // Visual C++ fallback #include <stdlib.h> #define countof(arr) _countof(arr) #elif defined(_STDEX_NATIVE_CPP_98_SUPPORT)// C++ 98 trick #include <cstddef> template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N]; #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x)) #else #define countof(arr) sizeof(arr) / sizeof(arr[0]) #endif 

Untuk kompiler dengan dukungan constexpr , kami akan mendeklarasikan versi constexpr dari template ini (yang sama sekali tidak diperlukan, untuk semua standar, implementasi melalui template COUNTOF_REQUIRES_ARRAY_ARGUMENT sudah cukup ), untuk sisanya kami memperkenalkan versi melalui fungsi template COUNTOF_REQUIRES_ARRAY_ARGUMENT . Visual Studio di sini sekali lagi dibedakan dengan adanya implementasi _countof di file header stdlib.h .

Fungsi COUNTOF_REQUIRES_ARRAY_ARGUMENT terlihat mengintimidasi dan mencari tahu apa yang dilakukannya cukup rumit. Jika Anda melihat lebih dekat, Anda dapat memahami bahwa dibutuhkan array elemen tipe template T dan ukuran N sebagai satu-satunya argumen - dengan demikian, dalam hal mentransfer tipe elemen lain (bukan array), kami mendapatkan kesalahan kompilasi, yang pasti menyenangkan. Melihat lebih dekat, Anda dapat mengetahui (dengan susah payah) bahwa ia mengembalikan array elemen char ukuran N. Pertanyaannya adalah, mengapa kita membutuhkan semua ini? Di sinilah operator sizeof ikut bermain dan kemampuan uniknya untuk bekerja pada waktu kompilasi. Ukuran panggilan ( COUNTOF_REQUIRES_ARRAY_ARGUMENT ) menentukan ukuran array elemen char yang dikembalikan oleh fungsi, dan karena sizeof standar (char) == 1, ini adalah jumlah elemen N dalam array asli. Elegan, cantik, dan sepenuhnya gratis.

selamanya


Makro pembantu kecil lain yang saya gunakan di mana pun loop tak terbatas diperlukan selamanya . Ini didefinisikan sebagai berikut:

 #if !defined(forever) #define forever for(;;) #else #define STRINGIZE_HELPER(x) #x #define STRINGIZE(x) STRINGIZE_HELPER(x) #define WARNING(desc) message(__FILE__ "(" STRINGIZE(__LINE__) ") : warning: " desc) #pragma WARNING("stdex library - macro 'forever' was previously defined by user; ignoring stdex macro definition") #undef STRINGIZE_HELPER #undef STRINGIZE #undef WARNING #endif 

Sintaksis contoh untuk mendefinisikan loop infinite eksplisit:

  unsigned int i = 0; forever { ++i; } 

Makro ini digunakan hanya untuk secara eksplisit mendefinisikan loop tak terbatas dan dimasukkan dalam perpustakaan hanya untuk alasan "tambahkan gula sintaksis". Di masa depan, saya mengusulkan untuk menggantinya dengan opsional melalui mendefinisikan makro plug-in SELAMANYA . Apa yang luar biasa dalam cuplikan kode di atas dari pustaka adalah makro PERINGATAN yang sama yang menghasilkan pesan peringatan di semua kompiler jika makro selamanya telah ditentukan oleh pengguna. Ini menggunakan makro __LINE__ standar yang sudah dikenal dan makro __FILE__ standar, yang dikonversi menjadi string dengan nama file sumber saat ini.

stdex_assert


Untuk menerapkan asert dalam runtime, stdex_assert makro diperkenalkan sebagai:

 #if defined(assert) #ifndef NDEBUG #include <iostream> #define stdex_assert(condition, message) \ do { \ if (! (condition)) { \ std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \ << " line " << __LINE__ << ": " << message << std::endl; \ std::terminate(); \ } \ } while (false) #else #define stdex_assert(condition, message) ((void)0) #endif #endif 

Saya tidak akan mengatakan bahwa saya sangat bangga dengan implementasi ini (itu akan berubah di masa depan), tetapi teknik yang menarik telah digunakan di sini yang ingin saya perhatikan. Untuk menyembunyikan pemeriksaan dari ruang lingkup kode aplikasi, konstruk do {} while (false) digunakan, yang akan dieksekusi, yang jelas sekali dan pada saat yang sama tidak akan memperkenalkan kode "layanan" dalam kode aplikasi umum. Teknik ini cukup berguna dan digunakan di beberapa tempat lain di perpustakaan.

Jika tidak, implementasinya sangat mirip dengan pernyataan standar - dengan makro NDEBUG tertentu, yang biasanya dibuat oleh kompiler dalam rilis build, menegaskan tidak melakukan apa-apa, jika tidak maka program akan terganggu dengan output pesan ke aliran kesalahan standar jika kondisi tegas tidak terpenuhi.

kecuali


Untuk fungsi yang tidak membuang pengecualian, kata kunci noexcept telah diperkenalkan dalam standar baru. Ini juga cukup sederhana dan tanpa rasa sakit untuk diterapkan melalui makro:

 #ifdef _STDEX_NATIVE_CPP11_SUPPORT #define stdex_noexcept noexcept #else #define stdex_noexcept throw() #endif 

Namun, perlu dipahami bahwa dengan standar noexcept dapat mengambil nilai bool , dan juga dapat digunakan untuk menentukan pada waktu kompilasi bahwa ekspresi yang diteruskan tidak membuang pengecualian. Fungsionalitas ini tidak dapat diimplementasikan tanpa dukungan kompiler, dan oleh karena itu hanya ada stdex_noexcept "stripped down" di perpustakaan.

Akhir bab kedua. Bab ketiga akan berbicara tentang seluk-beluk implementasi nullptr, mengapa berbeda untuk kompiler yang berbeda, serta pengembangan type_traits, dan apa bug lain dalam kompiler yang saya temui selama pengembangannya.

Terima kasih atas perhatian anda

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


All Articles