C++ 타입 체킹에 대한 작은 도전

@codemaru · August 13, 2007 · 6 min read

C언어에서 void 포인터는 다른 타입으로 자동으로 형 변환이 이루어진다. 그런데 이 녀석이 C++로 오면서는 엄격해져서 자동으로 형 변환이 이루어지지 않게 되었다. 별것 아닌일 같지만 이 놈 때문에 은근슬쩍 귀찮은 경우가 많다. 대표적인 예가 malloc을 사용하는 경우다. C에서는 아래와 같은 코드가 동작한다.

char \*buf = malloc(100);
```하지만 C++에서는 아래와 같이 명시적으로 캐스팅을 해줘야지 컴파일러가 불평하지 않는다.  
```cpp
char \*buf = (char \*) malloc(100);
```요기까지 오면 대부분의 사람들은 new, delete를 쓰라고 충고한다. 맞는 말이다. 하지만 malloc은 어디까지나 시작에 불과하다. 이러한 예는 너무나도 많이 찾을 수 있다. 최근에 만들었던 GetPtr이란 함수도 그러한 대표적인 함수다. 아래 함수 코드가 나와있다.  
```cpp
inline PVOID GetPtr(PVOID base, SIZE\_T offset)  
{  
    return (PVOID)((DWORD\_PTR) base + offset);  
}
```너무나 간단한 함수다. base에서 offset만큼 더한 포인터를 리턴해 주는 것이 하는 일이다. 이 함수를 어디다 쓰냐 하면 PE 포맷의 헤더를 구하기 위해서 사용한다. 아래는 그러한 무수한 예 중 한 가지 코드를 보여 주고 있다.  
```cpp
PIMAGE\_DOS\_HEADER dos = (PIMAGE\_DOS\_HEADER) base;  
PIMAGE\_NT\_HEADERS nt = (PIMAGE\_NT\_HEADERS) GetPtr(dos, dos->e\_lfanew);
```그동안 난 저런 코드를 쓰면서 한번도 바보 같다는 생각을 해보지 않았다. 모니터 해상도가 너무 높은 것도 불평을 하지 않았던 한 가지 이유가 될 수도 있겠다. 그러다 문득 오늘 지난 달에 마소에 연재했던 기사를 화장실에서 보게 되었다. 코드가 주르륵 나온다. 내가 인덴테이션 할 때는 이런 모양이 아니었는데. ㅠㅠ 마구 마구 다음 줄로 넘어간 내용들 때문에 코드는 이해하기가 너무 어려웠다. 쓴 나도 이모양이니 다른 분들은 더 심할게 분명한 사실이었다. 무엇 때문에 그리도 많은 코드가 줄을 넘길까? 라는 생각이 들어서 살펴보았더니 위와 같이 바보같은 타입 캐스팅 코드가 대부분이었다. 바보같은 타입 캐스팅을 하지 않고 코드를 써야 겠다고 다짐했다. 그리곤 화장실을 나왔다.  
  
어떻게 하면 바보같은 타입 캐스팅을 피할 수 있을까?  
  
방법1: 함수를 죄다 만들어 둔다.  
```cpp
PIMAGE\_DOS\_HEADER GetPtr(PVOID base, SIZE\_T offset);  
PIMAGE\_NT\_HEADERS GetPtr(PVOID base, SIZE\_T offset);
```위와 갈은 형태로 함수를 모두 만드는 것이다. 하지만 불행하게도 위와 같은 방법은 C++에서는 통하지 않는다. C++에서는 리턴 타입으로 함수를 구분하지 않기 때문이다. 인자가 틀려야 하는데 인자까지 모두 똑같기 때문에 함수를 만들수조차 없다.  
  
방법2: 템플릿을 써 봐?  
```cpp
template <typename T>  
T GetPtr(PVOID base, SIZE\_T offset);
```위와같은 코드를 상상해 볼 수 있다. 똑똑한 요즘 컴파일러 저정도 유추 못해 내려고? 라고 생각했다면 완전 잘못 생각한 것이다. 방법1과 마찬가지로 템플릿도 리턴 타입은 유추해내지 못한다. 오로지 인자로만 유추할 수 있다.  
  
방법3: C++은 연산자도 오버로딩 되지??  
```cpp
operator PIMAGE\_DOS\_HEADER(PVOID ptr);  
operator PIMAGE\_NT\_HEADERS(PVOID ptr);
```애석하게도 이 방법 또한 통하지 않는다. operator PIMAGE\_DOS\_HEADER 따위는 정적 멤버로 만들수 없다는 컴파일러의 공허한 에러 메시지만 메아리 칠 뿐이다.  
  
이제 더 이상 방법이 없을까? C++ 타입 체킹의 벽은 생각보다 단단했다. 단지 PIMAGE\_DOS\_HEADER를 두번 쓰기 싫을 뿐인데 컴파일러는 우리에게 자꾸 그걸 쓰라고 강요한다. ㅠㅠ 그냥 써야 할까? 아니다. 생각을 바꾸면 답이 보인다. 타입을 자동으로 변환하려 하지 말고 자동으로 변환되는 타입을 만들면 되는 것이다. 생뚱 맞은 말처럼 보인다면 아래 코드를 보자.  
```cpp
class CPEImagePtr  
{  
private:  
    PVOID m\_ptr;  
  
public:  
    CPEImagePtr(PVOID ptr) : m\_ptr(ptr) {}  
  
    operator PVOID() { return m\_ptr; }  
    operator PIMAGE\_DOS\_HEADER() { return (PIMAGE\_DOS\_HEADER) m\_ptr; }  
    operator PIMAGE\_NT\_HEADERS() { return (PIMAGE\_NT\_HEADERS) m\_ptr; }  
    operator PIMAGE\_SECTION\_HEADER() { return (PIMAGE\_SECTION\_HEADER) m\_ptr; }  
};  
  
inline CPEImagePtr GetPtr(PVOID base, SIZE\_T offset;
@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중