C ++ menangkap semua pengecualian

244

Apakah ada c ++ yang setara dengan Java

try {
    ...
}
catch (Throwable t) {
    ...
}

Saya mencoba men-debug kode Java / jni yang memanggil fungsi windows asli dan mesin virtual terus macet. Kode asli muncul dengan baik dalam pengujian unit dan hanya tampaknya macet ketika dipanggil melalui jni. Sebuah mekanisme penangkapan pengecualian generik akan terbukti sangat berguna.

Obediah Stane
sumber
2
Perhatikan bahwa sebagian besar kerusakan tidak disebabkan oleh pengecualian di C ++. Anda dapat menangkap semua pengecualian, tetapi itu tidak akan mencegah banyak crash.
Mooing Duck

Jawaban:

335
try{
    // ...
} catch (...) {
    // ...
}

akan menangkap semua pengecualian C ++, tetapi harus dianggap sebagai desain yang buruk. Anda dapat menggunakan mekanisme current_exception baru c ++ 11, tetapi jika Anda tidak memiliki kemampuan untuk menggunakan c ++ 11 (sistem kode lama yang membutuhkan penulisan ulang), maka Anda tidak memiliki penunjuk pengecualian bernama yang akan digunakan untuk mendapatkan pesan atau nama . Anda mungkin ingin menambahkan klausa tangkapan terpisah untuk berbagai pengecualian yang dapat Anda tangkap, dan hanya menangkap semua yang ada di bagian bawah untuk merekam pengecualian yang tidak terduga. Misalnya:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}
Greg D
sumber
68
Ini adalah praktik yang baik untuk menangkap pengecualian dengan referensi const. Seperti dalam: catch (std :: exception const & ex) {/ * ... * /}
coryan
12
@coryan: Mengapa praktik yang baik untuk ditangkap dengan referensi const?
Tim MB
19
Menghindari salinan yang tidak perlu adalah salah satu manfaatnya.
Greg D
21
-1: saran bahwa ini akan "menangkap semua pengecualian dalam C ++" adalah menyesatkan. Coba buat kesalahan pembagian dengan nol di dalam blok try. Anda akan melihat bahwa itu akan menghasilkan pengecualian yang tidak ditangkap, namun kode jelas dalam C ++. Akan lebih bermanfaat untuk menyatakan bahwa ini akan "menangkap semua pengecualian C ++" dan kemudian menambahkan beberapa pengecualian terstruktur pada catatan tentang kegunaan terbatas.
omatai
42
@ Tomatai: Tetap, ini akan menangkap semua pengecualian C ++. Pembagian dengan nol adalah perilaku yang tidak terdefinisi dan tidak menghasilkan pengecualian C ++.
Mooing Duck
151

Seseorang harus menambahkan bahwa seseorang tidak dapat menangkap "crash" dalam kode C ++. Mereka tidak melempar pengecualian, tetapi lakukan apa saja yang mereka suka. Ketika Anda melihat program macet karena mengatakan null-pointer dereference, itu melakukan perilaku yang tidak ditentukan. Tidak ada std::null_pointer_exception. Mencoba menangkap pengecualian tidak akan membantu di sana.

Hanya untuk kasus seseorang membaca utas ini dan berpikir dia bisa mendapatkan penyebab crash program. Debugger seperti gdb harus digunakan sebagai gantinya.

Johannes Schaub - litb
sumber
4
Nah, seperti yang ditunjukkan Shy, dimungkinkan dengan kompiler VC. Itu bukan ide yang baik, tetapi itu mungkin.
Shog9
7
ya dengan SEH. tetapi tidak dengan teknik standar c ++ ++ yang waras :) baik jika Anda tetap menggunakan windows, Anda hampir dapat melakukan semuanya :)
Johannes Schaub - litb
1
Mmm ... terima kasih atas berita gembira ini. Saya telah mencari jawaban mengapa pengecualian null-pointer saya tidak tertangkap!
Dalin Seivewright
10
Anda dapat menangkap segfault dengan SEH pada Windows dan memberi sinyal (2) / sigaction (2) pada sistem POSIX, yang mencakup sebagian besar sistem yang digunakan saat ini, tetapi seperti penanganan pengecualian, ini bukan sesuatu yang harus digunakan untuk kontrol aliran normal. Ini lebih dari "melakukan sesuatu yang berguna sebelum mati."
Adam Rosenfield
1
@AdamRosenfield sampai Anda menerapkan try { .. } catch(...) { ... }untuk menangkap menggunakan sinyal / sigaction, saya tidak akan menyebutnya "menangkap" :) Jika dalam penangan sinyal, relatif sulit bagi programmer untuk mengetahui di mana dalam kode crash terjadi (saya sedang berbicara tentang mendeteksi secara programatik), dibandingkan dengan mencoba / menangkap.
Johannes Schaub - litb
72

