Saya berpikir hari ini tentang blok coba / tangkap yang ada dalam bahasa lain. Googled untuk sementara ini tetapi tanpa hasil. Dari apa yang saya tahu, tidak ada yang namanya coba / tangkap di C. Namun, apakah ada cara untuk "mensimulasikan" mereka?
Tentu, ada trik assert dan trik lain tetapi tidak seperti coba / tangkap, yang juga menangkap pengecualian yang dimunculkan. Terima kasih
101
Jawaban:
C itu sendiri tidak mendukung pengecualian tetapi Anda dapat mensimulasikannya sampai taraf tertentu dengan panggilan
setjmp
danlongjmp
.Situs web ini memiliki tutorial bagus tentang cara mensimulasikan pengecualian dengan
setjmp
danlongjmp
sumber
try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')};
itu tidak akan berfungsi, bukan?Anda menggunakan goto di C untuk situasi penanganan kesalahan yang serupa.
Itu adalah ekuivalen terdekat dari pengecualian yang bisa Anda dapatkan di C.
sumber
goto
lebih digunakan untuk penanganan error, tapi terus kenapa ? Pertanyaannya bukan tentang penanganan kesalahan seperti itu tetapi secara eksplisit tentang persamaan coba / tangkap.goto
bukan ekuivalen dari coba / tangkap karena dibatasi untuk fungsi yang sama.goto
sebagai mekanisme coba / tangkap yang digunakan dalam sumber modern, diterima secara luas, dan ditinjau sejawat. Telusurigoto
padanan "lemparan", dan padananfinish
"tangkapan".Oke, saya tidak bisa menahan diri untuk tidak membalas ini. Izinkan saya mengatakan saya rasa itu bukan ide yang baik untuk mensimulasikan ini di C karena ini benar-benar konsep asing untuk C.
Kita dapat
menggunakanmenyalahgunakan preprocessor dan lokal variabel stack untuk memberikan penggunaan versi terbatas dari C ++ mencoba / melempar / menangkap.Versi 1 (lemparan cakupan lokal)
Versi 1 adalah lemparan lokal saja (tidak dapat meninggalkan ruang lingkup fungsi). Itu bergantung pada kemampuan C99 untuk mendeklarasikan variabel dalam kode (itu harus bekerja di C89 jika percobaan adalah hal pertama dalam fungsi).
Fungsi ini hanya membuat var lokal sehingga ia tahu jika ada kesalahan dan menggunakan goto untuk melompat ke blok catch.
Sebagai contoh:
Ini berhasil untuk sesuatu seperti:
Versi 2 (scope jumping)
Versi 2 jauh lebih kompleks tetapi pada dasarnya bekerja dengan cara yang sama. Ini menggunakan lompat jauh dari fungsi saat ini ke blok percobaan. Blok try kemudian menggunakan if / else untuk melewati blok kode ke blok catch yang memeriksa variabel lokal untuk melihat apakah ia harus menangkap.
Contoh itu diperluas lagi:
Ini menggunakan penunjuk global sehingga longjmp () tahu percobaan apa yang terakhir dijalankan. Kami
menggunakanpenyalahgunaan tumpukan sehingga fungsi anak juga dapat memiliki blok coba / tangkap.Menggunakan kode ini memiliki sejumlah sisi buruk (tetapi merupakan latihan mental yang menyenangkan):
sumber
bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}
. Tetapi variabel lokal juga akan didefinisikan ulang, jadi hal-hal menjadi sedikit di luar kendali.Di C99,Anda dapat menggunakansetjmp
/longjmp
untuk aliran kontrol non-lokal.Dalam satu cakupan, pola pengkodean terstruktur dan generik untuk C dengan adanya beberapa alokasi sumber daya dan beberapa penggunaan keluar
goto
, seperti dalam contoh ini . Ini mirip dengan bagaimana C ++ mengimplementasikan panggilan destruktor dari objek otomatis di bawah tenda, dan jika Anda tetap berpegang pada ini dengan rajin, ini akan memungkinkan Anda untuk tingkat kebersihan tertentu bahkan dalam fungsi yang kompleks.sumber
Sementara beberapa jawaban lain telah mencakup kasus sederhana menggunakan
setjmp
danlongjmp
, dalam aplikasi nyata ada dua masalah yang sangat penting.jmp_buf
akan membuat ini tidak berfungsi.jmp_buf
akan menyebabkan semua jenis rasa sakit dalam situasi ini.Solusi untuk ini adalah mempertahankan tumpukan thread-lokal
jmp_buf
yang diperbarui saat Anda pergi. (Saya pikir inilah yang digunakan lua secara internal).Jadi alih-alih ini (dari jawaban JaredPar yang luar biasa)
Anda akan menggunakan sesuatu seperti:
Sekali lagi versi yang lebih realistis dari ini akan menyertakan beberapa cara untuk menyimpan informasi kesalahan ke dalam
exception_state
, penanganan yang lebih baikMAX_EXCEPTION_DEPTH
(mungkin menggunakan realoc untuk mengembangkan buffer, atau semacamnya).PENAFIAN: Kode di atas ditulis tanpa pengujian apa pun. Ini murni agar Anda mendapatkan ide tentang bagaimana menyusun sesuatu. Sistem yang berbeda dan kompiler yang berbeda perlu menerapkan penyimpanan lokal thread secara berbeda. Kode tersebut mungkin berisi kesalahan kompilasi dan kesalahan logika - jadi meskipun Anda bebas menggunakannya sesuka Anda, UJI sebelum menggunakannya;)
sumber
Pencarian google cepat menghasilkan solusi kludgey seperti ini yang menggunakan setjmp / longjmp seperti yang disebutkan orang lain. Tidak ada yang sesederhana dan seanggun C ++ / Java's try / catch. Saya agak lebih memilih pengecualian Ada menangani diri saya sendiri.
Periksa semuanya dengan pernyataan if :)
sumber
Hal ini dapat dilakukan dengan
setjmp/longjmp
C. P99 memiliki toolset yang cukup nyaman untuk ini yang juga konsisten dengan model thread baru C11.sumber
Ini adalah cara lain untuk melakukan penanganan error di C yang lebih berkinerja daripada menggunakan setjmp / longjmp. Sayangnya, ini tidak akan bekerja dengan MSVC tetapi jika hanya menggunakan GCC / Clang adalah sebuah opsi, maka Anda dapat mempertimbangkannya. Secara khusus, ini menggunakan ekstensi "label sebagai nilai", yang memungkinkan Anda mengambil alamat label, menyimpannya dalam nilai, dan melompat ke sana tanpa syarat. Saya akan menyajikannya menggunakan contoh:
Jika Anda mau, Anda dapat memfaktorkan ulang kode umum di definisikan, yang secara efektif menerapkan sistem penanganan kesalahan Anda sendiri.
Kemudian contohnya menjadi
sumber
Peringatan: berikut ini tidak terlalu bagus tetapi berhasil.
Pemakaian:
Keluaran:
Perlu diingat bahwa ini menggunakan fungsi bertingkat dan
__COUNTER__
. Anda akan berada di sisi yang aman jika Anda menggunakan gcc.sumber
Redis menggunakan goto untuk mensimulasikan coba / menangkap, IMHO sangat bersih dan elegan:
sumber
errno
hanya boleh digunakan setelah panggilan sistem gagal dan bukan tiga panggilan kemudian.Di C, Anda dapat "mensimulasikan" pengecualian bersama dengan "reklamasi objek" otomatis melalui penggunaan manual if + goto untuk penanganan error eksplisit.
Saya sering menulis kode C seperti berikut (diringkas untuk menyoroti penanganan kesalahan):
Ini sepenuhnya ANSI C standar, memisahkan penanganan kesalahan dari kode jalur utama Anda, memungkinkan pelepasan tumpukan (manual) objek yang diinisialisasi seperti yang dilakukan C ++, dan sangat jelas apa yang terjadi di sini. Karena Anda secara eksplisit menguji kegagalan di setiap titik, itu membuatnya lebih mudah untuk memasukkan logging tertentu atau penanganan kesalahan di setiap tempat kesalahan dapat terjadi.
Jika Anda tidak keberatan dengan sedikit keajaiban makro, maka Anda dapat membuatnya lebih ringkas sambil melakukan hal-hal lain seperti mencatat kesalahan dengan jejak tumpukan. Sebagai contoh:
Tentu saja, ini tidak seanggun C ++ exception + destructors. Misalnya, menumpuk beberapa tumpukan penanganan kesalahan dalam satu fungsi dengan cara ini tidak terlalu bersih. Sebaliknya, Anda mungkin ingin memecahnya menjadi sub fungsi mandiri yang menangani kesalahan dengan cara yang sama, menginisialisasi + menyelesaikan secara eksplisit seperti ini.
Ini juga hanya berfungsi dalam satu fungsi dan tidak akan terus meningkatkan tumpukan kecuali pemanggil tingkat yang lebih tinggi menerapkan logika penanganan kesalahan eksplisit yang serupa, sedangkan pengecualian C ++ hanya akan terus meningkatkan tumpukan hingga menemukan penangan yang sesuai. Itu juga tidak memungkinkan Anda untuk melempar tipe arbitrer, tetapi hanya kode kesalahan.
Pengodean secara sistematis dengan cara ini (yaitu - dengan satu entri dan satu titik keluar) juga membuatnya sangat mudah untuk memasukkan logika pra dan posting ("akhirnya") yang akan dijalankan apa pun yang terjadi. Anda hanya menempatkan logika "akhirnya" Anda setelah label AKHIR.
sumber
Jika Anda menggunakan C dengan Win32, Anda dapat memanfaatkan Structured Exception Handling (SEH) untuk mensimulasikan coba / tangkap.
Jika Anda menggunakan C di platform yang tidak mendukung
setjmp()
danlongjmp()
, lihat Penanganan Pengecualian pjsip library ini, ia menyediakan implementasinya sendirisumber
Mungkin bukan bahasa utama (sayangnya), tetapi di APL, ada operasi ⎕EA (singkatan dari Execute Alternate).
Penggunaan: 'Y' ⎕EA 'X' di mana X dan Y adalah cuplikan kode yang diberikan sebagai string atau nama fungsi.
Jika X mengalami kesalahan, Y (biasanya penanganan kesalahan) akan dijalankan.
sumber