source

단언은 악인가?

factcode 2022. 8. 31. 22:31
반응형

단언은 악인가?

Go언어 작성자:

바둑은 주장을 제시하지 않는다.매우 편리합니다만, 지금까지의 경험에 의하면, 프로그래머는, 에러 처리나 보고의 적절한 방법을 생각하지 않기 위해서, 그것들을 목발로서 사용해 왔습니다.적절한 에러 처리는, 크래시가 아니고, 치명적이지 않은 에러 후에 서버가 동작을 계속하는 것을 의미합니다.적절한 에러 리포팅은, 에러가 직접적이고 요점을 나타내고 있기 때문에, 프로그래머가 큰 크래시 트레이스를 해석하지 않아도 됩니다.정확한 오류는 프로그래머가 코드를 잘 모를 때 특히 중요합니다.

이것에 대한 당신의 의견은 무엇입니까?

요, 문제 요.assert이치노

즉, 통상적인 에러 처리와는 달리 디버깅 중에 "발생할 수 없는" 케이스를 검출하기 위한 것입니다.

  • 주장: 프로그램 논리 자체의 오류입니다.
  • 오류 처리:프로그램의 버그가 원인이 아닌 입력 오류 또는 시스템 상태.

둘 다 아니에요.goto 않다assert 오용될 수 요.하지만 둘 다 오용될 수 있다.

Assert는 건전성 검사를 위한 것입니다.정확하지 않으면 프로그램을 중단시킬 수 있는 것들.검증용도 아니고 오류 처리용도 아닙니다.

나는 어설션을 많이 사용한다.처음 애플리케이션을 작성할 때(아마도 새로운 도메인용) 매우 편리합니다.(조기 최적화를 고려한다고 생각하는) 매우 화려한 에러 체크를 하는 대신에, 빠르게 코드화해, 많은 어설트를 추가합니다.동작에 대해 더 잘 알게 되면 수정 및 어설트 중 일부를 삭제하고 오류 처리를 개선하기 위해 변경합니다.

단언 때문에 프로그램 코딩/디버깅에 소비하는 시간이 훨씬 적습니다.

또한 단언은 프로그램을 망가뜨릴 수 있는 많은 것들을 생각하도록 도와준다는 것도 알게 되었습니다.

go 기능이 .panic이은 것, 음, 음, 음 대신 사용할 수 .assert

if x < 0 {
    panic("x is less than 0");
}

panic는 스택 트레이스를 에 어떤 으로든 「 트레이스」의 목적을 .assert.

만약 당신이 말하는 주장이 프로그램이 토해내고 존재하는 것을 의미한다면, 주장은 매우 나쁠 수 있습니다.이것은 그것들이 항상 잘못된 이라고 말하는 것이 아니라, 그것들은 매우 쉽게 오용되는 구조이다.그들은 또한 더 나은 대안을 많이 가지고 있다.그런 것들은 악당이라고 불릴 만한 좋은 대상이다.

예를 들어, 서드파티 모듈(또는 실제로는 모든 모듈)은, 콜링 프로그램을 종료하는 일은 거의 없습니다.이로 인해 호출 프로그래머는 그 시점에서 프로그램이 어떤 위험을 감수해야 하는지를 제어할 수 없습니다.대부분의 경우 데이터가 너무 중요하기 때문에 손상된 데이터를 저장하는 것이 데이터를 잃는 것보다 더 좋습니다.단언하면 데이터가 손실될 수 있습니다.

에 대한 몇 가지 대안이 있습니다.

  • 디버거를 사용하여
  • 콘솔/데이터베이스/기타 로그
  • 예외
  • 기타 유형의 오류 처리

참고 자료:

주장을 옹호하는 사람들조차 이러한 주장은 생산용이 아니라 개발에만 사용되어야 한다고 생각합니다.

이 담당자는 모듈에 파손된 데이터가 있을 가능성이 있으며 예외가 발생한 후에도 데이터가 지속될 경우 assert를 사용해야 한다고 말합니다.http://www.advogato.org/article/949.html이것은 확실히 타당한 포인트입니다만, 외부 모듈은, 파손된 데이터가 발신 프로그램에 얼마나 중요한지를(「용」을 종료하는 것에 의해서) 규정하지 말아 주세요.이 문제를 해결하는 적절한 방법은 프로그램이 현재 일관성 없는 상태에 있을 수 있음을 명확히 하는 예외를 발생시키는 것입니다.또한 우수한 프로그램은 대부분 모듈(메인 실행 파일에 약간의 글루 코드가 있음)로 구성되기 때문에 거의 항상 잘못된 작업이라고 단언합니다.

