Haruskah ada asersi dalam rilis build

20

Perilaku default assertdi dalam C ++ adalah tidak melakukan apa pun di build rilis. Saya kira ini dilakukan karena alasan kinerja dan mungkin untuk mencegah pengguna melihat pesan kesalahan yang tidak menyenangkan.

Namun, saya berpendapat bahwa situasi di mana sebuah assertakan dipecat tetapi dinonaktifkan bahkan lebih menyusahkan karena aplikasi kemudian mungkin akan crash dalam cara yang lebih buruk di telepon karena beberapa invarian rusak.

Selain itu, argumen kinerja bagi saya hanya diperhitungkan ketika itu adalah masalah yang dapat diukur. Sebagian besar assertdalam kode saya tidak lebih rumit dari

assert(ptr != nullptr);

yang akan berdampak kecil pada sebagian besar kode.

Ini mengarahkan saya ke pertanyaan: Haruskah pernyataan (yang artinya konsep, bukan implementasi spesifik) menjadi aktif dalam rilis rilis? Kenapa tidak)?

Harap dicatat bahwa pertanyaan ini bukan tentang cara mengaktifkan #undef _NDEBUGassert dalam build rilis (suka atau menggunakan implementasi menegaskan sendiri). Selain itu, ini bukan tentang mengaktifkan pernyataan di pihak ketiga / kode perpustakaan standar tetapi dalam kode yang dikendalikan oleh saya.

Tak seorangpun
sumber
Saya kenal pemain global di pasar perawatan kesehatan yang memasang versi debug sistem informasi rumah sakit mereka di lokasi pelanggan. Mereka memiliki perjanjian khusus dengan Microsoft untuk menginstal pustaka debug C ++ di sana juga. Nah, kualitas produk mereka adalah ...
Bernhard Hiller
2
Ada pepatah lama yang mengatakan "menghapus pernyataan adalah seperti mengenakan jaket pelampung untuk berlatih di pelabuhan, tetapi kemudian meninggalkan jaket pelampung saat kapal Anda berangkat ke lautan terbuka" ( wiki.c2.com/?AssertionsAsDefensiveProgramming ) . Saya pribadi tetap mengaktifkannya di build rilis secara default. Sayangnya, praktik ini tidak terlalu umum di dunia C ++, tapi setidaknya veteran C ++ terkenal James Kanze selalu berdebat untuk mempertahankan pernyataan tetap dalam susunan rilis: stackoverflow.com/a/12162195/3313064
Christian Hackl

Jawaban:

20

Klasik assertadalah alat dari pustaka C standar lama, bukan dari C ++. Ini masih tersedia di C ++, setidaknya untuk alasan kompatibilitas ke belakang.

Saya tidak punya garis waktu yang tepat untuk lib standar C yang ada, tetapi saya cukup yakin asserttersedia tidak lama setelah waktu ketika K&R C mulai beroperasi (sekitar 1978). Dalam C klasik, untuk menulis program yang kuat, menambahkan tes pointer NULL dan memeriksa batas array harus dilakukan jauh lebih sering daripada di C ++. Gross dari tes pointer NULL dapat dihindari dengan menggunakan referensi dan / atau smart pointer daripada pointer, dan dengan menggunakan std::vector, pemeriksaan batas array seringkali tidak diperlukan. Selain itu, kinerja yang mencapai pada 1980 jelas jauh lebih penting daripada hari ini. Jadi saya pikir itu sangat mungkin alasan mengapa "menegaskan" dirancang untuk menjadi aktif hanya dalam membangun debug secara default.

