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
728x90

전역 변수를 만들어놓았지만, 전역 변수가 초기화 되기 전에 사용되면서 크래시가 발생하는 경우가 있다.

이것을 Static Initialization Order Fiasco(정적 초기화 순서 실패, 정적 초기화 순서 재앙) 이라고 불린다.

참고 : https://en.cppreference.com/w/cpp/language/siof

 

Static Initialization Order Fiasco - cppreference.com

The static initialization order fiasco refers to the ambiguity in the order that objects with static storage duration in different translation units are initialized in. If an object in one translation unit relies on an object in another translation unit al

en.cppreference.com

 

예를 들어 아래와 같이 NamePool이라는 전역 객체가 있을 때 FName 객체의 생성자를 호출했을 때 NamePool 객체가 생성이 되지 않은 상태에서 NamePool에 데이터를 넣으려다가 크래시가 발생할 수 있다.

std::unordered_map<uint64, FString> NamePool;

FName::FName(FStringView InString)
{
    FString String = InString.data();
    HashCode = Hash(String.data());
    NamePool[HashCode] = String; // <- 여기서 Crash
}

FName::FName(const WIDECHAR* InString)
	:FName(FStringView(InString))
{
}

FString FName::ToString() const
{
    FString Out;
    ToString(Out);
    return Out;
}

void FName::ToString(FString& Out) const
{
    if (HashCode == 0)
    {
        return;
    }

    Out = NamePool[HashCode];
}

 

이를 방지하기 위해선 아래와 같이 코드를 수정하면 된다.

std::unordered_map<uint64, FString>& GetNamePool() // <- 이 함수가 중요
{
    static std::unordered_map<uint64, FString> NamePool;
    return NamePool;
}

FName::FName(FStringView InString)
{
    FString String = InString.data();
    HashCode = Hash(String.data());
    GetNamePool()[HashCode] = String;
}

FName::FName(const WIDECHAR* InString)
	:FName(FStringView(InString))
{
}

FString FName::ToString() const
{
    FString Out;
    ToString(Out);
    return Out;
}


void FName::ToString(FString& Out) const
{
    if (HashCode == 0)
    {
        return;
    }

    Out = GetNamePool()[HashCode];
}

 

이 방법을 "Construct on First Use" 아이디엄 이라고 부른다.

참고 : https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Construct_On_First_Use

 

More C++ Idioms/Construct On First Use - Wikibooks, open books for an open world

From Wikibooks, open books for an open world Ensure that an object is initialized before its first use. Specifically, ensure that non-local static object is initialized before its first use. Lazy construction/evaluation Static objects that have non-trivial

en.wikibooks.org

 

728x90

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

