UFO ET IT

릴리스 빌드에서 assert ()를 사용할 때 사용하지 않는 변수 경고 방지

ufoet 2021. 1. 13. 07:26
반응형

릴리스 빌드에서 assert ()를 사용할 때 사용하지 않는 변수 경고 방지


때로는 지역 변수가 assert ()에서 확인하는 목적으로 만 사용됩니다.

int Result = Func();
assert( Result == 1 );

릴리스 빌드에서 코드를 컴파일 할 때 assert ()는 일반적으로 비활성화되므로이 코드는 Result가 설정되지만 읽지 않는다는 경고를 생성 할 수 있습니다.

가능한 해결 방법은 다음과 같습니다.

int Result = Func();
if ( Result == 1 )
{
    assert( 0 );
}

그러나 너무 많은 타이핑이 필요하고 눈에 쉽지 않으며 조건이 항상 확인되도록합니다 (예, 컴파일러는 확인을 최적화 할 수 있지만 여전히).

경고를 발생시키지 않는 방식으로이 assert ()를 표현하는 대체 방법을 찾고 있지만 여전히 사용하기 쉽고 assert ()의 의미를 변경하지 않도록합니다.

(이 코드 영역에서 #pragma를 사용하여 경고를 비활성화하는 것은 옵션이 아니며 경고 수준을 낮추는 것도 옵션이 아닙니다 ...).


우리는 매크로를 사용하여 어떤 것이 사용되지 않았는지 구체적으로 나타냅니다.

#define _unused(x) ((void)(x))

그런 다음 귀하의 예에서 다음과 같습니다.

int Result = Func();
assert( Result == 1 );
_unused( Result ); // make production build happy

그런 식으로 (a) 프로덕션 빌드가 성공하고 (b) 코드에서 변수가 디자인의해 사용되지 않았으며 잊혀진 것이 아니라는 것이 분명합니다. 이것은 함수에 대한 매개 변수가 사용되지 않을 때 특히 유용합니다.


나는 이것보다 더 나은 답을 줄 수 없으며 그 문제를 해결하고 더 많은 것을 할 수 없습니다.

어리석은 C ++ 트릭 : 어설 션의 모험

#ifdef NDEBUG
#define ASSERT(x) do { (void)sizeof(x);} while (0)
#else
#include <assert.h>
#define ASSERT(x) assert(x)
#endif

임시 변수 사용을 피할 수있는 다른 매크로를 만들 수 있습니다.

#ifndef NDEBUG
#define Verify(x) assert(x)
#else
#define Verify(x) ((void)(x))
#endif

// asserts that Func()==1 in debug mode, or calls Func() and ignores return
// value in release mode (any braindead compiler can optimize away the comparison
// whose result isn't used, and the cast to void suppresses the warning)
Verify(Func() == 1);

int Result = Func();
assert( Result == 1 );

이 상황은 릴리스 모드에서 정말로 원하는 것을 의미합니다.

Func();

그러나 Func무효가 아닙니다. 즉, 결과를 반환 합니다 . 즉, 쿼리 입니다.

아마도 결과를 반환하는 것 외에도 Func무언가를 수정합니다 (그렇지 않으면 왜 그것을 호출하고 결과를 사용하지 않습니까?), 즉 명령 입니다.

에 의해 명령 쿼리 분리 원칙 (1) Func명령과 동시에 쿼리해서는 안됩니다. 즉, 쿼리에는 부작용이 없어야하며 명령의 "결과"는 개체의 상태에 대한 사용 가능한 쿼리로 표시되어야합니다.

Cloth c;
c.Wash(); // Wash is void
assert(c.IsClean());

보다 낫다

Cloth c;
bool is_clean = c.Wash(); // Wash returns a bool
assert(is_clean);

전자는 당신의 종류에 대한 어떤 경고도주지 않습니다.

즉, 내 대답은 다음과 같습니다. 다음과 같은 코드를 작성하지 마십시오. :)

업데이트 (1) : 명령 쿼리 분리 원칙 에 대한 참조를 요청했습니다 . Wikipedia 는 다소 유익합니다. 저는이 디자인 기법에 대해 Bertrand Meyer의 2nd Editon Object Oriented Software Construction 에서 읽었습니다 .

