MFC 이야기 : MFC에서 RTTI 의 실제 구현 MFC 마을



지난시간에는 MFC에서 RTTI 가 어떤 방식으로 구현 되었을지 원리에 대해서 알아 보았다.

이번시간에는 MFC 의 헤더파일과, 실제 MFC 응용프로그램의 소스 파일을 확인 해 봄으로써 실제 구현에 대해서 알아 보겠다.

이전 글을 보지 않았다면, 아래의 링크를 참조하라.


MFC 이야기 : MFC 에서 RTTI 구현 원리 




이번시간에는 MFC에서 RTTI 가 어떤식으로 실제 구현되었는지에 대해서 알아본다.

우선 데모 프로젝트를 만들고 View 와 Frame, Document 클래스에서 RTTI 매크로 부분을 찾아보자.


01: 
02: // DemoView.h : CDemoView 클래스의 인터페이스
03: //
04:
05: #pragma once
06:
07:
08: class CDemoView : public CView
09: {
10: protected: // serialization에서만 만들어집니다.
11: CDemoView();
12: DECLARE_DYNCREATE(CDemoView)
13:
14:
15:
16: // CDemoView.cpp
17:
18: IMPLEMENT_DYNCREATE(CDemoView, CView)


헤더에는 DECLARE_DYNCREATE 매크로가 선언되어 있고, CPP 파일에 IMPLEMENT 매크로를 통해 필요한 부분을 구현 해 놓았다. 매크로를 분석 해 보자.

01: 
02: // DECLARE 매크로 분석
03:
04: DECLARE_DYNCREATE(CDemoDoc) // DemoDoc.h
05:
06: //////////////////////////////////////////////////////////////////////////
07:
08: #define DECLARE_DYNCREATE(class_name) \
09: DECLARE_DYNAMIC(class_name) \
10: static CObject* PASCAL CreateObject();
11:
12: //////////////////////////////////////////////////////////////////////////
13:
14: #define DECLARE_DYNAMIC(class_name) \
15: protected: \
16: static CRuntimeClass* PASCAL _GetBaseClass(); \
17: public: \
18: static const CRuntimeClass class##class_name; \
19: static CRuntimeClass* PASCAL GetThisClass(); \
20: virtual CRuntimeClass* GetRuntimeClass() const; \
21:
22: //////////////////////////////////////////////////////////////////////////
23:
24: protected: \
25: static CObject* PASCAL CreateObject();
26: protected: \
27: static CRuntimeClass* PASCAL _GetBaseClass(); \
28: public: \
29: static const CRuntimeClass class##class_name; \
30: static CRuntimeClass* PASCAL GetThisClass(); \
31: virtual CRuntimeClass* GetRuntimeClass() const; \
32:

DECLARE_DYNCREATE 매크로를 사용하면 위와 같은 함수와 클래스들이 선언 된다.

CRuntimeClass* 를 돌려주는 가상함수 GetRuntimeClass 와 현재 클래스, 베이스 클래스의 CRuntimeClass* 를 리턴 해 주는
_GetBaseClass, GetThisClass 정적 함수가 선언된다. 그리고 CRuntimeClass 타입의 정적 구조체가 선언된다.


IMPLEMENT_DYNCREATE 를 분석 해 보자.

01: 
02:
03:
04: // IMPLEMENT 매크로 분석
05:
06: IMPLEMENT_DYNCREATE(CDemoDoc, CDocument)
07:
08: //////////////////////////////////////////////////////////////////////////
09:
10: #define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
11: CObject* PASCAL class_name::CreateObject() \
12: { return new class_name; } \
13: IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
14: class_name::CreateObject, NULL)
15:
16: //////////////////////////////////////////////////////////////////////////
17:
18: #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
19: CRuntimeClass* PASCAL class_name::_GetBaseClass() \
20: { return RUNTIME_CLASS(base_class_name); } \
21: AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
22: #class_name, sizeof(class class_name), wSchema, pfnNew, \
23: &class_name::_GetBaseClass, NULL, class_init }; \
24: CRuntimeClass* PASCAL class_name::GetThisClass() \
25: { return _RUNTIME_CLASS(class_name); } \
26: CRuntimeClass* class_name::GetRuntimeClass() const \
27: { return _RUNTIME_CLASS(class_name); }
28:
29: //////////////////////////////////////////////////////////////////////////
30:
31: #define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
32: #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
33:
34: //////////////////////////////////////////////////////////////////////////
35:
36: CObject* PASCAL class_name::CreateObject() \
37: { return new class_name; } \
38: CRuntimeClass* PASCAL class_name::_GetBaseClass() \
39: { return base_class_name::GetThisClass(); } \
40: AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
41: #class_name, sizeof(class class_name), wSchema, pfnNew, \
42: &class_name::_GetBaseClass, NULL, class_init }; \
43: CRuntimeClass* PASCAL class_name::GetThisClass() \
44: { return (CRuntimeClass*)(&class_name::class##class_name); } \
45: CRuntimeClass* class_name::GetRuntimeClass() const \
46: { return (CRuntimeClass*)(&class_name::class##class_name); }
47:
48: //////////////////////////////////////////////////////////////////////////
49:
50:

CreateObject 는 자신의 클래스 객체를 리턴하고, _GetBaseClass, GetThisClass 는 예상대로 베이스 클래스와 자신의 클래스의 GetRuntimeClass 함수를 호출해 CRuntimeClass 를 리턴한다.


이렇게 구현된 클래스는 다음과 같이 동적으로 생성할 수 있다.

01: 
02: // CMyClass.h
03: #pragma once
04:
05: class CMyClass : public CObject
06: {
07: protected:
08: DECLARE_DYNAMIC(CMyClass)
09: CMyClass() {};
10: };
11:
12: // CMyClass.cpp
13: #include "stdafx.h"
14: #include "MyClass.h"
15:
16: IMPLEMENT_DYNAMIC(CMyClass, CObject)
17:
18: // DemoView.cpp
19:
20: CDemoView::CDemoView()
21: {
22: // TODO: 여기에 생성 코드를 추가합니다.
23: CRuntimeClass* pcRuntimeClass = RUNTIME_CLASS(CMyClass);
24: CObject* pcObject2 = pcRuntimeClass->CreateObject();
25:
26: CObject* pcObject = new CMyClass;
27:
28: // CDemoDoc 와 pcObject 가 가리키는 CMyClass 는 같은 클래스가 아니고, 서로 상속관계도 아니므로
29: // ELSE 루틴으로 넘어간다.
30: if ( pcObject != NULL && pcObject->IsKindOf( RUNTIME_CLASS(CDemoDoc)) )
31: {
32: printf ( ("TRUE : %s\n"), pcObject->GetRuntimeClass()->m_lpszClassName );
33: }
34: else
35: {
36: printf ( ("FALSE : %s\n"), pcObject->GetRuntimeClass()->m_lpszClassName );
37: }
38: }


DYNCREATE 를 이용하면 isKindOf 함수를 이용할 수 있다. MFC는 RTTI를 위해 3종류의 매크로를 제공한다.

DYNMIC, DYNCREATE, SERIAL 이렇게 3가지다. 자세한 내용은 아래의 표를 참고하라.






위에서 구현한 예는 실제로 RTTI 라고 부를 수 없다. 컴파일 타임에 어떤 객체가 생성될지 이미 알고 있기 때문이다.

코드상에서 CMyClsas 와 같이 하드 코딩 되어있다. 런타임에 동적으로 생성되려면

01: 
02:
03: // 0, 1, 2 중 한가지 숫자
04: UINT nCondition = rand() % 3;
05:
06: LPCTSTR arszClassName[] = { _T("CAlpha"), _T("CBeta"), _T("Gamma") };
07:
08: // RUNTIME_CLASS 는 매크로이므로 인자로 변수를 받지 못한다. 아래 코드는 컴파일되지 않는다.
09: CRuntimeClass* pcRuntimeClass = RUNTIME_CLASS(arszClassName[nCondition]);
10:
11: // MFC RTTI는 문자열로 클래스를 생성하기 위해 FromName 을 지원한다.
12: CRuntimeClass* pcRuntimeClass = CRuntimeClass::FromName(arszClassName[nCondition]);
13:
14: }

11번째 줄과 같이 할수 있어야 한다. 이를 위해서는 DECLARE_SERIAL, IMPLEMENT_SERIAL 을 이용해야 한다.
01: 
02: // MyClass.h
03: #pragma once
04:
05: class CMyClass : public CObject
06: {
07: protected:
08: DECLARE_SERIAL(CMyClass)
09: CMyClass() {};
10: };
11:
12:
13: // MyClass.cpp
14: #include "stdafx.h"
15: #include "MyClass.h"
16:
17:
18: IMPLEMENT_SERIAL(CMyClass, CObject, 1)
19:
20:
21:
22: // DemoVicw.cpp
23: CDemoView::CDemoView()
24: {
25: // TODO: 여기에 생성 코드를 추가합니다.
26: CObject* pcObject = new CMyClass;
27:
28: CRuntimeClass* pcRuntimeClass = RUNTIME_CLASS(CMyClass);
29: CObject* pcObject2 = pcRuntimeClass->CreateObject();
30:
31: // DECLARE, IMPLEMENT SERIAL을 이용했으므로 FromName을 이용한 생성이 가능하다.
32: CRuntimeClass* pcRuntimeClassFromName = CRuntimeClass::FromName(_T("CMyClass"));
33: CObject* pcObject3 = pcRuntimeClassFromName->CreateObject();
34:
35: // 출력 결과는 TRUE다.
36: if ( pcObject2 != NULL && pcObject2->IsKindOf( RUNTIME_CLASS(CObject)) )
37: {
38: printf ( ("TRUE : %s\n"), pcObject->GetRuntimeClass()->m_lpszClassName );
39: }
40: else
41: {
42: printf ( ("FALSE : %s\n"), pcObject->GetRuntimeClass()->m_lpszClassName );
43: }
이에 대한 자세한 내용은 http://blog.naver.com/PostView.nhn?blogId=carfedm&logNo=140070752898 를 참고하라.




       - http://msdn.microsoft.com/ko-kr/library/1ybbhxe3.aspx (MSDN : Using CObject)


덧글

  • 진우 2022/05/28 20:26 # 삭제 답글

    C# 에서는 Type.GetMembers() 로 런타임에 객체가 갖고있는 멤버함수 목록, 멤버변수 목록을 얻어올수있는데
    혹시 C++ 리플렉션에도 이런 기능이 있을까요?
    MFC CRuntimeClass 에서는 이런 기능이 없는것 같더라구요..
  • hurtownia kawy sprze 2022/09/16 19:44 # 삭제 답글

    요점까지 잘 썼습니다
댓글 입력 영역


시계

라운드 시계

위키피디아