Reflection

  • 프로그램이 런타임, 그러니까 실행중에 자기 자신의 구조를 검사하고 조작할 수 있는 능력
  • 일반적인 C++에서는 컴파일 시점에 모든 정보가 결정되고, 컴파일된 상태의 C++ 언어는 CPU로 전달된다
    • 어떤 class A가 어떤 멤버 변수 혹은 함수를 가지고 있는지 알 수 없다
    • 리플랙션이 지원되는 언어에서는 이게 가능하다
  • CPU는 변수도 클래스도 일절 모른다
    • 이 같은 개념은 컴파일러가 cpu에 명령 및 변수를 전달하고 이를 메모리 주소값으로 변환시키게 하기 위한 정보일 뿐이다
  • 위 같은 정보를 추적할 수 있도록 하는 것이 리플렉션 시스템이다
    • UE에서 제공하는 UPROPERTY, UFUNCTION, UCLASS 등과 같은 매크로로 C++ 코드를 리플렉션 시스템에 등록해서 추적할 수 있다
    • 등록된 정보는 UField 오브젝트로 생성되어 보관된다

C++와 리플랙션

RTTI를 지원하기는 하지만, 다른 언어에서의 리플랙션보다는 제한적이다
런타임에 객체의 동적 타입을 확인하는 데 그친다
값을 조작하거나 구조의 내부(멤버 변수 또는 멤버 함수 정보)를 알수 없다

언리얼에서의 리플랙션

  • 언리얼은 자체적인 리플랙션 시스템을 구축하고 있다
  • 대표적인 예시가 UFUNCTION 또는 UPROPPERTY 매크로
    • 언리얼 에디터 -> 디테일 패널에서 특정 오브젝트의 값을 변경할 수 있다.
      • 에디터가 이미 실행 중인 프로그램이라고 생각할 때, 이는 클래스에서 정의한 값을 동적으로 변경하는 것이다.
    • specifier로 마킹된 메타데이터를 런타임에 파악해 작동
      • 블루프린트에서 사용할 수 있는 변수와 함수 판단
      • UPROPERTY 매크로를 통해 UObject를 참조하는 포인터를 수집해 Garbage Collection에 활용
  • 프로퍼티 시스템이라 부르기도 한다

작동 순서

  1. 개발자의 코드 작성
  2. UBT가 컴파일러 호출 전, 그러니까 실제 빌드 전에 UHT를 호출
  3. 호출된 UHT가 헤더파일을 스캔하고 사용된 언리얼 매크로를 파싱 및 분석해 메타데이터 추출
  4. UHT는 이를 바탕으로 .generated.h 및 .generated.cpp를 생성해, 아래 코드들을 생성한다
    • 클래스와 멤버를 리플랙션에 등록하는 코드
    • StaticClass 함수 정의
    • 런타임용 함수 호출 코드
    • UPROPERTY로 선언된 UObject* 들을 추적하는 코드 등
  5. UBT가 컴파일러를 호출
    • 리플랙션 목적으로 추가된 코드까지 포함해 컴파일