C++에서 try 안에 throw; 가 되는군요.

winner의 이미지

아시다시피 throw; 재전파를 위한 녀석인데 catch 안에 안 두어도 compile이 되네요.
더욱 흥미가 있는 것은

try
{
    throw;
}
catch (...)
{
    cout << "throw" << endl;
}

못 잡아냅니다.

여기에 대해서 아는 바가 있으신 분 없나요?

feanor의 이미지

C++ 표준 15.1.8에 따르면

"If no exception is presently being handled, executing a throw-expression with no operand calls std::terminate()."

즉, 현재 예외가 없는데 throw; 하면 프로그램이 종료됩니다. 예외는 함수 경계를 넘어서 전파될 수 있으므로 throw; 가 문법적으로 catch 안에 있을 필요는 없습니다.

winner의 이미지

가장 최근 개정안만 무상으로 제공하지 않는 C 표준과는 달리 C++ 표준은 모든 것을 제공하지 않아서 찾아볼 수가 없네요.
그런데 catch 안에 없어도 유용할 수 있는 것은 어떤 경우죠?

feanor의 이미지

void rethrow(int i) {
    if (i > 1) throw;
}
 
int main(int argc, char **argv) {
    try {
        throw argc;
    } catch (int i) {
        rethrow(i);
    }
}

컴파일해서 그냥 실행하면 정상 종료하고 인자를 하나 주면 잡지 않은 예외로 종료합니다.

winner의 이미지

이건... 할 말이 없습니다.

lifthrasiir의 이미지

저게 유용한 상황이 조금 있긴 합니다. (보통 대부분은 굉장히 인위적인 예시긴 합니다만, 이 경우는 아니네요.)

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가 올 일은 크게 없어 보이네요.

winner의 이미지

확실히 기상천회합니다만 아래의 코드는 어떨까요?

try를 두번 씁니다만 구조를 좀 변경했습니다.
throw; 는 결국 최종적으로 catch block 안에 있습니다.

try {
    try {
        // 뭔가 복잡한 코드
    } catch (BaseException e) {
        // BaseException과 그 서브클래스에 공통된 코드 잔뜩
        throw;
    }
} catch (SpecificException1 e) {
    // SpecificException1에 특화된 코드 찔끔
} catch (SpecificException2 e) {
    // SpecificException2에 특화된 코드 찔끔
}

줄 수는 12줄로 똑같고, 다른 의미 없이 완전히같은 구조로 보입니다.

제시하신 구조와 제가 쓴 구조가 서로 커버하지 못하는 실행영역이 있을까요? 혹시 아시는 바가 있다면 위치를 주석으로 삽입해서 알려주시면 감사하겠습니다.

lifthrasiir의 이미지

음! 같은 코드 맞습니다. 거기까지는 미처 생각지 못 했습니다. (하지만 덜 복잡해 보인다는 장점 정도는 있을...라나?)

winner의 이미지

원래는 하는 작업이 try ~ catch가 깔끔하긴 한데 굳이 뭔가를 날릴 이유는 없었기에 이 질문은 시작되었습니다. 즉 catch 안에서 붙잡은 예외변수가 필요없는 거죠.

원래 source는 goto로 되어 있었습니다.

bool processes_with_exception()
{
    object.obj;
    bool isSuccess = true;
 
    isSucess = obj.process();
    if (!isSuccess)
        goto EXIT;
 
    isSuccess = obj.process();
    if (!isSuccess)
        goto EXIT;
 
    final_process();
    return true;
 
EXIT:
    obj.logging_exception();
    return false;
}

이 code 조각은 함수로 이루어져 있어서 그래도 goto 로 이동하는 것이 한 곳 뿐입니다.
하지만 만일 함수틀을 벗겨내면 return 이 이동하는 위치도 만들어줘야 하므로 goto label 을 두군데에 만들어야 하죠.
저는 이 code를 참조해서 변경을 시켜야 했는데 goto가 맘에 안 드는데다 함수의 반환값도 필요없어서 함수틀은 벗겨내고 try ~ catch 로 바꾸고자 생각했습니다.

