ใน C คุณสามารถ "จำลอง" ข้อยกเว้นพร้อมกับ "การเรียกคืนวัตถุ" โดยอัตโนมัติผ่านการใช้ if + goto ด้วยตนเองสำหรับการจัดการข้อผิดพลาดอย่างชัดเจน
ฉันมักจะเขียนรหัส C ดังต่อไปนี้ (ต้มเพื่อเน้นการจัดการข้อผิดพลาด):
#include <assert.h>
typedef int errcode;
errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
errcode ret = 0;
if ( ( ret = foo_init( f ) ) )
goto FAIL;
if ( ( ret = goo_init( g ) ) )
goto FAIL_F;
if ( ( ret = poo_init( p ) ) )
goto FAIL_G;
if ( ( ret = loo_init( l ) ) )
goto FAIL_P;
assert( 0 == ret );
goto END;
/* error handling and return */
/* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */
FAIL_P:
poo_fini( p );
FAIL_G:
goo_fini( g );
FAIL_F:
foo_fini( f );
FAIL:
assert( 0 != ret );
END:
return ret;
}
นี่เป็นมาตรฐาน ANSI C โดยสิ้นเชิงแยกการจัดการข้อผิดพลาดออกจากรหัสเมนไลน์ของคุณอนุญาตให้สแต็ก (ด้วยตนเอง) คลี่คลายวัตถุเริ่มต้นได้เหมือนกับที่ C ++ ทำและเห็นได้ชัดว่าเกิดอะไรขึ้นที่นี่ เนื่องจากคุณกำลังทดสอบความล้มเหลวอย่างชัดเจนในแต่ละจุดจึงทำให้ง่ายต่อการแทรกการบันทึกหรือการจัดการข้อผิดพลาดในแต่ละที่จึงอาจเกิดข้อผิดพลาดได้
หากคุณไม่สนใจเวทมนตร์มาโครเล็กน้อยคุณสามารถทำให้สิ่งนี้กระชับมากขึ้นในขณะที่ทำสิ่งอื่น ๆ เช่นการบันทึกข้อผิดพลาดด้วยสแต็กเทรซ ตัวอย่างเช่น:
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '" #X "' failed! %d, %s\n", __FILE__, __LINE__, ret, strerror( ret ) ); goto LABEL; } while ( 0 )
typedef int errcode;
errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
errcode ret = 0;
TRY( ret = foo_init( f ), FAIL );
TRY( ret = goo_init( g ), FAIL_F );
TRY( ret = poo_init( p ), FAIL_G );
TRY( ret = loo_init( l ), FAIL_P );
assert( 0 == ret );
goto END;
/* error handling and return */
FAIL_P:
poo_fini( p );
FAIL_G:
goo_fini( g );
FAIL_F:
foo_fini( f );
FAIL:
assert( 0 != ret );
END:
return ret;
}
แน่นอนว่านี่ไม่ได้หรูหราเท่ากับข้อยกเว้น C ++ + ตัวทำลาย ตัวอย่างเช่นการซ้อนสแต็กการจัดการข้อผิดพลาดหลายรายการภายในฟังก์ชันเดียววิธีนี้ไม่สะอาดนัก แต่คุณอาจต้องการแยกสิ่งเหล่านั้นออกเป็นฟังก์ชันย่อยที่มีอยู่ในตัวซึ่งจัดการข้อผิดพลาดในทำนองเดียวกันเริ่มต้น + สรุปอย่างชัดเจนเช่นนี้
นอกจากนี้ยังใช้งานได้ภายในฟังก์ชั่นเดียวและจะไม่กระโดดขึ้นสแต็กเว้นแต่ผู้โทรในระดับที่สูงกว่าจะใช้ตรรกะการจัดการข้อผิดพลาดอย่างชัดเจนที่คล้ายกันในขณะที่ข้อยกเว้น C ++ จะกระโดดขึ้นไปเรื่อย ๆ จนกว่าจะพบตัวจัดการที่เหมาะสม และไม่อนุญาตให้คุณโยนประเภทโดยพลการ แต่เป็นเพียงรหัสข้อผิดพลาดแทน
การเข้ารหัสด้วยวิธีนี้อย่างเป็นระบบ (เช่น - ด้วยการเข้าเพียงครั้งเดียวและจุดทางออกเดียว) ทำให้ง่ายมากในการแทรกตรรกะก่อนและหลัง ("ในที่สุด") ซึ่งจะดำเนินการไม่ว่าจะเกิดอะไรขึ้นก็ตาม คุณแค่ใส่ตรรกะ "สุดท้าย" ไว้หลังป้ายกำกับ END