Ini adalah cara Anda dapat merekayasa balik tipe pengecualian dari dalam catch(...)jika perlu (mungkin berguna saat menangkap yang tidak diketahui dari perpustakaan pihak ketiga) dengan GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

dan jika Anda mampu menggunakan Boost Anda dapat membuat bagian tangkapan Anda lebih sederhana (di luar) dan berpotensi lintas platform

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}
bobah
sumber
58
try {
   // ...
} catch (...) {
   // ...
}

Perhatikan bahwa bagian ...dalam catchadalah elipsis nyata, yaitu. tiga titik.

Namun, karena pengecualian C ++ tidak selalu merupakan subkelas dari Exceptionkelas dasar , tidak ada cara untuk benar-benar melihat variabel pengecualian yang dilemparkan saat menggunakan konstruksi ini.

Greg Hewgill
sumber
24
Di C ++ 11 ada: coba {std :: string (). At (1); // ini menghasilkan tangkapan std :: out_of_range} (...) {eptr = std :: current_exception (); // capture}
Mohammad Alaggan
2
@ bfontaine: Ya, tapi saya mengatakan bahwa untuk membedakan catchspecifier dari placeholder kode yang ada di komentar ( // ...) yang jelas bukan sintaks C ++.
Greg Hewgill
1
@GregHewgill: ya, itu hanya tipografi nitpicking.
bfontaine
1
@ Bfontaine: Cukup adil. :)
Greg Hewgill
44

tidak mungkin (dalam C ++) untuk menangkap semua pengecualian dengan cara yang portabel. Ini karena beberapa pengecualian bukan pengecualian dalam konteks C ++. Ini termasuk hal-hal seperti pembagian dengan kesalahan nol dan lain-lain. Dimungkinkan untuk meretas dan dengan demikian mendapatkan kemampuan untuk melemparkan pengecualian ketika kesalahan ini terjadi, tetapi itu tidak mudah dilakukan dan tentu saja tidak mudah untuk mendapatkan yang benar dengan cara yang portabel.

Jika Anda ingin menangkap semua pengecualian STL, Anda dapat melakukannya

try { ... } catch( const std::exception &e) { ... }

Yang akan memungkinkan Anda menggunakan e.what(), yang akan mengembalikan const char*, yang dapat memberi tahu Anda lebih banyak tentang pengecualian itu sendiri. Ini adalah konstruk yang menyerupai konstruk Jawa, yang paling sering Anda tanyakan.

Ini tidak akan membantu Anda jika seseorang cukup bodoh untuk melemparkan pengecualian yang tidak diwarisi darinya std::exception.

Lebih jelas
sumber
2
mengapa ini tidak ada di atas?
Ivan Sanz-Carasa
31

Singkatnya, gunakan catch(...). Namun, catatan yang catch(...)dimaksudkan untuk digunakan bersama dengan throw;dasarnya:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

Ini adalah cara yang tepat untuk digunakan catch(...).

Mellester
sumber
6
lebih baik menggunakan RAII untuk manajemen memori yang secara otomatis menangani situasi pengecualian ini.
paykoob
1
@paykoob Bagaimana cara menangani kasus di mana Anda mengatur untuk membuat foo baru tetapi gagal pada bilah. Atau ketika konstruktor bilah mencoba membuka file tetapi gagal dan karena itu melempar. maka Anda mungkin berakhir dengan foo
menggantung
2
@ MelleSterk Bukankah tumpukan masih bisa dibersihkan dalam kasus itu, yang akan menjalankan Foodestruktor? Saya pikir itulah inti dari RAII. Namun, jika Anda memerlukan pointer ke Foodaripada hanya membuat Foodi stack, maka Anda harus membungkus pointer di sesuatu yang lain yang dinyatakan pada stack.
reirab
ya otomatis foo = std :: make_unique <Foo> (); bilah otomatis = std :: make_unique <Bar> (); // adalah pengecualian aman dan tidak akan bocor, tidak ada tangkapan (...) diperlukan
paulm
Jawaban ini layak dipilih jika hanya untuk diskusi dimulai :)
Cristik
21

adalah mungkin untuk melakukan ini dengan menulis:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

Tetapi ada risiko yang sangat tidak terlihat di sini: Anda tidak dapat menemukan jenis kesalahan yang tepat yang telah dilemparkan di tryblok, jadi gunakan jenis ini catchketika Anda yakin bahwa tidak peduli apa pun jenis pengecualiannya, program harus tetap ada dengan cara yang didefinisikan dalam catchblok.

