throw catch 에 관해서,

mr.lee의 이미지

제가 Java source를 C++ source로 자동변환하는 프로그램을 제작하고 있는데요. 자바의 문법을 C++로 표현해내는 문법 및 클래스 프레임웍은 연구가 거의 끝났구요. 그렇게 소스를 변환해주는 프로그램도 거의 다 되어 있는데.. throw에 있어서 질문이 하나 있습니다.

저는 GC를 처음엔 Hans Bohem의 gc library를 사용할려고 했으나, 사실 이 변환 프레임웍이 모바일에 사용될거라서 (MIDP/CLDC -> Brew) bohem gc는 좀 맞지않는듯 하더라구요. 그래서 Smart pointer로 gc를 구현하고 있습니다.

스마트 포인터기반의 GC에서 자바의 throw를 C++에서 표현하면,

(모든 클래스들은 이름이 Class_ 로 바꾸고.. 원래 클래스명은 typedef SmartPointer<Class_> Class 로 사용됩니다)

throw new Exception() => throw Exception(new Exception_()) 이 됩니다.

그리고,

catch (Exception e) 는 변환필요없이 그대로 두죠.. catch(Exception& e)로 받으면 복사생성자 호출이 없으므로 보다 더 효율적이나.. 자바에선, e를 받았다가 e에 다른 객체를 대입해도 원 객체는 그대로 있게 되는데 레퍼런스로 받으면 원객체가 변하기때문에 스마트포인터에서는 인자를 레퍼런스없이 그대로 받아 복사생성자가 호출되게 합니다. 그래야 자바의 흐름과 똑같이 되니깐요.

또한, 여기서 변환시 좀 까다로워지는것이.

Java에선 throw (new Exception()) 식의 사용도 가능하나, C++에선 throw (Exception(new Exception_())) 이 에러납니다. 그니깐 C++에선 throw (Class()) 처럼 괄호를 둘러치면 에러가나는건데.. new객체나 기존객체를 던지는건 괄호쳐도 에러 안나거든요.

throw (new Class()) 혹은 throw (a) 같이 말이지요.

또한 자바에선 throw (new Class().a.new Exception()) 머 이런것도 가능하니..이걸 C++로 옮기면 throw [a type]::Exception(new [a type]::Exception_((new Class_())->a)) 이렇게 되어야 하는데..

그래서, 복합괄호, 다중괄호 등에 따른 벗겨내야할 괄호유무 및 복사생성자의 오버헤드 등으로... 가능하면 다음과같은 형태를 띠면 변환도 쉽고 효율도 올라가는데..

throw new Exception() 은 new ClassName을 new ClassName_으로 바꾸는 변환만 적용하고 특별히 throw를 위한 변환없이 그대로 사용하고,

catch도 변환없이 catch (Exception e) 로 받으면.. Exception 은 Exception_* 을 인자로 받는 생성자가 있기때문에 컴파일도 문제없이 잘 되거든요.

throw ((new Exception())) 뭐 이런것도 C++에서도 그대로 수용되기때문에, 외곽괄호에 대해서도 신경을 안써도 되는데,

문제는, 실행시 에러가 나거든요.

이걸 생성자 인자를 어떻게 잘 표현하면 에러 안나게 포인터를 객체로 catch할수 있을까요? 뭔가 될듯하기도 한데, 잘 안되네요...

kdoll의 이미지

혹시 일반 객체포인터를 던지면 에러가 나나요?

만일 나지 않는다면.... 스마트 포인터를 던져서 발생하는 문제가 아닐가

생각이 듭니다만.... 저도 한번 던져 보아야 겠네요.

님의 글을 읽고 궁금한것이 하나 생겼는데요..

MIDP를 BREW로 변환할때

자바 클래스 맴버들은 어떻게 변환하시는지요.. static맴버들 이요

BREW에서 데이타 영역에 사용하는 변수(외부변수나 클래스 변수)

들은 사용을 못하게 막고 있는것으로 알고 있는데.....

