과학 라이브러리에서 오류를 어떻게보고해야합니까?


11

다른 소프트웨어 엔지니어링 분야에는 라이브러리가 오류 또는 기타 예외적 인 조건을 처리하는 방법에 대한 철학이 많이 있습니다. 내가 본 몇 가지 :

  1. 포인터 인수가 반환 한 결과와 함께 오류 코드를 반환합니다. 이것이 PETSc가하는 일입니다.
  2. 센티넬 값으로 오류를 반환합니다. 예를 들어, malloc은 메모리를 할당 할 수 없으면 NULL sqrt을 반환하고 음수를 전달하면 NaN을 반환합니다.이 방법은 많은 libc 함수에서 사용됩니다.
  3. 예외를 던지십시오. 거래 II, Trilinos 등에서 사용
  4. 변형 유형을 반환합니다. 예를 들어, Result올바르게 실행되면 유형의 객체를 반환 하고 유형 Error을 사용하여 실패한 방법을 설명하는 C ++ 함수가 반환 됩니다 std::variant<Error, Result>.
  5. assert와 crash를 사용하십시오. p4est 및 igraph의 일부에서 사용됩니다.

각 접근 방식의 문제점 :

  1. 모든 오류를 검사하면 추가 코드가 많이 발생합니다. 결과가 저장 될 값은 항상 먼저 선언되어야하며 한 번만 사용될 수있는 많은 임시 변수가 도입됩니다. 이 접근 방식은 어떤 오류가 발생했는지 설명하지만 심층 통화 스택의 위치 또는 이유를 결정하기 어려울 수 있습니다.
  2. 오류 사례는 무시하기 쉽습니다. 또한 전체 출력 유형 범위가 그럴듯한 결과 인 경우 많은 함수가 의미있는 센티넬 값을 가질 수 없습니다. # 1과 동일한 문제가 많습니다.
  3. C 또는 Fortran이 아닌 C ++, Python 등에서 만 가능합니다. setjmp / longjmp sorcery 또는 libunwind를 사용하여 C에서 흉내낼 수 있습니다 .
  4. C 또는 Fortran이 아닌 C ++, Rust, OCaml 등에서 만 가능합니다. 매크로 마법을 사용하여 C에서 흉내낼 수 있습니다.
  5. 아마도 가장 유익한 정보 일 것입니다. 그러나 파이썬 래퍼를 작성하는 C 라이브러리 에이 접근법을 채택하면 범위를 벗어난 인덱스를 배열에 전달하는 것과 같은 어리석은 실수로 인해 파이썬 인터프리터가 충돌합니다.

인터넷에서 오류 처리에 대한 많은 조언은 운영 체제, 임베디드 개발 또는 웹 응용 프로그램의 관점에서 작성되었습니다. 충돌은 용납 할 수 없으며 보안에 대해 걱정해야합니다. 과학적 응용 프로그램은 이러한 문제가 거의 같은 정도로 거의 없습니다.

또 다른 고려 사항은 어떤 종류의 오류를 복구 할 수 있는지 여부입니다. malloc 실패는 복구 할 수 없으며, 어떠한 경우에도 OS 메모리 부족 킬러가이를 수행합니다. 배열 크기에 대한 범위를 벗어난 인덱스도 복구 할 수 없습니다. 사용자에게는 라이브러리가 할 수있는 가장 좋은 일은 유익한 오류 메시지로 충돌하는 것입니다. 한편, 직접 선형 분해 솔버를 사용하면 반복 선형 솔버가 수렴하지 못하는 것을 복구 할 수 있습니다.

과학 라이브러리는 오류를 어떻게보고하고 처리해야합니까? 물론 라이브러리가 어떤 언어로 구현되는지에 달려 있다는 것을 알고 있습니다. 그러나 충분히 유용한 라이브러리에 대해서는 사람들이 구현 한 언어가 아닌 다른 언어로 호출하기를 원할 것입니다.

