__purecall이 무엇일까?

@codemaru · October 03, 2007 · 4 min read

R6025를 새롭게 소개해 드린 이유는 __purecall에 대해서 설명하기 위해서 였습니다. 그 녀석과 __purecall이 매우 밀접한 관계에 있거든요. 아래 코드를 봅시다. R6025 예제보다 더 작위적이죠. 아직 생성되지 않은 가상 함수를 호출하기 위해서 func란 함수를 이용합니다. Entry를 main으로 고치고 컴파일해서 실행해 보면 R6025 에러가 날 겁니다. 하지만 이번에는 조금 다르게 해봅니다. 아래 코드를 Visual C++의 콘솔 프로젝트에 타이핑해 넣고 CRT를 연결하지 않고, 진입점을 Entry로 설정해두고 컴파일 해 봅니다. 아마도 에러가 날 겁니다. __purecall을 찾을 수 없다는 에러죠.

CRT를 연결하지 않는 방법은 간단합니다. 여러분이 지정한 CRT 라이브러리를 링크시에 무시하도록 만들면 됩니다. 만약 멀티 스레드 CRT를 선택했다면 프로젝트 속성 창에서 링크 필드에 있는 무시 라이브러리 목록에 libcmt.lib를 추가해 주면 됩니다.

#include <windows.h>  
  
class CBase;  
class CDerived;  
void func(CBase \*p);  
  
class CBase  
{  
public:  
    CBase() { func(this); }  
    virtual void test() = 0;  
};  
  
class CDerived : public CBase  
{  
public:  
    void test()  
    {  
        TCHAR buf[] = \_T("hello\n");  
        WriteConsole(GetStdHandle(STD\_OUTPUT\_HANDLE)  
                    , buf  
                    , sizeof(buf) / sizeof(TCHAR) - 1  
                    , NULL  
                    , NULL);  
    }  
};  
  
void func(CBase \*p)  
{  
    p->test();  
}  
  
int Entry()  
{  
    CDerived a;  
    return 0;  
}

에러를 없애기 위해서는 아래와 같이 _purecall을 만들어서 넣어주면 됩니다. __purecall이 없다는데 _purecall을 만드는 이유는 Visual C++ 컴파일러가 함수 명 앞에 언더바를 붙이기 때문입니다. 이 녀석을 추가한 다음 컴파일을 하면 정상적으로 컴파일이 될 겁니다. 실행하면 purecall이란 메시지가 출력 될 겁니다.

int \_purecall()  
{  
    TCHAR buf[] = \_T("purecall\n");  
    WriteConsole(GetStdHandle(STD\_OUTPUT\_HANDLE)  
                , buf  
                , sizeof(buf) / sizeof(TCHAR) - 1  
                , NULL  
                , NULL);  
    return 0;  
}

아마 이쯤되면 여러분 모두가 눈치를 챘을 겁니다. 맞습니다. 순수 가상 함수는 이론적으로는 존재하지 않는 함수 입니다. 하지만 실질적으로 컴파일러는 그 부분을 __purecall로 채웁니다. 즉, 가상 함수를 저장하는 vtable의 함수 부분을 __purecall로 대체한다는 것이죠. 그렇게 함으로써 런타임에 에러를 출력해 줄 수 있죠. 만약 그 부분을 채우지 않고 NULL로 두었다면 잘못된 연산을 수행했다는 메시지와 함께 종료될 것 입니다.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중