혹시 Brew가 버전업 하면서 변했는지요?

mr.lee의 이미지

스마트포인터는 일종의 '방법'을 일컫는 용어지요.. c++의 객체가 자기 존속범위가 끝나면 자동으로 삭제되면서 소멸자가 호출되는 특성과, reference count 를 이용하여 GC를 구현하는..물론, thread safe, STL 에서 까다롭게 다뤄져야하고 특히 자바의 프레임웍을 구현해낼려면 Object, Array template클래스와 더불어 꽤 복잡게 서로 교묘하게 define되어야 합니다만...

SmartPointer<Class> 는 스마트포인터를 구현한 템플릿클래스이며, 이걸 던지는것은 지금 문제의 핵심과 무관하구요... "스마트포인터를 던진다" 는것은 잘못된 표현이며 "스마트포인터 클래스의 포인터를 던진다"가 맞는 표현이겠지요. 또한 여기서 제가 하고자하는것은 클래스포인터를 던지고 그 클래스를 타입으로 갖는 스마트포인터 클래스에서 생성자의인자로 받으면서 catch할려고하는거지요.

템플릿클래스이던 아니던 상관없구요.. 단지 포인터를 던지고, "그 포인터를 인-자로 받는 생성자가 있는 클래스"로 받을때.. (explict하게 선언하지 않았다면 이건 정당한 방법이니깐요. ClassA* a = new ClassA; ClassB b = a; 가 되는것처럼이죠...) 문법상 하자가 없기땜에 컴파일은 되는데 실행시 오류가나죠. 이건 throw 시의 메모리 메카니즘과 관련있을것같은데... throw는 auto 메모리에 객체를 만들어놓고 레퍼런스를 던지거든요. 그리고 catch가 되면 사라지죠.. 때문에 Class a; throw a; 하면 복사생성자가 호출안될것 같지만, 호출되거든요. 즉 a의 레퍼런스가 throw 된다기보담 a의 복사생성자로 생성되는 객체를 auto 메모리에 올려놓고 그놈의 레퍼런스를 던지죠.. new로 던지는 포인터는 어떻게 되는지 잘모르겠네요. 포인터 값을 auto 메모리에 올리고 *&를 던지는게 아닌가 싶은데..그렇다고 해서 왜 에러가날까 싶네요. 여튼 뭔가 잘 ...정의해보면 될것같기도해서 말이죠... 아니면 컴파일시의 옵션을 어케 하면 될련가싶기도하고. 절대 안되는것 같기도 하네요.

클래스 static member는 그대로 사용되죠. 자바에서 A.MEMBER 라면 C++에선 A::MEMBER로 바꿔지죠.. 브루는, ARM기반이고.. arm compiler로는 C++의 모든 스펙을 다 사용할순없습니다. arm compiler는 embed c++ specification이구요. 여긴 ANSI C++의 몇가지가 빠져있습니다. GCC cross compile을 하여야 하고. 환경을 만들어야 합니다. 브루 헤더도 몇개는 수정을 좀 해야하구요. 준비는 좀 까다롭죠. 브루 포럼(영어포럼)에서 gcc 로 찾아보면 꽤 나옵니다..

doldori의 이미지

말로 설명하는 것보다 컴파일은 되는데 실행할 때 오류가 난다는 코드를 올리는
것이 어떤 상황인지 이해하기는 더 좋겠습니다. 관련되는 부분만 간추려서요.

mr.lee의 이미지

음..에러메세지라고 해봤자..

Aborted (core dumped)

이것뿐.

#include <iostream>

class Exception {};

class T {
    public:
        T(Exception* e) {}
};

void throwException() {
    throw new Exception();
}

int main() {
    try {
        throwException();
    } catch (T e) {
        std::cout << "Catch exception" << std::endl;
    }

    return 0;
}

물론, 이런식으로 사용은 되지만 첫번째에 장황하게 밝힌이유로 이런식은 아무 필요없는거구요..

#include <iostream>

class Exception {
    public:
        int b;
        Exception(int a) : b(a) {}
};

