C++23에서는 비동기 프로그래밍과 지연 계산을 더욱 편리하게 구현할 수 있도록 std::generator가 도입되었습니다. 이번 글에서는 std::generator의 개념과 사용법, 그리고 이전 버전과 비교하여 어떻게 개선되었는지 알아보겠습니다.
std::generator란 무엇인가요?
std::generator는 C++23에서 도입된 코루틴(coroutine) 기반의 제네레이터로, 지연된 값의 시퀀스를 순차적으로 생성하고 반환할 수 있는 기능입니다. 이를 통해 복잡한 데이터 생성 로직을 간결하고 효율적으로 구현할 수 있으며, 특히 범위 기반 for 루프와 함께 사용하여 자연스러운 반복 처리를 할 수 있습니다.
이전 버전에서는 어떻게 했나요?
C++20에서는 코루틴이 도입되었지만, 이를 직접 사용하기 위해서는 복잡한 프레임워크를 구축하거나 서드파티 라이브러리를 사용해야 했습니다.
예제: 기존 방식의 제네레이터 구현
#include <vector>
#include <functional>
std::vector<int> generateNumbers(int n) {
std::vector<int> numbers;
for (int i = 0; i < n; ++i) {
numbers.push_back(i);
}
return numbers;
}
int main() {
for (int num : generateNumbers(10)) {
std::cout << num << ' ';
}
return 0;
}
- 문제점:
- 모든 값을 한 번에 생성하여 벡터에 저장하므로 메모리 오버헤드가 발생합니다.
- 지연 계산이 아니므로, 필요한 만큼만 값을 생성할 수 없습니다.
- 복잡한 데이터 생성 로직을 표현하기 어렵습니다.
C++23의 std::generator를 사용한 개선
std::generator를 사용하면 코루틴을 통해 필요한 시점에 값을 생성하여 반환할 수 있습니다.
예제: std::generator 사용
#include <generator>
#include <iostream>
std::generator<int> generateNumbers(int n) {
for (int i = 0; i < n; ++i) {
co_yield i;
}
}
int main() {
for (int num : generateNumbers(10)) {
std::cout << num << ' ';
}
return 0;
}
- co_yield를 사용하여 값을 순차적으로 생성합니다.
- 범위 기반 for 루프에서 자연스럽게 사용할 수 있습니다.
- 메모리 오버헤드가 적고, 지연 계산이 가능합니다.
어떻게 좋아졌나요?
- 지연 계산 지원: 필요한 시점에 값을 생성하므로 메모리 사용량을 최소화할 수 있습니다.
- 코드 간결화: 복잡한 데이터 생성 로직을 간결하고 직관적으로 표현할 수 있습니다.
- 범위 기반 for 루프와 호환: 표준 반복자 패턴을 따르므로 사용이 편리합니다.
- 성능 향상: 불필요한 데이터 생성과 메모리 할당을 피하여 성능을 최적화할 수 있습니다.
상세한 예제와 비교
1. 피보나치 수열 생성
기존 방식
std::vector<int> generateFibonacci(int n) {
std::vector<int> sequence;
int a = 0, b = 1;
while (n-- > 0) {
sequence.push_back(a);
int next = a + b;
a = b;
b = next;
}
return sequence;
}
int main() {
for (int num : generateFibonacci(10)) {
std::cout << num << ' ';
}
return 0;
}
- 모든 피보나치 수를 벡터에 저장하므로 메모리 오버헤드 발생
C++23 방식
#include <generator>
std::generator<int> generateFibonacci(int n) {
int a = 0, b = 1;
while (n-- > 0) {
co_yield a;
int next = a + b;
a = b;
b = next;
}
}
int main() {
for (int num : generateFibonacci(10)) {
std::cout << num << ' ';
}
return 0;
}
- 지연 계산을 통해 메모리 사용을 최소화
2. 파일에서 라인 단위로 읽기
#include <generator>
#include <fstream>
#include <string>
std::generator<std::string> readLines(const std::string& filename) {
std::ifstream file(filename);
std::string line;
while (std::getline(file, line)) {
co_yield line;
}
}
int main() {
for (const auto& line : readLines("data.txt")) {
std::cout << line << '\n';
}
return 0;
}
- 큰 파일을 처리할 때 메모리 사용량을 줄이고, 필요한 만큼만 데이터를 읽어올 수 있습니다.
주의 사항
- 컴파일러 지원: std::generator는 C++23 기능이므로, 이를 지원하는 컴파일러와 표준 라이브러리가 필요합니다.
- 코루틴 이해 필요: 코루틴의 동작 원리를 이해하고 사용하는 것이 좋습니다.
- 예외 처리: 코루틴 내에서 예외가 발생하면 예외 처리를 적절히 해야 합니다.
std::generator와 std::ranges의 조합
std::generator는 std::ranges와 함께 사용할 수 있어 더욱 강력한 기능을 제공합니다.
#include <generator>
#include <ranges>
auto evenNumbers(int start, int end) {
for (int i = start; i <= end; ++i) {
if (i % 2 == 0) {
co_yield i;
}
}
}
int main() {
auto evens = evenNumbers(1, 20) | std::views::take(5);
for (int num : evens) {
std::cout << num << ' ';
}
return 0;
}
- std::views와 함께 사용하여 필터링, 슬라이싱 등 다양한 조작이 가능합니다.
요약
C++23의 std::generator는 코루틴을 기반으로 한 제네레이터로, 지연된 값의 시퀀스를 효율적으로 생성하고 처리할 수 있게 해줍니다. 이전에는 복잡한 코드나 외부 라이브러리를 사용해야 했지만, 이제는 표준 라이브러리만으로 간결하고 성능 효율적인 코드를 작성할 수 있습니다. 이를 통해 비동기 프로그래밍, 데이터 스트림 처리 등 다양한 분야에서 개발자의 생산성을 높일 수 있습니다.
참고 자료:
'개발 이야기 > C++' 카테고리의 다른 글
[C++23 새기능 소개] 다차원 첨자 연산자(Multidimensional Subscript Operator) (0) | 2024.12.08 |
---|---|
[C++23 새기능 소개] if consteval (0) | 2024.12.08 |
[C++23 새기능 소개] std::move_only_function (0) | 2024.12.08 |
[C++23 새기능 소개] deducing this (0) | 2024.12.08 |
[C++23 새기능 소개] std::mdspan (0) | 2024.12.08 |