c++ - 파이썬 - 표명




assert()는 C에서 사용해야합니다. C++? (3)

assert() 함수를 구체적으로 사용해야하는 곳은 무엇입니까? 정수 값이 0보다 크거나 포인터가 null인지 여부를 판단하는 것과 같은 상황 인 경우이를 확인하기 위해 개인 함수를 사용할 수 있습니다. 이런 상황에서, 우리는 사용자 정의 서면 수표보다 assert() 를 어디에 사용해야합니까?


나는 assert()assert() 나의 견해를 버릴거야. 나는 assert() 가 다른 곳에서하는 것을 발견 할 수 있지만, 는 언제 어떻게 사용하는지에 대한 제안을위한 좋은 포럼을 제공한다.

assertstatic_assert 는 비슷한 기능을합니다. foo 함수가 있다고 가정 해 봅시다. 예를 들어, 인수가 null이 아닌 것으로 가정하는 foo(void*) 함수가 있다고 가정 해 보겠습니다.

void foo(void* p) {
  assert(p);
  ...
}

귀하의 기능에는 그것에 관심이있는 몇 사람이 있습니다.

첫째, 귀하의 기능을 호출하는 개발자. 그는 당신의 문서를 보았을지도 모릅니다. 아마도 널 포인터를 인자로 허용하지 않는 것에 대한 그 부분을 놓칠 것입니다. 그는 함수에 대한 코드를 읽지 않을 수도 있지만 디버그 모드로 실행할 때 어설 션은 자신의 부적절한 함수 사용을 잡을 수 있습니다 (특히 테스트 사례가 좋은 경우).

두 번째로 (더 중요한 것은) 코드를 읽는 개발자입니다. 그에게 당신의 주장은 이 줄 뒤에 p가 0이 아니라고 말합니다. 이것은 때때로 간과되는 것이지만, assert 매크로의 가장 유용한 기능이라고 생각합니다. 조건을 문서화 하고 시행 합니다.

실용적 일 때마다 assert s를 사용하여이 정보를 인코딩해야합니다. 저는 이것을 "코드의이 시점에서 이것은 사실입니다" 라고 말하고 싶습니다. (그리고 이것은 주석보다 훨씬 더 강력합니다.) 물론 그러한 진술이 실제로 / 많은 정보를 전달하지 않는다면 필요하지 않습니다.


나는 간단하고 강력한 포인트를 만들어야한다고 생각한다.

assert ()내부 일관성을 검사합니다.

전제 조건, 사후 조건 및 불변성을 확인하는 데 사용하십시오.

코드가 로컬에서 제어 할 수없는 외부 요인, 상황으로 인해 불일치가 발생할 수있는 경우 예외가 발생합니다. 예외는 사전 조건을 고려하여 사후 조건을 충족시킬 수없는 경우에 대한 것입니다. 좋은 예 :

  • new int 는 전제 조건까지 괜찮습니다. 따라서 메모리를 사용할 수 없으면 throwing이 유일한 합리적인 응답입니다. ( malloc 의 사후 조건은 "유효한 포인터 또는 NULL "입니다.)
  • 생성자의 사후 조건은 불변식이 설정되는 객체의 존재입니다. 유효한 상태를 구성 할 수없는 경우 throwing이 유일한 합리적인 응답입니다.

assert 은 위의 것에 사용되어서는 안된다. 대조적으로,

void sort (int * begin, int * end) {
    // assert (begin <= end); // OPTIONAL precondition, possibly want to throw
    for (int * i = begin, i < end; ++i) {
        assert (is_sorted (begin, i)); // invariant
        // insert *i into sorted position ...
    }
}

is_sorted 를 확인하는 것은 알고리즘이 전제 조건에 따라 올바르게 작동하는지 확인하는 것입니다. 예외는 합당한 응답이 아닙니다.

긴 이야기를 짧게 줄이기 위해 : assert 는 프로그램이 완전히 정확하다면 예외가 될 것입니다. 예외가 코드가 정확할지라도 잘못 될 수 있습니다.

무효 입력이 예외를 트리거하는지 여부는 스타일의 문제입니다.


컨텍스트 : 나는 다음 버전이로드되기 전에 몇 주 동안 머무르는 종류의 생활용 서버 소프트웨어를 작성합니다. 따라서 내 대답은 고도로 방어적인 코드로 비아냥을받을 수 있습니다.

원리.

assert 를 사용할 곳을 구체적으로 살펴보기 전에 그 기본 원리를 이해하는 것이 중요합니다.

assert방어 프로그래밍 에서 필수적인 도구입니다. 검증 가정 (실제로 주장)을 돕고 프로그래밍 오류 (사용자 오류와 구별됨)를 잡습니다. assert 의 목표는 복구가 일반적으로 즉시 가능하지 않은 오류 상황을 탐지하는 것입니다.

