Programming/C++ & Unreal
C++ RTTI 구현
장형이
2021. 5. 2. 21:50
개요
RTTI는 Run Time Type Information의 약자로 프로그램이 실행된 시점에 Type을 알 수 있는 기능이다.
기본
#include <iostream>
using namespace std;
class A {
};
class B {
};
int main() {
A a;
B b;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
/* 출력
class A
class B
*/
return 0;
}
기본적으로 typeid()으로 타입을 가져와서, name()을 사용하면 const char*를 얻을 수 있다.
구현
// Main.cpp
#include <iostream>
#include "RTTI.h"
class A {
DECLARE_RTTI;
};
class B {
DECLARE_RTTI;
};
IMPLEMENT_RTTI(A);
IMPLEMENT_RTTI(B, A);
using namespace std;
int main() {
A a;
B b;
cout << a.GetRtti()->GetClassName() << endl;
cout << b.GetRtti()->GetClassName() << endl;
/* 출력
A
B
*/
cout << b.GetRtti()->IsChildOf(a.GetRtti()) << endl;
cout << a.GetRtti()->IsChildOf(b.GetRtti()) << endl;
/* 출력
1
0
*/
cout << b.GetRtti()->IsParentOf(a.GetRtti()) << endl;
cout << a.GetRtti()->IsParentOf(b.GetRtti()) << endl;
/* 출력
0
1
*/
return 0;
}
typeid 기능을 비슷하게 따라 해 보았다.
// RTTI.h
#pragma once
#include "MacroOverloading.h" // 매크로 오버로딩을 위한 함수들이 있다. (PP_MACRO_OVERLOAD) - https://rextester.com/discussion/ONP80107/C-Macro-overload
// 헤더 정의 작성용 매크로이다. 클래스의 static 멤버로 구현된다.
#define DECLARE_RTTI private: \
static CRTTI m_rtti;\
public:\
static CRTTI* GetRtti() { return &m_rtti; }
// static 멤버를 구현하기 위해서 소스 파일에서 사용하기 위한 매크로이다.
#define IMPLEMENT_RTTI_1(CLASS_NAME) CRTTI CLASS_NAME::m_rtti = CRTTI(#CLASS_NAME)
#define IMPLEMENT_RTTI_2(CLASS_NAME,PARENT_CLASS) CRTTI CLASS_NAME::m_rtti = CRTTI(#CLASS_NAME, PARENT_CLASS::GetRtti())
#define IMPLEMENT_RTTI(...) PP_MACRO_OVERLOAD(IMPLEMENT_RTTI, __VA_ARGS__)
class CRTTI {
private:
const char* m_className;
CRTTI* m_parent;
public:
CRTTI(const char* cClassName_, CRTTI* ptrParent_ = nullptr) {
m_className = cClassName_;
m_parent = ptrParent_;
}
const char* GetClassName() {
return m_className;
}
// m_parent부터 거슬러 올라가면서, 인자를 찾을 수 있는지 확인한다.
bool IsChildOf(CRTTI* ptrRtti) {
CRTTI* ptrCurrentRtti = m_parent;
while (ptrCurrentRtti != nullptr)
{
if (ptrCurrentRtti == ptrRtti) {
return true;
}
ptrCurrentRtti = ptrCurrentRtti->m_parent;
}
return false;
}
// 인자부터 거슬러 올라가면서, this를 찾을 수 있는지 확인한다.
bool IsParentOf(CRTTI* ptrRtti) {
CRTTI* ptrCurrentRtti = ptrRtti;
while (ptrCurrentRtti != nullptr)
{
if (ptrCurrentRtti == this) {
return true;
}
ptrCurrentRtti = ptrCurrentRtti->m_parent;
}
return false;
}
bool IsSame(CRTTI* ptrRtti) {
return ptrRtti == this;
}
};
용도 및 후기
하이레벨(어플리케이션단) 코딩에서 굳이 런타임에 타입을 알아내서 분기를 타야 한다면, 높은 확률로 코드를 잘못 짜고 것일 듯하다..
실전에서의 RTTI는 로그 작성할 때 말고는 딱히 유용하게 써먹을 곳은 모르겠지만, 따라 하는 게 크게 어렵지 않고 재미있었다.
(구현이라 써놓고 너무 부끄러울 정도로 대충 짠 느낌... -////-)