그래서 변경한 것이

object obj;
 
try {
    bool isSuccess = true;
 
    isSucess = obj.process();
    if (!isSuccess)
        throw isSuccess;
 
    isSuccess = obj.process();
    if (!isSuccess)
        throw isSuccess;
 
    final_process();
}
catch (bool) { // 실제 잡아내는 예외변수는 필요없기에 예외의 타입만 넣었습니다.
    obj.logging_exception();
}

추후 obj.process의 반환값을 isSuccess로 받는게 아니라 아예 obj.process 안에서 예외변수를 throw 하는 형태로 변경할 생각을 해보고 있었습니다.

그런데 역시 뭔가를 throw 해야만 하는 것인가 하는 생각이 들더군요. 제가 현재 필요한 부분은 try ~ catch 제어흐름 뿐인데 말이죠. 예, 그래서 처음 발제한 코드에 대해서 착상을 해본 거죠. throw; 가 예외재전파를 한다는 것은 배웠지만 재전파를 위해 catch 안에서 하지 않으면 어떻게 되나 하고 말이죠.

결국 여기에 쓴 질문은 compile 오류도 안나면서 예외재전파가 아니면 적법하지 않은 throw; 에 대해서 묻게 되었습니다만 제가 해결해보려던 것에 대해서 다른 아이디어가 있으시다면 의견 부탁드립니다.

그러니까 try ~ catch 제어흐름을 쓰면서 throw 하는 예외변수는 없앨 수 있는가? 하는게 제 첫번째 의문이죠.

lifthrasiir의 이미지

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의 내부를 고치실 수 있다면 이런 식으로 설계를 바꾸시는 게 최선의 선택이라 생각합니다.

winner의 이미지

설명을 위해 process와 logging_exception 이라고 썼습니다만 하나의 객체로 묶여 있어 글을 수정하긴 했습니다. obj.process에서 문제가 있는데 바로 throw를 하지 않고, obj 안에 뭔가 기록을 남겨놓고, throw를 하기 때문에 원래의 try ~ catch 의 용법과는 다른 throw; 의 필요성(예외재전파가 아닌 흐름제어만 쓰고 싶다는)을 생각하게 되는 계기가 된 것 같습니다. 기록 위치가 obj의 member 일 뿐이지 거의 errono 처럼 전역변수를 쓰는 느낌이죠.

processes_with_exception 도 processes_with_exception을 호출하는 것도 제가 만든게 아니라 그 부분은 일단 관심 밖이긴 했습니다. 하지만 제가 써야하는 object(설명을 위한 가칭입니다.) class 역시 제가 만든게 아닌데다가 다른데에서 널리 쓰이기에 어디부터 손대야 정상적으로 동작하는 걸 확인하면서 고쳐나갈수 있을까 고민하던 중이었습니다.

하여간 저도 설계 오류라고 결론을 내리긴 했습니다.

이상한 녀석(object)과 춤추려고 하니 그 녀석의 잘못된 버릇을 고치고, 그 녀석과 춤추는 모든 녀석들에게 양해를 구하기 전에는 저도 같이 이상한 춤을 좀 춰져야 할 것 같네요.

하지만 덕분에 재미있는 것을 좀 많이 알았습니다. object의 잘못을 용서하렵니다.

seoleda의 이미지

try ~ catch 의 제어흐름과 비슷한 코드로서 do { } while(0)과 break를 이용한,

다음과 같은 코드가 생각나긴 하는데...

bool isSuccess=true;
do{
   isSuccess = obj.process1();
   if (isSuccess==false) break;
 
   isSuccess = obj.process2();
   if (isSuccess==false) break;
 
   obj.final_process();
   return true;
}while(0);
 
if (isSuccess==false){
   obj.logging_exception();
   return false;
}

써보니깐 기존 코드에 비해서 깔끔해 보이지는 않네요...

do 와 while을 define로 이름을 좀 바꾸면 보기에 좋으려나요? ㅎㅎ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.