저게 유용한 상황이 조금 있긴 합니다. (보통 대부분은 굉장히 인위적인 예시긴 합니다만, 이 경우는 아니네요.)
try {
// 뭔가 복잡한 코드
} catch (BaseException e) {
// BaseException과 그 서브클래스에 공통된 코드 잔뜩
try {
throw;
} catch (SpecificException1 e) {
// SpecificException1에 특화된 코드 찔끔
} catch (SpecificException2 e) {
// SpecificException2에 특화된 코드 찔끔
}
}
요건 다음 코드랑 같습니다만 더 짧습니다.
try {
// 뭔가 복잡한 코드
} catch (SpecificException1 e) {
// BaseException과 그 서브클래스에 공통된 코드 잔뜩
// SpecificException1에 특화된 코드 찔끔
} catch (SpecificException2 e) {
// BaseException과 그 서브클래스에 공통된 코드 잔뜩
// SpecificException2에 특화된 코드 찔끔
} catch (BaseException e) {
// BaseException과 그 서브클래스에 공통된 코드 잔뜩
}
즉, 예외끼리 상속 관계에 있는 경우 코드를 단순화하기 위해 사용할 수 있습니다. (dynamic_cast 써도 되긴 하는데 RTTI가 필요하기 때문에 오버헤드가 늘어난다는 문제가 있지요. 뭐 요즘 C++ 코드라면 크게 상관 없겠지만;) 이 정도 말고는 try 안에 throw가 올 일은 크게 없어 보이네요.
이 code 조각은 함수로 이루어져 있어서 그래도 goto 로 이동하는 것이 한 곳 뿐입니다.
하지만 만일 함수틀을 벗겨내면 return 이 이동하는 위치도 만들어줘야 하므로 goto label 을 두군데에 만들어야 하죠.
저는 이 code를 참조해서 변경을 시켜야 했는데 goto가 맘에 안 드는데다 함수의 반환값도 필요없어서 함수틀은 벗겨내고 try ~ catch 로 바꾸고자 생각했습니다.
추후 obj.process의 반환값을 isSuccess로 받는게 아니라 아예 obj.process 안에서 예외변수를 throw 하는 형태로 변경할 생각을 해보고 있었습니다.
그런데 역시 뭔가를 throw 해야만 하는 것인가 하는 생각이 들더군요. 제가 현재 필요한 부분은 try ~ catch 제어흐름 뿐인데 말이죠. 예, 그래서 처음 발제한 코드에 대해서 착상을 해본 거죠. throw; 가 예외재전파를 한다는 것은 배웠지만 재전파를 위해 catch 안에서 하지 않으면 어떻게 되나 하고 말이죠.
결국 여기에 쓴 질문은 compile 오류도 안나면서 예외재전파가 아니면 적법하지 않은 throw; 에 대해서 묻게 되었습니다만 제가 해결해보려던 것에 대해서 다른 아이디어가 있으시다면 의견 부탁드립니다.
그러니까 try ~ catch 제어흐름을 쓰면서 throw 하는 예외변수는 없앨 수 있는가? 하는게 제 첫번째 의문이죠.
throw를 쓰는 게 그다지 좋은 해법은 아닙니다만 해결하는게 쉽지는 않을 것 같습니다. 일단 logging_exception이 저기 나온 그대로의 코드라면 processes_with_exception을 wrap하는 processes_with_exception_outer 같은 걸 만들어서 리턴값에 따라 에러를 처리하게 하는 쪽이 자명한 해결책이겠습니다만, 그러지는 않을테니(...) 질문을 올리셨겠죠. logging_exception이 함수가 아니라 processes_with_exception 안의 변수들을 참조하는 코드 뭉치라면 일반적으로 쉬운 해결책은 없다고 봅니다.
설계의 잘못이라고 생각할 수도 있겠습니다. process 안에서 return으로 오류를 반환하게 하는 것이 아닌 throw로 오류를 반환하게 해야 이런 오류 전파를 쉽게 처리할 수 있다는 논리죠. 이를테면:
void processes_with_exception() {
try {
process(); // throw process_failed(); 같은 걸 호출할 수 있음
process(); // 상동
} catch (process_failed) {
logging_exception();
throw;
}
}
(또는 processes_with_exception이 bool을 반환하게도 할 수 있습니다만) 만약 process의 내부를 고치실 수 있다면 이런 식으로 설계를 바꾸시는 게 최선의 선택이라 생각합니다.
설명을 위해 process와 logging_exception 이라고 썼습니다만 하나의 객체로 묶여 있어 글을 수정하긴 했습니다. obj.process에서 문제가 있는데 바로 throw를 하지 않고, obj 안에 뭔가 기록을 남겨놓고, throw를 하기 때문에 원래의 try ~ catch 의 용법과는 다른 throw; 의 필요성(예외재전파가 아닌 흐름제어만 쓰고 싶다는)을 생각하게 되는 계기가 된 것 같습니다. 기록 위치가 obj의 member 일 뿐이지 거의 errono 처럼 전역변수를 쓰는 느낌이죠.
processes_with_exception 도 processes_with_exception을 호출하는 것도 제가 만든게 아니라 그 부분은 일단 관심 밖이긴 했습니다. 하지만 제가 써야하는 object(설명을 위한 가칭입니다.) class 역시 제가 만든게 아닌데다가 다른데에서 널리 쓰이기에 어디부터 손대야 정상적으로 동작하는 걸 확인하면서 고쳐나갈수 있을까 고민하던 중이었습니다.
하여간 저도 설계 오류라고 결론을 내리긴 했습니다.
이상한 녀석(object)과 춤추려고 하니 그 녀석의 잘못된 버릇을 고치고, 그 녀석과 춤추는 모든 녀석들에게 양해를 구하기 전에는 저도 같이 이상한 춤을 좀 춰져야 할 것 같네요.
C++ 표준
C++ 표준 15.1.8에 따르면
"If no exception is presently being handled, executing a throw-expression with no operand calls std::terminate()."
즉, 현재 예외가 없는데 throw; 하면 프로그램이 종료됩니다. 예외는 함수 경계를 넘어서 전파될 수 있으므로 throw; 가 문법적으로 catch 안에 있을 필요는 없습니다.
모르겠군요.
가장 최근 개정안만 무상으로 제공하지 않는 C 표준과는 달리 C++ 표준은 모든 것을 제공하지 않아서 찾아볼 수가 없네요.
그런데 catch 안에 없어도 유용할 수 있는 것은 어떤 경우죠?
예제 코드
컴파일해서 그냥 실행하면 정상 종료하고 인자를 하나 주면 잡지 않은 예외로 종료합니다.
헉!!!
이건... 할 말이 없습니다.
저게 유용한 상황이
저게 유용한 상황이 조금 있긴 합니다. (보통 대부분은 굉장히 인위적인 예시긴 합니다만, 이 경우는 아니네요.)
요건 다음 코드랑 같습니다만 더 짧습니다.
즉, 예외끼리 상속 관계에 있는 경우 코드를 단순화하기 위해 사용할 수 있습니다. (dynamic_cast 써도 되긴 하는데 RTTI가 필요하기 때문에 오버헤드가 늘어난다는 문제가 있지요. 뭐 요즘 C++ 코드라면 크게 상관 없겠지만;) 이 정도 말고는 try 안에 throw가 올 일은 크게 없어 보이네요.
약간 이상한 느낌입니다.
확실히 기상천회합니다만 아래의 코드는 어떨까요?
try를 두번 씁니다만 구조를 좀 변경했습니다.
throw; 는 결국 최종적으로 catch block 안에 있습니다.
줄 수는 12줄로 똑같고, 다른 의미 없이 완전히같은 구조로 보입니다.
제시하신 구조와 제가 쓴 구조가 서로 커버하지 못하는 실행영역이 있을까요? 혹시 아시는 바가 있다면 위치를 주석으로 삽입해서 알려주시면 감사하겠습니다.
음! 같은 코드
음! 같은 코드 맞습니다. 거기까지는 미처 생각지 못 했습니다. (하지만 덜 복잡해 보인다는 장점 정도는 있을...라나?)
이 질문을 시작하게 된 계기는 이렇습니다.
원래는 하는 작업이 try ~ catch가 깔끔하긴 한데 굳이 뭔가를 날릴 이유는 없었기에 이 질문은 시작되었습니다. 즉 catch 안에서 붙잡은 예외변수가 필요없는 거죠.
원래 source는 goto로 되어 있었습니다.
이 code 조각은 함수로 이루어져 있어서 그래도 goto 로 이동하는 것이 한 곳 뿐입니다.
하지만 만일 함수틀을 벗겨내면 return 이 이동하는 위치도 만들어줘야 하므로 goto label 을 두군데에 만들어야 하죠.
저는 이 code를 참조해서 변경을 시켜야 했는데 goto가 맘에 안 드는데다 함수의 반환값도 필요없어서 함수틀은 벗겨내고 try ~ catch 로 바꾸고자 생각했습니다.
그래서 변경한 것이
추후 obj.process의 반환값을 isSuccess로 받는게 아니라 아예 obj.process 안에서 예외변수를 throw 하는 형태로 변경할 생각을 해보고 있었습니다.
그런데 역시 뭔가를 throw 해야만 하는 것인가 하는 생각이 들더군요. 제가 현재 필요한 부분은 try ~ catch 제어흐름 뿐인데 말이죠. 예, 그래서 처음 발제한 코드에 대해서 착상을 해본 거죠. throw; 가 예외재전파를 한다는 것은 배웠지만 재전파를 위해 catch 안에서 하지 않으면 어떻게 되나 하고 말이죠.
결국 여기에 쓴 질문은 compile 오류도 안나면서 예외재전파가 아니면 적법하지 않은 throw; 에 대해서 묻게 되었습니다만 제가 해결해보려던 것에 대해서 다른 아이디어가 있으시다면 의견 부탁드립니다.
그러니까 try ~ catch 제어흐름을 쓰면서 throw 하는 예외변수는 없앨 수 있는가? 하는게 제 첫번째 의문이죠.
throw를 쓰는 게
throw를 쓰는 게 그다지 좋은 해법은 아닙니다만 해결하는게 쉽지는 않을 것 같습니다. 일단 logging_exception이 저기 나온 그대로의 코드라면 processes_with_exception을 wrap하는 processes_with_exception_outer 같은 걸 만들어서 리턴값에 따라 에러를 처리하게 하는 쪽이 자명한 해결책이겠습니다만, 그러지는 않을테니(...) 질문을 올리셨겠죠. logging_exception이 함수가 아니라 processes_with_exception 안의 변수들을 참조하는 코드 뭉치라면 일반적으로 쉬운 해결책은 없다고 봅니다.
설계의 잘못이라고 생각할 수도 있겠습니다. process 안에서 return으로 오류를 반환하게 하는 것이 아닌 throw로 오류를 반환하게 해야 이런 오류 전파를 쉽게 처리할 수 있다는 논리죠. 이를테면:
(또는 processes_with_exception이 bool을 반환하게도 할 수 있습니다만) 만약 process의 내부를 고치실 수 있다면 이런 식으로 설계를 바꾸시는 게 최선의 선택이라 생각합니다.
감사합니다.
설명을 위해 process와 logging_exception 이라고 썼습니다만 하나의 객체로 묶여 있어 글을 수정하긴 했습니다. obj.process에서 문제가 있는데 바로 throw를 하지 않고, obj 안에 뭔가 기록을 남겨놓고, throw를 하기 때문에 원래의 try ~ catch 의 용법과는 다른 throw; 의 필요성(예외재전파가 아닌 흐름제어만 쓰고 싶다는)을 생각하게 되는 계기가 된 것 같습니다. 기록 위치가 obj의 member 일 뿐이지 거의 errono 처럼 전역변수를 쓰는 느낌이죠.
processes_with_exception 도 processes_with_exception을 호출하는 것도 제가 만든게 아니라 그 부분은 일단 관심 밖이긴 했습니다. 하지만 제가 써야하는 object(설명을 위한 가칭입니다.) class 역시 제가 만든게 아닌데다가 다른데에서 널리 쓰이기에 어디부터 손대야 정상적으로 동작하는 걸 확인하면서 고쳐나갈수 있을까 고민하던 중이었습니다.
하여간 저도 설계 오류라고 결론을 내리긴 했습니다.
이상한 녀석(object)과 춤추려고 하니 그 녀석의 잘못된 버릇을 고치고, 그 녀석과 춤추는 모든 녀석들에게 양해를 구하기 전에는 저도 같이 이상한 춤을 좀 춰져야 할 것 같네요.
하지만 덕분에 재미있는 것을 좀 많이 알았습니다. object의 잘못을 용서하렵니다.
try ~ catch 의
try ~ catch 의 제어흐름과 비슷한 코드로서 do { } while(0)과 break를 이용한,
다음과 같은 코드가 생각나긴 하는데...
써보니깐 기존 코드에 비해서 깔끔해 보이지는 않네요...
do 와 while을 define로 이름을 좀 바꾸면 보기에 좋으려나요? ㅎㅎ
댓글 달기