프로그램의 버그를 검출하기 위해서 사용합니다.사용자 입력이 나쁘지 않습니다.

올바르게 사용한다면, 그들은 악하지 않다.

그 논리에 따르면 브레이크 포인트도 악입니다.

Assert는 디버깅 보조 도구로서 사용해야 하며, 그 외에는 사용하지 마십시오."Evil"은 오류 처리 대신 사용하려고 할 때 사용됩니다.

프로그래머인 귀하에게 있어서는 안 될 문제를 검출하여 수정하고 가정이 진실임을 확인할 수 있도록 지원합니다.

에러 처리와는 관계가 없습니다만, 유감스럽게도, 일부의 프로그래머는 에러 처리를 악용해 「악」이라고 선언합니다.

assert이 기능은 매우 유용하며 문제가 발생한 첫 번째 징후에서 프로그램을 중지함으로써 예기치 않은 오류가 발생했을 때 역추적을 크게 줄일 수 있습니다.

to on on on on 、 on 、 on 、 on 、 on 、 on on 、 on 、 on on 、 on on 、 on 、 on on on 。assert.

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

올바른 버전은 다음과 같습니다.

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

결니니결결...★★★★★★★★★★★★★...하지 않을 수 assert악용될 수 있습니다.아, 아, 아, 아, 아, 아, 아, 네.

최근 코드에 몇 가지 단언을 추가하기 시작했는데, 그 방법은 다음과 같습니다.

나는 내 코드를 정신적으로 경계 코드와 내부 코드로 나눕니다.경계 코드는 사용자 입력을 처리하고 파일을 읽고 네트워크에서 데이터를 가져오는 코드입니다.이 코드에서는 입력이 유효한 경우(인터랙티브 사용자 입력의 경우)에만 종료되는 루프에서 입력을 요청하거나 복구할 수 없는 파일/네트워크 손상 데이터의 경우 예외를 발생시킵니다.

내부 코드가 전부예요예를 들어, 내 클래스의 변수를 설정하는 함수는 다음과 같이 정의될 수 있습니다.

void Class::f (int value) {
    assert (value < end);
    member = value;
}

또, 네트워크로부터 입력되는 함수는, 다음과 같이 표시됩니다.

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

이렇게 하면 두 겹의 수표를 받을 수 있어요.런타임에 데이터가 결정되면 항상 예외 또는 즉각적인 오류 처리가 발생합니다., 그 는 "" "" " " " " "Class::fassert는, 코드의 를 호출했을 에, 「」라고 것을 합니다.Class::f내부 하지 못할 수 유효한 인수를 계산했을 가능성 때문입니다).value 누가 그 함수를 것이 .value 값보다 크거나 같을 수 .end.

이것은 내가 읽고 있는 몇 군데에 들어맞는 것 같다.잘 작동하는 프로그램에서는 주장을 위반하는 것이 불가능해야 하고 예외는 여전히 가능한 예외적이고 잘못된 경우여야 한다.왜냐하면 이론상으로는 모든 입력을 검증하고 있기 때문에 내 주장이 발동되는 것은 불가능하기 때문입니다.그렇다면 프로그램이 잘못된 것입니다.

디버깅과 릴리스에서 다른 작업을 수행하는 코드는 피하는 것이 좋습니다.

조건에 따라 디버거를 중단하고 모든 파일/행 정보를 갖는 것은 정확한 식과 정확한 값도 유용합니다.

"디버깅만으로 조건을 평가"하는 어설션을 갖는 것은 퍼포먼스 최적화일 수 있으며, 따라서 프로그램 중 0.0001%에서만 유용합니다.이 프로그램에서는 사용자가 무엇을 하고 있는지 알 수 있습니다.다른 모든 경우 이 표현은 실제로 프로그램 상태를 변경할 수 있으므로 유해합니다.

assert(2 == ShroedingersCat.GetNumEars());디버깅과 릴리스에서 프로그램이 다른 작업을 수행하도록 합니다.

예외를 발생시키는 일련의 아사트 매크로를 개발하여 디버깅버전과 릴리즈버전 모두에서 실행하고 있습니다.를 들면, 「 」입니다.THROW_UNLESS_EQ(a, 20);파일, 행 및 실제 값 등을 모두 포함하는 what() 메시지에서 예외를 발생시킵니다.매크로만이 이 기능을 수행할 수 있습니다.디버거는 특정 예외 유형의 'throw'에서 중단되도록 구성할 수 있습니다.

