CRuntieClass Reflection

Programming/C,CPP,CS 2016. 8. 17. 14:25 Posted by TanSanC
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

최근에 Open Platform으로 갖춰야 할것 중 하나가 개발 툴인데, 
IDE같은 개발 툴들은 대부분 Import, Plug-in, User Defined Control(ActiveX control) 등과 같이, System이 개발 된 당시에는 존제하지 않았던 class 나 객체들을 자연스럽게 설치, 추가하여 사용할 수 있도록 해주는 부분들이 매우 많은 범위에 존제합니다..
이때 framework  구현부는 실제 class의 실체를 모른 상태에서 runtime시에 binding 되는 class의 객체를 생성해야 할 때가 있다.

class의 인스턴스 생성방법은 다들 알다시피 new를 통해 생성합니다.

BaseClass * pInstance = new SubClass; 

바꾸어 말하면 우리는 반드시 SubClass를 알고 있어야 한다는 것입니다. 그래야만 instance를 생성 할 수 있습니다.

아래와 같이 CameraPlugIn class를 framework에서 모르는 상태라고 한다면 절대 instance를 만들 수 없다는 예기죠.


IPlugIn * pPlugIn = new CameraPlugIn; 



만약 우리가  CameraPlugIn를 모르는 상태 즉,  PlugIn Framework 개발당시에는 CameraPlugIn이라는 class를 모르는 상태에서  camera.dll또는 lib을 download받아서, 이를 link 또는 load 해서 사용할 수 있는 기능을 제공하려 할때,
class를 모르는 상태에서 class 를 runtime시에 동적으로 search 하고 instance를 생성할 수 있도록 하는 기술이 필요하게됩니다.

이러한 기술을 리플렉션( Reflection) 이라한다.

C# 에서도 이와같은 리플렉션은 지원하고 있고, Runtime중에 type binding을 지원하는 Dyanimc 이라는 새로운 타입이 추가될 예정이라고 합니다.

그외  루비, 스몰토크, PHP, 파이썬, 펄 등의 언어에서는 리플렉션 객체를 제공하며, 대부분 유사한 형태를 띄고 있습니다.


아쉽게도 C++ 에서는 공식적으로 지원하지 않는 기능이지만, 전세계의 수많은 똑똑한 개발자 분들이 이런 기능을 다양한 방법으로 제공하고 있습니다.

아마 MFC의 CRuntimeClass는 리플랙션과 비슷한 기능을 위해 만들어진 가벼운 Macro 정도이고 실제 훨씬 편리하고 플랙서블한 그런 library들도 많이 있으니 관심이 있다면 구글링으로 찾아보셔도 좋을 것입니다.


그중 하나의 기술 (?, 그렇죠 말그대로 Programming 테크닉이라 불리울 만한 기술이죠. )MFC에서 Runtime class 라는 것이 있습니다.


저도 나름 이와 비슷한 아이디어를 떠올리면서 구현을 했었는데 , 거의 구현이 완료되고 나서 생각해보니 MFC에 이런게 있었다는 것이 생각이나, Source code를 뒤져봤답니다.

역시나 이미 상용화되어서 아주 ~~ 아주 잘 쓰고 있는 메크로들이라 그런지, 제가 생각했던 개념과 유사한 뼈대위에 많은 살들이 붙어있더군요.
(!! 메크로에 Templete 까지 넣어서 만들었을 줄이야 ㅠ_ㅠ)

회사에서 사용하기 위해 작성한 코드가 좋은 예가 될수 있을텐데, 회사의 자산이라 공개를 할 수 없어서  MFC의 Runtime class의 내용을 살펴보는 것으로 대신 하겠습니다.

저는 사실 한 일주일 쯤 해서  이런 고민에 빠져 있었습니다.

만약 내가 개발 툴을 만들어주고, application 개발자는 자기가 짠 class를 이 개발툴에 plug-in 한 후에 ,
개발 툴에서 재공하는 Generator 를 이용해  Generator("TestClass") 라고 하면 testclass의 instance를 만들어준다고 합시다.
이때 개발툴을 개발할 당시에는 TestClass를 모르고 있기 때문에 일반적인 개발 방법으론, 이를 구현할 수가 없습니다.
즉, Runtime 중에 class를 register하고 이를 parsing하여 instance를 생성 시킬 수 있는 기술이 필요하다.!!

class라는 게 어찌 보면 type인데, 내가 type을 모른 상태에서 object를 생성할 수 있을까?
예를 들면 
Object* pA = CreateInstance("TestClass");
라고하면 pA는 class TestClass 로 생성이되는 이런걸 만들고 싶었습니다.
하지만 흔히 이렇게 사용할 수 있으려면, 기본적으로 TestClass를 CreateInstance 내에서 알고 있어야 합니다. 
그래야 "TestClass" 라는 스트링을 parsing한 다음에 TestClass로 생성을 해줄 수가 있기 때문입니다.
그래서 대부분 이런 코딩을 하겠죠?


Object* CreateInstance(char* pClassName)
{
       :
        if(strcmp(pClassName,"TestClass")==0)
                return (Object*)new TestClass;
}

CreateInstance를 제너럴하게 만들고 싶어서 pClassName 으로 들어오는 class이름이 무엇이든간에 이에 맞는 class를 찾아서 있으면 해당 class의 instance를 생성해주고, 없으면 null 을 return하기를 원한다면? 어떻게 만들까? 음... class들을 관리하는 table과 class name,그리고 class generator을 만들어서 구현 할 수 있을것 입니다.

Object* CreateInstance(char* pClassName)
{
       :
       for(;;)
        if(strcmp(pClassName,gClassTable[i].szName)==0)
                return (Object*) gClassTable[i].Construct();
}