따로, C 라이브러리에서 전역 어설 션 핸들러 함수 포인터를 공개 API의 일부로 정의하면 접근법 # 5가 실질적으로 향상 될 수 있다고 생각합니다. 어설 션 처리기는 기본적으로보고 파일 / 줄 번호 및 충돌로 설정됩니다. 이 라이브러리의 C ++ 바인딩은 대신 C ++ 예외를 발생시키는 새로운 어설 션 핸들러를 정의합니다. 마찬가지로 Python 바인딩은 CPython API를 사용하여 Python 예외를 발생시키는 어설 션 핸들러를 정의합니다. 그러나 나는이 접근법을 취하는 예를 모른다.


또 다른 고려 사항은 성능 저하입니다. 이러한 다양한 방법이 소프트웨어 속도에 어떤 영향을 줍니까? 코드의 "제어"부분 (예 : 입력 파일 처리)과 계산 비용이 많이 드는 "엔진"에서 서로 다른 오류 처리를 사용해야합니까?
LedHead

최상의 답변은 언어마다 다릅니다.
chrylis

답변:


10

나는 당신에게 내 관점을 줄 것이고, 그것은 당신이 참조하는 거래 II 프로젝트에 인코딩되어 있습니다.

먼저, 복구 할 수없는 오류와 복구 할 수없는 오류의 두 가지 오류 조건이 있습니다.

  • 전자는 예를 들어 입력 파일을 읽을 수없는 경우, 예를 들어 파일에서 정보를 읽는 $HOME/.dealii중이거나 존재하지 않을 수있는 경우입니다. 읽기 기능은 후자의 기능을 파악하기 위해 호출 기능으로 돌아 가야합니다. 또한 현재 자원을 사용할 수 없지만 1 분 안에 다시있을 수도 있습니다 (원격으로 마운트 된 파일 시스템).

  • 후자는 예를 들어, 크기가 10 인 벡터를 크기가 20 인 벡터에 추가하려는 경우 : 시도해 볼 수있는 것은 없습니다. 코드에는 버그가 있습니다. 우리가 추가를 시도한 지점.

이 두 가지 조건은 사용중인 프로그래밍 언어에 관계없이 다르게 처리해야합니다.

  • 두 번째 경우에는 상환이 없으므로 프로그램을 종료하십시오. 예외를 던지거나 호출자에게 수행 할 수있는 작업이 없음을 나타내는 오류 코드를 반환하여이를 수행 할 수 있지만, 프로그래머가 문제를 훨씬 쉽게 디버그 할 수있게하므로 프로그램을 즉시 중단 할 수도 있습니다.

  • 전자의 경우, 처리 할 수 있는 예외적 인 상황이 발생했습니다. 비록 C와 Fortran이 이것을 표현할 수단이 없었지만, 나중에 온 모든 합리적인 언어는 언어 예외에 대한 "예외"를 제공함으로써 그러한 "예외"반환을 처리하기위한 방법을 언어 표준에 통합했습니다. 이것들을 사용하십시오 – 그것이 그들이있는 것입니다; 그것들은 또한 당신이 그것들을 무시하는 것을 잊을 수없는 방식으로 설계되었습니다 (그렇다면 예외는 단지 한 단계 더 높은 수준으로 전파됩니다).

다시 말해서, 내가 여기서 옹호하는 것 (및 거래 II가하는 것)은 상황에 따라 전략 3과 5가 혼합 된 것입니다. C 또는 Fortran과 같은 언어에서는 3이 작동하지 않는다는 것이 사실입니다.이 경우 원하는 언어를 표현하기 어려운 언어를 사용하지 않는 것이 좋습니다.

