오류 관리

이 단원에서는 GSL 함수들이 오류를 보고하는 방법과 이러한 오류들을 다루는 방법에 대해 서술합니다. 오류 관리 기능들은 함수들이 반환하는 상태 값을 보고, 해당 함수의 실행이 정상적으로 이루어졌는 지 판별할 수 있습니다. 오류가 발생했을 시, 실패한 원인이 무엇인지에 대한 판별도 제공할 수 있습니다. 또, 사용자 정의 오류 관리 함수를 추가하여 라이브러리의 기본 행동들에 변화를 줄 수도 있습니다.

이 단원에서 다루는 함수들은 gsl_errno.h 에 기술되어 있습니다.

오류 보고

이 라이브러리는 POSIX 스레드 라이브러리에 기술된 스레드-안전 오류 보고 규약을 따릅니다. 함수들은 오류 발생시 각각의 오류를 나타내는 0이 아닌 오류값을 반환하고, 성공적으로 실행되었을 시 0 값을 반환합니다.

int status = gsl_function (...)

 if (status) { /* an error occurred */
   .....
   /* status value specifies the type of error */
 }

라이브러리의 명령어 집합들은 필요한 작업을 더 이상 수행할 수 없을 때 오류를 보고합니다. 예를 들어서, 근을 찾는 함수는 요구되는 정확도 이상으로 근이 수렴하지 않을 때 0이 아닌 오류 값을 반환합니다. 이러한 상황은 수학 라이브러리를 사용할 때 빈번히 발생합니다. 따라서 호출한 함수들의 반환 값을 반드시 확인해야 합니다.

어떤 상황이든지 명령어가 오류를 보고한 상황의 반환 값은 오류의 종류를 나타냅니다. 반환 값은 표준 C 라이브러리의 errno 값과 유사합니다. 호출자는 반환 값을 보고 어떤 행동을 취할지 선택할 수 있습니다. 만약 중요한 오류가 아니라면 무시할 수도 있습니다.

코드가 반환하는 오류 보고 외에도 라이브러리에는 오류 관리 함수인 gsl_error() 가 있습니다. 이 함수는 다른 라이브러리의 다른 함수들에서 오류를 보고할 때 호출자에게 값을 반환하기 직전에 호출됩니다. 이 오류 관리 함수의 기본 행동은 메세지를 화면에 출력하고 프로그램을 정지시킵니다.

gsl: file.67: ERROR: invalid argument supplied by user
Default GSL error handler invoked.
Aborted

gsl_error() 관리 함수의 제공 목적은 디버거 아래에서 실행 되는 상황에서, 라이브러리의 오류를 잡을 수 있는 중단점을 설정하는 함수를 제공함에 있습니다. 이는 반환 값을 보고 오류를 잡는 상업용 프로그램에서는 사용해서는 안됩니다.

오류 값

라이브러리 함수들이 반환하는 오류 값 번호들은 gsl_errno.h 에 정의되어 있습니다. 모두 접두사 GSL 이 붙어있고 0이 아닌 정수 형태의 상수값으로 정의되어 있습니다. 1024 이상의 오류 값은 응용 프로그램용으로 예약되었으며 라이브러리에서 사용되지 않습니다. 대부분의 오류 값은 C 라이브러리에서 해당 오류 값와 동일한 기본 이름을 사용합니다. 다음은 가장 일반적인 오류 값들입니다.

int GSL_EDOM

도메인 오류: 수학 함수에서 인수 값이 함수가 정의된 정의역에 포함되지 않는 경우 사용됩니다. 표준 C 라이브러리의 EDOM 와 같은 상황에서 사용됩니다.

int GSL_ERANGE

범위 오류: 수학 함수에서 오버플로우나 언더플로우로 인해 결과 값을 나타낼 수 없는 경우 사용됩니다. 표준 C 라이브러리의 ERANGE 와 같은 상황에서 사용됩니다.

int GSL_ENOMEM

사용 가능한 메모리가 없음: 용량이 꽉 찼기 때문에 시스템이 가상 메모리를 더 할당할 수 없음을 나타냅니다. 표준 C 라이브러리의 ENOMEM 와 같은 상황에서 사용됩니다. 이 오류는 GSL 구현체에서 malloc() 계열의 함수로 메모리 할당이 불가능할 때 보고됩니다.

