Melontarkan Pengecualian dalam C ++ game DLL? Pro dan kontra


8

Apa pro dan kontra dari menggunakan Pengecualian di C ++ dalam kaitannya dengan pengembangan game.

Panduan gaya Google mengatakan bahwa mereka tidak menggunakan Pengecualian karena berbagai alasan. Apakah alasan yang sama berkaitan dengan pengembangan game?

Kami tidak menggunakan pengecualian C ++ ... - google-styleguide.googlecode.com

Beberapa masalah untuk dipikirkan.

  • Bagaimana hal itu berkaitan dengan pengembangan perpustakaan yang digunakan melalui banyak proyek.
  • Bagaimana pengaruhnya terhadap pengujian unit, pengujian integrasi, dll?
  • Apa yang dilakukan untuk menggunakan perpustakaan pihak ketiga.

3
Anehnya tidak ada yang punya pro
David Young

Jawaban:


7

Ada juga beberapa konsekuensi buruk untuk menggunakan pengecualian di C ++ jika Anda tidak tahu semua detail cara kerjanya. Karena banyak game ditulis dalam C ++ ini mungkin menjadi faktor.

Misalnya, dalam C ++ melempar pengecualian pada destruktor dapat memiliki konsekuensi serius. Ini dapat menyebabkan semua jenis perilaku dan crash yang tidak terdefinisi, karena Anda dapat terjebak dalam situasi di mana Anda memiliki lebih dari satu pengecualian aktif dalam penerbangan. (Lihat Butir # 8 dari Efektif C ++ untuk info lebih lanjut tentang ini.)


4
+1 untuk C ++ yang Efektif. Membaca edisi ke 3 saat kita bicara. Pengecualian baik-baik saja dengan saya, selama Anda sangat ketat tentang di mana Anda menggunakannya, di mana Anda menangkapnya dan Anda dapat menjamin bahwa setiap kode pihak ke-3 melakukan hal yang sama.
Anthony

9

Joel pada pandangan Pengecualian Perangkat Lunak.

Tampilan menarik saat ini tidak ada di salah satu jawaban,

Alasannya adalah bahwa saya menganggap pengecualian tidak lebih baik daripada "goto", dianggap berbahaya sejak 1960-an, karena mereka membuat lompatan tiba-tiba dari satu titik kode ke titik lainnya. Bahkan mereka secara signifikan lebih buruk daripada goto:

  1. Mereka tidak terlihat dalam kode sumber. Melihat sekumpulan kode, termasuk fungsi yang bisa atau tidak melempar pengecualian, tidak ada cara untuk melihat pengecualian mana yang dilempar dan dari mana. Ini berarti bahwa pemeriksaan kode yang teliti sekalipun tidak mengungkapkan potensi bug.

  2. Mereka membuat terlalu banyak kemungkinan titik keluar untuk suatu fungsi. Untuk menulis kode yang benar, Anda benar-benar harus memikirkan setiap jalur kode yang mungkin melalui fungsi Anda. Setiap kali Anda memanggil fungsi yang dapat menimbulkan pengecualian dan tidak langsung menangkapnya, Anda menciptakan peluang untuk bug kejutan yang disebabkan oleh fungsi yang diakhiri secara tiba-tiba, meninggalkan data dalam keadaan tidak konsisten, atau jalur kode lain yang tidak Anda temui. memikirkan tentang.

http://www.joelonsoftware.com/items/2003/10/13.html


Halleluja ... Referensi hebat. Saya memiliki keengganan dengan pengecualian untuk alasan ini, tetapi tampaknya ini adalah cara yang disukai untuk melakukan sesuatu saat ini. Bagus untuk melihat artikel yang bagus yang memberikan tampilan lain
Toad

1
+1 karena pengecualian memiliki risiko terlalu tinggi untuk gagal terlambat, yaitu saat game sudah dikirim.
martinpi

Saya sepenuhnya setuju dengan poin nomor 2. Saya tidak akan berperang melawan bahasa yang menggunakan pengecualian di seluruh, tetapi saya juga tidak menganggap mereka cara yang 'benar' untuk menangani kondisi kesalahan juga.
Kylotan

5

Pengecualian tidak didukung dan karenanya sangat tidak disarankan dalam setidaknya satu lingkungan pengembangan konsol modern.

Kebanyakan pengembang konsol yang saya tahu lebih suka untuk tidak menggunakannya karena penambahan overhead pada executable dan filosofi bahwa mereka hanya tidak diperlukan. RTTI dipandang dengan cara yang sama.


Konsol mana yang tidak didukung?
David Young

Saya mencoba untuk menjadi malu-malu karena NDA, meskipun sudah terungkap lebih khusus pada SO sebelumnya.
Dan Olson

2

Dari semua proyek yang saya kerjakan dalam game, tidak ada yang menggunakan pengecualian. Fungsi panggilan overhead adalah alasan utama. Seperti disebutkan sebelumnya, seperti RTTI, itu, di sebagian besar studio, bukan subjek diskusi. Bukan karena kebanyakan coders berasal dari latar belakang Java / akademisi, karena terus terang, kebanyakan tidak.
Itu tidak mempengaruhi pengujian unit karena Anda hanya menegaskan kondisi. Untuk perpustakaan, Anda lebih baik tanpa pengecualian karena Anda akan memaksanya pada semua orang yang menggunakannya.
Meskipun membuat beberapa situasi sedikit lebih sulit untuk ditangani, itu juga (imo) memaksa Anda untuk memiliki pengaturan yang lebih terkontrol, karena pengecualian dapat menyebabkan penyalahgunaan. Toleransi kesalahan bagus, tetapi tidak jika mengarah ke pengkodean yang ceroboh.
Pikiran Anda, ini berasal dari seseorang yang tidak pernah menggunakan penanganan perkecualian (baiklah, sekali atau dua kali dalam alat - itu tidak melakukannya untuk saya, saya kira itu yang Anda tumbuh dengan).


1
Anda benar, tetapi sayangnya daripada menggunakan penanganan kesalahan alternatif, kebanyakan programmer game hanya kembali ke crash atau mengembalikan NULL (yang mungkin akan menyebabkan crash). Kami belum menemukan pengganti yang layak.
tenpn

Returning NULL, atau kode kesalahan, adalah pengganti yang sangat masuk akal selama Anda memeriksa kode kembali juga.
JasonD

Ya tapi kebanyakan programmer tidak. Itulah poin saya.
tenpn

1
Ini masalah bahasa C ++ sebanyak apapun - jika Anda melihat baris kode yang mengatakan function();Anda tidak langsung tahu apakah kode kesalahan diabaikan atau tidak. Ini mungkin alasan mengapa bahasa yang memungkinkan Anda mengabaikan nilai pengembalian mencoba dan memaksa Anda untuk memilih pengecualian.
Kylotan

1
Penegakan argumen kesalahan tidak lagi menjadi masalah. Kode modern yang tidak menggunakan pengecualian bergantung pada Errorkelas yang ringan dan jika diabaikan menghasilkan pernyataan yang dipecat. Jadi itu benar-benar tidak dapat diabaikan tidak lebih dari pengecualian dapat diabaikan.
Samaursa 3-15

1

Yang paling penting bagi saya sebagai pengembang game profesional adalah bahwa pengecualian harus ditulis oleh orang-orang yang memahami bagaimana pengecualian bekerja (yang hanya ada sedikit di industri game) yang dibohongi oleh mereka juga, dan kode yang mendasari yang menjalankannya (yaitu kekacauan bercabang dan akan membunuh prosesor in-order Anda) harus ditulis oleh orang-orang yang menyediakan compiler / linker Anda. Masalahnya adalah mereka hanya memiliki sumber daya yang terbatas, sehingga mereka berkonsentrasi pada penulisan optimisasi yang lebih baik dan perbaikan untuk kode yang digunakan pengembang game ... yang biasanya bukan pengecualian. Jika Anda memiliki bug pengecualian pada perangkat keras modern dengan kompiler baru, siapa yang akan Anda hubungi? ahli pengecualian Anda yang menganggap semuanya baik-baik saja dengan kode, atau vendor yang mengatakan, ya, segera, kami

Secara inheren tidak ada yang salah dengan pengecualian, hanya saja itu tidak baik karena kenyataan menghalangi teori.


0

Bagi saya, penanganan pengecualian dapat menjadi hal yang bagus jika semuanya sesuai dengan RAII dan Anda mencadangkan pengecualian untuk jalur yang benar-benar luar biasa (mis: file korup sedang dibaca) dan Anda tidak perlu membuang batas-batas modul dan seluruh tim Anda ada di dalamnya. ......

Zero-Cost EH

Sebagian besar kompiler hari ini menerapkan penanganan pengecualian tanpa biaya yang dapat membuatnya lebih murah daripada secara manual bercabang pada kondisi kesalahan dalam jalur eksekusi reguler, meskipun sebagai gantinya membuat jalur luar biasa sangat mahal (ada juga beberapa kode mengasapi darinya, meskipun mungkin tidak lebih daripada jika Anda benar-benar menangani setiap kemungkinan kesalahan secara manual). Fakta bahwa melempar sangat mahal seharusnya tidak menjadi masalah meskipun jika pengecualian digunakan seperti yang dimaksudkan dalam C ++ (untuk keadaan yang benar-benar luar biasa yang seharusnya tidak terjadi dalam kondisi normal).

Pembalikan Efek Samping

Yang mengatakan, membuat semuanya sesuai dengan RAII jauh lebih mudah diucapkan daripada dilakukan. Sangat mudah untuk sumber daya lokal yang disimpan dalam fungsi untuk membungkusnya menjadi objek C ++ dengan destruktor yang membersihkan diri mereka sendiri, tetapi tidak mudah untuk menulis logika undo / rollback untuk setiap efek samping yang dapat terjadi pada seluruh perangkat lunak. Pertimbangkan betapa sulitnya untuk menulis sebuah wadah seperti std::vectoryang merupakan urutan akses acak paling sederhana untuk menjadi pengecualian-aman. Sekarang gandakan kesulitan itu di semua struktur data dari seluruh perangkat lunak berskala besar.

Sebagai contoh dasar, pertimbangkan pengecualian yang ditemui dalam proses memasukkan item ke grafik adegan. Untuk memulihkan dengan benar dari pengecualian itu, Anda mungkin perlu membatalkan perubahan dalam grafik adegan dan mengembalikan sistem kembali ke keadaan seolah-olah operasi tidak pernah terjadi. Itu berarti menghapus anak-anak yang dimasukkan ke grafik adegan secara otomatis saat menemukan pengecualian, mungkin dari penjaga lingkup.

Melakukan ini secara menyeluruh dalam perangkat lunak yang menyebabkan banyak efek samping dan berurusan dengan banyak kondisi persisten jauh lebih mudah dikatakan daripada dilakukan. Seringkali saya pikir solusi pragmatis adalah tidak repot dengan itu demi mendapatkan barang yang dikirim. Dengan basis kode yang tepat yang memiliki semacam sistem pusat undo dan struktur data yang persisten dan minimnya fungsi yang menyebabkan efek samping, Anda mungkin dapat mencapai pengecualian-keamanan di seluruh papan ... tetapi banyak hal-hal yang Anda butuhkan jika semua yang akan Anda lakukan adalah mencoba membuat kode Anda aman di mana-mana.

Ada sesuatu yang salah secara praktis di sana karena pembalikan efek samping secara konseptual adalah masalah yang sulit, tetapi itu menjadi lebih sulit di C ++. Sebenarnya lebih mudah terkadang membalikkan efek samping dalam C sebagai respons terhadap kode kesalahan daripada melakukannya sebagai respons terhadap pengecualian dalam C ++. Salah satu alasannya adalah karena titik tertentu dari fungsi apa pun berpotensi throwdalam C ++. Jika Anda mencoba untuk menulis sebuah wadah generik yang bekerja dengan tipe T, Anda bahkan tidak bisa mengantisipasi di mana titik keluar itu, karena apa pun yang terlibat Tbisa melempar. Bahkan membandingkan satu Tdengan yang lain untuk kesetaraan bisa melempar. Kode Anda harus menangani setiap kemungkinan , dan jumlah kemungkinan berlipat ganda secara eksponensial dalam C ++ sedangkan dalam C, jumlahnya jauh lebih sedikit.

Kurangnya Standar

Masalah lain dari penanganan pengecualian adalah kurangnya standar pusat. Ada beberapa seperti semoga semua orang setuju bahwa destruktor tidak boleh melempar karena rollback seharusnya tidak pernah gagal, tetapi itu hanya mencakup kasus patologis yang umumnya akan merusak perangkat lunak jika Anda melanggar aturan.

Seharusnya ada beberapa standar yang lebih masuk akal seperti tidak pernah membuang dari operator pembanding (semua fungsi yang secara logika tidak dapat diubah tidak boleh dilempar), tidak pernah membuang dari pemindah kode, dll. Jaminan semacam itu akan membuat jauh lebih mudah untuk menulis kode pengecualian-aman, tapi kami tidak punya jaminan seperti itu. Kita harus pergi dengan aturan bahwa segala sesuatu bisa dilemparkan - jika tidak sekarang, maka mungkin di masa depan.

Lebih buruk lagi, orang-orang dari latar belakang bahasa yang berbeda memandang pengecualian dengan sangat berbeda. Dalam Python, mereka benar-benar memiliki filosofi "lompatan sebelum Anda melihat" yang melibatkan pemicu pengecualian dalam jalur eksekusi reguler! Ketika Anda kemudian mendapatkan pengembang seperti itu menulis kode C ++, Anda bisa melihat pengecualian dilemparkan ke kiri dan kanan untuk hal-hal yang tidak benar-benar luar biasa, dan menangani semuanya bisa menjadi mimpi buruk jika Anda mencoba menulis kode generik, misalnya

Penanganan Kesalahan

Penanganan kesalahan memiliki kelemahan yang harus Anda periksa untuk setiap kemungkinan kesalahan yang bisa terjadi. Tapi itu memang memiliki satu keuntungan yang kadang-kadang Anda dapat semacam memeriksa kesalahan sedikit terlambat di belakang, seperti glGetError, untuk secara signifikan mengurangi titik keluar dalam suatu fungsi serta membuatnya eksplisit. Kadang-kadang Anda dapat terus menjalankan kode sedikit lebih lama sebelum memeriksa dan menyebarkan dan memulihkan dari kesalahan, dan kadang-kadang itu benar-benar bisa lebih mudah daripada penanganan pengecualian (terutama dengan pembalikan efek samping). Tapi Anda mungkin tidak terlalu repot dengan kesalahan baik dalam permainan, mungkin hanya mematikan permainan dengan pesan jika Anda menemukan hal-hal seperti file yang rusak, kehabisan memori, dll.

Bagaimana hal itu berkaitan dengan pengembangan perpustakaan yang digunakan melalui banyak proyek.

Bagian buruk dari pengecualian dalam konteks DLL adalah bahwa Anda tidak dapat dengan aman membuang pengecualian dari satu modul ke modul lainnya kecuali Anda dapat menjamin bahwa mereka dibangun oleh kompilator yang sama, gunakan pengaturan yang sama, dll. Jadi jika Anda menulis seperti arsitektur plugin dimaksudkan agar orang dapat digunakan dari semua jenis kompiler dan mungkin bahkan dari berbagai bahasa seperti Lua, C, C #, Java, dll., seringkali pengecualian mulai menjadi gangguan besar karena Anda harus menelan setiap pengecualian dan menerjemahkannya ke kesalahan kode tetap di semua tempat.

Apa yang dilakukan untuk menggunakan perpustakaan pihak ketiga.

Jika itu adalah dylib, maka mereka tidak dapat melempar pengecualian dengan aman karena alasan yang disebutkan di atas. Mereka harus menggunakan kode kesalahan yang juga berarti Anda, menggunakan perpustakaan, harus terus-menerus memeriksa kode kesalahan. Mereka dapat membungkus dylib mereka dengan lilitan pembungkus C ++ yang terhubung secara statis yang Anda buat sendiri yang menerjemahkan kode kesalahan dari dylib ke dalam pengecualian yang dilemparkan (pada dasarnya melemparkan dari biner ke biner Anda). Secara umum saya pikir sebagian besar lib pihak ketiga bahkan tidak perlu repot dan hanya menempel pada kode kesalahan jika mereka menggunakan dylibs / shared libs.

Bagaimana pengaruhnya terhadap pengujian unit, pengujian integrasi, dll?

Saya biasanya tidak menemukan tim yang menguji jalur kesalahan / luar biasa dari kode mereka. Saya biasa melakukannya dan benar-benar memiliki kode yang dapat pulih dengan baik bad_allockarena saya ingin membuat kode saya sangat kuat, tetapi tidak terlalu membantu ketika saya satu-satunya yang melakukannya dalam sebuah tim. Pada akhirnya saya berhenti mengganggu. Saya membayangkan untuk perangkat lunak mission-critical yang mungkin diperiksa seluruh tim untuk memastikan kode mereka pulih dengan baik dari pengecualian dan kesalahan dalam semua kasus yang mungkin, tetapi itu banyak waktu untuk berinvestasi. Waktu masuk akal dalam perangkat lunak mission-critical tetapi mungkin bukan permainan.

Cinta / Benci

Pengecualian juga merupakan fitur cinta / benci saya dari C ++ - salah satu fitur yang paling berdampak besar dalam bahasa menjadikan RAII lebih seperti persyaratan daripada kenyamanan, karena itu mengubah cara Anda harus menulis kode secara terbalik dari, katakanlah , C untuk menangani fakta bahwa setiap baris kode tertentu dapat menjadi titik keluar implisit untuk suatu fungsi. Terkadang saya hampir berharap bahasa tersebut tidak memiliki pengecualian. Tetapi ada saat-saat ketika saya menemukan pengecualian benar-benar berguna, tetapi mereka terlalu banyak faktor yang mendominasi bagi saya apakah saya memilih untuk menulis sepotong kode dalam C atau C ++.

Mereka akan secara eksponensial lebih berguna bagi saya jika komite standar benar-benar fokus pada penetapan standar ABI yang memungkinkan pengecualian untuk secara aman dilemparkan ke seluruh modul, setidaknya dari kode C ++ ke C ++. Akan luar biasa jika Anda bisa dengan aman menggunakan berbagai bahasa, meskipun itu semacam mimpi pipa. Hal lain yang akan membuat pengecualian secara eksponensial lebih bermanfaat bagi saya adalah jika noexceptfungsi benar-benar menyebabkan kesalahan kompiler jika mereka mencoba memanggil fungsi apa pun yang bisa melempar, bukan hanya memanggil std::terminateuntuk menemukan pengecualian. Itu di sebelah tidak berguna (mungkin lebih baik menyebutnya icantexceptbukan noexcept). Akan jauh lebih mudah untuk memastikan bahwa semua destruktor aman dari pengecualian, misalnya, jikanoexceptsebenarnya menyebabkan kesalahan kompilator jika destructor melakukan apa saja yang bisa melempar. Jika itu terlalu mahal untuk ditentukan secara menyeluruh pada waktu kompilasi, maka buat saja noexceptfungsinya (yang seharusnya dilakukan oleh semua destruktor) hanya diperbolehkan untuk memanggil noexceptfungsi / operator, dan menjadikannya kesalahan kompiler untuk membuang dari salah satu dari mereka.


-2

Sejauh yang saya lihat ada dua alasan utama itu tidak digunakan secara tradisional dalam pengembangan game:

  • Sebagian besar programmer game adalah lulusan, yang belajar di Java dan Haskell, atau programmer C, yang tidak memiliki pengalaman pengecualian. Karenanya mereka cukup takut pada mereka dan tidak tahu bagaimana menggunakannya dengan benar.
  • Tumpukan yang berliku ketika pengecualian dilemparkan memiliki implikasi kinerja (meskipun ini adalah jenis kebijaksanaan yang diterima, dan beberapa referensi akan bagus).

3
Pemrogram Java tidak tahu cara menggunakan Pengecualian? Java didasarkan pada penanganan eksepsi. Bahkan pemrogram Java yang paling dasar pun memahami blok Coba, Tangkap, Akhirnya. Anda benar-benar tidak dapat memprogram di Jawa tanpa melempar Pengecualian atau menanganinya.
David Young

4
Lebih dari itu hanya menumpuk gulungan. Pengecualian menambahkan overhead untuk setiap panggilan fungsi. codeproject.com/KB/cpp/exceptionhandler.aspx
Jonathan Fischoff

@ David Young Saya berbicara lebih banyak tentang jaminan yang kuat / lemah, dan sisi yang lebih kompleks dalam menggunakan pengecualian, bahwa lulusan universitas mungkin tidak berpengalaman. Untuk menambah itu, banyak universitas jauh lebih tertarik pada bagaimana Anda menyusun OOP Anda sehingga kurang menekankan pada belajar bagaimana menggunakan pengecualian Java dengan benar. Tapi kau benar, aku tidak jelas.
tenpn

Juga, mungkin "takut" adalah kata yang salah. :)
tenpn

3
Saya akan mengatakan mereka sering kebalikan dari "takut" - jika Anda tidak memiliki aturan yang ketat "tidak ada pengecualian, tidak ada pengecualian", lulusan Jawa baru-baru ini akan lebih sering melemparkan pengecualian daripada kembali, seperti halnya programmer C pemula gunakan goto dan break daripada menulis invarian loop yang baik.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.