Infintyyy
sumber
31
Saya harap Anda mendapatkan semacam lencana untuk menjawab pertanyaan hampir 5 tahun setelah jawaban superior diberikan!
4
@DrEval Seperti yang Anda inginkan;) stackoverflow.com/help/badges/17/necromancer?userid=2580505
Andreas
18

Kamu bisa memakai

catch(...)

tapi itu sangat berbahaya. Dalam bukunya Debugging Windows , John Robbins menceritakan kisah perang tentang bug yang benar-benar jahat yang ditutupi oleh perintah catch (...). Anda jauh lebih baik menangkap pengecualian khusus. Tangkap apa pun yang menurut Anda dapat dicekal oleh blok percobaan Anda, tetapi biarkan kode tersebut melemparkan pengecualian lebih tinggi jika sesuatu yang benar-benar tidak terduga terjadi.

John D. Cook
sumber
1
Saya baru saja menangkap beberapa penggunaan ini dan dibumbui di beberapa penebangan pada tahap itu. Tidak melakukan apa-apa dengan pengecualian jelas meminta masalah.
jxramos
15

Biarkan saya sebutkan ini di sini: Jawa

try 
{
...
}
catch (Exception e)
{
...
}

mungkin TIDAK menangkap semua pengecualian! Sebenarnya saya pernah mengalami hal semacam ini sebelumnya, dan itu sangat memprovokasi; Pengecualian berasal dari Throwable. Jadi secara harfiah, untuk menangkap semuanya, Anda TIDAK ingin menangkap Pengecualian; Anda ingin menangkap Throwable.

Saya tahu ini kedengarannya menggelikan, tetapi ketika Anda telah menghabiskan beberapa hari mencoba mencari tahu dari mana "pengecualian tanpa tertangkap" berasal dari kode yang dikelilingi oleh blok coba ... tangkap (Pengecualian) "berasal, tetap dengan kamu.

Paul Sonier
sumber
2
Tentu saja, Anda tidak boleh menangkap objek Galat - jika Anda seharusnya menangkapnya, mereka akan menjadi Pengecualian. Objek kesalahan adalah hal yang benar-benar fatal, seperti kehabisan ruang tumpukan dll.
SCdF
1
Tidak ada pengecualian runtime yang sebagian besar merupakan perkecualian GoodProgrammerExpected !!!
OscarRyz
3
Kami memiliki bug yang sangat serius yang disebabkan oleh penangkapan OutOfMemoryError karena blok (Throwable) tangkapan alih-alih membiarkannya membunuh hal-hal ...
Trejkaz
1
Tentu saja catch(Exception)mungkin tidak menangkap semua pengecualian di Jawa, Anda membuatnya dicampur dengan C # ... Java = catch(Thowable), C # = catch(Exception). Jangan sampai mereka bingung.
Chef Firaun
2
@OscarRyz Kedengarannya seperti CoderMalfunctionError(yang sebenarnya adalah Errorsubclass Java nyata ... meskipun tidak berarti seperti apa terdengarnya.)
reirab
9

Nah, jika Anda ingin menangkap semua pengecualian untuk membuat minidump misalnya ...

Seseorang melakukan pekerjaan di Windows.

Lihat http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus Dalam artikel itu, ia menjelaskan bagaimana ia menemukan cara menangkap semua jenis pengecualian dan ia memberikan kode yang berfungsi.

Berikut daftar yang bisa Anda tangkap:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

Dan penggunaannya: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // lakukan ini untuk satu utas ch.SetThreadExceptionHandlers (); // untuk setiap thred


Secara default, ini membuat minidump di direktori saat ini (crashdump.dmp)

Setelah terkejut
sumber
4

Sebuah mekanisme penangkapan pengecualian generik akan terbukti sangat berguna.

Diragukan. Anda sudah tahu kode Anda rusak, karena mogok. Makan pengecualian mungkin menutupi ini, tetapi itu mungkin hanya akan menghasilkan bug yang lebih buruk, lebih halus.

Yang Anda inginkan adalah debugger ...