C/C++ - Reflection Library (RTTR, ENTT::META)  (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
728x90

APC (Asynchronous Pprocedure Call) 을 사용할 때 개발자가 사용할 수 있는 함수이다.

 

참조 자료

https://repnz.github.io/posts/apc/user-apc/

 

APC Series: User APC API · Low Level Pleasure

APC Series: User APC API Sun, May 17, 2020 Hey! Long time no see. Coronavirus makes it harder for me to write posts, I hope I’ll have the time to write - I have a lot I want to share! One of the things I did in the last few weeks is to explore the APC me

repnz.github.io

 

https://repnz.github.io/posts/apc/kernel-user-apc-api/

 

APC Series: User APC Internals · Low Level Pleasure

APC Series: User APC Internals Wed, Jun 3, 2020 Hey! This is the second part of the APC Series, If you haven’t read it I recommend you to read the first post about User APC API. where I explore the internals of APC objects in Windows. In this part I’ll

repnz.github.io

 

https://repnz.github.io/posts/apc/wow64-user-apc/

 

APC Series: KiUserApcDispatcher and Wow64 · Low Level Pleasure

APC Series: KiUserApcDispatcher and Wow64 Sun, Jun 28, 2020 I recommend to read the previous posts before reading this one: Let’s continue our discussion about APC internals in windows: This time we’ll discuss APC dispatching in user mode and how APC w

repnz.github.io

 

https://sevrosecurity.com/2020/04/08/process-injection-part-1-createremotethread/

 

Process Injection Part 1 | CreateRemoteThread() | Sevro Security

Process Injection using Direct Syscalls and CreateRemoteThread

sevrosecurity.com

 

https://sevrosecurity.com/2020/04/13/process-injection-part-2-queueuserapc/

 

Process Injection Part 2 | QueueUserAPC() | Sevro Security

Low Level Process Injection using QueueUserAPC() via direct x86 asm syscalls to bypass AV, EDR, and Sysmon.

sevrosecurity.com

 

728x90

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

C/C++ - Reflection Library (RTTR, ENTT::META)  (0) 2025.04.13
C/C++ - 정적 초기화 순서 실패  (0) 2025.04.13
C/C++ - Interrupt 감지  (0) 2023.04.07
C/C++ - push_macro()  (0) 2022.10.12
C/C++ - vcpkg에서 MT, MD 변경 방법  (0) 2022.02.17
728x90

프로그램이 에러로 인해 죽는다거나 했을 때 감지하는 방법이 없을까하고 찾다보니 

c++의 signal을 이용하면 가능한 것을 확인했다.

 

#include <iostream>
#include <csignal>
#include <thread>

using namespace std;
using namespace std::literals::chrono_literals;

void signalHandler(int signum)
{
    cout << "interrupt signal (" << signum << ") received. \n";
}

int main()
{
    signal(SIGSEGV, signalHandler); // segment violation
    
    while(1)
    {
        cout << "sleep..." << endl;
        std::this_thread::sleep_for(1ms);
        
        static int count = 0;
        ++count;
        
        if (count > 100)
        {
            struct n
            {
                int a;
            }
            
            n* nn = nullptr;
            printf("int a : %d \n", nn->a); // nullptr 참조 후 프로그램 죽음.
        }
    }
}

 

결과

 

ref : http://seorenn.blogspot.com/2011/02/sigsegv.html

728x90
728x90

Memory Leak을 Detect하는 코드를 사용할 때 아래와 같이 new를 새롭게 define해서 사용한다.

   1 #ifdef _DEBUG
   2 #define new new( _CLIENT_BLOCK, __FILE__, __LINE__ )
   3 #endif

 

개발을 하다보면 이 코드가 문제가 될때가 있다. 예를 들면 다른 OpenSource를 사용할때...

그럴때에는 그 헤더를 사용할 때에는 push_macro()를 이용해서 문제를 해결할 수 있다.

#pragma push_macro("new")
#undef new

#include "OpenSource.h" // 오픈소스 헤더

#pragma pop_macro("new")

 

자세한 내용은 아래의 글을 참조하자.

https://dataonair.or.kr/db-tech-reference/d-lounge/technical-data/?mod=document&uid=235804

 

C++ 프로그래밍 : 알면 유용한 메모리 연산자

C++ 프로그래밍 알면 유용한 메모리 연산자 C/C++는 메모리를 직접 다룰 수 있어 개발자 사이에서도 호불호가 극명하게 갈리는 편이다. 메모리를 직접 제어해야 하는 어려움은 다른 언어에도 많은

dataonair.or.kr

 

728x90
728x90

 

기본적으로는 MD로 되기 때문에 vcpkg\scripts\buildsystems\msbuild\vcpkg.targets에서

<VcpkgOSTarget>windows</VcpkgOSTarget> == MD

<VcpkgOSTarget>windows-static</VcpkgOSTarget> == MT

 

 

728x90
728x90
#include <iostream>
#include <memory> 
#include <windows.h>
#include <winuser.h>

bool IsScreenLocked()
{
	HDESK desktopHandle = OpenInputDesktop(0, false, DESKTOP_SWITCHDESKTOP);
	if (desktopHandle == 0)
	{
		desktopHandle = OpenDesktopA("Default", 0, false, DESKTOP_SWITCHDESKTOP);
	}
	if (desktopHandle != 0)
	{
		if (SwitchDesktop(desktopHandle))
		{
			CloseDesktop(desktopHandle);
		}
		else
		{
			CloseDesktop(desktopHandle);
			return true;
		}
	} return false;
}

bool IsScreenSaverRunning()
{
	int isRunning = 0;
	if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &isRunning, 0))
	{
		return false;
	}
	if (isRunning)
	{
		return true;
	}
	return false;
}

int main()
{
	while (true)
	{
		if (IsScreenLocked())
		{
			printf("LockScreen : true");
		}
		else
		{
			printf("LockScreen : false");
		}

		if (IsScreenSaverRunning())
		{
			printf("ScreenSaver : true");
		}
		else
		{
			printf("ScreenSaver : false");
		}

		Sleep(100);
	}
}

 

참조 : https://icodebroker.tistory.com/9207

728x90
728x90

Framework에서 기본적인 Sleep()을 주다보니 정확하지 않은 시간동안 잠들었다가 깨어나기 때문에 발생하는 frame loss라던가 정확한 latency 측정이 되지 않아서 정확하게 지정된 시간동안 잠들었다가 깨어나는 Sleep()을 찾게되었다.

 

동작에 대해 간단히 설명하면 Sleep(16)을 원한다면 Sleep(15)를 하고 남은 1ms은 while()을 돌면서 대기하는 방식이다.

 

참고 : https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/

 

Making an accurate Sleep() function | computerBear

There are many cases in programming where I wish the system Sleep() function was more accurate. Real-time rendering immediately comes to mind, as do animated GUI’s. In both of these cases you want some piece of code to run on a fixed timer, executing pre

blat-blatnik.github.io

 

728x90

+ Recent posts