오류를 복구 할 수없는 경우에도 일부 시스템은 충돌하지 않아야합니다. 예를 들어 통계 샘플링 체계에서 주어진 입력에 대한 우도 함수를 평가하기 위해 여러 쿼리에 대해 일련의 함수가 반복적으로 호출되는 경우가 있습니다. 문제가 해당 상황에서 이해가되지 않기 때문에 평가자가 음수 값을 처리하지 못할 수 있습니다 (예 : 두께 의 금속판의 강성 평가)x), 평가자를 반복적으로 호출해야하므로 충돌이 아니라 예외를 발생시켜야합니다. 이러한 경우, 음의 값을 전달하는 것이 복구 불가능하더라도 프로그램을 중단하기보다는 예외를 처리해야합니다. 나는 2 년 전에이 입장에 동의하지 않았지만, xSDK 커뮤니티 소프트웨어 가이드 라인이 프로그램이 절대로 충돌하지 않아야한다는 요구 사항을 인코딩 한 후 (또는 적어도 충돌에서 예외로 전환 할 수있는 방법이 있어야 함) 거래를 바꾸었다. II에는 Assert을 호출하는 대신 예외를 던질 수있는 옵션이 abort()있습니다.)


상황을 처리 할 수 ​​없을 때 예외를 throw하고 처리 할 수있을 때 오류 코드를 반환하는 것이 좋습니다. 문제는 throw 된 예외를 처리하는 것이 까다 롭다는 것입니다. 응용 프로그램 프로그래머는 예외를 포착하고 처리하기 위해 가능한 모든 예외 유형을 알아야합니다. 그렇지 않으면 프로그램이 중단됩니다. 충돌은 괜찮으며 처리 할 수없는 상황에도 환영합니다. 예를 들어 충돌 지점은 파이썬과 함께 기본적으로보고되기 때문에 처리 할 수 있는 상황에서는 (대부분) 환영받지 못합니다.
cdalitz

@ cdalitz : C ++의 디자인 결함으로 모든 유형의 객체를 던질 수 있습니다. 그러나 모든 합리적인 소프트웨어 (Trilinos 제외)는에서 파생 된 예외 만 발생 std::exception하며 파생 된 유형을 모른 채 참조로 잡을 수 있습니다.
Wolfgang Bangerth

1
그러나 나는 원래 질문에 요약 된 이유로 오류 코드를 반환하는 것에 동의하지 않습니다. (i) 오류 코드는 너무 자주 무시되며 결과적으로 오류가 전혀 처리되지 않습니다. (ii) 많은 경우에 함수의 반환 유형이 고정되어 있다고 합리적으로 반환 될 수있는 예외적 인 값은 없습니다. (iii) 함수는 서로 다른 반환 유형을 가지며, 각 경우에 "예외"값이 오류를 나타내는 값을 개별적으로 정의해야합니다.
Wolfgang Bangerth

WB는 썼습니다 (죄송합니다, '@'트릭이 어떤 이유로 작동하지 않으며 어떤 이유로 인해 사용자 이름이 StackExchage에 의해 제거되었습니다) : "오류 코드가 너무 자주 무시됩니다." 예외 포착에는 더 많은 기능이 포함됩니다. 많은 소프트웨어 개발자가 try / catch 블록에서 모든 함수 호출을 브라케팅하는 데 어려움을 겪지는 않습니다. 그러나 그것은 대부분 미각의 문제입니다. 문서에 함수가 던지는 예외와 예외를 명확하게 명시하고 있다면 처리 할 수 ​​있습니다. 그러나 다시 말할 수 있습니다 : 문서를 작성하는 의무는 너무 자주 무시됩니다 ;-)
cdalitz

그러나 요점은 예외 포착을 잊어 버린 경우 다운 스트림 문제가 없다는 것입니다. 프로그램이 중단됩니다. 문제가 발생한 위치를 쉽게 찾을 수 있습니다. 오류 코드 확인을 잊어 버린 경우 정의되지 않은 내부 상태로 인해 프로그램이 나중에 중단 될 수 있지만 원래 문제는 완전히 명확하지 않은 상태입니다. 이런 종류의 버그를 찾는 것은 대단히 어렵습니다.
Wolfgang Bangerth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.