class T {
    public:
        T(Exception* e) {}
};

void throwException() {
    throw new Exception(5);
}

int main() {
    try {
        throwException();
    } catch (T* e) {
        std::cout << "Catch exception : " << e->b << std::endl;
        delete e;
    }

    return 0;
}
doldori의 이미지

예외 핸들러가 없어서 그런 것이군요.
catch (Exception* e) 로 바꾸면 됩니다.
그리고
T e
이것은 T 클래스의 디폴트 생성자가 정의되지 않았으므로 문법 에러입니다.

mr.lee의 이미지

제가 위에 장황하게 설명을 해놨는데, 잘못 이해하고 계신듯...

#include <iostream>

using namespace std;

class Exception {
    public:
        int a;
        Exception(int b) : a(b) {
            cout << "create exception" << endl;
        }
        ~Exception() {
            cout << "delete exception" << endl;
        }
};

class T {
    public:
        Exception* a;
        T(Exception* e) : a(e) {
            cout << "T constructor" << endl;
        }
};

void throwException() {
    throw new Exception(5);
}

void testMethod(T e) {
    cout << "Test method" << endl;
    cout << e.a->a << endl;
}

int main() {
    testMethod(new Exception(5));
    try {
        throwException();
    } catch (Exception* e) {
        cout << "Catch exception1" << endl;
        cout << e->a << endl;
        delete e;
    }
    try {
        throwException();
    } catch (T e) {
        cout << "Catch exception2" << endl;
        cout << e.a->a << endl;
    }

    return 0;
}

실행결과

create exception
T constructor
Test method
5
create exception
Catch exception1
5
delete exception
create exception
Aborted (core dumped)

그리고, 디폴트생성자가 없어도 생성자를 explicit로 선언하지 않는이상 assign type이 인자로 들어갑니다..

doldori의 이미지

글을 수정하셨군요. 두 번째 코드도 잘못된 것인데 맞게 고친다면

void throwException() { 
    throw new T(new Exception(5)); 
} 

로 해야 catch (T* e)로 받을 수 있겠죠.
그런데 예외를 던지면서 굳이 new를 써야 하는 이유가 있는지 의문입니다.
mr.lee의 이미지

아예. 두번째 예시는 여기서 바로 작성해서 실수를 했네요. 그 밑의 글을 봐주세요.

doldori의 이미지

윽... 그 사이에 또 새로운 포스팅이...
두 번째 try-catch 블럭은

    try { 
        throwException(); 
    } catch (Exception* e) { 
        cout << "Catch exception2" << endl; 
        cout << e->a << endl; 
    } 

가 되어야 합니다. throwException()에서 Exception* 예외를 던졌으니까요.
mr.lee의 이미지

맨 윗글에, 제 질문의 의도를 적어놓았는데 계속 핵심을 잘못 이해하고 계신듯하네요.

제가 궁금한것은...

포인터를 throw한것을 어떻게 catch 합니까?

가 아니라

throw 한 포인터를, 그 포인터를 인자로 하는 생성자가 있는 클래스에서 받으면 객체생성되면서 인자로 드감으로 컴파일은 되는데 실행시 오류가나니, 어떻게 오류안나게 가능할까요?

하는것이랍니다...

왜 그렇게 이상하게 할려고 하느냐? 는..맨윗글에 소상하게 적어놓았구요..

doldori의 이미지

SaNha wrote:
맨 윗글에, 제 질문의 의도를 적어놓았는데 계속 핵심을 잘못 이해하고 계신듯하네요.

글이 장황해서 핵심이 뭔지 파악이 안됩니다.

SaNha wrote:
throw 한 포인터를, 그 포인터를 인자로 하는 생성자가 있는 클래스에서 받으면 객체생성되면서 인자로 드감으로 컴파일은 되는데 실행시 오류가나니, 어떻게 오류안나게 가능할까요?

