compile time assertion

반응형

중요한 안건은 두가지.
runtime assert를 compile time assert로 대체.
이를 대체하면서 적절한 에러메세지 출력.

안전한 형변환을 위해 아래와 같이 만들었다고 생각해보자.
#include <assert.h>

// 안전한 형변환을 위해 선언.
template <class To, class From>
To safe_reinterpret_cast(From from)
{
 assert(sizeof(from) <= sizeof(To));
 return reinterpret_cast<To>(from);
}

int _tmain(int argc, _TCHAR* argv[])
{
 int i   = 4;
 char* p = safe_reinterpret_cast<char*>(i);
 return 0;
}

즉, To에 쓰인 자료형이 From에 쓰인 자료형의 모든 비트데이터를 수용할 수 있는 크기인지가 관건이다.
그렇지 않다면 assert(런타임 어설트)가 발생한다.

만일, 이러한 에러를 컴파일 타임에 잡아낼 수 있을까?
관심있게 봐야 할 부분은 assert() 함수 이다.
이 구문에서 인자로 사용되고 있는 비교식은 컴파일러 입장에서 볼 때 상수로 분류된다.
즉, 컴파일러에게 0이 아닐 경우에는 적법하고, 0일 경우에는 문제가 되는 구문을 언어적 구조로 표현하여
넘겨주면 되지 않을까?..

0일 경우에 문제가 되는 구문중 하나가 길이가 0인 배열이다.
c와 c++에서는 길이가 0인 배열을 허용하지 않는다는 것에 주목하자.
아래와 같이 매크로를 살짝 만들어 줄 수 있다.
#define  STATIC_CHECK(expr) {char unnamed[(expr)?1:0];}

이제 컴파일 타임 어설트도 만들었으니 런타임 어설트를 대신하여 사용해 보자.

template <class To, class From>
To safe_reinterpret_cast(From from)
{
 STATIC_CHECK(sizeof(from) <= sizeof(To));
 return reinterpret_cast<To>(from);
}

int _tmain(int argc, _TCHAR* argv[])
{
 int i   = 4;
 void* point = &i;
 char p = safe_reinterpret_cast<char, void*>(point);
 return 0;
}

이를 컴파일 하면 "error C2466: 상수 크기 0의 배열을 할당할 수 없습니다."와 같은 문구가 뜨면서 컴파일시 에러가 뜬다.

이제 에러메세지에 특별한 의미를 부여해야 한다.
컴파일러는 에러메세지에서 템플릿의 이름을 언급해 줄 수 있다.
template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
#define  STATIC_CHECK2(expr) (CompileTimeError<(expr) != 0>())

위와같이 bool상수를 인자로 하고, bool값이 true인 경우에 대해서만 정의하므로
false에 대한 템플릿의 특화는 정의돼어 있지 않다는 에러메세지를 받을 수 있다.
이 에러메세지는 프로그래머가 의도한 에러메세지임을 알아야 한다.

template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};

#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }


// 안전한 형변환을 위해 선언.
template <class To, class From>
To safe_reinterpret_cast(From from)
{
 STATIC_CHECK(sizeof(from) <= sizeof(To), Destination_Type_Too_Narrow)
 return reinterpret_cast<To>(from);
}

define static_check가 false를 얻으면 컴파일 타임에서 에러가 나고 에러메세지는
정의한 메크로에 의해 아래와 같은 형식으로 출력이 된다.

'ERROR_Destination_Type_Too_Narrow'은(는) 정의되지 않은 struct 'CompileTimeError<__formal>'을(를) 사용합니다.
위의 메세지를 통해 디버깅을 좀 더 빨리 편하게 할 수 있다.

'Study > C++' 카테고리의 다른 글

warning C4251  (0) 2010.06.26
참조카운팅 + 스마트포인터 템플릿  (0) 2010.06.16
FileChecker  (0) 2010.06.09
STL의 std::string::find_last_of 사용 시 주의 사항  (0) 2010.06.04
__int8, __int16, __int32, __int64  (0) 2010.05.30
TAGS.

Comments