예:

char const* strstr(char const* haystack, char const* needle) {
  assert(haystack); assert(needle);

  // ...
}

대안.

C에서? 거의 대안이 없습니다. 함수가 오류 코드를 전달하거나 감시 가치를 반환 할 수 있도록 설계되지 않은 한 정식으로 문서화됩니다.

C ++에서 예외는 완벽하게 수용 가능한 대안입니다. 그러나 assert 은 메모리 덤프를 생성하여 잘못된 상황이 감지되는 순간 (디버깅에 도움이 됨) 프로그램이 어떤 상태인지 정확하게 볼 수 있도록 도와줍니다. 예외는 스택을 풀어서 컨텍스트를 잃게됩니다. ...).

또한, 예외는 (불행히도) 높은 레벨의 핸들러 (또는 동료 개발자로부터의 나쁜 캐치) (물론 그렇게하지 않을 것임)에 의해 잡힐 수 있습니다. 이 경우 너무 늦을 때까지 오류를 완전히 놓칠 수 있습니다.

사용하지 않는 곳.

첫째, assert디버그 코드에서만 유용하다는 것을 이해해야합니다. 릴리스에서는 NDEBUG 가 정의되고 코드가 생성되지 않습니다. 결과로서 릴리스 assert 은 주석과 동일한 가치가 있습니다.

  • 소프트웨어의 올바른 동작에 필요한 검사에는 절대로 사용하지 마십시오. 오류 조건을 확인하고 처리해야합니다. 항상.

둘째로, 잘못된 입력이 당신의 삶의 일부라는 것을 이해해야합니다. 오류가 발생할 때마다 컴파일러가 assert 메시지를 표시하도록 하시겠습니까? 흠! 따라서:

  • 입력 데이터 유효성 검사에 절대 사용하지 마십시오. 입력 데이터의 유효성을 검사하고 오류를 사용자에게 적절하게보고해야합니다. 항상.

셋째, 충돌은 인정 되지 않는다는 것을 이해해야한다. 프로그램이 원활하게 실행될 것으로 예상됩니다. 따라서 릴리스 모드에서 주장을 떠나야한다는 유혹을 받아서는 안됩니다. 릴리스 코드는 최종 사용자의 손에 들어가며 절대로 충돌하지 않아야합니다. 최악의 경우 오류 메시지가 표시되는 동안 종료해야합니다. 이 과정에서 사용자 데이터가 손실되지 않으며 사용자를 재시작 할 때 그녀가 있던 위치로 되돌아 가면 더 좋을 것입니다. 예를 들면 그것은 현대의 브라우저가하는 일입니다.

  • Release에서 단언하지 마십시오.

참고 : 서버 코드의 경우 어설 션을 '히트'할 때 대부분의 경우 다음 쿼리를 처리하기 위해 정상적으로 돌아갈 수 있습니다.

그것을 사용하는 곳.

assert 은 디버그 모드에서 켜져 있으므로 디버깅을 위해 사용해야합니다. 새로운 코드를 테스트 할 때마다 테스트 스위트가 실행될 때마다 언제든지 소프트웨어가 사용자 (또는 팀원)의 손에있을 때마다 소프트웨어가 QA 부서의 손에있을 때마다 새 코드를 테스트 합니다 . 어설 션을 사용하면 오류를 발견하고 오류의 전체 컨텍스트를 제공하므로 복구 할 수 있습니다.

  • 개발 및 테스트주기 중에 사용하십시오.

더욱 좋다. 릴리스에서 코드가 실행되지 않는다는 것을 알고 있으므로 값 비싼 검사를 수행 할 여력이 있습니다.

참고 : 성능 만 확인하려면 릴리스 바이너리도 테스트해야합니다.

그리고 릴리스?

글쎄, 내가 작업하는 코드베이스에서 우리는 (backtrace를 사용하여) 문제를 기록하고, 인코딩 된 오류 응답을 반환하는 높은 수준의 처리기에서만 잡히는 특정 예외에 의해 값싼 어설 션 (다른 것은 무시 됨)을 대체합니다. 서비스를 재개하십시오. 개발 팀은 자동으로 통보받습니다.

배포 된 소프트웨어에서 내가 본 모범 사례는 메모리 덤프를 생성하고 분석을 위해 개발자에게 다시 스트리밍 함과 동시에 사용자 데이터를 손실하지 않고 불행한 사용자를 위해 가능한 한 예의 바르게 행동하는 것을 의미합니다. 나는이 작업의 어려움을 고려할 때 서버 측에서 일하는 것이 정말 축복이라고 생각합니다.)





assert