무슨 뜻인지 한참 생각했습니다. 그러니까 T::T(Exception* e)가 있으니까
Exception* 예외를 던지면 catch (T e)에서 받아줘야 하는 거 아니냐 그런 뜻입니까?
답은 '아니오'입니다.
void f()
{
    try {
        throw E() ;
    }
    catch(H) {
        // when do we get here?
    }
}

여기서 catch 블럭이 받을 수 있는 예외는 다음의 4가지 중 하나일 때입니다.
1. H와 E가 같은 형일 때
2. H가 E의 public 기초 클래스일 때
3. H와 E가 모두 포인터형이고 그것들이 가리키는 개체간의 관계가 1이나 2일 때
4. H가 참조형이고 H가 참조하는 개체와 E의 관계가 1이나 2일 때
올리신 코드는 여기에 해당하지 않습니다.
mr.lee의 이미지

예..말씀하신대로 C99의 정석이겠지요.

저도 안될거라고 생각은 하지만 - 사실 위배된다면 컴파일에서 에러가 나야된다고 봅니다.. - 꼼수내지는 컴파일옵션이나 기타 어떠한 테크닉으로 혹시 가능하게 할 방법이 없을까 싶어서 질문을 올려봤네요.

'뭔가 잘하면 될듯' 하기도 해서 말이지요.

왜냐면, 말씀하신 부분은 일종의 '규약'이고 'Specification' 이므로 컴파일시 오류가 나야하는것인데, 컴파일이 되므로 실행시 'Memory'와 관련된다고 봐집니다. throw할때의 메모리 드리블링과 관련이 있다면 어떤 테크닉으로 (꼼수에 가깝겠군요) 가능하게 할수도 있지 않을까 싶어서..사실 웬만한 변형은 다 가해봤거든요. 안된다고 결론은 내리고 있지만, 혹시 또 모르니..

doldori의 이미지

실수로 C++을 C99로 말씀하셨다고 생각하겠습니다.
올리신 코드가 컴파일 에러가 나야 한다는 이유는 던져진 예외를 받을 catch 블럭이
없기 때문이겠지요? 그러나 예외라는 것은 기본적으로 실행 시간 메커니즘이므로
컴파일러의 검사 범위를 벗어납니다. 올리신 코드는 C++의 문법에 어긋나는 것은
없습니다. 다만 실행시에 발생한 예외를 처리할 핸들러가 없다는 것이 문제이지요.
메모리와는 관계없는 이야기입니다.

mr.lee의 이미지

예.. 아무래도 그렇게는 catch 할 수가 없겠네요.

시간내서 답변달아주셔서 정말 감사합니다.

제가 두가지 착각을 하고 있었네요.

첫째는, 함수호출 시에는 포인터를 넘기고, 그것을 인자로 하는 생성자가 있는 클래스로 받아지기때문에 같은식으로 catch를 작성하고 컴파일이 되었기때문에 예외가 catch구문에서 '잡혔다' 고 착각했었던것이고

둘째는, 그렇게 컴파일이 되고 catch되었는데 실행시 Aboarted (core dumped) 나버리니깐 그것이 '에러메세지' 라고 생각했었던 것이네요.

그래서 어떻게 꼼수를 내보면 혹 되는 방법도 존재하지 않을까 궁금했었네요..

결론은, catch에서 잡힌게 아니라 핸들러가 맞지 않으므로 pass 된것이고 결국 예외를 잡는 구문이 없으므로 Aborted 된것이니 '실행 오류' 라고 볼수도 없는것이군요.

atie의 이미지

doldori wrote:
실수로 C++을 C99로 말씀하셨다고 생각하겠습니다.
올리신 코드가 컴파일 에러가 나야 한다는 이유는 던져진 예외를 받을 catch 블럭이
없기 때문이겠지요? 그러나 예외라는 것은 기본적으로 실행 시간 메커니즘이므로
컴파일러의 검사 범위를 벗어납니다. 올리신 코드는 C++의 문법에 어긋나는 것은
없습니다. 다만 실행시에 발생한 예외를 처리할 핸들러가 없다는 것이 문제이지요.
메모리와는 관계없는 이야기입니다.

