반응형
모던 Vulkan (C++ 버전) 시리즈의 세 번째 글입니다. 지난 글(#2)에서는 Vulkan-HPP와 Modern C++ 스타일을 적용해 인스턴스를 생성하고 Validation Layer와 익스텐션 설정 과정을 간소화했습니다. 이제 인스턴스가 준비되었으니, 시스템에 장착된 GPU(물리 디바이스)를 탐색하고, Compute 또는 Graphics에 적합한 큐 패밀리를 선택한 뒤 로지컬 디바이스를 생성하고 큐를 확보하는 과정을 C++ RAII 패턴을 활용해 다시 구현해보겠습니다.입문 시리즈에서 C 스타일 코드로 했던 과정을 이제 Vulkan-HPP와 RAII(Unique*), 예외 처리 등을 통해 더 깔끔하고 유지보수하기 쉬운 형태로 재작성합니다.목표인스턴스가 준비되었다고 가정하고, 물리 디바이스(Phys..
모던 Vulkan (C++ 버전) 시리즈의 두 번째 글입니다. 이번 글에서는 인스턴스를 생성하는 과정을 Vulkan-HPP와 Modern C++ 스타일로 재작성해보겠습니다. 이전 입문 시리즈에서 C 스타일로 인스턴스를 만들고 Validation Layer를 설정했던 과정을 다시 돌아보며, 이번에는 RAII를 활용한 vk::UniqueInstance, 예외 처리 모드 등을 적용하여 더 깔끔하고 유지보수하기 쉬운 코드를 작성하는 방법을 제시합니다.목표Vulkan C API 스타일로 작성했던 인스턴스 생성 코드를 Vulkan-HPP 기반 C++ 코드로 재구성vk::ApplicationInfo, vk::InstanceCreateInfo 등을 이용하여 Application, Engine, API 버전 정보 설정v..
왜 Vulkan-HPP인가?이전 입문 시리즈에서는 주로 C 스타일 Vulkan API를 사용했습니다. C 스타일 API는 다음과 같은 특징을 갖습니다.함수 포인터 기반 호출: vkCreateInstance, vkEnumeratePhysicalDevices 등리소스 관리의 수동성: vkDestroy* 함수를 일일이 호출해야 함타입 안전성 부족: 단순 enum, void* 기반 핸들, 에러 처리 시 VK_SUCCESS 등을 if문으로 체크이런 패턴은 어느 정도 익숙해지면 사용할 수 있지만, 실수하기 쉽고 코드량이 많아집니다. Modern C++(C++17 이상)에서는 RAII, 스마트 포인터, enum class, std::optional, std::variant 등 다양한 언어 기능을 통해 코드 품질을 높..
이전 글에서는 퍼사드(Facade) 패턴을 모던 C++ 관점에서 재해석하며, 상속 없이 람다와 함수 합성으로 복잡한 서브시스템을 단순한 인터페이스로 감싸고, std::expected, std::format, coroutine, Ranges 등을 활용해 비동기 처리나 조건부 처리, 로깅, 에러 처리 등 다양한 요구사항에 쉽게 대응할 수 있음을 확인했습니다. 이번에는 구조적 패턴 중 플라이웨이트(Flyweight) 패턴을 다룹니다.플라이웨이트 패턴은 많은 수의 유사한 객체를 효율적으로 관리하기 위해 객체들이 공유할 수 있는 상태를 중앙에서 관리하는 패턴입니다. 예를 들어, 텍스트 처리기에서 각 문자를 개별 객체로 표현하면 메모리 낭비가 심하므로, 공유 가능한 폰트 정보나 형태 정보를 플라이웨이트 객체로 한 ..
이전 글에서는 데코레이터(Decorator) 패턴을 모던 C++ 관점에서 재해석하며, 상속 없이도 람다 합성을 통해 객체에 동적으로 기능을 추가하는 방법을 살펴봤습니다. 이번에는 구조적 패턴 중 퍼사드(Facade) 패턴을 다룹니다.퍼사드 패턴은 복잡한 서브시스템을 단일하고 단순한 인터페이스로 감싸, 클라이언트가 시스템을 쉽게 사용할 수 있게 하는 패턴입니다. 전통적으로는 Facade 클래스를 만들어 서브시스템 객체들을 정적 호출 계층으로 감싸았으나, 이는 서브시스템 변경 시 Facade 수정 필요, 에러 처리나 비동기 처리 시 복잡성 증가 등의 문제를 야기합니다.C++20 이상에서는 Concepts, 람다, std::function, std::expected, std::format, coroutine,..
이전 글에서는 컴포지트(Composite) 패턴을 모던 C++ 관점에서 재해석하며, std::variant와 std::visit를 통해 상속 없이 부분-전체 구조를 값 기반으로 표현하고, Ranges, coroutine, std::expected, std::format 등을 활용해 조건부 처리, 비동기 연산, 로깅 등 다양한 요구사항에도 쉽게 대응할 수 있음을 확인했습니다. 이번에는 구조적 패턴 중 데코레이터(Decorator) 패턴을 다룹니다.데코레이터 패턴은 객체에 동적으로 새로운 기능을 추가하기 위한 패턴이며, 전통적 구현에서는 컴포넌트를 상속한 데코레이터 클래스 계층을 통해 기능을 장식(Decorate)해야 했습니다. 그러나 이는 클래스 증가와 유지보수 어려움을 초래합니다. C++20 이상에서는 ..
이전 글에서는 브리지(Bridge) 패턴을 모던 C++ 관점에서 재해석하며, 상속 기반 추상/구현 분리 없이도 람다, Concepts, std::expected, coroutine, Ranges, std::format 등을 활용해 추상과 구현을 유연하게 연결할 수 있음을 확인했습니다. 이번에는 구조적 패턴 중 컴포지트(Composite) 패턴을 다룹니다.컴포지트 패턴은 객체를 트리 구조로 구성해, 개별 객체(Leaf)와 복합 객체(Composite)를 동일하게 다룰 수 있게 하는 패턴입니다. 전통적으로는 Component 추상 클래스, Leaf, Composite 클래스 상속 계층을 정의했으나, 이는 클래스 증가와 유지보수 어려움을 야기합니다.C++20 이상에서는 std::variant, std::vis..
이전 글에서는 어댑터(Adapter) 패턴을 모던 C++ 관점에서 재해석하며, 기존 인터페이스를 원하는 다른 인터페이스로 변환하기 위해 상속 없이도 람다, Concepts, std::expected, std::format, coroutine, Ranges 등을 활용할 수 있음을 확인했습니다. 이번에는 구조적 패턴 중 브리지(Bridge) 패턴을 다룹니다.브리지 패턴은 추상(Abstraction)과 구현(Implementation)을 분리하여 둘을 독립적으로 확장 가능하게 만드는 패턴입니다. 전통적으로는 추상 클래스 계층과 구현 클래스 계층을 상속 기반으로 구분하고, 추상 클래스가 구현 객체를 참조하는 구조를 사용했습니다. 이 방식은 새로운 추상이나 구현 타입 추가 시 클래스 증가와 복잡성 상승을 초래합니..
이전 글에서는 비지터(Visitor) 패턴을 모던 C++ 관점에서 재해석하며, std::variant와 std::visit를 통해 이중 디스패치 없이도 다양한 요소 타입별 로직을 처리하는 법을 살펴보았습니다. 이제는 구조적(Structural) 패턴 중 하나인 어댑터(Adapter) 패턴을 다룹니다.어댑터 패턴은 기존 클래스나 함수 인터페이스를 클라이언트가 원하는 다른 인터페이스로 변환하는 패턴입니다. 전통적으로는 어댑터 클래스를 상속 기반으로 정의하고, 기존 인터페이스를 변환하는 메서드를 구현했지만, 이 방식은 클래스 증가, 상속 기반 구조로 인한 유지보수 어려움을 야기합니다.C++20 이상에서는 Concepts로 원하는 인터페이스 요구사항을 명시하고, 람다나 함수 합성으로 변환 로직을 구현하면 상속 ..
이전 글에서는 템플릿 메서드(Template Method) 패턴을 모던 C++ 관점에서 재해석하며, 상속 없이도 람다, Concepts, std::expected, std::format, coroutine, Ranges 등을 활용해 알고리즘 골격을 선언적이고 타입 안전하게 표현할 수 있음을 확인했습니다. 이번에는 행동(Behavioral) 패턴 중 비지터(Visitor) 패턴을 다룹니다.비지터 패턴은 객체 구조(예: 복합 구조나 노드 트리)를 순회하면서, 각 요소 타입에 따라 다른 연산을 수행할 수 있게 하는 패턴입니다. 전통적으로는 각 요소 클래스에 accept(Visitor&) 메서드를 정의하고, 비지터 인터페이스에 각 요소 타입별 visit() 메서드를 두어 이중 디스패치를 실현했으나, 이는 클래스..