업데이트 (2) : j_random_hacker는 "OTOH, 이전에 값을 반환 한 모든"명령 "함수 f ()는 이제 일부 변수 last_call_to_f_succeeded 또는 이와 유사한 변수를 설정해야합니다"라고 설명합니다. 이는 계약에서 어떤 것도 약속하지 않는 기능, 즉 "성공"할지 여부 또는 유사한 개념에 대해서만 적용됩니다. 으로 계약에 의해 디자인 , 기능 관련 수있을 것이다 사후 조건을 가진, 그래서 객체가 "IsEmpty 함수 ()"이 될 것입니다 "() 웁니다"후 "인코딩은 ()"라는 메시지 문자열이 "IsEncoded ()"입니다 후, 확인할 필요가 없습니다. 같은 방식으로, 그리고 다소 대칭 적으로, "X ()"프로 시저를 호출하기 전에 특수 함수 "IsXFeasible ()"을 호출하지 않습니다.


최근 C ++에서는 다음과 같이 말합니다.

[[maybe_unused]] int Result = Func();
assert( Result == 1 );

이 속성에 대한 자세한 내용 https://en.cppreference.com/w/cpp/language/attributes/maybe_unused참조 하십시오 .

(void)Result트릭 과 비교할 때 변수 선언을 직접 장식하기 때문에 나중에 무언가를 추가하는 것이 아니라 더 좋습니다.


다음을 사용할 수 있습니다.

Check( Func() == 1 );

그리고 원하는대로 Check (bool) 함수를 구현하십시오. assert를 사용하거나 특정 예외를 발생 시키거나 로그 파일 또는 콘솔에 기록하거나 디버그 및 릴리스에서 다른 구현을 갖거나 모두 조합 할 수 있습니다.


이것은 assert, IMHO의 잘못된 사용입니다. Assert는 오류보고 도구가 아니라 전제 조건을 주장하기위한 것입니다. Result가 다른 곳에서 사용되지 않으면 전제 조건이 아닙니다.


가장 간단한 것은 어설 션이 존재할 경우에만 해당 변수를 선언 / 할당하는 것입니다. NDEBUG나타내는 경우 매크로가 특별히 정의 되지 않습니다 영향을받을 (때문에 방법은 라운드 그냥 완료 -DNDEBUG, 해제 디버깅 할 수있는 편리한 방법입니다, 내 생각) 그래서에 @AdamPeterson에 의해 참조 코멘트 (Jardel의 대답은 작동합니다 @이 불통 사본 대답):

#ifndef NDEBUG
int Result =
#endif
Func();
assert(Result == 1);

또는 귀하의 취향에 맞지 않는 경우 모든 종류의 변형이 가능합니다. 예 :

#ifndef NDEBUG
int Result = Func();
assert(Result == 1);
#else
Func();
#endif

일반적으로이 항목은 다른 NDEBUG매크로 상태 로 빌드 할 수있는 다른 번역 단위 , 특히 re. 공개 헤더 파일의 어설 션 또는 기타 조건부 콘텐츠. 위험은 귀하 또는 귀하의 라이브러리 사용자가 실수로 라이브러리의 컴파일 된 부분 내에서 사용 된 것과 다른 인라인 함수 정의를 인스턴스화하여 하나의 정의 규칙을 조용히 위반 하고 런타임 동작을 정의되지 않게 만들 수 있다는 것입니다.


C ++ 17을 사용하면 다음을 수행 할 수 있습니다.

[[maybe_unused]] int Result = Func();

어설 션 대체에 비해 약간의 추가 타이핑이 필요합니다. 이 답변을 참조하십시오 .

참고 : "c ++ assert unused variable"에 대한 첫 번째 Google 히트이기 때문에 추가되었습니다.


반환 값 전에 함수 내에서 assert를 이동해야합니다. 반환 값이 참조되지 않은 지역 변수가 아니라는 것을 알고 있습니다.

또한 자체 사전 및 사후 조건을 가진 자체 포함 된 장치를 생성하기 때문에 어쨌든 함수 내부에있는 것이 더 합리적입니다.

함수가 값을 반환하는 경우이 반환 값에 대해 릴리스 모드에서 일종의 오류 검사를 수행해야 할 가능성이 있습니다. 따라서 처음에는 참조되지 않은 변수가 아니어야합니다.

편집하지만이 경우 게시 조건은 X 여야합니다 (댓글 참조).

나는이 점에 강하게 동의하지 않는다. 입력 매개 변수와 그것이 멤버 함수라면 어떤 객체 상태에서든 포스트 조건을 결정할 수 있어야한다. 전역 변수가 함수의 출력을 수정하면 함수를 재구성해야합니다.


확실히 "_ASSERT"와 같은 어설 션 정의를 제어하기 위해 매크로를 사용합니다. 따라서 다음과 같이 할 수 있습니다.