int GSL_EINVAL

잘못된 인수: 이는 잘못된 인수를 라이브러리 내 함수에 전달한 대다수의 문제를 나타내기 위해 사용됩니다. 표준 C 라이브러리의 EINVAL 와 유사합니다.

각각의 오류 값들은 함수 gsl_strerror() 를 사용해 오류 메세지로 바꿀 수 있습니다.

const char * gsl_strerror(const int gsl_errno)

이 함수는 각각의 오류 값 gsl_errorno 에 대한 설명을 문자열로 반환합니다. 예를 들어서:

printf ("error: %s\n", gsl_strerror (status));

이 코드는 error: output range error 와 같은 오류 메세지를 반환할 것입니다. 이는 오류 값 GSL_ERANGE 를 기술하는 메세지입니다.

오류 관리자

GSL 오류 관리자는 기본적으로 짧은 메세지를 출력하고, abort() 를 부릅니다. 이 기본 설정은 라이브러리 함수들이 오류를 보고할 때 코어 덤프(core-dump)를 일으키며 프로그램을 정지시킵니다. 페일-세이프(fail-safe) 방식을 기본 설정으로 하도록 의도되었기 때문에, 라이브러리 내장 기능들의 반환값을 확인하지 않습니다. 이러한 방식으로 프로그램을 짜는 것은 권장하지 않습니다.

만약 기본 오류 관리자를 종료시키면, 프로그래머는 직접 각 함수들의 반환 값을 확인하고 다룰 책임이 생깁니다. 오류 발생시 행동할 절차를 새로운 오류 관리자를 제공해 수정할 수도 있습니다. 예를 들어서 오류 관리자가 파일에 모든 오류 기록을 저장하도록 하거나, 특정 오류 조건(언더플로우 같은)을 무시, 아니면 디버거를 실행시켜 현재 프로세스에 연결할 수도 있습니다.

모든 GSL 오류 관리자들은 gsl_error_handler_t 자료형으로 정의됩니다. 이는 gsl_errno.h 에 정의되어 있습니다.

type gsl_error_handler_t

GSL 오류 관리자 함수의 자료형입니다. 오류 관리자는 4개의 인자를 넘겨 받습니다. 이 인자들은 각각 오류의 이유(문자열), 오류가 발생한 소스 파일의 이름(문자열), 그 파일에서 오류가 발생한 줄 숫자(정수), 그리고 오류 값(정수)을 나타냅니다. 소스 파일과 줄 숫자는 컴파일 시간에 전처리기에 의해 __FILE__ 그리고 __LINE__ 를 이용해 결정됩니다. 오류 관리자는 void 형태로 값을 반환합니다.

사용자 정의 오류 관리자는 다음과 같이 정의되어야 합니다.

void handler(const char *reason, const char *file, int line, int gsl_errno)

사용자 정의 오류 관리자를 사용하기 위해서는 gsl_set_error_handler() 함수를 호출해야합니다. 이 함수도 gsl_errno.h 에 정의되어 있습니다.

gsl_error_handler_t *gsl_set_error_handler(gsl_error_handler_t *new_handler)

이 함수는 GSL 명령어 집합을 위한 new_handler 는 새로운 오류 관리자를 설정합니다. 이전 관리자는 반환됩니다(나중에 복구할 수 있습니다.) 사용자 정의 오류 관리자 함수가 전역 변수로 저장된다는 사실을 유의해야합니다. 이로 인해 한 개의 프로그램에 1개의 오류 관리자만이 사용될 수 있습니다. 이 함수는 다중 스레드 프로그램에서 사용될 수 없습니다. 주 스레드에서 프로그램 전체 오류 관리를 하도록 하는 예외 상황에서는 사용할 수 있습니다. 다음 예제는 어떻게 새로운 오류 관리자를 설정하고 복구하는지 예시를 보여줍니다.

/* save original handler, install new handler */
old_handler = gsl_set_error_handler (&my_handler);
/* code uses new handler */
.....
/* restore original handler */
gsl_set_error_handler (old_handler);

