3. Implementation/MFC

DECLARE_DYNCREATE 매크로와 RUNTIME_CLASS 매크로의 관계

SSKK 2009. 1. 19. 13:30

MFC로 생성한 SDI 또는 MDI 기반의 애플리케이션의 경우 CWinApp::InitInstance()에 보면 다음의 코드가 있다.

 

             CMultiDocTemplate* pDocTemplate;

             pDocTemplate = new CMultiDocTemplate(

                           IDR_TABBEDTYPE,

                           RUNTIME_CLASS(CTabbedViewDoc),

                           RUNTIME_CLASS(CChildFrame), // custom MDI child frame

                           RUNTIME_CLASS(CTabbedViewView));

             AddDocTemplate(pDocTemplate);

 

RUNTIME_CLASS 매크로 내에 CDocument를 상속한 CTabbedViewDoc 클래스가 선언되어 있다. 그리고 CTabbedViewDoc 클래스 선언부에는 반드시 DECLARE_DYNCREATE 매크로가 선언되어 있다.

 

DECLARE_DYNCREATE 매크로를 정의한 코드를 살펴보면,

 

#define DECLARE_DYNCREATE(class_name) \

             DECLARE_DYNAMIC(class_name) \

             static CObject* PASCAL CreateObject();

 

 

그리고, 내부에 선언된 DECLARE_DYNAMIC 매크로를 살펴보면

 

#define DECLARE_DYNAMIC(class_name) \

public: \

             static const CRuntimeClass class##class_name; \

             virtual CRuntimeClass* GetRuntimeClass() const; \

 

 

 

CTestClass 내에 위 매크로를

 

DECLARE_DYNCREATE(CTestClass)

 

과 같이 선언했다고 가정했을 경우 아래와 같은 코드가 추가되는 것과 같다.

 

class CTestClass

{

             // ….

Static const CRuntimeClass classCTestClass;   // ##은 문자열 결합

virtual CRuntimeClass * GetRuntimeClass() const;

static CObject * PASCAL CreateObject();

 

// ….

}

 

 

그리고 RUNTIME_CLASS 매크로의 정의 코드를 살펴보면 다음과 같다.

 

#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

#ifdef _AFXDLL

#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())

#else

#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)

#endif

 

위와 같이 선언되어 있는데, APP의 경우 DLL이 아니므로 결국

 

#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

 

와 같다. 그러면 다시 처음에 소개된 코드에서 매크로를 원 코드로 변환하면 아래와 같다.

 

             CMultiDocTemplate* pDocTemplate;

             pDocTemplate = new CMultiDocTemplate(

                           IDR_TABBEDTYPE,

((CRuntimeClass*)(&class_name::classCTabbedViewDoc)),

((CRuntimeClass*)(&class_name::classCChildFrame)),

((CRuntimeClass*)(&class_name::classCTabbedViewView)) );

             AddDocTemplate(pDocTemplate);

 

 

위와 같이 CMultiDocTemplate에 인자로 넘어가는 값은 DECLARE_DYNCREATE 매크로에 의해 선언된 멤버 변수의 포인터를 넘기는 것이고, 이 포인터는 CMultiDocTemplate의 부모 클래스인 CDocTemplate 의 생성자에서 다음과 같이 저장된다.

 

CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,

             CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)

{

             // … 중략

             m_pDocClass = pDocClass;

             m_pFrameClass = pFrameClass;

             m_pViewClass = pViewClass;

             // … 중략

}

 

CDocTemplate은 위의 멤버 변수들을 통해서 필요할 때 각각의 클래스에 해당하는 새로운 인스턴스를 생성한다.

 

아래 코드를 살펴보면, CDocTemplate::CreateNewFrame 내에서 CreateObject 함수 호출을 통해서 CFrameWnd 인스턴스를 생성하고, 그 인스턴스를 통해서 CFrameWnd::LoadFrame 을 호출하면 CDocTemplate::CreateNewFrame 에서 넘겨준 CreateContext 인자를 이용하여 CFrameWnd::LoadFrame 함수 내에서 Create를 호출하여 View를 생성한다. 그리고 CDocTemplate::CreateNewDocument() 내에서 CreateObject 함수를 호출하여 Document 인스턴스를 생성한다.

 

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)

{

             // … 중략

             CCreateContext context;

             context.m_pCurrentFrame = pOther;

             context.m_pCurrentDoc = pDoc;

             context.m_pNewViewClass = m_pViewClass;

             context.m_pNewDocTemplate = this;

 

             CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();

             // … 중략

 

             // create new from resource

             if (!pFrame->LoadFrame(m_nIDResource,

                                        WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

                                        NULL, &context))

             {

                           TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create a frame.\n");

                           // frame will be deleted in PostNcDestroy cleanup

                           return NULL;

             }

 

             // it worked !

             return pFrame;

}

 

 

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,

             CWnd* pParentWnd, CCreateContext* pContext)

{

             // … 중략

             if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,

               pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) // 뷰 생성

             {

                           return FALSE;   // will self destruct on failure normally

             }

 

             // … 중략

             return TRUE;

}

 

 

CDocument* CDocTemplate::CreateNewDocument()

{

             // 중략

             CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();

             // 중략

             return pDocument;

}

 

긴 내용이지만 위의 내용을 짧게 요약하면 다음과 같다.

 

DECLARE_DYNCREATE 매크로는 RUNTIME_CLASS 매크로에서 필요로 하는 멤버 변수(class##class_name) MFC 프레임워크 내에서 동적으로 인스턴스를 생성하기 위한 멤버 함수(CreateObject)를 제공하기 위해서 선언을 하는 것이다.