Shog9
sumber
13
Saya tidak setuju, ada banyak kasus dalam aplikasi waktu nyata di mana saya lebih suka menangkap pengecualian yang tidak diketahui, menulis apa pun ke log / mengejar beberapa tindakan kesalahan generik tindakan, daripada membiarkan aplikasi crash.
f0ster
3
Saya agak curiga Anda berpikir tentang kasus di mana Anda dapat mengejar beberapa tindakan kesalahan generik, mudah mengabaikan yang mana tumpukan hancur atau memori habis dan penanganan kesalahan umum tidak akan berhasil juga. Tidak ada yang salah dengan menangkap kesalahan yang dapat Anda pulihkan, tetapi IMHO tangkapan-semua harus benar-benar hanya ada sebagai terisolasi (tumpukan terpisah, memori pra-dialokasikan), logika yang ditulis dengan cermat disebut sebelum penghentian program; jika Anda tidak tahu apa masalahnya, Anda tidak dapat yakin bahwa masalahnya dapat dipulihkan.
Shog9
1
Yaitu memasang pengatur sinyal yang melepaskan beberapa log yang Anda buat selama runtime untuk mencari tahu di mana program crash dan, mudah-mudahan, mengapa.
jelas
3
  1. Bisakah Anda menjalankan aplikasi Java yang menggunakan JNI dari jendela konsol (luncurkan dari baris perintah java) untuk melihat apakah ada laporan apa pun yang mungkin telah terdeteksi sebelum JVM macet. Saat dijalankan secara langsung sebagai aplikasi Java window, Anda mungkin kehilangan pesan yang akan muncul jika Anda lari dari jendela konsol.

  2. Kedua, dapatkah Anda mematikan implementasi JNI DLL Anda untuk menunjukkan bahwa metode di DLL Anda sedang dimasukkan dari JNI, Anda kembali dengan benar, dll?

  3. Untuk berjaga-jaga jika masalahnya adalah salah penggunaan metode antarmuka JNI dari kode C ++, apakah Anda telah memverifikasi bahwa beberapa contoh JNI sederhana mengkompilasi dan bekerja dengan pengaturan Anda? Saya sedang berpikir khususnya menggunakan metode antarmuka JNI untuk mengkonversi parameter ke format C ++ asli dan mengubah hasil fungsi menjadi tipe Java. Berguna untuk mematikannya untuk memastikan bahwa konversi data berfungsi dan Anda tidak akan terpuruk dalam panggilan mirip COM ke antarmuka JNI.

  4. Ada hal-hal lain untuk diperiksa, tetapi sulit untuk menyarankan tanpa mengetahui lebih lanjut tentang apa metode asli Java Anda dan apa yang coba dilakukan oleh implementasi JNI dari mereka. Tidak jelas bahwa menangkap pengecualian dari level kode C ++ terkait dengan masalah Anda. (Anda dapat menggunakan antarmuka JNI untuk mengubah kembali pengecualian sebagai Java, tetapi tidak jelas dari apa yang Anda berikan bahwa ini akan membantu.)

orcmid
sumber
2

Untuk masalah sebenarnya tentang tidak dapat melakukan debug dengan benar pada program yang menggunakan JNI (atau bug tidak muncul saat menjalankannya di bawah debugger):

Dalam hal ini sering membantu untuk menambahkan pembungkus Java di sekitar panggilan JNI Anda (yaitu semua metode asli adalah pribadi dan metode publik Anda di kelas memanggil mereka) yang melakukan beberapa pemeriksaan kewarasan dasar (memeriksa bahwa semua "objek" dibebaskan dan "objek" tidak digunakan setelah membebaskan) atau sinkronisasi (hanya menyinkronkan semua metode dari satu DLL ke instance objek tunggal). Biarkan metode pembungkus java mencatat kesalahan dan melemparkan pengecualian.

Ini akan sering membantu untuk menemukan kesalahan nyata (yang secara mengejutkan sebagian besar dalam kode Java yang tidak mematuhi semantik fungsi yang disebut menyebabkan beberapa double-frees atau sejenisnya) lebih mudah daripada mencoba men-debug program Java paralel besar-besaran dalam sebuah debugger asli ...

Jika Anda tahu penyebabnya, simpan kode dalam metode pembungkus Anda yang menghindarinya. Lebih baik Anda memiliki metode pembungkus Anda melemparkan pengecualian daripada kode JNI Anda crash VM ...

mihi
sumber
1

Yah ini sangat tergantung pada lingkungan kompiler. gcc tidak menangkap ini. Visual Studio dan Borland terakhir yang saya gunakan lakukan.

Jadi kesimpulan tentang crash adalah bahwa itu tergantung pada kualitas lingkungan pengembangan Anda.

Spesifikasi C ++ mengatakan bahwa tangkapan (...) harus menangkap pengecualian, tetapi tidak dalam semua kasus.

Setidaknya dari apa yang saya coba.

Jan
sumber
1

Waspadalah

try{
// ...
} catch (...) {
// ...
}

hanya menangkap pengecualian tingkat bahasa, pengecualian / kesalahan tingkat rendah lainnya seperti Access Violationdan Segmentation Faulttidak akan tertangkap.

muaz
sumber
Hal-hal seperti Segmentasi Fault sebenarnya bukan pengecualian, mereka adalah sinyal; dengan demikian, Anda tidak dapat menangkapnya seperti pengecualian biasa. Namun, ada beberapa solusi seperti ini .
MAChitgarha