기본 오류 관리자(오류 발생시 abort() 를 호출)를 사용하려면 오류 관리자에 NULL 을 넣어주면 됩니다.

old_handler = gsl_set_error_handler (NULL);
gsl_error_handler_t *gsl_set_error_handler_off()

오류 관리자 기능을 아무것도 하지 않도록 설정해 꺼버립니다. 어떤 오류가 발생해도 프로그램이 계속 작동하도록 하기 때문에, 라이브러리 함수들의 반환 값을 반드시 확인해야 합니다. 실제 상용 프로그램 단계에서 설정하는 것을 권장합니다. 이전 관리자가 반환되기 때문에 나중에 복구할 수도 있습니다.

gsl_errno.h 에 정의된 GSL_ERROR 매크로를 사용자가 정의해서 라이브러리를 재컴파일하면, 특정 응용 프로그램에서 오류의 행동을 수정할 수 있습니다.

사용자 정의 함수에서 GSL 오류 보고 사용하기

GSL 코드를 이용해 수치 계산 함수를 프로그램 안에 작성했다면 라이브러리와 같은 오류 보고 규약를 사용하는 것이 효율적입니다.

오류를 보고하기 위해서는 함수에서 gsl_error() 를 호출해서 오류를 설명하는 문자열과 gsl_errno.h 에 기술된 적절한 오류 값, 아니면 특정한 값(예: NaN )을 넘겨주어야 합니다. gsl_errno.h 에서는 이러한 과정을 효율적으로 처리해 줄 수 있는 두 개의 매크로를 제공합니다.

GSL_ERROR(reason, gsl_errno)

GSL 규약에 따라 오류를 보고하고 gsl_errno 상태 값을 반환합니다. 이는 다음과 같은 함수로 확장해 볼 수 있습니다.

gsl_error (reason, __FILE__, __LINE__, gsl_errno);
return gsl_errno;

gsl_errno.h 에 정의 되어있고, do {...} while(0) 로 감싸져 있습니다. 이는 구문 분석 문제를 방지하기 위함입니다.

다음은 함수가 요구하는 정밀도를 tolerance 까지 만족시키지 못했을 때, 매크로를 사용해 오류를 보고하는 예제를 나타냅니다. 오류를 보고를 위해 함수에서 오류 값 GSL_ETOL 를 반환해야 합니다.

    if (residual > tolerance){
    GSL_ERROR("residual exceeds tolerance", GSL_ETOL);
}
GSL_ERROR_VAL(reason, gsl_errno, value)

GSL_ERROR 매크로와 똑같습니다. 하지만 사용자 정의 값 value 를 오류 값 대신에 반환합니다. 이는 부동 소수점 값을 반환하는 수치 함수에서 쓰일 수 있습니다.

다음 예제는 GSL_ERROR_VAL 사용해 수학적 특이점에서 NaN 값을 반환하는 것을 보여줍니다.

    if (x == 0){
GSL_ERROR_VAL("argument lies on singularity", GSL_ERANGE, GSL_NAN);
}

예제

다음은 오류가 보고될 수 있는 함수의 반환 값을 확인하는 예제 코드입니다.

#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_fft_complex.h>
...
int status;
size_t n = 37;
gsl_set_error_handler_off();
status = gsl_fft_complex_radix2_forward (data, stride, n);

if (status) {
     if (status == GSL_EINVAL) {
          fprintf (stderr, "invalid argument, n=%d\n", n);
     } else {
          fprintf (stderr, "failed, gsl_errno=%d\n", status);
     }
     exit (-1);
}
...

함수 gsl_fft_complex_radix2_forward() 는 2의 거듭 제곱인 정수형 길이만을 인자로 받습니다. 만약 변수 n 가 2의 거듭 제곱이 아니라면, 함수는 GSL_EINVAL 값을 반환해 길이 인자가 부적절 하다고 알릴 것입니다. 함수 gsl_set_error_handler_off() 를 호출해 기본 오류 관리자를 멈추어 프로그램이 정지하는 것을 막습니다. else 구문은 다른 오류들을 감지합니다.