나는 단언하는 것을 몹시 싫어한다.하지만 나는 그들이 악하다고까지 말하지 않을 것이다.

기본적으로 아사트는 체크되지 않은 예외와 동일한 작업을 수행하지만 유일한 예외는 (통상) 최종 제품에 대해 아사트를 유지해서는 안 된다는 것입니다.

시스템 디버깅 및 구축 중에 스스로 안전망을 구축한 경우 고객, 지원 헬프 데스크 또는 현재 구축 중인 소프트웨어를 사용할 수 있는 모든 사용자에 대해 이 안전망을 거부하는 이유는 무엇입니까?단언 및 예외 상황 모두에 대해서만 예외를 사용합니다.적절한 예외 계층을 생성하면 서로 빠르게 식별할 수 있습니다.단, 이 경우 주장은 그대로 유지되며 그렇지 않으면 손실될 수 있는 실패 시 귀중한 정보를 제공할 수 있습니다.

그래서 나는 바둑의 창시자들을 완전히 이해한다. 단언들을 완전히 배제하고 프로그래머들에게 예외들을 이용하여 상황을 처리하도록 강요한다.이에 대한 간단한 설명이 있는데, 예외는 일을 위한 더 나은 메커니즘일 뿐인데 왜 오래된 주장을 고수하는가?

이러한 답변에 대한 나의 문제는 그것이 일반적인 치명적인 오류와 무엇이 다른지, 그리고 왜 주장이 예외의 서브셋이 될 수 없는지를 명확하게 규정하는 사람이 없다는 것이다.여기서 말하는 바와 같이 예외가 검출되지 않으면 어떻게 됩니까?그것이 명명법에 의한 주장이 되는가?그리고 왜 /nothing/이 처리할 수 없는 예외를 발생시킬 수 있는 언어로 제한을 가하려고 합니까?

그래, 단언은 사악하다.

적절한 에러 처리를 실시할 필요가 있는 장소에서 사용되는 경우가 많습니다.처음부터 적절한 생산품질 에러 처리에 익숙해져라!

일반적으로 단위 검정을 작성하는 데 방해가 됩니다(검정 하니스와 상호 작용하는 사용자 정의 어설션을 작성하는 경우는 제외).이것은, 적절한 에러 처리를 실시할 필요가 있는 장소에서 사용되고 있기 때문입니다.

대부분의 경우 릴리스 빌드에서 컴파일되므로 실제로 릴리스한 코드를 실행할 때 사용할 수 있는 "테스트"가 없습니다.멀티 스레드 환경에서는 릴리스 코드에만 가장 심각한 문제가 나타나는 경우가 많기 때문에 이는 좋지 않을 수 있습니다.

때로는 고장난 설계를 위한 목발이기도 합니다. 즉, 코드 설계를 통해 사용자가 불러서는 안 되는 방식으로 코드를 호출할 수 있으며 이를 "예측"할 수 있습니다.디자인을 고치다!

2005년에 블로그에 더 많은 글을 올렸습니다.http://www.lenholgate.com/blog/2005/09/assert-is-evil.html

악이라기 보다는 오히려 역효과를 내죠영구 오류 확인과 디버깅은 구분됩니다.Assert는 모든 디버깅이 영속적이어야 한다고 생각하게 하며 많이 사용했을 때 엄청난 가독성 문제를 일으킵니다.영구적인 오류 처리는 필요한 경우보다 더 나은 것이어야 하며, 단언은 자체 오류를 야기하므로 상당히 의심스러운 관행입니다.

단답: 아니요, 저는 주장이 유용할 수 있다고 생각합니다.

이런 것들이 많이 나오는데, 주장의 변호를 혼란스럽게 만드는 한 가지 문제는 주장들이 종종 주장 확인에 근거한다는 것입니다.따라서 어설션을 사용할 수 있는 경우의 다른 예를 생각해 보겠습니다.

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

입력에 예외를 사용하는 이유는 입력이 잘못될 수 있기 때문입니다.알고리즘에서 예기치 않은 버그를 찾기 위해 목록이 정렬되어 있다고 주장합니다.이 어설션은 디버깅빌드에만 있기 때문에 체크는 비싸지만 루틴의 호출 하나하나에 관계없이 실행할 수 있습니다.

