728x90

Reflection이란 쉽게 말하면 프로그램이 Runtime 중에 자료형, 변수명, 데이터 값을 가져올 수 있는 기능이다.

이 Reflection을 이용하면 작업해야하는 코드량을 줄일 수 있다.

예를 들면 구조체를 만들어서 등록하면 UI가 자동으로 생성하게한다거나, Backend에서 내려주는 데이터를 자동으로 Parsing한다거나 등등 다양하게 활용이 가능하다.

 

참고 : https://learn.microsoft.com/ko-kr/cpp/dotnet/reflection-cpp-cli?view=msvc-170

 

리플렉션(C++/CLI)

자세한 정보: 리플렉션(C++/CLI)

learn.microsoft.com

 

다른 언어에서는 언어차원에서 기본으로 제공되고 있으나, C++에서는 현재 C++차기 버전에 추가된다고 한다.

https://isocpp.org/files/papers/P2996R4.html

 

Reflection for C++26

 

isocpp.org

 

하지만, 지금 당장 사용을 해야되기 때문에 라이브러리를 여러가지 조사를 해보았다.

 

1. RTTR

- git : https://github.com/rttrorg/rttr

 

GitHub - rttrorg/rttr: C++ Reflection Library

C++ Reflection Library. Contribute to rttrorg/rttr development by creating an account on GitHub.

github.com

 

이 라이브러리를 가장 처음에 접했다. 잘만들어져있고 개발할 때 잘 사용하였으나 치명적인 약점이 있다.

이 라이브러리는 DLL로만 빌드 및 배포가 가능하기 때문에 프로그램에 RTTR을 추가하게 되면 DLL도 같이 배포를 해야만 한다.

 

2. ENTT::META

- git : https://github.com/skypjack/entt

 

GitHub - skypjack/entt: Gaming meets modern C++ - a fast and reliable entity component system (ECS) and much more

Gaming meets modern C++ - a fast and reliable entity component system (ECS) and much more - skypjack/entt

github.com

 

이 라이브러리는 Header Only이다. 즉 Header만 추가해서 사용하면 모든 기능을 사용할 수 있다.

src/entt/meta 에 있는 헤더들을 이용해서 개발하면 된다.

 

이 라이브러리는 meta로 만들어졌다가 뒤에 entt 에 포함되면서 좀더 사용이 용이하게 개량되었다.

이 라이브러리의 사용법과 굉장히 유사하지만, stl container가 추가되었고, 좀 더 사용이 편하게 개량되었다고 보면된다.

https://github.com/skypjack/meta

 

GitHub - skypjack/meta: Header-only, non-intrusive and macro-free runtime reflection system in C++

Header-only, non-intrusive and macro-free runtime reflection system in C++ - skypjack/meta

github.com

 

예제 코드

#include <entt/meta/meta.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/policy.hpp>
#include <entt/meta/container.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <map>

std::map<entt::id_type, std::string> gHasheNames;
#define REGISTER_TYPE(Type)                                                                             \
    gHasheNames[entt::hashed_string(typeid(Type).name()).value()] = typeid(Type).name();                \
    entt::meta_factory<Type>().type(entt::hashed_string(typeid(Type).name()));

#define REGISTER_BASE(ChildType, ParentType)                                                            \
    entt::meta_factory<ChildType>().base<ParentType>();

// 수정: entt::as_ref_t 정책 추가
#define REGISTER_DATA(Type, Data, Name)                                                                 \
    entt::meta_factory<Type>().data<&Type::Data, entt::as_ref_t>(entt::hashed_string(Name));            \
    gHasheNames[entt::hashed_string(Name).value()] = Name;

// item 구조체 정의
struct item
{
    int price;
    std::string name;
};

// Person 구조체 정의
struct Person
{
    int age;                            // 나이 (정수)
    std::string name;                   // 이름 (문자열)
    std::vector<std::string> hobbies;   // 취미 (문자열 벡터)
    std::vector<item> items;            // 아이템 (item 구조체 벡터)

    // 생성자 추가
    Person(int age_, std::string name_, std::vector<std::string> hobbies_, std::vector<item> items_)
        : age(age_), name(name_), hobbies(hobbies_), items(items_) {}
};

// Student 구조체 정의 (Person 상속)
struct Student : public Person
{
    int grade;                          // 학년 (정수)
    std::string school;                 // 학교 (문자열)
    std::vector<std::string> subjects;  // 과목 (문자열 벡터)

    // 생성자 추가
    Student(int age_, std::string name_, std::vector<std::string> hobbies_, std::vector<item> items_,
        int grade_, std::string school_, std::vector<std::string> subjects_)
        : Person(age_, name_, hobbies_, items_), grade(grade_), school(school_), subjects(subjects_) {}
};

// Teacher 구조체 정의 (Person 상속)
struct Teacher : public Person
{
    int salary;                         // 연봉 (정수)
    std::string subject;                // 과목 (문자열)
    std::vector<std::string> students;  // 학생 (문자열 벡터)

    // 생성자 추가
    Teacher(int age_, std::string name_, std::vector<std::string> hobbies_, std::vector<item> items_,
        int salary_, std::string subject_, std::vector<std::string> students_)
        : Person(age_, name_, hobbies_, items_), salary(salary_), subject(subject_), students(students_) {}
};