사족, C++에서는 실행시 검사를 하지만, 자바에서는 컴파일시 검사를 합니다. exception handling을 놓고 두 언어을 비교할때 가장 큰 차이점이죠.

흥미있는 주제여서 세심하게 보았는데, 유익했습니다. 질답을 해주신 두 분께 감사합니다.

----
I paint objects as I think them, not as I see them.
atie's minipage

doldori의 이미지

atie wrote:
사족, C++에서는 실행시 검사를 하지만, 자바에서는 컴파일시 검사를 합니다. exception handling을 놓고 두 언어을 비교할때 가장 큰 차이점이죠.

아, 그렇습니까? 컴파일 타임에 예외 핸들러 검사를 어떤 식으로 하는지 설명을
좀 해주시면 고맙겠습니다. 저는 자바를 몰라서요.
예를 들어 E라는 예외를 던지는 라이브러리를 사용할 때 내가 작성하는 소스에
E에 대한 예외 핸들러가 있는지 검사한다는 뜻인가요? 그리고 핸들러가 없는
경우에는 컴파일 에러가 나나요? 만약 그렇다면 자바 컴파일러를 만드는 사람들은
꽤나 골치가 아프겠는데요. :shock:
atie의 이미지

doldori wrote:
atie wrote:
사족, C++에서는 실행시 검사를 하지만, 자바에서는 컴파일시 검사를 합니다. exception handling을 놓고 두 언어을 비교할때 가장 큰 차이점이죠.

아, 그렇습니까? 컴파일 타임에 예외 핸들러 검사를 어떤 식으로 하는지 설명을
좀 해주시면 고맙겠습니다. 저는 자바를 몰라서요.
예를 들어 E라는 예외를 던지는 라이브러리를 사용할 때 내가 작성하는 소스에
E에 대한 예외 핸들러가 있는지 검사한다는 뜻인가요? 그리고 핸들러가 없는
경우에는 컴파일 에러가 나나요? 만약 그렇다면 자바 컴파일러를 만드는 사람들은
꽤나 골치가 아프겠는데요. :shock:

예외 핸들러가 있는지를 검사하는 것이 아니라 예외가 정의되었나를 검사합니다. 자바에서 예외를 던진다(throwing an exception) 라는 용어의 정의는 "create an exception object and handing it to runtime system"으로 됩니다. 그리고 예상할 수 있듯이, 모든 예외는 자바 언어가 제공하는 Exception 클래스의 상속 계층을 따릅니다.
따라서, 특정 에러 이벤트에 대한 예외 핸들러는 그 특정 에러를 정의한 예외가 발생하면 어떻게 한다라고 코드상에 명시가 되던지 아니면, 상위의 예외를 사용하여 명시를 하던지, 최종적으로는 Exception 클래스르 쓰던지 아뭏튼 코드상에 분명히 나타나므로 컴파일시 검사가 됩니다. 물론, 실행시 적당한 핸들러를 찾는 것은 메쏘드의 call stack에서 역순으로 찾아나가는 것이고요.

----
I paint objects as I think them, not as I see them.
atie's minipage

doldori의 이미지

아, 그렇군요. 제가 엉뚱한 상상을 하고 있었네요. 그런데 설명해주신 내용만으로는
구체적인 차이점을 잘 모르겠습니다. :oops: 추가적인 설명을 요구한다면 atie님을
너무 귀찮게 하는 거겠지요. 혹시라도 나중에 제가 자바를 배우게 되면 자연히 알게 될
테니까요. 아무튼 답변 감사합니다.

익명 사용자의 이미지

throw catch 는 간단합니다

func() {
int a = 100;
throw a;
}

main ()
{
try{
func();
}
catch ( int &a)
{
printf("%d\n",a);
}
}

결과는 100 ^^

익명 사용자의 이미지

소멸된 클래스에 대한 포인터 접근이라 컴파일단계에선 잡히지않고 실행시만 에러남

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.