Selain itu, untuk penanganan kesalahan nyata dalam kode produksi, suatu fungsi yang hanya menguji beberapa kondisi atau invarian, dan crash program jika kondisi tidak terpenuhi, dalam banyak kasus tidak cukup fleksibel. Untuk debugging mungkin ok, karena orang yang menjalankan program dan mengamati kesalahan biasanya memiliki debugger untuk menganalisis apa yang terjadi. Untuk kode produksi, bagaimanapun, solusi yang masuk akal perlu menjadi fungsi atau mekanisme yang

  • menguji beberapa kondisi (dan menghentikan eksekusi pada lingkup di mana kondisi gagal)

  • memberikan pesan kesalahan yang jelas jika kondisi tidak berlaku

  • memungkinkan lingkup luar untuk mengambil pesan kesalahan dan mengeluarkannya ke saluran komunikasi tertentu. Saluran ini mungkin berupa stderr, file log standar, kotak pesan dalam program GUI, kesalahan umum menangani panggilan balik, saluran kesalahan yang didukung jaringan, atau apa pun yang paling cocok dengan perangkat lunak tertentu.

  • memungkinkan lingkup luar pada basis per-kasus untuk memutuskan apakah program harus berakhir dengan anggun, atau jika harus dilanjutkan.

(Tentu saja, ada juga situasi di mana mengakhiri program segera jika kondisi yang tidak terpenuhi adalah satu-satunya pilihan yang masuk akal, tetapi dalam kasus seperti itu, itu harus terjadi dalam rilis rilis juga, bukan hanya di debug build).

Karena klasik asserttidak menyediakan fitur ini, itu tidak cocok untuk rilis build, anggap build rilis adalah apa yang digunakan seseorang untuk diproduksi.

Sekarang Anda dapat bertanya mengapa tidak ada fungsi atau mekanisme seperti itu di lib standar C yang menyediakan fleksibilitas semacam ini. Sebenarnya, di C ++, ada mekanisme standar yang memiliki semua fitur ini (dan banyak lagi), dan Anda tahu itu: itu disebut pengecualian .

Namun, dalam C, sulit untuk mengimplementasikan mekanisme standar tujuan umum yang baik untuk penanganan kesalahan dengan semua fitur yang disebutkan karena kurangnya pengecualian sebagai bagian dari bahasa pemrograman. Jadi sebagian besar program C memiliki mekanisme penanganan kesalahan sendiri dengan kode kembali, atau "goto", atau "lompatan panjang", atau campuran dari itu. Ini sering merupakan solusi pragmatis yang sesuai dengan jenis program tertentu, tetapi tidak "cukup tujuan umum" untuk masuk ke perpustakaan standar C.