int main()
{
    // 구조체를 EnTT 리플렉션 시스템에 등록
    REGISTER_TYPE(Person)
        REGISTER_DATA(Person, age, "age")
        REGISTER_DATA(Person, name, "name")
        REGISTER_DATA(Person, hobbies, "hobbies")
        REGISTER_DATA(Person, items, "items")

    REGISTER_TYPE(Student)
        REGISTER_BASE(Student, Person)
        REGISTER_DATA(Student, grade, "grade")
        REGISTER_DATA(Student, school, "school")
        REGISTER_DATA(Student, subjects, "subjects")

    REGISTER_TYPE(Teacher)
        REGISTER_BASE(Teacher, Person)
        REGISTER_DATA(Teacher, salary, "salary")
        REGISTER_DATA(Teacher, subject, "subject")
        REGISTER_DATA(Teacher, students, "students")

    // Person, Student, Teacher 객체를 포함하는 벡터 생성
    std::vector<std::unique_ptr<Person>> people;
    people.push_back(std::make_unique<Person>(30, "kim", std::vector<std::string>{ "reading", "gaming" }, std::vector<item>{ {100, "sword"}, {200, "shield"} }));
    people.push_back(std::make_unique<Student>(20, "lee", std::vector<std::string>{ "studying" }, std::vector<item>{}, 3, "harvard", std::vector<std::string>{ "physics", "chemistry" }));
    people.push_back(std::make_unique<Teacher>(40, "park", std::vector<std::string>{ "teaching" }, std::vector<item>{}, 2000000, "math", std::vector<std::string>{ "alice", "bob" }));

    // 각 객체의 모든 멤버를 출력하고 수정
    for (auto& person_ptr : people)
    {
        // 객체를 meta_handle로 래핑하여 실제 타입 유지
        entt::meta_handle meta_handle(person_ptr.get());

        // 객체의 런타임 타입 가져오기
        auto meta_type = meta_handle.type();

        std::cout << "Type: " << meta_type.info().name() << std::endl;

        // 모든 데이터 멤버 순회
        for (auto member : meta_type.data())
        {
            // 멤버 이름 가져오기
            std::string member_name = gHasheNames[member.first];
            auto value = member.second.get(meta_handle);

            // 타입별로 처리
            if (value.type().id() == entt::resolve<int>().id()) {
                int val = value.cast<int>();
                std::cout << member_name << ": " << val << std::endl;
                // 수정: 1 증가
                member.second.set(meta_handle, entt::meta_any{ val + 1 });
                std::cout << "Modified " << member_name << ": " << member.second.get(meta_handle).cast<int>() << std::endl;
            }
            else if (value.type().id() == entt::resolve<std::string>().id()) {
                std::string val = value.cast<std::string>();
                std::cout << member_name << ": " << val << std::endl;
                // 수정: "_modified" 추가
                auto& str_ref = value.cast<std::string&>();
                str_ref += "_modified";
                std::cout << "Modified " << member_name << ": " << member.second.get(meta_handle).cast<std::string>() << std::endl;
            }
            else if (value.type().id() == entt::resolve<std::vector<std::string>>().id()) {
                auto vec_str = value.cast<std::vector<std::string>>();
                std::cout << member_name << ": ";
                for (const auto& str : vec_str) std::cout << str << " ";
                std::cout << std::endl;
                // 수정: "new_element" 추가
                auto& vec_ref = value.cast<std::vector<std::string>&>();
                vec_ref.push_back("new_element");
                std::cout << "Modified " << member_name << ": ";
                for (const auto& str : member.second.get(meta_handle).cast<std::vector<std::string>>()) std::cout << str << " ";
                std::cout << std::endl;
            }
            else if (value.type().id() == entt::resolve<std::vector<item>>().id()) {
                auto vec_item = value.cast<std::vector<item>>();
                std::cout << member_name << ": ";
                for (const auto& it : vec_item) std::cout << it.name << "(" << it.price << ") ";
                std::cout << std::endl;
                // 수정: 새로운 아이템 추가
                auto& vec_ref = value.cast<std::vector<item>&>();
                vec_ref.push_back({ 300, "new_item" });
                std::cout << "Modified " << member_name << ": ";
                for (const auto& it : member.second.get(meta_handle).cast<std::vector<item>>()) std::cout << it.name << "(" << it.price << ") ";
                std::cout << std::endl;
            }
            else {
                std::cout << member_name << ": Unknown type" << std::endl;
            }
        }
        std::cout << "-----" << std::endl;
    }

    return 0;
}

 

 

 

 

728x90

'Basic Programming > C, C++' 카테고리의 다른 글

C/C++ - 정적 초기화 순서 실패  (0) 2025.04.13
C/C++ - QueueUserAPC  (0) 2023.04.20
C/C++ - Interrupt 감지  (0) 2023.04.07
C/C++ - push_macro()  (0) 2022.10.12
C/C++ - vcpkg에서 MT, MD 변경 방법  (0) 2022.02.17

+ Recent posts