C++ RTTI 직접 구현하기 (1): TypeId 기반으로 dynamic_cast 없이 타입 체크하기 (2편)

C++ RTTI 직접 구현하기 (1): TypeId 기반으로 dynamic_cast 없이 타입 체크하기

🚀 들어가며

이전 글에서 C++의 dynamic_cast와 RTTI 개념을 살펴봤습니다.

그리고 중요한 사실을 확인했습니다.

❗ dynamic_cast는 안전하지만, 런타임 비용이 발생한다

또한 다운캐스팅이 왜 위험한지도 메모리 관점에서 확인했습니다.

그렇다면 자연스럽게 이런 질문이 나옵니다.

❓ dynamic_cast 없이 안전하게 타입을 확인할 수는 없을까?

이번 글에서는
👉 C++ RTTI를 직접 구현하는 첫 번째 단계
TypeId 기반 커스텀 RTTI 시스템을 만들어보겠습니다.


🎯 목표

이번 글의 목표는 다음과 같습니다:

  • dynamic_cast 없이 타입 확인
  • 빠른 타입 비교
  • 안전한 캐스팅 구조 구현

🧠 핵심 아이디어

핵심은 매우 간단합니다.

각 타입마다 고유한 ID를 부여하자

그리고 그 ID를 이용해서 타입을 비교합니다.


🔥 TypeId 기반 RTTI 구현

1️⃣ 베이스 클래스 설계

#pragma once

#include <memory>

class CraftObject
{
public:
    virtual ~CraftObject() {}

    // 현재 객체의 타입 ID 반환
    virtual size_t GetType() const = 0;

    // 타입 비교 함수
    virtual bool Is(size_t id) const
    {
        return false;
    }

    // 템플릿 기반 타입 체크
    template<typename T>
    bool IsTypeOf() const
    {
        return Is(T::TypeId());
    }

    // 안전한 캐스팅 함수
    template<typename T, typename U>
    static std::shared_ptr<T> Cast(const std::shared_ptr<U>& object)
    {
        if (!object)
            return nullptr;

        if (object->Is(T::TypeId()))
            return std::static_pointer_cast<T>(object);

        return nullptr;
    }
};

🔍 핵심 기능 설명

✔ GetType()

virtual size_t GetType() const = 0;

👉 각 객체가 자신의 타입 ID를 반환


✔ Is()

virtual bool Is(size_t id) const

👉 현재 객체가 특정 타입인지 확인


✔ Cast()

std::static_pointer_cast<T>

👉 dynamic_cast 대신 사용하는 안전한 캐스팅


⚙️ 매크로를 이용한 자동화

각 클래스마다 반복되는 코드를 줄이기 위해 매크로를 사용합니다.

#define TYPE_DECLARATIONS(Type, ParentType)                         \
    using super = ParentType;                                       \
protected:                                                          \
    static size_t TypeIdClass()                                     \
    {                                                               \
        static int runtimeTypeId = 0;                               \
        return reinterpret_cast<size_t>(&runtimeTypeId);            \
    }                                                               \
public:                                                             \
    static size_t TypeId()                                          \
    {                                                               \
        return Type::TypeIdClass();                                 \
    }                                                               \
    virtual size_t GetType() const override                         \
    {                                                               \
        return Type::TypeIdClass();                                 \
    }                                                               \
    virtual bool Is(size_t id) const override                       \
    {                                                               \
        return (id == TypeIdClass()) ? true : ParentType::Is(id);   \
    }

🧠 가장 중요한 부분: TypeId 생성 방식

static int runtimeTypeId = 0;
return reinterpret_cast<size_t>(&runtimeTypeId);
👉 이 코드가 핵심입니다.

왜 주소를 사용하는가?

  • static 변수는 프로그램 전체에서 단 하나만 존재
  • 즉, 주소가 유일함
  • 이 주소를 그대로 타입 ID로 사용

👉 결과:

“주소 자체가 타입의 고유 ID가 된다”


🎯 사용 예제

class Actor : public CraftObject
{
    TYPE_DECLARATIONS(Actor, CraftObject)
};

class Player : public Actor
{
    TYPE_DECLARATIONS(Player, Actor)
};
std::shared_ptr<CraftObject> obj = std::make_shared<Player>();

if (obj->Is(Player::TypeId()))
{
    // Player 타입
}

if (obj->Is(Actor::TypeId()))
{
    // Actor의 자식이므로 true
}

🔥 이 구조의 장점

1. 매우 빠름

👉 포인터(주소) 비교 1번


2. 상속 구조 지원

👉 ParentType::Is() 재귀 호출


3. dynamic_cast 대체 가능

👉 Cast() 함수로 안전한 캐스팅


⚠️ 한계점

이 구조에도 한계가 있습니다.

  • 타입 이름 없음
  • 디버깅 어려움
  • 리플렉션 확장 제한

👉 즉:

❗ “엔진 수준 시스템으로는 부족”


🧩 다음 단계

이제 우리는 다음 단계로 넘어갈 준비가 되었습니다.

❓ 타입 정보를 더 풍부하게 표현할 수 없을까?


🔥 다음 글 예고

다음 글에서는:

👉 TypeInfo 기반 RTTI 구조 구현

  • 타입 이름 저장
  • 부모 체인 구조
  • Unreal Engine 스타일 RTTI

🎮 마무리

이번 글에서는
👉 dynamic_cast 없이 타입을 확인하는 방법을 직접 구현해봤습니다.

이 구조는 실제로:

  • 게임 엔진
  • ECS 시스템
  • 런타임 객체 관리

에서 매우 자주 사용되는 방식입니다.


👉 다음 편에서 계속됩니다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off