그런데, CreateInstance라는 함수를 만들때는 "TestClass라는 것이 없는데 , 앞으로 누군가가 만들것이다." 라고 한다면? 흠 흠.. 이경우는 난감한가요? 분명 이렇게 되어야 합니다. gClassTable 에 RegisterClass라는 API를 만들어서 이름과 생성자를 등록하여 사용할 수 있도록 해야합니다.

void RegisterClass(char* classname, Object *(*fpConstruct)())
{
      :
    gClassTable[i].szName = classname;
    gClassTable[i].Construct= fpConstruct;
}

가 있어야 되겠죠. 헌데 이렇게 되면 상당히 귀찮아지는 것이 fpConstruct를 만드는 일이 됩니다. 이럴때 나온 개념이 runtimeclass라는 넘이 됩니다. MFC의 CRuntimeClass를 살펴보면, 클레스 전체를 살펴보면 좋겠지만.. 대충 필요한 것들.. 핵심이 될만한 것들만 언급하자면,

struct CRuntimeClass
{
// Attributes
1) LPCSTR m_lpszClassName;
2) CRuntimeClass* m_pBaseClass;
3) CObject* CreateObject();
4) static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);
5) CRuntimeClass* m_pNextClass;       // linked list of registered classes
}

아마 가장 핵심이 되는 것이 위의 1)~4) 까지의 내용일 겁니다. 왜냐? 다음 RuntimeClass를 만드는 메크로들을 보면 알 수 있습니다.

#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
#define DECLARE_DYNAMIC(class_name) \
public: \
 static const CRuntimeClass class##class_name; \
 virtual CRuntimeClass* GetRuntimeClass() const; \

#define _DECLARE_DYNAMIC(class_name) \
public: \
 static CRuntimeClass class##class_name; \
 virtual CRuntimeClass* GetRuntimeClass() const; \

#define IMPLEMENT_RUNTIMECLASS(class_name) \
 const CRuntimeClass class_name::class##class_name = { \
  #class_name, sizeof(class class_name), \
   }; \  
 CRuntimeClass* class_name::GetRuntimeClass() const \
  { return RUNTIME_CLASS(class_name); } \

이런 메크로들이 있습니다. 약간 길죠?. IMPLEMENT_RUNTIMECLASS 는 설명하기 귀찮은 것들은 다 뺐습니다. 이렇게 메크로만 보면 "뭐하자는 거지?" 라고 의문이 드는 것들이 실제 사용하는 예를 보면 "아하!!" 라고 하게 됩니다.

in MyTestControl.hpp--------------- 
class MyTestControl
{
   _DECLARE_DYNAMIC(MyTestControl)   //<-- static const Runtimeclass  classMyTestControl 가
                                                            // MyTestControl class안에 정의된다.

   :
   :

};
in MyTestControl.cpp--------------- 
파일 맨 위에
IMPLEMENT_RUNTIMECLASS(class_name) // static 객체인 MyTestControl::classMyTestControl 를 초기화 한다.

in MyTestApp.cpp

void main(void)
{
   AfxInitClass(RUNTIME_CLASS(MyTestControl));
   
   CRuntimeClass::CreateObject("MyTestControl");
  
}

대충의 설명을 하자면 ,
1. Runtime class로 선언하고자 하는 class 내부에 DECLARE_DYNAMIC(classname) 을 해주고나면, static runtime class 하나가 생기는데 "class"+classname 의 형태로 된다.
2. 이 "classMyTestControl"라고 새로 만들어진 static runtimeclass 는 멤버함수인 3).의 CreateObject() 를 MyTestControl를 생성할 수 있는 함수로 만든다.
3. 그 형식은 아래와 같다.

CreateObject() 
{
  return new MyTestControl;
}

따라서 우리는 메크로에 의해 생성된 classMyTestControl 이라는 runtime class를 이용해서 MyTestControl이라는 class의 instance를 생성할 수 있게 됩니다.


 마지막으로 한가지 설명 안하고 넘어간 것이 있는데.

5) CRuntimeClass* m_pNextClass;       // linked list of registered classes

이 녀석에 대한 내용이다. 이게 왜 있는가? 제가 개발할때, 위와 같은 구현을 완료하고 나서 Register 단계( MFC에서는 AfxInitClass 단계일듯하다.)에서 결국 table을 만들고, table과 Runtime class를 묶는 작업을 하고, search하는 함수를 만들고... 등등을 작업했었다.

CSystemClassFactroy
{
 list runtimeClass;
 RegisterClass(CRuntimeCalss);
};

application에서 사용할때는 아래와 같이 register를 해야 되는 상황이 있었습니다.

main()
{
   // application 초기화
   Factroy.RegisterClass(MyTestControl);
    :
    :

}

이때 문득 MFC에서는 어떻게 하지? 라는 생각이 들어 이 RuntimeClass를 살펴본바... m_pNextClass 를 이용해서 각 register시에 RuntimeClass들을 줄줄이 사탕으로 엮어놓은 것이라 추측 할수 있었다.!!! 아하.!! 요렇게 하면 runtime class를 managing 하는 manager class가 필요 없겠구나.!! 하는 필(feel)을 받고, 제 코드도 요렇게 바꿨답니다. ㅎㅎ 



찾아보기 키워드!!! 

[SEAL Reflex] 
[LibReflection] 

[XCppRefl 라이브러리]

 
<참고 링크>
[MSDN : C++ 리플렉션 in Visual C++ ]http://msdn.microsoft.com/ko-kr/library/y0114hz2(VS.90).aspx