#ifdef _ASSERT 
int Result =
#endif /*_ASSERT */
Func();
assert(Result == 1);

대부분의 답변 은 경고를 억제하기 위해 빌드에서 static_cast<void>(expression)트릭을 사용하는 것이 Release좋지만 실제로 확인하려는 의도라면 실제로 차선책 Debug입니다. 문제의 어설 션 매크로의 목표는 다음과 같습니다.

  1. Debug모드 에서 검사 수행
  2. Release모드 에서 아무것도하지 않음
  3. 모든 경우에 경고를 표시하지 않습니다.

문제는 보이드 캐스트 방식이 두 번째 목표에 도달하지 못한다는 것입니다. 경고는 없지만 어설 션 매크로에 전달한 표현식은 계속 평가 됩니다. 예를 들어 변수 검사 만 수행하면 큰 문제가 아닐 것입니다. 그러나 ASSERT(fetchSomeData() == data);(내 경험상 매우 일반적인) 주장 검사에서 일부 기능을 호출하면 어떻게 될까요? fetchSomeData()기능은 여전히 호출됩니다. 빠르고 간단 할 수도 있고 그렇지 않을 수도 있습니다.

정말로 필요한 것은 경고 억제뿐만 아니라 더 중요한 것은 디버그 전용 검사 표현식의 평가하지 않는입니다. 이것은 전문 Assert 라이브러리 에서 가져온 간단한 트릭으로 얻을 수 있습니다 .

void myAssertion(bool checkSuccessful)
{
   if (!checkSuccessful)
    {
      // debug break, log or what not
    }
}

#define DONT_EVALUATE(expression)                                    \
   {                                                                 \
      true ? static_cast<void>(0) : static_cast<void>((expression)); \
   }

#ifdef DEBUG
#  define ASSERT(expression) myAssertion((expression))
#else
#  define ASSERT(expression) DONT_EVALUATE((expression))
#endif // DEBUG

int main()
{
  int a = 0;
  ASSERT(a == 1);
  ASSERT(performAHeavyVerification());

  return 0;
}

All the magic is in the DONT_EVALUATE macro. It is obvious that at least logically the evaluation of your expression is never needed inside of it. To strengthen that, the C++ standard guarantees that only one of the branches of conditional operator will be evaluated. Here is the quote:

5.16 Conditional operator [expr.cond]

logical-or-expression ? expression : assignment-expression

Conditional expressions group right-to-left. The first expression is contextually converted to bool. It is evaluated and if it is true, the result of the conditional expression is the value of the second expression, otherwise that of the third expression. Only one of these expressions is evaluated.

I have tested this approach in GCC 4.9.0, clang 3.8.0, VS2013 Update 4, VS2015 Update 4 with the most harsh warning levels. In all cases there are no warnings and the checking expression is never evaluated in Release build (in fact the whole thing is completely optimized away). Bare in mind though that with this approach you will get in trouble really fast if you put expressions that have side effects inside the assertion macro, though this is a very bad practice in the first place.

Also, I would expect that static analyzers may warn about "result of an expression is always constant" (or something like that) with this approach. I've tested for this with clang, VS2013, VS2015 static analysis tools and got no warnings of that kind.


int Result = Func();
assert( Result == 1 );
Result;

This will make the compiler stop complaining about Result not being used.

But you should think about using a version of assert that does something useful at run-time, like log descriptive errors to a file that can be retrieved from the production environment.


I'd use the following:

#ifdef _DEBUG
#define ASSERT(FUNC, CHECK) assert(FUNC == CHECK)
#else
#define ASSERT(FUNC, CHECK)
#endif

...

ASSERT(Func(), 1);

This way, for release build, the compiler don't even need to produce any code for assert.


If this code is inside a function, then act on and return the result:

bool bigPicture() {

   //Check the results
   bool success = 1 != Func();
   assert(success == NO, "Bad times");

   //Success is given, so...
   actOnIt();

   //and
   return success;
}

// Value is always computed.  We also call assert(value) if assertions are
// enabled.  Value is discarded either way.  You do not get a warning either
// way.  This is useful when (a) a function has a side effect (b) the function
// returns true on success, and (c) failure seems unlikely, but we still want
// to check sometimes.
template < class T >
void assertTrue(T const &value)
{
  assert(value);
}

template < class T >
void assertFalse(T const &value)
{ 
  assert(!value);
}

ReferenceURL : https://stackoverflow.com/questions/777261/avoiding-unused-variables-warnings-when-using-assert-in-a-release-build

반응형