Doc Brown
sumber
Ah, benar-benar lupa tentang warisan C (setelah semua itu #include <cassert>). Itu benar-benar masuk akal sekarang.
Tidak ada yang
1
Saya sepenuhnya tidak setuju dengan seluruh jawaban ini. Melontarkan pengecualian ketika aplikasi Anda telah menemukan bahwa kode sumbernya bermasalah adalah pilihan terakhir dalam bahasa seperti Java yang tidak dapat berbuat lebih baik, tetapi dalam C ++, tentu saja mengakhiri program dengan segera dalam situasi seperti itu. Anda tidak ingin tumpukan unwinding menyebabkan setiap operasi lebih lanjut akan dieksekusi pada saat itu. Program kereta mungkin menulis data yang mungkin rusak ke file, basis data atau soket jaringan. Hanya crash ASAP (dan mungkin proses restart oleh mekanisme eksternal).
Christian Hackl
1
@ChristianHackl: Saya setuju ada situasi di mana penghentian program adalah satu-satunya pilihan, tetapi kemudian harus terjadi dalam mode rilis juga, dan merupakan assertalat yang salah untuk ini juga (melemparkan pengecualian mungkin juga merupakan reaksi yang salah ). Namun, saya cukup yakin pada kenyataannya assertjuga digunakan untuk banyak tes di C di mana di C ++ orang akan menggunakan pengecualian hari ini.
Doc Brown
15

Jika Anda mendapati diri Anda ingin menegaskan diaktifkan dalam rilis Anda telah meminta menegaskan untuk melakukan pekerjaan yang salah.

Maksud dari penegasan adalah bahwa mereka tidak diaktifkan dalam rilis. Hal ini memungkinkan untuk menguji invarian selama pengembangan dengan kode yang seharusnya harus kode perancah. Kode yang harus dihapus sebelum dirilis.

Jika Anda memiliki sesuatu yang Anda rasa harus diuji bahkan selama rilis kemudian tulis kode yang mengujinya. The If throwmembangun bekerja sangat baik. Jika Anda ingin mengatakan sesuatu yang berbeda dari lemparan lainnya cukup gunakan pengecualian deskriptif yang mengatakan apa yang ingin Anda katakan.

Bukannya Anda tidak bisa mengubah cara Anda menggunakan pernyataan. Melakukan hal itu tidak memberi Anda sesuatu yang berguna, bertentangan dengan harapan, dan membuat Anda tidak memiliki cara yang bersih untuk melakukan apa yang dimaksudkan untuk dilakukan. Tambahkan tes yang tidak aktif dalam rilis.

Saya tidak berbicara tentang implementasi spesifik penegasan tetapi konsep penegasan. Saya tidak ingin menyalahgunakan pernyataan atau membingungkan pembaca. Saya bermaksud bertanya mengapa ini terjadi sejak awal. Mengapa tidak ada release_assert tambahan? Apakah tidak perlu untuk itu? Apa alasan di balik pernyataan dilumpuhkan dalam rilis? - Tidak ada seorang pun

Mengapa tidak ada relase_assert? Terus terang karena menegaskan tidak cukup baik untuk produksi. Ya ada kebutuhan tetapi tidak ada yang memenuhi kebutuhan itu dengan baik. Oh tentu Anda bisa mendesain sendiri. Secara otomatis fungsi throwIf Anda hanya membutuhkan bool dan pengecualian untuk melempar. Dan itu mungkin sesuai dengan kebutuhan Anda. Tapi Anda benar-benar membatasi desain. Itu sebabnya tidak mengejutkan saya bahwa tidak ada yang dipanggang dalam pernyataan seperti melempar sistem pengecualian di perpustakaan bahasa Anda. Jelas bukan karena Anda tidak bisa melakukannya. Yang lainnya punya . Tetapi berurusan dengan kasus di mana kesalahan terjadi adalah 80% pekerjaan untuk sebagian besar program. Dan sejauh ini belum ada yang menunjukkan kepada kami ukuran yang bagus untuk semua solusi. Berurusan secara efektif dengan kasus-kasus ini dapat menjadi rumit. Jika kita memiliki sistem release_assert kalengan yang tidak memenuhi kebutuhan kita, saya pikir itu akan melakukan lebih banyak kerusakan daripada bagus. Anda meminta abstraksi yang bagus yang berarti Anda tidak perlu memikirkan masalah ini. Saya juga menginginkannya, tetapi sepertinya belum sampai.

Mengapa pernyataan dinonaktifkan dalam rilis? Pemberitahuan dibuat pada puncak usia kode perancah. Kode kami harus dihapus karena tahu kami tidak menginginkannya dalam produksi tetapi tahu kami ingin menjalankan dalam pengembangan untuk membantu kami menemukan bug. Pemberitahuan adalah alternatif yang lebih bersih dari if (DEBUG)pola yang memungkinkan kami meninggalkan kode tetapi menonaktifkannya. Ini sebelum pengujian unit berjalan sebagai cara utama untuk memisahkan kode pengujian dari kode produksi. Asserts masih digunakan hari ini, bahkan oleh penguji unit ahli, baik untuk membuat harapan yang jelas dan untuk menutupi kasus bahwa mereka masih melakukan lebih baik daripada pengujian unit.

Mengapa tidak membiarkan kode debug dalam produksi saja? Karena kode produksi perlu tidak mempermalukan perusahaan, tidak memformat hard drive, tidak merusak database, dan tidak mengirim presiden mengancam email. Singkatnya itu bagus untuk dapat menulis kode debugging di tempat yang aman di mana Anda tidak perlu khawatir tentang itu.

candied_orange
sumber
2
Saya kira pertanyaan saya adalah: Mengapa kode ini harus dihapus sebelum dirilis? Pemeriksaan tidak banyak menguras kinerja dan jika mereka gagal pasti ada masalah yang saya lebih suka pesan kesalahan yang lebih langsung tentang. Apa keuntungan menggunakan pengecualian dan bukannya tawaran menawarkan saya? Saya mungkin tidak ingin kode yang tidak terkait dapat melakukannya catch(...).
Tidak ada yang
2
@Nobody Keuntungan terbesar untuk tidak menggunakan pernyataan untuk kebutuhan yang tidak menegaskan adalah tidak membingungkan pembaca Anda. Jika Anda tidak ingin kode dinonaktifkan maka jangan gunakan idiom yang menandakan bahwa itu akan menjadi. Seburuk menggunakan if (DEBUG)untuk mengontrol sesuatu selain kode debugging. Optimalisasi mikro mungkin tidak ada artinya dalam kasus Anda. Tetapi idiom itu tidak boleh dirusak hanya karena Anda tidak membutuhkannya.
candied_orange
Saya pikir saya tidak membuat maksud saya cukup jelas. Saya tidak berbicara tentang implementasi spesifik asserttetapi konsep penegasan. Saya tidak ingin menyalahgunakan assertatau membingungkan pembaca. Saya bermaksud bertanya mengapa ini terjadi sejak awal. Mengapa tidak ada tambahan release_assert? Apakah tidak perlu untuk itu? Apa alasan di balik pernyataan dilumpuhkan dalam rilis?
Tidak ada yang
2
You're asking for a good abstraction.Saya tidak yakin tentang hal itu. Saya terutama ingin berurusan dengan masalah di mana tidak ada pemulihan dan itu seharusnya tidak pernah terjadi. Ambil ini bersama-sama dengan Because production code needs to [...] not format the hard drive [...]dan saya akan mengatakan untuk kasus-kasus di mana invarian rusak, saya akan menegaskan di UB kapan saja.
Tidak ada yang
1
@Tidak ada "masalah di mana tidak ada pemulihan" dapat diatasi dengan melemparkan pengecualian dan tidak menangkapnya. Anda dapat mencatat bahwa hal itu seharusnya tidak pernah terjadi dalam teks pesan pengecualian.
candied_orange
4

Pernyataan adalah alat debugging , bukan teknik pemrograman defensif. Jika Anda ingin melakukan validasi dalam semua keadaan, maka lakukan validasi dengan menulis kondisional - atau buat makro Anda sendiri untuk mengurangi pelat tungku.

amon
sumber
4

assertadalah bentuk dokumentasi, seperti komentar. Seperti komentar, Anda biasanya tidak mengirimkannya ke pelanggan - mereka tidak termasuk dalam kode rilis.

Tetapi masalah dengan komentar adalah bahwa mereka bisa menjadi ketinggalan zaman, namun mereka tertinggal. Itu sebabnya pernyataan bagus - mereka diperiksa dalam mode debug. Ketika pernyataan tersebut menjadi usang, Anda dengan cepat menemukannya, dan masih akan tahu bagaimana cara memperbaiki pernyataan tersebut. Komentar itu yang sudah ketinggalan zaman 3 tahun lalu? Tebakan siapa saja.

MSalters
sumber
1
Jadi batalkan invarian yang gagal sebelum sesuatu yang buruk terjadi, bukankah ada use case yang valid?
Tidak seorang pun
3

Jika Anda tidak ingin "pernyataan" dimatikan, jangan ragu untuk menulis fungsi sederhana yang memiliki efek serupa:

void fail_if(bool b) {if(!b) std::abort();}

Artinya, assertadalah untuk tes di mana Anda lakukan ingin mereka pergi dalam produk dikirimkan. Jika Anda ingin tes itu menjadi bagian dari perilaku program yang ditentukan, assertadalah alat yang salah.

Nicol Bolas
sumber
1
Saya sadar akan hal ini. Pertanyaannya lebih pada garis mengapa default adalah seperti itu.
Tidak ada yang
3

Tidak ada gunanya untuk berdebat apa yang harus dilakukan, itu melakukan apa yang dilakukannya. Jika Anda menginginkan sesuatu yang berbeda, tulis fungsi Anda sendiri. Sebagai contoh, saya memiliki Assert yang berhenti di debugger atau tidak melakukan apa-apa, saya memiliki AssertFatal yang akan membuat crash aplikasi, saya memiliki fungsi bool yang Ditegaskan dan AssertionFailed yang menegaskan dan mengembalikan hasilnya sehingga saya dapat menegaskan dan menangani situasi.

Untuk masalah yang tidak terduga, Anda perlu memutuskan apa cara terbaik bagi pengembang dan pengguna untuk menanganinya.

gnasher729
sumber
Bukan maksud saya untuk berdebat tentang apa yang harus dilakukan dengan tegas. Motivasi saya untuk pertanyaan ini adalah untuk menemukan alasan di balik pernyataan tidak berada dalam rilis karena saya akan menulis sendiri dan ingin mengumpulkan informasi tentang kasus penggunaan, harapan dan jebakan.
Tidak ada yang
Hm, bukankah verify(cond)cara normal untuk menegaskan dan mengembalikan hasilnya?
Deduplicator
1
Saya mengkode dalam Ruby, bukan C, tapi saya tidak setuju dengan sebagian besar jawaban di atas, karena menghentikan program dengan mencetak backtrace berfungsi dengan baik bagi saya sebagai teknik pemrograman defensif ....... komentar saya tidak cocok dengan ukuran komentar maksimum dalam karakter jadi saya menulis jawaban lain di bawah ini.
Nakilon
2

Seperti yang ditunjukkan orang lain, assertadalah semacam benteng pertahanan terakhir Anda terhadap kesalahan programmer yang seharusnya tidak pernah terjadi. Itu adalah pemeriksaan kewarasan yang diharapkan tidak akan gagal ke kiri dan ke kanan pada saat Anda mengirim.

Itu juga dirancang untuk dihilangkan dari rilis rilis stabil, untuk alasan apa pun yang mungkin berguna bagi pengembang: estetika, kinerja, apa pun yang mereka inginkan. Ini adalah bagian dari apa yang memisahkan debug build dari build rilis, dan menurut definisi build rilis tidak memiliki pernyataan seperti itu. Jadi ada subversi desain jika Anda ingin merilis analog "rilis build dengan pernyataan di tempat" yang akan menjadi upaya rilis rilis dengan _DEBUGdefinisi preprocessor dan tidak NDEBUGterdefinisi; ini sebenarnya bukan rilis build lagi.

Desainnya bahkan meluas ke perpustakaan standar. Sebagai contoh yang sangat mendasar di antara banyak, banyak implementasi std::vector::operator[]akan assertpemeriksaan kewarasan untuk memastikan Anda tidak memeriksa vektor di luar batas. Dan perpustakaan standar akan mulai berkinerja jauh, jauh lebih buruk jika Anda mengaktifkan pemeriksaan seperti itu dalam rilis rilis. Patokan vectormenggunakanoperator[]dan ctor pengisian dengan pernyataan seperti itu yang disertakan pada array dinamis lama yang biasa akan sering menunjukkan array dinamis menjadi jauh lebih cepat hingga Anda menonaktifkan pemeriksaan tersebut, sehingga mereka sering melakukan dampak kinerja jauh, jauh dari cara sepele. Pemeriksaan null pointer di sini dan pemeriksaan di luar batas sebenarnya dapat menjadi beban besar jika pemeriksaan seperti itu diterapkan jutaan kali pada setiap frame dalam loop kritis sebelum kode semudah mendereferensi pointer cerdas atau mengakses array.

Jadi, Anda kemungkinan besar menginginkan alat yang berbeda untuk pekerjaan itu dan yang tidak dirancang untuk dihilangkan dari rilis build jika Anda ingin rilis build yang melakukan pemeriksaan kewarasan seperti itu di bidang utama. Yang paling berguna yang saya temukan secara pribadi adalah logging. Dalam hal itu, ketika pengguna melaporkan bug, banyak hal menjadi lebih mudah jika mereka melampirkan log dan baris terakhir dari log memberi saya petunjuk besar tentang di mana bug itu terjadi dan apa yang mungkin terjadi. Kemudian, setelah mereproduksi langkah-langkah mereka dalam membangun debug, saya mungkin juga mendapatkan kegagalan pernyataan, dan bahwa kegagalan pernyataan lebih lanjut memberi saya petunjuk besar untuk merampingkan waktu saya. Namun karena logging relatif mahal, saya tidak menggunakannya untuk menerapkan pemeriksaan kewarasan tingkat sangat rendah seperti memastikan array tidak diakses di luar batas dalam struktur data generik.

Namun akhirnya, dan agak setuju dengan Anda, saya bisa melihat kasus yang masuk akal di mana Anda mungkin benar-benar ingin memberikan sesuatu kepada penguji seperti build debug selama pengujian alpha, misalnya, dengan sekelompok kecil penguji alfa yang, misalnya, menandatangani NDA . Di sana mungkin merampingkan pengujian alfa jika Anda memberikan sesuatu kepada penguji Anda selain versi rilis lengkap dengan beberapa info debug yang dilampirkan bersama dengan beberapa fitur debug / pengembangan seperti tes yang dapat dijalankan dan lebih banyak keluaran verbose saat mereka menjalankan perangkat lunak. Saya setidaknya melihat beberapa perusahaan game besar melakukan hal seperti itu untuk alpha. Tapi itu untuk sesuatu seperti pengujian alfa atau internal di mana Anda benar-benar mencoba untuk memberikan sesuatu kepada penguji selain dari rilis rilis. Jika Anda benar-benar mencoba mengirimkan rilis build, maka menurut definisi, seharusnya tidak_DEBUG didefinisikan atau yang benar-benar membingungkan perbedaan antara "debug" dan "rilis" build.

Mengapa kode ini harus dihapus sebelum dirilis? Pemeriksaan tidak banyak menguras kinerja dan jika mereka gagal pasti ada masalah yang saya lebih suka pesan kesalahan yang lebih langsung tentang.

Seperti yang ditunjukkan di atas, cek tidak selalu sepele dari sudut pandang kinerja. Banyak yang mungkin sepele, tetapi sekali lagi, bahkan lib standar menggunakannya dan itu dapat mempengaruhi kinerja dengan cara yang tidak dapat diterima bagi banyak orang dalam banyak kasus jika, katakanlah, akses acak melintasi std::vectormemakan waktu 4 kali lebih lama dalam apa yang seharusnya menjadi rilis rilis yang dioptimalkan karena batas-batasnya memeriksa yang seharusnya tidak pernah gagal.

Dalam mantan tim kami benar-benar harus membuat matriks dan pustaka vektor kami mengecualikan beberapa pernyataan di jalur kritis tertentu hanya untuk membuat debug membangun berjalan lebih cepat, karena pernyataan itu memperlambat operasi matematika dengan lebih dari urutan besarnya ke titik di mana itu mulai mengharuskan kita untuk menunggu 15 menit sebelum kita bahkan dapat melacak kode yang diinginkan. Rekan kerja saya sebenarnya ingin menghapus sajaassertslangsung karena mereka menemukan bahwa melakukan itu membuat perbedaan besar. Sebaliknya, kami memutuskan untuk membuat jalur debug kritis untuk menghindarinya. Ketika kami membuat jalur kritis tersebut menggunakan data vektor / matriks secara langsung tanpa melalui pemeriksaan batas, waktu yang diperlukan untuk melakukan operasi penuh (yang mencakup lebih dari sekadar vektor / matriks matematika) berkurang dari menit ke detik. Jadi itu adalah kasus yang ekstrem tetapi yang pasti penegasan tidak selalu diabaikan dari sudut pandang kinerja, bahkan tidak dekat.

Tapi juga hanya caranya saja assertsyang dirancang. Jika mereka tidak memiliki dampak kinerja yang sangat besar di seluruh papan, maka saya mungkin lebih suka jika mereka dirancang sebagai lebih dari fitur membangun debug atau kita dapat menggunakan vector::atyang mencakup batas memeriksa bahkan dalam rilis rilis membangun dan membuang di luar batas akses, misalnya (belum dengan hit kinerja besar). Tetapi saat ini saya menemukan desain mereka jauh lebih berguna, mengingat dampak kinerja besar mereka dalam kasus saya, sebagai fitur debug-build-only yang dihilangkan ketika NDEBUGdidefinisikan. Untuk kasus-kasus yang pernah saya tangani, itu membuat perbedaan besar untuk build rilis untuk mengecualikan cek kewarasan yang seharusnya tidak pernah benar-benar gagal sejak awal.

vector::at vs. vector::operator[]

Saya pikir perbedaan dari dua metode ini menjadi inti dari ini juga sebagai alternatif: pengecualian. vector::operator[]implementasi biasanya assertuntuk memastikan bahwa akses di luar batas akan memicu kesalahan yang mudah direproduksi ketika mencoba mengakses vektor di luar batas. Tetapi pelaksana perpustakaan melakukan ini dengan asumsi bahwa tidak akan ada biaya sepeser pun dalam rilis rilis yang dioptimalkan.

Sementara vector::atitu disediakan yang selalu melakukan out of bounds memeriksa dan melempar bahkan dalam rilis rilis, tetapi memiliki penalti kinerja ke titik di mana saya sering melihat kode jauh lebih banyak menggunakan vector::operator[]daripada vector::at. Banyak desain C ++ menggemakan gagasan "membayar untuk apa yang Anda gunakan / butuhkan", dan banyak orang sering mendukung operator[], yang bahkan tidak repot-repot dengan batas-batas memeriksa membangun rilis, berdasarkan pada gagasan bahwa mereka tidak perlu batas memeriksa di rilis rilis dioptimalkan mereka. Tiba-tiba jika pernyataan diaktifkan dalam rilis build, kinerja keduanya akan identik, dan penggunaan vektor akan selalu lebih lambat dari array dinamis. Jadi sebagian besar dari desain dan manfaat pernyataan didasarkan pada gagasan bahwa mereka menjadi bebas dalam rilis rilis.

release_assert

Ini menarik setelah menemukan niat ini. Secara alami setiap kasus penggunaan akan berbeda, tetapi saya pikir saya akan menemukan beberapa kegunaan untuk release_assertyang melakukan pengecekan dan akan merusak perangkat lunak yang menampilkan nomor baris dan pesan kesalahan bahkan dalam versi rilis.

Untuk beberapa kasus yang tidak jelas dalam kasus saya di mana saya tidak ingin perangkat lunak pulih dengan anggun seperti jika ada pengecualian. Saya ingin crash bahkan dalam rilis dalam kasus-kasus sehingga pengguna dapat diberi nomor baris untuk melaporkan ketika perangkat lunak menemukan sesuatu yang seharusnya tidak pernah terjadi, masih dalam ranah kewarasan memeriksa kesalahan programmer, bukan kesalahan input eksternal seperti pengecualian, tetapi cukup murah untuk dilakukan tanpa khawatir tentang biaya dalam rilis.

Sebenarnya ada beberapa kasus di mana saya akan menemukan crash keras dengan nomor baris dan pesan kesalahan lebih baik untuk pulih dari pengecualian dilemparkan yang mungkin cukup murah untuk disimpan dalam rilis. Dan ada beberapa kasus di mana tidak mungkin untuk pulih dari pengecualian, seperti kesalahan yang ditemui saat mencoba memulihkan dari yang sudah ada. Di sana saya akan menemukan yang sempurna untuk release_assert(!"This should never, ever happen! The software failed to fail!");dan tentu saja itu akan menjadi murah karena cek akan dilakukan di dalam jalur yang luar biasa di tempat pertama dan tidak akan dikenakan biaya apa pun di jalur eksekusi normal.


sumber
Tujuan saya bukan untuk mengaktifkan pernyataan dalam kode pihak ketiga, lihat hasil edit yang menjelaskan. Kutipan dari komentar saya menghilangkan konteks dari pertanyaan saya: Additionally, the performance argument for me only counts when it is a measurable problem.Jadi idenya adalah memutuskan berdasarkan kasus per kasus kapan harus mengeluarkan pernyataan dari rilis.
Tidak ada yang
Salah satu masalah dengan komentar terakhir adalah bahwa pustaka standar menggunakan yang sama assertyang bergantung pada _DEBUG/NDEBUGdef preprocessor yang sama dengan apa yang Anda gunakan saat Anda menggunakan assertmakro. Jadi tidak ada cara untuk mengaktifkan pernyataan secara selektif hanya untuk satu bagian kode tanpa mengaktifkannya untuk lib standar juga, misalnya, dengan asumsi ia menggunakan lib standar.
Ada pengecekan STL iterator yang dapat Anda nonaktifkan secara terpisah yang menonaktifkan beberapa asersi, tetapi tidak semua.
Ini pada dasarnya adalah alat kasar, bukan granular, dan selalu dengan maksud desain yang dinonaktifkan dalam rilis, sehingga rekan tim Anda mungkin menggunakannya di area kritis dengan asumsi bahwa itu tidak akan mempengaruhi kinerja rilis, perpustakaan standar menggunakannya bahwa cara di area kritis, lib pihak ketiga lain menggunakannya seperti itu, dll. Dan ketika Anda menginginkan sesuatu, Anda dapat menonaktifkan / mengaktifkan satu kode secara spesifik dan bukan yang lain, kami kembali melihat lagi sesuatu selain assert.
vector::operator[]dan vector::at, misalnya, akan memiliki kinerja yang hampir sama jika pernyataan diaktifkan dalam rilis build, dan tidak akan ada gunanya menggunakan operator[]lagi dari sudut pandang kinerja karena itu tetap akan melakukan pemeriksaan batas.
2

Saya sedang mengkode dalam Ruby, bukan C / C ++, jadi saya tidak akan berbicara tentang perbedaan antara pernyataan dan pengecualian, tetapi saya ingin membicarakannya sebagai hal yang menghentikan runtime . Saya tidak setuju dengan sebagian besar jawaban di atas, karena menghentikan program dengan mencetak backtrace bekerja dengan baik bagi saya sebagai teknik pemrograman defensif.
Jika ada cara untuk menegaskan rutin (sama sekali tidak peduli bagaimana itu ditulis secara sintaksis dan jika kata "menegaskan" digunakan atau ada dalam bahasa pemrograman atau dsl sama sekali) untuk dipanggil yang berarti beberapa pekerjaan harus dilakukan dan produk segera kembali dari "dirilis, siap pakai" ke "perlu tambalan" - sekarang baik menulis ulang untuk penanganan pengecualian nyata atau memperbaiki bug yang menyebabkan data yang salah muncul.

Maksud saya Assert bukanlah hal yang harus Anda jalani dan sering hubungi - itu adalah sinyal berhenti yang menunjukkan bahwa Anda harus melakukan sesuatu untuk membuatnya tidak pernah terjadi lagi. Dan mengatakan bahwa "rilis build seharusnya tidak memiliki penegasan" seperti mengatakan "rilis build seharusnya tidak memiliki bug" - bung, hampir mustahil, atasi itu.
Atau pikirkan tentang mereka sebagai "gagal unittests yang dibuat dan dijalankan oleh pengguna akhir". Anda tidak dapat memprediksi semua hal yang akan dilakukan pengguna dengan program Anda, tetapi jika terlalu serius kesalahannya harus dihentikan - ini mirip dengan cara Anda membangun jaringan pipa - Anda menghentikan proses dan tidak menerbitkan, apakah Anda ? Penegasan memaksa pengguna untuk berhenti, melaporkan dan menunggu bantuan Anda.

Nakilon
sumber