문제

내가 사용한다면 assert() 그러면 어설션이 실패합니다. assert() 전화할 것이다 abort(), 실행 중인 프로그램이 갑자기 종료됩니다.내 프로덕션 코드에서는 이를 감당할 수 없습니다.런타임에 주장하면서 실패한 주장을 포착하여 적절하게 처리할 수 있는 방법이 있습니까?

도움이 되었습니까?

해결책

예, 실제로는 있습니다.C++의 경우처럼 사용자 정의 Assert 함수를 직접 작성해야 합니다. assert() 정확히 C야 assert(), 와 더불어 abort() "기능"이 번들로 제공됩니다.다행히도 이는 놀라울 정도로 간단합니다.

주장.hh

template <typename X, typename A>
inline void Assert(A assertion)
{
    if( !assertion ) throw X();
}

위 함수는 조건자가 유지되지 않으면 예외를 발생시킵니다.그러면 예외를 잡을 수 있는 기회가 생깁니다.예외를 catch하지 않으면 terminate() 호출되어 다음과 유사하게 프로그램이 종료됩니다. abort().

프로덕션용으로 구축할 때 어설션을 최적화하면 어떻게 되는지 궁금할 것입니다.이 경우 프로덕션용으로 빌드하고 있음을 나타내는 상수를 정의한 다음 이를 참조할 수 있습니다. Assert().

디버그.hh

#ifdef NDEBUG
    const bool CHECK_WRONG = false;
#else
    const bool CHECK_WRONG = true;
#endif

메인.cc

#include<iostream>

struct Wrong { };

int main()
{
    try {
        Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5);
        std::cout << "I can go to sleep now.\n";
    }
    catch( Wrong e ) {
        std::cerr << "Someone is wrong on the internet!\n";
    }

    return 0;
}

만약에 CHECK_WRONG 상수이고 다음 호출은 Assert() 어설션이 상수 표현식이 아니더라도 프로덕션에서 컴파일됩니다.참고로 조금 아쉬운점이 있습니다 CHECK_WRONG 조금 더 입력해 보겠습니다.그러나 그 대가로 우리는 다양한 어설션 그룹을 분류하고 적절하다고 판단되는 대로 각 어설션을 활성화 및 비활성화할 수 있다는 이점을 얻습니다.예를 들어 프로덕션 코드에서도 활성화하려는 어설션 그룹을 정의한 다음 개발 빌드에서만 보고 싶은 어설션 그룹을 정의할 수 있습니다.

그만큼 Assert() 기능은 입력과 동일합니다.

if( !assertion ) throw X();

그러나 이는 프로그래머의 의도를 명확하게 나타냅니다.주장을 해보세요.일반 접근 방식과 마찬가지로 이 접근 방식을 사용하면 주장을 파악하기가 더 쉽습니다. assert()에스.

이 기술에 대한 자세한 내용은 Bjarne Stroustrup의 The C++ 프로그래밍 언어 3e, 섹션 24.3.7.2를 참조하세요.

다른 팁

glib의 오류 보고 기능 주장 후에 계속하는 접근 방식을 취하십시오.glib는 Gnome(GTK를 통해)이 사용하는 기본 플랫폼 독립 라이브러리입니다.다음은 전제 조건을 확인하고 전제 조건이 실패할 경우 스택 추적을 인쇄하는 매크로입니다.

#define RETURN_IF_FAIL(expr)      do {                  \
 if (!(expr))                                           \
 {                                                      \
         fprintf(stderr,                                \
                "file %s: line %d (%s): precondition `%s' failed.", \
                __FILE__,                                           \
                __LINE__,                                           \
                __PRETTY_FUNCTION__,                                \
                #expr);                                             \
         print_stack_trace(2);                                      \
         return;                                                    \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                       \
 {                                                                  \
        fprintf(stderr,                                             \
                "file %s: line %d (%s): precondition `%s' failed.",     \
                __FILE__,                                               \
                __LINE__,                                               \
                __PRETTY_FUNCTION__,                                    \
                #expr);                                                 \
         print_stack_trace(2);                                          \
         return val;                                                    \
 };               } while(0)

다음은 gnu 툴체인(gcc)을 사용하는 환경을 위해 작성된 스택 추적을 인쇄하는 함수입니다.

void print_stack_trace(int fd)
{
    void *array[256];
    size_t size;

    size = backtrace (array, 256);
    backtrace_symbols_fd(array, size, fd);
}

매크로를 사용하는 방법은 다음과 같습니다.

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.

    if( ptr != NULL )        // Necessary if you want to define the macro only for debug builds
    {
       ...
    }

    return ptr;
}

void doSomethingElse(char *ptr)
{
    RETURN_IF_FAIL(ptr != NULL);
}

C/C++의 어설션은 디버그 빌드에서만 실행됩니다.따라서 런타임에는 이런 일이 발생하지 않습니다.일반적으로 주장은 버그가 발생하는 경우 이를 표시해야 하며 일반적으로 코드 등에 가정을 표시해야 합니다.

런타임(릴리스 시)에 오류를 확인하는 코드를 갖고 싶다면 어설션 대신 예외를 사용해야 할 것입니다.귀하의 답변은 기본적으로 어설션 구문으로 예외 발생기를 래핑합니다.이것이 작동하는 동안 처음에 예외를 발생시키는 것보다 내가 볼 수 있는 특별한 이점은 없습니다.

"assert.h"(Mac OS 10.4)에 있는 내용은 다음과 같습니다.

#define assert(e) ((void) ((e) ? 0 : __assert (#e, __FILE__, __LINE__)))
#define __assert(e, file, line) ((void)printf ("%s:%u: failed assertion `%s'\n", file, line, e), abort(), 0)

이를 기반으로 abort() 호출을 throw( 예외 )로 대체합니다.그리고 printf 대신 문자열을 예외 오류 메시지로 형식화할 수 있습니다.결국 다음과 같은 결과를 얻게 됩니다.

#define assert(e) ((void) ((e) ? 0 : my_assert (#e, __FILE__, __LINE__)))
#define my_assert( e, file, line ) ( throw std::runtime_error(\
   std::string(file:)+boost::lexical_cast<std::string>(line)+": failed assertion "+e))

나는 그것을 컴파일하려고 시도하지 않았지만 의미를 알 수 있습니다.

메모:"예외" 헤더와 부스트 헤더가 항상 포함되어 있는지 확인해야 합니다(오류 메시지 형식을 지정하는 데 사용하기로 결정한 경우).그러나 "my_assert"를 함수로 만들고 프로토타입만 선언할 수도 있습니다.다음과 같은 것 :

void my_assert( const char* e, const char* file, int line);

그리고 필요한 모든 헤더를 자유롭게 포함할 수 있는 곳에 구현하세요.

필요한 경우 #ifdef DEBUG로 감싸고, 항상 검사를 실행하고 싶은 경우에는 사용하지 마세요.

어설션에 대한 정보가 포함된 문자열을 던지고 싶다면 다음을 수행하세요.http://xll8.codeplex.com/SourceControl/latest#xll/ensure.h

_set_error_mode(_OUT_TO_MSGBOX);

저를 믿으세요. 이 기능이 도움이 될 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top