본문 바로가기
  • fishing...
  • eating...
MISCELLANEOUSNESS

throw, try, catch를 C언어 버젼으로 구현할 수 있다? 없다?

by 회색뿔 2010. 6. 23.


c언어의 goto문은 function간의 이동은 막혀있다.
이는 call로 호출되어 ret로 반환되어야하는 function call의 기계어의 단편화된 호출규약의 무결성을 강제한다고 이해하면 될것이다.
           -->   반대로 goto문은 기계어로 jmp [label]과 같이 변환된다. 즉, function call과는 거리가 멀다.

이러한 제약을 탈피하여, 에러 핸들링 및 분기를 제어할 수 있는 방법은 setjmp와 longjmp를 이용하는 방법이 있다. 이를 이용하면 C++의 exception throw를 구현하는 것과 비슷한 효과를 얻을 수 있다.

다음의 내용을 읽어 보자.
1. setjmp는 반드시 longjmp보다 먼저 호출 
2. 이때 setjmp를 호출하고 jmpb를 셋업하는 루틴은 계속 액티브
3. longjmp가 호출되기 전에는 복귀될 수 없다.  
3-1 만일 복귀된다면 그 결과는 예측할 수 없다. 
4. setjmp는 프로그램의 저수준 서브루틴에서 만난 에러와 예외를 취급하는데 유용

FUNCTION
스택에 저장되는 6가지의 register의 값을 저장할 수 있는 저장공간
jmp_buf
sigjmp_buf 

6개의 register 값을 저장하는 명령어
setjmp
sigsetjmp

원래 호출 위치로 돌아가는 함수
이때, 저장되었던 register값이 복원된다.
longjmg
siglongjmp
=====================> C++의 Try Catch개념을 구현할 수 있다.
일반적인 경우
#include 
void bar( void )
{
	longjmp( env, 2 );
}

void foo( void )
{
	sleep(2);
	bar();
	//longjmp( env, 1 );                                     // throw
}

jmp_buf env;    // 스택을 관리하는 6개의 register를 저장한다.
int main()
{
     if( ( ret = setjmp(env) ) == 0 )                       // try
          foo();
     else if( ret == 1 )                                         // catch
          // exception 1;
     else if( ret == 2 )                                         // catch
          // exception 2;
}
signal을 이용하여 Interrupt를 발생시킬 경우
jmp에 관련된 명령어 들의 앞에 sig를 붙여 주면 된다.
#include 

void bar( void )
{
	siglongjmp( env, 2 );
}

void foo( void )
{
	sleep(2);
	bar();
	//siglongjmp( env, 1 );                                     // throw
}

jmp_buf env;    // 스택을 관리하는 6개의 register를 저장한다.
int main()
{
	if( ( ret = sigsetjmp(env) ) == 0 )                       // try
		foo();
	else if( ret == 1 )                                         // catch
		// exception 1;
	else if( ret == 2 )                                         // catch
		// exception 2;
}
※ 소스코드는 CentOS 5.3에서 작성되었습니다. ※