제품 코드를 유닛으로 테스트해야 하지만 이는 코드가 올바른지 확인하는 다른 보완적인 방법입니다.유닛 테스트에서는 루틴이 인터페이스에 준거하고 있는 것을 확인할 수 있습니다.또한 어설션은 구현이 기대한 대로 정확하게 동작하고 있는지 확인할 수 있는 세밀한 방법입니다.

주장은 악은 아니지만 쉽게 오용될 수 있다.나는 "오류 처리와 보고에 대한 적절한 생각을 피하기 위해 주장은 종종 목발로 사용된다"는 말에 동의한다.나는 이것을 꽤 자주 봐왔다.

개인적으로, 저는 주장을 사용하는 것을 좋아합니다. 왜냐하면 그 주장은 제가 코드를 작성할 때 했을 수 있는 추측을 기록하기 때문입니다.코드를 유지하는 동안 이러한 가정이 깨지면 테스트 중에 문제를 발견할 수 있습니다.다만, 프로덕션 빌드를 실시할 때는, 코드에서 모든 어설션을 삭제하는 것을 중시하고 있습니다(#ifdefs 를 사용합니다).생산 빌드에서의 어설션을 배제함으로써, 누군가가 그것들을 목발로 잘못 사용할 위험을 없앱니다.

주장에도 또 다른 문제가 있다.어설션은 런타임에만 체크됩니다.그러나 대부분의 경우 수행하려는 검사가 컴파일 시간에 수행되었을 수 있습니다.컴파일 시에 문제를 검출하는 것이 좋습니다.C++ 프로그래머의 경우 Boost는 BOOST_STATIC_ASSERT를 제공합니다.C 프로그래머의 경우, 이 문서(링크 텍스트)는 컴파일 시에 어사션을 실행하는 데 사용할 수 있는 기술에 대해 설명합니다.

요약하면, 내가 따르는 경험의 법칙은: 프로덕션 빌드에서는 어사션을 사용하지 말고 가능하면 컴파일 시에 검증할 수 없는 것(즉, 런타임에 체크할 필요가 있다)에만 어사션을 사용합니다.

적절한 에러 보고를 고려하지 않고 어설션을 사용한 것은 인정합니다.하지만, 그것이 올바르게 사용되었을 때 큰 도움이 된다는 것을 빼앗아 가는 것은 아닙니다.

특히 "Crash Early" 원칙을 따르는 경우 유용합니다.예를 들어 기준 카운트 메커니즘을 구현한다고 가정합니다.코드의 특정 위치에서는 refcount가 0 또는 1이어야 합니다.또한 리카운트가 잘못되었을 경우 프로그램이 즉시 크래시되지는 않지만 다음 메시지 루프 중에 문제가 발생한 원인을 찾기 어려울 수 있습니다.Assert는 원점에 가까운 오류를 검출하는 데 도움이 됩니다.

assert입력이 적기 때문에 에러 처리에 악용되고 있습니다.

따라서 언어 디자이너로서 적절한 오류 처리는 더 적은 타이핑으로도 할 수 있다는 것을 알아야 합니다.예외 메커니즘이 장황하기 때문에 주장을 배제하는 것은 해결책이 아닙니다.잠깐, 바둑도 예외는 없어.아쉽다 :)

나는 그것을 보고 저자의 머리를 걷어차고 싶었다.

나는 항상 코드로 어설트(assert)를 사용하고, 코드를 더 쓰면 결국 그것들을 모두 교체한다.프로젝트 완료가 가까워지면 삭제되는 예외를 작성하는 대신 필요한 로직을 작성하지 않고 코드에 부딪혔을 때 경고를 받고 싶을 때 사용합니다.

또한 예외는 제가 싫어하는 생산 코드와 더 쉽게 섞입니다.단언은 보다 알아차리기 쉽다.throw new Exception("Some generic msg or 'pretend i am an assert'");

assert는 사용하지 않습니다.일반적으로 다음과 같은 예를 나타냅니다.

int* ptr = new int[10];
assert(ptr);

이건 안 좋아, 난 절대 안 해 내 게임이 몬스터들을 많이 할당해준다면?왜 내가 게임을 중단시켜야 하는가? 대신, 당신은 우아하게 오류를 처리해야 한다. 그래서 다음과 같은 것을 한다:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

언급URL : https://stackoverflow.com/questions/1854302/is-assert-evil

반응형