본문 바로가기

Library/Windows Programming

Windows Template Library Startup 1

WTL을 프로젝트에 이용하기로 마음 먹었다면, PCH(Precompile Header)를 사용하는게 좋다. 대부분의 C++ 컴파일러가 아직 export 키워드를 구현하지 않았기 때문에, 분할 컴파일 모형을 적용할 수 없으며, 템플릿 라이브러리는 아직까지 대부분 헤더 파일만의 형태로 제공된다.


완전히 빈 상태인 stdafx.h와 같은 PCH에서 출발한다면, 다음과 같은 내용이 포함되어야 한다.


stdafx.h
#include <atlbase.h> // base ATl classes
#include <atlapp.h> // base WTL classes

#include <atlwin.h> // ATL GUI classes
#include <atlframe.h> // WTL frame window classes
#include <atlcrack.h> // WTL enhanced msg map macros
#include <atlmisc.h> // WTL utility classes

extern CAppModule _Module


여기서 CAppModule은 MFC의 AFX_STATE_MODULE, CWinApp의 역할을 하는 클래스로, CWinApp처럼 반드시 전역적으로, 하나만 생성되어야 하는 클래스이다. extern CAppMododule _Module 라인이 의아할텐데, 이것은 코드가 헤더 파일에서 구현되어야 하기 때문이다. 즉, 실제로 CAppModule이 선언되고 인스턴스화되는 곳은 프로그램의 엔트리 포인트가 존재하는 cpp 파일이지만, WTL을 사용하는 구현 코드는 각기 다른 헤더 파일에 존재한다. 그렇지만, 이 헤더 파일에 포함되는 코드들이 CAppModule을 사용할 필요가 있기 때문에 이런 묘한 방법을 사용하는 것이다. 왜 묘한지 다음을 보도록하자.


mywindow.h
class CMyWindow : public CWindowImpl< CMyWindow, CWindow, CFrameWinTraits >
{
public:
    DECLARE_WND_CLASS(_T("WTL Window"))

    BEGIN_MSG_MAP(CMyWindow)
        MSG_WM_CREATE(OnCreate)
    END_MSG_MAP()

    int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        pLoop->....
    }
};

startup.cpp
#include "stdafx.h"
#include "mywindow.h"

CAppModule _Module;

int APIENTRY _tWinMain(...)
{
    ....
}


문제는 mywindow.h이다. 어떤 헤더 파일도 include 하지 않았는데, 이것이 어떻게 컴파일 되는가? 전처리기는 include는 지시된 파일만 포함시키며, 그렇기 때문에 이상야릇한 stdafx.h의 extern CAppModule _Module, startup.cpp의 CAppModule _Module 선언이 존재한다. 즉, 실제로 컴파일러가 컴파일하는 것은 startup.cpp 파일이고, 이 파일을 컴파일 하기 전에 먼저 pre-processing 단계를 거친다. 즉, stdafx.h는 stdafx.cpp와 조합되어 PCH 형태로 만들어져 미리 컴파일되어 있겠지만, stdafx.h의 내용 자체는 startup.cpp와 합쳐지며, include 순서대로 mywindow.h가 포함되어 최종적으로 컴파일 할 파일이 생성된다. 따라서, mywindow.h가 어떤 헤더 파일도 포함하지 않아도, 이 파일이 혼자서 컴파일되는 것이 아니기 때문에 컴파일 에러가 나지 않는다. 그리고, 템플릿 코드는 export 키워드가 지원되지 않는 이상 해당 템플릿 선언이 있는 같은 파일에 구현되어야 한다. 따라서, OnCreate() 메서드가 필요로 하는 CAppModule 인스턴스는, 실제로는 startup.cpp에 존재한다고 알려주어야하며, 이런 이유 때문에 stdafx.h에 extern 선언되어 있는 것이다.


이것이 WTL 프로그램의 대략적인 몸체라고 할 수 있는데, 이것을 이해한다면 WTL의 한 발을 내딛었다고 할 수 있다.