C++가 '우리는 집에서 시도해 보세요'라고 말한다.
Source: Hacker News
개요
예외를 지원하는 많은 언어¹는 finally 절도 제공하므로 다음과 같이 쓸 수 있습니다:
try {
// ⟦ stuff ⟧
} finally {
always();
}
간단히 살펴보면 이 제어 구조가 Java, C#, Python, JavaScript에는 존재하지만 C++에는 존재하지 않음을 알 수 있습니다.
C++ 접근법
C++에서는 스코프를 벗어날 때 소멸자를 이용해 코드를 실행할 수 있습니다. 소멸자는 스코프가 종료될 때 실행되기 때문입니다. 이는 Windows Implementation Library의 wil::scope_exit 함수가 사용하는 기법으로, 사용자가 제공한 람다가 사용자 정의 객체 안에 저장되고 그 객체의 소멸자가 람다를 실행합니다.
auto ensure_cleanup = wil::scope_exit([&] { always(); });
/* ⟦ stuff ⟧ */
예외 처리 특이점
원리는 동일하지만, finally 블록이나 소멸자 자체가 예외를 발생시킬 경우를 언어마다 다르게 처리합니다.
보호 블록에서 예외가 발생하지 않은 경우
보호 블록을 예외 없이 빠져나갈 때, finally 블록이나 소멸자에서 발생한 잡히지 않은 예외는 try 블록으로부터 발생한 것으로 간주되어 던져집니다. 모든 언어가 이 동작에 동의하는 것으로 보입니다.
보호 블록과 finally/소멸자 모두에서 예외가 발생한 경우
| 언어 | 보호 블록과 finally/소멸자 모두 예외를 던질 때의 동작 |
|---|---|
| Java, Python, JavaScript, C# | finally 블록에서 발생한 예외가 원래 예외를 덮어쓰기 때문에 원래 예외가 사라집니다. 업데이트: Python 3.2부터는 원래 예외를 새로운 예외의 context 로 저장하지만, 전파되는 예외는 여전히 새로운 예외입니다. |
| C++ | 다른 예외가 이미 활성화된 상태에서 소멸자가 예외를 던지면 프로그램이 종료됩니다.¹ |
따라서 C++에서는 스코프를 벗어날 때 코드를 실행할 수 있지만, 프로그램 종료를 피하려면 그 코드가 예외를 밖으로 전달하지 않도록 해야 합니다.
각주
¹ Microsoft 컴파일러는 Structured Exception Handling(SEH)을 위해 __try와 __finally 키워드도 지원합니다. 이는 C 코드를 위해 설계된 것이며, C++에서 사용할 경우 C++ 예외와 혼동을 일으킬 수 있습니다. 자세한 내용은 oldnewthing.com에서 논의된 바를 참고하십시오.
² 이 때문에 wil::scope_exit은 람다가 예외를 던지면 프로세스를 종료한다고 문서화하고 있습니다. 대안인 wil::scope_exit_log는 람다에서 발생한 예외를 기록한 뒤 무시합니다. Java와 같은 동작을 흉내 내는 변형은 존재하지 않습니다.