단언하기 (Assert) 본문
단언하기 (Assert)
프로그래밍 시간의 대부분은 사실 프로그래밍 그 자체보다는 디버깅에 소모가 많이 된다.
디버깅을 할 때도 컴파일러의 도움을 받을 수 있다면 훨씬 빠르게 디버깅할 수 있다.
여기서는 컴파일러 도움을 받을 때 사용하는 assert(단언하기)에 대해서 말씀드리겠다.
How to use Assert
사용법은 간단하다.
<cassert>를 include 해주고
assert라고 적어주고 그 안에 필요한 조건을 적는데
지금은 이 성질을 테스트하기 위해서 false를 적어보겠다.
debug 모드에서 실행시켜보면
런타임 에러가 발생을 하고
더 좋은건 Assertion failed라고 어디서 오류가 났는지 까지도 알려준다.
그런데 이 assert의 특징은 release 모드로 바꾸면 작동을 안 한다.
아무 일도 없는 듯이 넘어간다.
프로그래머가 테스트할 때 debug 모드에서만 작동하는 것이다.
여기서 NDEBUG가 정의되어있으면 assert가 작동을 안 한다.
Debug 모드에서는 _DEBUG가 정의되어있다.
assert라는 게 모드에 따라서 작동을 할 수도 있고 안 할 수도 있다는 것은 중요한 성질 중 하나다.
조금 더 이해하기 쉬운 예제를 들어보자.
int number가 있고
만약 이 number가 반드시 5여야만 한다면 이렇게 assert를 걸어버리는 것이다.
예를 들어 프로그램이 중간영역에서 어떤 과정을 거치는데
결과적으로 number가 5 여야지만 정상작동을 하는 거라고 가정해보자.
이런 것들을 assert 없이 주석만 남겨놓는다면
결국은 프로그래머가 문제가 생겼을 때 다시 와서'아 여기서 5여야 하는 건가?' 하고 찍어봐야 된다는 것이다.
굉장히 번거롭고 현실적으로 실용성이 없는 방식이다.
이것보다는 코멘트 남길 필요도 없이 assert를 박아 넣어 버리는 것이다.
그러면 debug 모드에서는 이 오류를 찾아준다.
release모드에서는 이 assert를 실행시키지 않는다.
왜냐하면 assert를 실행시키는 것도 연산량을 먹는 것이기 때문에 느려진다.
release모드에서는 가급적 프로그램이 빠르게 돌아야 하기 때문에 assert를 빼버린다.
예제를 한 개 더 보자.
이번에는 array를 사용해보자.
이때 my_array를 출력할 때 0 1 2 3 4는 괜찮은데
5를 넣을 때부터는 문제가 생긴다.
런타임 에러 난다.
그런데 이때 printValue 함수를 만들었다.
array<int, 5> 를 레퍼런스로 받고,
안전장치를 위해서 나름 const도 넣어주고,
인덱스로 const int& ix 를 넣어주는 함수를 짰다.
이 함수가 내가 만든 api라고 가정을 해보자.
다른 프로그래머들은 이 내용을 모르고 사용한다.
만약 다른 어떤 프로그래머가 printValue를 실행을 시키면서
my_array를 넣고 100을 넣으려는 시도를 한다고 생각해보자.
그럼 이 printValue 함수를 사용하는 사람들을 쫓아다니면서
매번 "여기에는 이 사이즈만큼만 넣어줘야 해요"라고 얘기하고 다닐 수는 없다.
그럴 때 assert를 사용한다.
일단 ix는 0 이상이어야 한다.
그리고 my_array.size() - 1 여야 한다.
이렇게 되면 debug 모드로 실행시켰을 때
런타임 에러가 나긴 하지만대신 어디서 어떤 것 때문에 문제가 생겼는지 알 수 있다.
그렇다면 다른 프로그래머는 '아 내가 100이라는 숫자를 넣은 게 너무 커서 여기서 assert가 발생을 했구나'
'내가 이렇게 하면 안 되겠구나' 생각을 할 수 있을 것이다.
이 두 개를 &&로 묶어서 하나로 합칠 수 있지만 그냥 두 개로 쪼개 쓰는 게 좋다.
그래야 나중에 위가 문제인지, 아래가 문제인지 정확하게 알 수 있기 때문이다.
그래서 assert는 런타임에 체크를 해주는 것이다.
실행을 했을 때 사용자가 지정 해준 범위 외에 행동을 하려고 한다고 하면 알려주는 것이다.
참고로 여기서 말씀드린 사용자는 다른 프로그래머를 말한다.
static assert
이번에는 아예 컴파일 타임에 오류를 발생시키는 static assert를 알려드리겠다.
static_assert라고 치고 뒤에 true나 false를 넣을 수 있다.
예를 들어
원래 assert를 써보면 x는 5여야 한다. assert(x == 5); 이게 가능하다.
(*논리연산자니까 등호 == 두 개 되는 거 주의)
왜냐하면 x는 변수니까 런타임에 결정이 돼도 assert는 가능하다.
그런데 static_assert는 컴파일 타임에 결정되는 경우에만 사용할 수 있다.
예를 들어 이 경우에는 x가 변수니까 컴파일 타임에 달라질 수 있다.
여기서 중간중간에 x는 10으로 바뀔 수도 있으니까 static_assert에서는 사용할 수없다.
const 상수일 때는 사용할 수 있다.
The difference between assert and static assert.
assert와 static_assert의 차이가 한 가지 더 있는데
static_assert는 뒤에 문구를 남길 수 있다.
이런 식으로 기록을 남길 수 있다.
x를 10으로 바꿔서 실행시켜보자.
그러면 바꾸는 순간 벌써 물결이 생긴다.
빌드해보면
x should be 5라면서 오류가 난다.
에러 메시지에 x should be 5 이게 출력이 된다.
여기까지 assert와 static_assert의 사용법과 컴파일러를 이용을 해서
내 프로그램이 문제가 생길 여지를 미리 차단하는 방법에 대해서 설명을 드렸다.
'💘 C++ > 함수' 카테고리의 다른 글
생략부호 (Ellipsis) (0) | 2022.08.21 |
---|---|
명령줄 인수 (Command line arguments) (0) | 2022.08.20 |
방어적 프로그래밍의 개념 (Defensive Programming) (0) | 2022.08.20 |
재귀적 함수 호출 (Recursive Function Call) (0) | 2022.08.18 |
std::vector를 stack처럼 사용하기 (0) | 2022.08.17 |