Altruistic Programmer's Blog (KR)

이타주의 프로그래머의 블로그

Archive for the ‘exception’ tag

C++ 라이브러리 만들기는 어려워

with 2 comments

[옛날 블로그 글입니다. 2009.08.24]
[추가1. VC++의 예외 바이너리 호환성에 대해 내용 추가(정정)했습니다]

오랜만에 시간이 좀 나서, 요새 머리를 괴롭히는 이슈를 좀 적어 봅니다.

회사에서 C++ 라이브러리를 만들고 있습니다. 이녀석을 바이너리(dll)로 배포할 목적입니다. 컴파일러 버전별로 다 제공하기에는 무리가 있으니, 컴파일러 버전에 독립적인 바이너리가 되도록 하고 싶습니다.

이 목적을 달성하기 위해서는

  • DLL에서 할당한 메모리를 EXE에서 해제하면 안됩니다.
  • DLL의 인터페이스에서 STL을 쓰면 안됩니다.
  • DLL에서 예외를 던지면 안됩니다.

이 규칙을 모두 만족하는 인터페이스란

  • 바로 Windows API와 같은 C 함수들
  • COM 인터페이스

정도가 되겠네요.

DLL에서 할당한 메모리는 DLL에서 해제

메모리를 할당해 준 CRT가 해제해야 한다는 얘기죠. 컴파일러 버전에 독립적이려면 DLL을 정적 CRT 라이브러리와 링크하는게 좋은데, 이러면 DLL과 EXE가 사용하는 CRT가 달라집니다. 그래서 DLL에서 할당한 메모리를 EXE에서 해제하면 안됩니다.

같은 컴파일러 버전으로 빌드된 DLL과 EXE이더라도 CRT가 다르면 조심해야 합니다. 같은 CRT(DLL)를 쓴다면 신경 안 써도 되구요.

그래서 DLL에서 문자열이나 메모리 블럭을 건네줄 일이 있다면, COM처럼 자기네 CRT에서 할당/해제하는 SysAlloc(), SysFree() 같은 함수를 만들어 제공하기도 합니다. DLL에서 SysAlloc()으로 메모리 블럭을 할당해서 주면 EXE에서 SysFree()로 해제하는 식이죠.

아니면 전통적인 방식으로 EXE에서 DLL에게 '메모리 블럭 크기가 몇이냐?' 물어보고 나서, EXE가 메모리를 할당해서 DLL에게 '여기에 넣어줘'라고 말하는 방법이 있습니다.

SysAlloc() 방식은 크기를 물어보는 등의 귀찮은 작업이 없는게 장점이고, 전통적인 방식은 사용자(EXE)가 자신의 메모리 풀 같은 것은 만들어 쓸 때 유리한 면이 있습니다.

DLL의 인터페이스에서 STL을 쓰면 안됨

STL의 구현이 컴파일러 버전별로 달라서 바이너리 호환성이 없는 것도 문제고, 메모리가 DLL경계를 넘어서는 경우도 있어서 안된다고 하네요.

네, 그래서 다른 언어 부럽지 않게 std::string을 반환하고 싶은 바램은 포기해야 합니다.

DLL에서 예외를 던지면 안됨

역시나 C++에 바이너리 표준이 없기 때문인데요, 예외 매커니즘을 구현하는 방식도 표준화 되어 있지 않기 때문에 컴파일러별로 다른게 현실입니다. 그래도 Visual C++이라면 같지 않을까 하는 기대감에 테스트를 해봤는데요.

VC++ 2008에서 예외를 던지는 DLL을 만듭니다. 그리고 VC++ 6.0, 2003, 2005, 2008, 2010에서 예외를 받는 EXE를 만듭니다. 결과는.. VC++ 6.0, 2003에서는 프로그램이 죽습니다. -_-;;

[추가1. 위에 테스트는 std::exception 오브젝트를 던지는 것였는데, std::exception의 구현차이 때문에 예외를 못받은 것 같습니다. 그냥 직접 만든 클래스의 포인터를 넘기니 모든 버전의 VC++에서 예외를 잘 받네요. 고민입니다. gcc도 비슷하다면 예외를 써도 되지않을까 고민중입니다.. ]

이로서 다른 언어 부럽지 않게 예외 처리를 하고 싶은 바램도  포기해야 합니다.

마무리

요즘의 언어처럼 쓰기 편한 인터페이스를 만들고 싶었는데 C++은 C++스럽게 써야만 하는 부분이 있었네요. 이래서 MS에서 닷넷 프레임웍을 만들었나 싶은 생각도 들었습니다.

참고자료

Written by muscly

August 24th, 2009 at 3:26 pm

이벤트핸들러에서 예외 던지기

with 2 comments

[옛날 블로그 글입니다. 2009.07.25]

콜백함수나 이벤트핸들러에서 무언가 문제가 생겨서 예외를 던지고 싶습니다. 어떻게 해야 할까요?

그냥 예외를 던지면 콜백함수나 이벤트핸들러를 호출해 준 라이브러리로 예외가 날라가는데, 그 라이브러리는 예외를 처리할 수가 없습니다. 왜냐면 그 라이브러리는 누가 호출되는지 모르고 그냥 등록된 함수만 호출한거지요. 그래서 그 예외를 던진 녀석이 누군지도 모르고, 또 몰라야만 하고, 예외가 어떤 타입이며, 무슨 의미인지 알 수가 없습니다.

이런 고민에 자료를 찾아보니 같은 고민을 하신 분이 계시네요.
해결책을 던져주지는 않지만 훌륭한 통찰을 제공해주고 있습니다.
http://se.informatik.uni-oldenburg.de:30000/346/1/callback.pdf

우선 이 문서를 읽고 내린 결론부터 얘기하면

  • 콜백함수나 이벤트핸들러에서는 예외를 내지 않는다 -_-;;
  • 라이브러리가 아닌, 그 예외를 처리할 수 있는 누군가에게 함수호출등으로 예외를 알린다.

문서에서 말하는 훌륭한 통찰이란, 예외가 어디로 던져져야 하는지에 대한 규칙입니다.

  • 예외의 발생으로 (안좋은) 영향을 받는 곳 (“나쁜 뉴스를 숨기지 말아라”)
  • 예외로 인한 나쁜 결과를 벌충할 수 있는 곳 (“네 문제를 도와줄 수 없는 곳에 가져가지 마라”)
  • 재발생을 막기위해 예외의 원인을 제거할 수 있는 곳 (“증상이 아니라 병을 고쳐라”)

관련해서 좋은 아이디어나, 자료 있으시면 알려주세요~

Written by muscly

July 25th, 2009 at 3:20 pm