SlideShare a Scribd company logo
최동민 / Nexon Korea
Ranges
for The C++ Standard Library
발표 전…
• 다룰 것
• Ranges와 Ranges를 이루는 문법들
• 다루지 않을 것
• Concept
• 시간의 제약
• 코드 생략
• const, constexpr, explicit, overload, noexcept, namespace 등
• 가독성을 위해
문제 인식
당신의 코드는 안녕하십니까?
불편함의 시작
• 태초에 Iterator 가 있었습니다.
• 매우 불편했습니다.
for (std::vector<int>::const_iterator it = numbers.begin();
it != numbers.end(); ++it)
{
// ***
}
C++11 – 혁신의 바람
“내 너희에게 auto와
range-based for 를 주노라”
C++11 – 혁신의 바람
for (auto e : numbers)
{
// ***
}
for (std::vector<int>::const_iterator it = numbers.begin();
it != numbers.end(); ++it)
{
// ***
}
해피엔딩?
똘이장군
아직도 풀리지 않은 숙제
• 나는 컨테이너 전체를 소팅 하고 싶은데
왜 begin(), end() 요?
std::vector<int> numbers{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::sort(numbers.begin(), numbers.end());
새로운 희망
• 오늘 소개할 Ranges 라이브러리 사용 시
• 모든 STL 알고리즘 함수에 대해
범위 기반 알고리즘 함수 제공
ranges::sort(numbers);
std::sort(numbers.begin(), numbers.end());
Ranges
안녕하세요. 처음 뵙겠습니다.
Ranges?
http://pythoncentral.io/pythons-range-function-explained/
Ranges
• 범위를 다루는 Generic Library
• <Begin, End>
• 지원 컴파일러
• clang 3.4.0 이상
• gcc 4.9.0 이상
• 차기 C++ 표준 포함 예정
• https://github.com/ericniebler/range-v3
Ranges
• VS로는 못하나요?
Ranges
• Clang 3.7 이 없는데요?
Ranges : 범위 기반 알고리즘 제공
• partition
auto it = ranges::partition(v, [](auto e){
return e % 2 == 0;
}
auto it = std::partition(v.begin(), v.end(), [](auto e){
return e % 2 == 0;
}
이게 다입니까?
!( )
Ranges 를 왜 써야 하나요?
• 편의성(Convenience)
• 코드가 짧아져요.(!)
• 느긋한 계산(Lazy Evaluation)
• 무한 범위 표현 가능
• 불필요한 계산 회피
• 조합성(Composability)
• 함수형 프로그래밍에서 내세우는 그것
• 직관적인 표현 가능
• 코드 생산성 향상
기능 소개
여러분의 코드에 미칠 영향들
Ranges : 컨테이너로의 전환
• [0, 10) 으로 채우기
• 2009년의 나
• 2010년의 나
• 2016년의 나
int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<int> v(10);
for (int i = 0; i < 10; ++i)
v[i] = i;
vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Ranges : 컨테이너로의 전환
• [0, 10) 으로 채우기
• std::iota()
• ranges::iota()
//#include<numeric>
vector<int> v(10);
std::iota(v.begin(), v.end(), 0);
vector<int> v(10);
ranges::iota(v, 0);
Ranges : 컨테이너로의 전환
• [0, 10) 으로 채우기
• ranges::view::iota()
using namespace ranges;
vector<int> v = view::iota(0, 10);
Ranges : 컨테이너로의 전환
• ranges::view::iota()
vector range
(iota_view)
view::iota
User-Defined Conversion
User-Defined Conversion
• 분수 클래스 with Getter
struct Fraction{
int numerator;
int denominator;
double get(){
return static_cast<double>(numerator) / denominator;
}
};
int main(){
Fraction f{ 10, 20 };
cout << f.get() << endl; // 0.5
}
(http://en.cppreference.com/w/cpp/language/cast_operator)
User-Defined Conversion
• 마치 처음부터 double 이었던 것 처럼
(http://en.cppreference.com/w/cpp/language/cast_operator)
struct Fraction{
int numerator;
int denominator;
operator double(){
return static_cast<double>(numerator) / denominator;
}
};
int main(){
Fraction f{ 10, 20 };
cout << f << endl; // 0.5
}
Ranges : 기본 구조
• view::iota() 를 구현해 봅시다.
template <class T>
struct simple_iota_view{
T from_;
T to_;
operator vector<T>(){
vector<T> v;
v.reserve(to_ - from_);
for (; from_ != to_; ++from_)
v.emplace_back(from_);
return v;
}
};
Ranges : 기본 구조
• 비교
vector<int> v = simple_iota_view<int>{ 0, 10 };
현재 결과
우리가 원하는 결과
vector<int> v = view::iota(0, 10);
Ranges : 기본 구조
• Factory Functor
struct simple_iota_fn
{
template<class T>
simple_iota_view<T> operator()(T beg, T end)
{
return { beg, end };
}
};simple_iota;
Ranges : 기본 구조
• 최종 결과
vector<int> v = simple_iota(0, 10);
Ranges : 기본 구조
template <class T>
struct simple_iota_view
{
// ***
};
struct simple_iota_fn
{
// ***
} simple_iota; 호출할 Functor Instance
구현체 클래스
Functor 클래스
• 왜 이렇게 복잡하죠?
Ranges : 파이프라인
• Linux/Unix 파이프라인
• Vertical Bar ( | )
• Bar 왼쪽의 연산 결과를
오른쪽 연산의 입력으로 사용
http://www.guru99.com/linux-pipe-grep.html
grep(cat(“sample”), “Apple”)
현재 C++ 에서는?
Ranges : 파이프라인
• R 파이프라인
• %>% 를 통해 전달
http://www.r-statistics.com/2014/08/simpler-r-coding-with-pipes-the-present-and-future-of-the-magrittr-package/
Ranges : 파이프라인
• R 파이프라인
http://www.r-statistics.com/2014/08/simpler-r-coding-with-pipes-the-present-and-future-of-the-magrittr-package/
Ranges : 파이프라인
vector<int> v = view::iota(0, 5) |
view::cycle |
view::take(15);
Ranges : 파이프라인
• Vertical Bar ( | ) 를 통해 전달
• 예시
• 0 ~ 4 를 반복하는 임의의 길이의 수열을 구해라
• [ 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0 …. ]
vector<int> v =
view::iota(0, 5) |
view::cycle |
view::take(15);
[0, 1, 2, 3, 4] 수열을 만들고
그걸 무한히 반복하게 한 뒤
그 중 앞에서 15개(원하는 만큼)만 추린다.
Ranges : 파이프라인
• 비교 (0 ~ 4 를 반복하는 임의의 길이의 수열을 구해라)
for 루프
std::generate()
ranges
vector<int> v =
view::iota(0, 5) |
view::cycle |
view::take(15);
vector<int> v(15);
for (int i = 0; i < v.size(); ++i)
v[i] = i % 5;
int n = 0;
vector<int> v(15);
std::generate(v.begin(), v.end(), [&n] {
return n++ % 5;
});
Ranges : 파이프라인
• [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라
• 0 + 1 + 2 + 3 + 4 = 10
• 5 + 6 + 7 + 8 + 9 = 35 …
vector<int> v =
view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
for (auto e : v)
cout << e << " ";
cout << endl;
// 10 35 60
auto quotient = [](int a, int b){
return a / 5 == b / 5;
};
auto sum = [](auto rng) {
return ranges::accumulate(rng, 0);
};
Ranges : 파이프라인
• [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라
• 0 + 1 + 2 + 3 + 4 = 10
• 5 + 6 + 7 + 8 + 9 = 35 …
vector<int> v =
view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
for (auto e : v)
cout << e << " ";
cout << endl;
// 10 35 60
[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
[10,35,60]
Ranges : 파이프라인
• [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라
• 0부터 5개씩 그룹 지어 각 그룹의 합을 6개 구해라?
vector<int> v =
view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
for (auto e : v)
cout << e << " ";
cout << endl;
// 10 35 60
?
Ranges : 파이프라인
• 조합성(Composability)
• [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라
• 0부터 5개씩 그룹 지어 각 그룹의 합을 6개 구해라?
vector<int> v =
view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
for (auto e : v)
cout << e << " ";
cout << endl;
// 10 35 60
vector<int> v =
view::iota(0) |
view::group_by(quotient) |
view::transform(sum) |
view::take(6);
for (auto e : v)
cout << e << " ";
cout << endl;
// 10 35 60 85 110 135
Ranges : 출력
• 컨테이너 안에 무엇이 들었나
https://docs.python.org/3/tutorial/datastructures.html
vector<int> v = { ? };
Ranges : 출력
• 옆동네 파이썬에서는..
• 컨테이너 원소 쉽게 확인 가능
https://docs.python.org/3/tutorial/datastructures.html
Ranges : 출력
• 우리는..?
auto sq = [](int n) { return n * n; };
std::vector<int> v =
view::iota(0, 10) |
view::transform(sq);
for (auto e : v)
cout << e << " ";
cout << endl;
// 0 1 4 9 16 25 36 49 64 81
Ranges : 출력
• 이상과 현실
• 추가적인 코드가 불가피
cout << v << endl;
// 0 1 4 9 16 25 36 49 64 81
https://namu.wiki/w/%ED%98%84%EC%8B%A4%EC%9D%80%20%EC%8B%9C%EA%B6%81%EC%B0%BD
Ranges : 출력
• view 연산의 결과는 (대부분) 그대로 출력 가능
cout << view::iota(0, 10) << endl;
Ranges : 출력
• 파이프라인을 auto로 받아서 출력
• 컨테이너를 “범위”로 만들어 출력
auto rng = view::iota(0, 10) |
view::transform(sq);
cout << rng << endl;
cout << view::all(v) << endl;
Ranges : 출력
• 그룹 지어진 범위 출력
auto quotient = [](int a, int b) {
return a / 5 == b / 5;
};
auto rng = view::iota(0, 15) |
view::group_by(quotient);
cout << rng << endl;
// [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]]
Ranges : 출력
• Insertion Operator 오버로딩 *it 는 하위 range 일 수 있음
//struct view_interface
friend Stream &operator<<(Stream &sout, Derived &rng){
auto it = ranges::begin(rng);
auto const e = ranges::end(rng);
if(it == e)
return sout << "[]";
sout << '[' << *it;
while(++it != e)
sout << ',' << *it;
sout << ']';
return sout;
}
자료형
이게 어떻게 가능하지?
의문의 rng
auto quotient = [](int a, int b) {
return a / 5 == b / 5;
};
auto rng = view::iota(0) |
view::group_by(quotient);
vector<int> v = rng | view::take(10);
cout << view::all(v) << endl;
대체 타입이 뭐지?
삐리리 불어봐 재규어
Ranges : 타입
• 개념적 타입
• Range<T> 로 표현
• 중첩 가능
cppcon 2015 에릭 니블러
// Range<Range<int>>
auto rng = view::iota(0, 15) |
view::group_by(quotient);
// [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]]
Ranges : 타입
• 실제 타입
• take_exactly_view_<iota_view<int,void>,0>
auto rng = view::iota(0) |
view::take(10);
cout << typeid(rng).name() << endl;
//take_exactly_view_<iota_view<int,void>,0>
* 가독성을 위해 간략화함
Ranges : 기본 구조
• 생각해 봅시다
template <class T>
struct simple_iota_view;
simple_iota(0);
template <class T>
struct simple_take_view;
simple_take(10)
simple_take_view<simple_iota_view>
Ranges : 타입
• 표현식 템플릿 (expression template)
• 수식 전체가 하나의 타입
• 값이 실제로 필요할 때 계산
auto sum = v1 + v2 + v3;
//우항 : VecSum<VecSum<Vec, Vec>, Vec>
cout << sum[0] << endl;
struct VecSum {
double operator[](size_t i) {
return _u[i] + _v[i];
}
}
Ranges : 타입
• 표현식 별 타입
• iota | cycle | take
auto rng = view::iota(0, 5) |
view::cycle |
view::take(15);
cout << typeid(rng).name() << endl;
// take_exactly_view_<cycled_view<iota_view<int>>>
* 가독성을 위해 간략화함
Ranges : 타입
• 표현식 별 타입
• iota | group_by | transform
* 가독성을 위해 간략화함
auto rng = view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
cout << typeid(rng).name() << endl;
?
Ranges : 타입
• ?AU?$transform_view@U?$group_by_view@U?$tak
e_exactly_view_@U?...생략
이말년 시리즈
Ranges : 타입
• 대체 왜…?
• iota | cycle | take
• iota | group_by | transform
auto rng = view::iota(0, 5) |
view::cycle |
view::take(15);
// take_exactly_view_<cycled_view<iota_view<int>>>
auto rng = view::iota(0, 15) |
view::group_by(quotient) |
view::transform(sum);
// ?AU?$transform_view@U?$group_by_view@U?$take_exa
ctly_view_@U?...생략
Ranges : 타입
• Type Displayer
• 컴파일러 오류 메시지로 타입 확인
Effective Modern C++ : 항목 4
template <class T>
struct TD;
/***/
TD<decltype(rng)>();
Ranges : 타입
• Type Displayer 로 확인한 타입
• iota | group_by(람다) | transform(람다) * 가독성을 위해 간략화함
error : implicit instantiation of undefined template
'TD<transform_view<group_by_view<iota_view<int>,
(lambda at type_of_range.cpp:32:18)>,
(lambda at type_of_range.cpp:36:13)>>
Ranges : 타입
• 실제 타입은 매번 바뀌는데
어떻게 모두 Range<T> 처럼 사용할 수 있죠?
Effective Modern C++ : 항목 4
Ranges : 타입
• 개념적 타입 Range<T> 가 가능한 이유
• 공통 인터페이스 상속
• Range<T>는 일종의 인터페이스
• view_façade 를 상속받아
User-Define View 쉽게 정의 가능
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
iota_view
iota_view
iota_view
view_interface
view_facade
iota_view
표현식
우리 아이가 간결해 졌어요
Ranges : 자연수 표현
• 자연수
• [명사] <수학> 1부터 시작하여 하나씩 더하여 얻는 수를 통틀어 이르
는 말. 1, 2, 3 따위이다. (후략)
• 𝑥 ∈ ℕ
• 기존에 자연수를 표현하던 방법
네이버 국어사전
void foo(vector<int> v) {
for(auto i : v) {
/***/
}
}
// v는 자연수로 이루어진 벡터
assert(0 < i);
Ranges : 자연수 표현
• Ranges 자연수 표현
• 무한 수열 표현
• 반드시 view::take() 와 같은 한정 함수와 함께 사용
auto naturals = view::ints(1);
auto s = naturals | view::take(10);
cout << s << endl;
//[1,2,3,4,5,6,7,8,9,10]
Range Comprehension
• 수학 시간에 배운 것처럼 Range 만들기
• 지금까지 소개한 방식
// 집합 S = { 2 * x | x는 자연수 }
auto twice = [](int i) { return 2 * i; };
auto s = view::ints(1) | view::transform(twice);
Range Comprehension
• 수학 시간에 배운 것처럼 Range 만들기
• view::for_each 를 이용한 방식
// 집합 S = { 2 * x | x는 자연수 }
auto s = view::ints(1) | view::for_each([](int i){
return ranges::yield(i * 2);
});
Range Comprehension
• view::for_each 연산자 오버로딩
• view::for_each
• operator extraction equal (>>=) 오버로딩
auto s = view::ints(1) | view::for_each([](int i){
return ranges::yield(i * 2); //
});
auto s = view::ints(1) >>= [](int i) {
return ranges::yield(i * 2);
};
Range Comprehension
• 피타고라스 수
• 𝑎2 + 𝑏2 = 𝑐2 을 만족하는 세 자연수 쌍 𝑎, 𝑏, 𝑐 100개를 구하시오.
auto triples = view::ints(1) >>= [] (int z) { return
view::ints(1, z+1) >>= [=](int x) { return
view::ints(x, z+1) >>= [=](int y) { return
yield_if(x*x + y*y == z*z,
std::make_tuple(x, y, z));
};};};
http://ericniebler.com/2014/04/27/range-comprehensions/
View
Action
Ranges의 양대 산맥
Ranges : View / Action namespace
• ranges::view
• 데이터를 특정 시점으로 보기 위한 연산들
• 원본 데이터의 복사나 변환 X
• Lazy Evaluation 가능
• 표현식 템플릿
• ranges::action
• 데이터 원본을 직접 수정하는 연산들 (STL처럼)
auto sum = v1 + v2 + v3;
//우항 : VecSum<VecSum<Vec, Vec>, Vec>
http://www.chippostudy.com/subview/23255
Ranges : View namespace
lazy_yield_if adjacent_filter adjacent_remove_if all bounded
const counted cycle delimit drop
indirect intersperse ints iota iter_take_while
move partial_sum remove_if repeat repeat_n
split stride tail take_exactly take_while
c_str chunk closed_ints closed_iota concat
drop_while for_each generate generate_n group_by
iter_transform iter_zip_with join keys make_view
replace replace_if reverse single slice
tokenize transform unbounded unique values
Ranges : Action namespace
drop drop_while join split stable_sort
make_action remove_if shuffle slice sort
stride take take_while transform unique
Ranges : Action
• 컨테이너 직접 수정
std::default_random_engine gen;
std::vector<int> v = view::iota(0, 10);
action::shuffle(v, gen);
cout << view::all(v) << endl;
//[2,7,8,4,0,6,1,9,3,5]
v |= action::shuffle(gen);
Ranges : Action
• 복사
• 이동
auto v2 = v | ranges::copy | action::sort;
cout << view::all(v2) << endl;
//[0,1,2,3,4,5,6,7,8,9]
auto v2 = v | ranges::move | action::sort;
cout << view::all(v2) << endl;
//[0,1,2,3,4,5,6,7,8,9]
Ranges : Action
• View 와 함께 파이프라이닝
• 특정한 순회 방식에 알고리즘 적용
• 추가 컨테이너 필요 X
// 짝수만 셔플
v = view::ints(0, 10);
v | view::stride(2) | action::shuffle(gen);
cout << view::all(v) << endl;
//[6,1,8,3,2,5,0,7,4,9]
Summary
Summary
• 장점
• 간결하고 강력한 표현
• 범위기반 알고리즘
• 파이프라인
• Lazy Evaluation
• Range Comprehension
• 표준 컨테이너를 매개로 기존 코드에 쉽게 이식 가능
• 단점
• 컴파일 시간 (개선 중)
Thanks!
http://ericniebler.com/
QnA
Reference
• 라이브러리 : https://github.com/ericniebler/range-v3
• 매뉴얼 : https://ericniebler.github.io/range-v3/
• 기타 참조
• http://ericniebler.com/
• Effective Modern C++
• http://stackoverflow.com/search?q=range-v3
• https://en.wikipedia.org/
• http://www.r-statistics.com/
• http://en.cppreference.com/

More Related Content

What's hot

덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012Esun Kim
 
C Programming - Refresher - Part III
C Programming - Refresher - Part IIIC Programming - Refresher - Part III
C Programming - Refresher - Part III
Emertxe Information Technologies Pvt Ltd
 
Rancher kubernetes storages
Rancher kubernetes storagesRancher kubernetes storages
Rancher kubernetes storages
Tetsurou Yano
 
Chapter2
Chapter2  Chapter2
Chapter2
Fang-Ling Lin
 
Arm device tree and linux device drivers
Arm device tree and linux device driversArm device tree and linux device drivers
Arm device tree and linux device drivers
Houcheng Lin
 
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012Esun Kim
 
C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현
Bongseok Cho
 
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
내훈 정
 
Handling inline assembly in Clang and LLVM
Handling inline assembly in Clang and LLVMHandling inline assembly in Clang and LLVM
Handling inline assembly in Clang and LLVM
Min-Yih Hsu
 
게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv
Seungmo Koo
 
Emcjp item21
Emcjp item21Emcjp item21
Emcjp item21
MITSUNARI Shigeo
 
eBPF Trace from Kernel to Userspace
eBPF Trace from Kernel to UserspaceeBPF Trace from Kernel to Userspace
eBPF Trace from Kernel to Userspace
SUSE Labs Taipei
 
Study on Android Emulator
Study on Android EmulatorStudy on Android Emulator
Study on Android Emulator
Samael Wang
 
SystemC Tutorial
SystemC TutorialSystemC Tutorial
SystemC Tutorialkocha2012
 
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
Chris Ohk
 
Profiling - 실시간 대화식 프로파일러
Profiling - 실시간 대화식 프로파일러Profiling - 실시간 대화식 프로파일러
Profiling - 실시간 대화식 프로파일러
Heungsub Lee
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11
nekko1119
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
MITSUNARI Shigeo
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심흥배 최
 

What's hot (20)

덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
 
C Programming - Refresher - Part III
C Programming - Refresher - Part IIIC Programming - Refresher - Part III
C Programming - Refresher - Part III
 
Rancher kubernetes storages
Rancher kubernetes storagesRancher kubernetes storages
Rancher kubernetes storages
 
Chapter2
Chapter2  Chapter2
Chapter2
 
Arm device tree and linux device drivers
Arm device tree and linux device driversArm device tree and linux device drivers
Arm device tree and linux device drivers
 
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
 
C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현C++20에서 리플렉션 기능 구현
C++20에서 리플렉션 기능 구현
 
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
 
Handling inline assembly in Clang and LLVM
Handling inline assembly in Clang and LLVMHandling inline assembly in Clang and LLVM
Handling inline assembly in Clang and LLVM
 
게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv
 
Emcjp item21
Emcjp item21Emcjp item21
Emcjp item21
 
eBPF Trace from Kernel to Userspace
eBPF Trace from Kernel to UserspaceeBPF Trace from Kernel to Userspace
eBPF Trace from Kernel to Userspace
 
Study on Android Emulator
Study on Android EmulatorStudy on Android Emulator
Study on Android Emulator
 
SystemC Tutorial
SystemC TutorialSystemC Tutorial
SystemC Tutorial
 
The Internals of "Hello World" Program
The Internals of "Hello World" ProgramThe Internals of "Hello World" Program
The Internals of "Hello World" Program
 
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
 
Profiling - 실시간 대화식 프로파일러
Profiling - 실시간 대화식 프로파일러Profiling - 실시간 대화식 프로파일러
Profiling - 실시간 대화식 프로파일러
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 

Similar to [C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library

[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)
NAVER D2
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
명신 김
 
C++ Advanced 강의 4주차
 C++ Advanced 강의 4주차 C++ Advanced 강의 4주차
C++ Advanced 강의 4주차
HyunJoon Park
 
C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부
Gwangwhi Mah
 
[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary
Chris Ohk
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
KWANGIL KIM
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
Sang Heon Lee
 
[C++ Korea] Effective Modern C++ Study item14 16 +신촌
[C++ Korea] Effective Modern C++ Study item14 16 +신촌[C++ Korea] Effective Modern C++ Study item14 16 +신촌
[C++ Korea] Effective Modern C++ Study item14 16 +신촌
Seok-joon Yun
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
Jaeseung Ha
 
C Language For Arduino
C Language For ArduinoC Language For Arduino
C Language For Arduino
영욱 김
 
Multithread programming 20151206_서진택
Multithread programming 20151206_서진택Multithread programming 20151206_서진택
Multithread programming 20151206_서진택
JinTaek Seo
 
c++ opencv tutorial
c++ opencv tutorialc++ opencv tutorial
c++ opencv tutorial
TaeKang Woo
 
모던 C++ 정리
모던 C++ 정리모던 C++ 정리
모던 C++ 정리
Hansol Kang
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
JinTaek Seo
 
2 1. variables & data types
2 1. variables & data types2 1. variables & data types
2 1. variables & data types웅식 전
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
Jongwook Choi
 
About Visual C++ 10
About  Visual C++ 10About  Visual C++ 10
About Visual C++ 10
흥배 최
 
5 swift 기초함수
5 swift 기초함수5 swift 기초함수
5 swift 기초함수
Changwon National University
 
2013 C++ Study For Students #1
2013 C++ Study For Students #12013 C++ Study For Students #1
2013 C++ Study For Students #1
Chris Ohk
 
[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?
NAVER D2
 

Similar to [C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library (20)

[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
C++ Advanced 강의 4주차
 C++ Advanced 강의 4주차 C++ Advanced 강의 4주차
C++ Advanced 강의 4주차
 
C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부
 
[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary[C++ Korea 2nd Seminar] C++17 Key Features Summary
[C++ Korea 2nd Seminar] C++17 Key Features Summary
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 
[C++ Korea] Effective Modern C++ Study item14 16 +신촌
[C++ Korea] Effective Modern C++ Study item14 16 +신촌[C++ Korea] Effective Modern C++ Study item14 16 +신촌
[C++ Korea] Effective Modern C++ Study item14 16 +신촌
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
 
C Language For Arduino
C Language For ArduinoC Language For Arduino
C Language For Arduino
 
Multithread programming 20151206_서진택
Multithread programming 20151206_서진택Multithread programming 20151206_서진택
Multithread programming 20151206_서진택
 
c++ opencv tutorial
c++ opencv tutorialc++ opencv tutorial
c++ opencv tutorial
 
모던 C++ 정리
모던 C++ 정리모던 C++ 정리
모던 C++ 정리
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
 
2 1. variables & data types
2 1. variables & data types2 1. variables & data types
2 1. variables & data types
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
 
About Visual C++ 10
About  Visual C++ 10About  Visual C++ 10
About Visual C++ 10
 
5 swift 기초함수
5 swift 기초함수5 swift 기초함수
5 swift 기초함수
 
2013 C++ Study For Students #1
2013 C++ Study For Students #12013 C++ Study For Students #1
2013 C++ Study For Students #1
 
[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?
 

[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library

  • 1.
  • 2. 최동민 / Nexon Korea Ranges for The C++ Standard Library
  • 3. 발표 전… • 다룰 것 • Ranges와 Ranges를 이루는 문법들 • 다루지 않을 것 • Concept • 시간의 제약 • 코드 생략 • const, constexpr, explicit, overload, noexcept, namespace 등 • 가독성을 위해
  • 4. 문제 인식 당신의 코드는 안녕하십니까?
  • 5. 불편함의 시작 • 태초에 Iterator 가 있었습니다. • 매우 불편했습니다. for (std::vector<int>::const_iterator it = numbers.begin(); it != numbers.end(); ++it) { // *** }
  • 6. C++11 – 혁신의 바람 “내 너희에게 auto와 range-based for 를 주노라”
  • 7. C++11 – 혁신의 바람 for (auto e : numbers) { // *** } for (std::vector<int>::const_iterator it = numbers.begin(); it != numbers.end(); ++it) { // *** }
  • 9. 아직도 풀리지 않은 숙제 • 나는 컨테이너 전체를 소팅 하고 싶은데 왜 begin(), end() 요? std::vector<int> numbers{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; std::sort(numbers.begin(), numbers.end());
  • 10. 새로운 희망 • 오늘 소개할 Ranges 라이브러리 사용 시 • 모든 STL 알고리즘 함수에 대해 범위 기반 알고리즘 함수 제공 ranges::sort(numbers); std::sort(numbers.begin(), numbers.end());
  • 13. Ranges • 범위를 다루는 Generic Library • <Begin, End> • 지원 컴파일러 • clang 3.4.0 이상 • gcc 4.9.0 이상 • 차기 C++ 표준 포함 예정 • https://github.com/ericniebler/range-v3
  • 15. Ranges • Clang 3.7 이 없는데요?
  • 16. Ranges : 범위 기반 알고리즘 제공 • partition auto it = ranges::partition(v, [](auto e){ return e % 2 == 0; } auto it = std::partition(v.begin(), v.end(), [](auto e){ return e % 2 == 0; }
  • 18. Ranges 를 왜 써야 하나요? • 편의성(Convenience) • 코드가 짧아져요.(!) • 느긋한 계산(Lazy Evaluation) • 무한 범위 표현 가능 • 불필요한 계산 회피 • 조합성(Composability) • 함수형 프로그래밍에서 내세우는 그것 • 직관적인 표현 가능 • 코드 생산성 향상
  • 20. Ranges : 컨테이너로의 전환 • [0, 10) 으로 채우기 • 2009년의 나 • 2010년의 나 • 2016년의 나 int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; vector<int> v(10); for (int i = 0; i < 10; ++i) v[i] = i; vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  • 21. Ranges : 컨테이너로의 전환 • [0, 10) 으로 채우기 • std::iota() • ranges::iota() //#include<numeric> vector<int> v(10); std::iota(v.begin(), v.end(), 0); vector<int> v(10); ranges::iota(v, 0);
  • 22. Ranges : 컨테이너로의 전환 • [0, 10) 으로 채우기 • ranges::view::iota() using namespace ranges; vector<int> v = view::iota(0, 10);
  • 23. Ranges : 컨테이너로의 전환 • ranges::view::iota() vector range (iota_view) view::iota User-Defined Conversion
  • 24. User-Defined Conversion • 분수 클래스 with Getter struct Fraction{ int numerator; int denominator; double get(){ return static_cast<double>(numerator) / denominator; } }; int main(){ Fraction f{ 10, 20 }; cout << f.get() << endl; // 0.5 } (http://en.cppreference.com/w/cpp/language/cast_operator)
  • 25. User-Defined Conversion • 마치 처음부터 double 이었던 것 처럼 (http://en.cppreference.com/w/cpp/language/cast_operator) struct Fraction{ int numerator; int denominator; operator double(){ return static_cast<double>(numerator) / denominator; } }; int main(){ Fraction f{ 10, 20 }; cout << f << endl; // 0.5 }
  • 26. Ranges : 기본 구조 • view::iota() 를 구현해 봅시다. template <class T> struct simple_iota_view{ T from_; T to_; operator vector<T>(){ vector<T> v; v.reserve(to_ - from_); for (; from_ != to_; ++from_) v.emplace_back(from_); return v; } };
  • 27. Ranges : 기본 구조 • 비교 vector<int> v = simple_iota_view<int>{ 0, 10 }; 현재 결과 우리가 원하는 결과 vector<int> v = view::iota(0, 10);
  • 28. Ranges : 기본 구조 • Factory Functor struct simple_iota_fn { template<class T> simple_iota_view<T> operator()(T beg, T end) { return { beg, end }; } };simple_iota;
  • 29. Ranges : 기본 구조 • 최종 결과 vector<int> v = simple_iota(0, 10);
  • 30. Ranges : 기본 구조 template <class T> struct simple_iota_view { // *** }; struct simple_iota_fn { // *** } simple_iota; 호출할 Functor Instance 구현체 클래스 Functor 클래스 • 왜 이렇게 복잡하죠?
  • 31. Ranges : 파이프라인 • Linux/Unix 파이프라인 • Vertical Bar ( | ) • Bar 왼쪽의 연산 결과를 오른쪽 연산의 입력으로 사용 http://www.guru99.com/linux-pipe-grep.html grep(cat(“sample”), “Apple”) 현재 C++ 에서는?
  • 32. Ranges : 파이프라인 • R 파이프라인 • %>% 를 통해 전달 http://www.r-statistics.com/2014/08/simpler-r-coding-with-pipes-the-present-and-future-of-the-magrittr-package/
  • 33. Ranges : 파이프라인 • R 파이프라인 http://www.r-statistics.com/2014/08/simpler-r-coding-with-pipes-the-present-and-future-of-the-magrittr-package/
  • 34. Ranges : 파이프라인 vector<int> v = view::iota(0, 5) | view::cycle | view::take(15);
  • 35. Ranges : 파이프라인 • Vertical Bar ( | ) 를 통해 전달 • 예시 • 0 ~ 4 를 반복하는 임의의 길이의 수열을 구해라 • [ 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0 …. ] vector<int> v = view::iota(0, 5) | view::cycle | view::take(15); [0, 1, 2, 3, 4] 수열을 만들고 그걸 무한히 반복하게 한 뒤 그 중 앞에서 15개(원하는 만큼)만 추린다.
  • 36. Ranges : 파이프라인 • 비교 (0 ~ 4 를 반복하는 임의의 길이의 수열을 구해라) for 루프 std::generate() ranges vector<int> v = view::iota(0, 5) | view::cycle | view::take(15); vector<int> v(15); for (int i = 0; i < v.size(); ++i) v[i] = i % 5; int n = 0; vector<int> v(15); std::generate(v.begin(), v.end(), [&n] { return n++ % 5; });
  • 37. Ranges : 파이프라인 • [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라 • 0 + 1 + 2 + 3 + 4 = 10 • 5 + 6 + 7 + 8 + 9 = 35 … vector<int> v = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); for (auto e : v) cout << e << " "; cout << endl; // 10 35 60 auto quotient = [](int a, int b){ return a / 5 == b / 5; }; auto sum = [](auto rng) { return ranges::accumulate(rng, 0); };
  • 38. Ranges : 파이프라인 • [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라 • 0 + 1 + 2 + 3 + 4 = 10 • 5 + 6 + 7 + 8 + 9 = 35 … vector<int> v = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); for (auto e : v) cout << e << " "; cout << endl; // 10 35 60 [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]] [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] [10,35,60]
  • 39. Ranges : 파이프라인 • [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라 • 0부터 5개씩 그룹 지어 각 그룹의 합을 6개 구해라? vector<int> v = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); for (auto e : v) cout << e << " "; cout << endl; // 10 35 60 ?
  • 40. Ranges : 파이프라인 • 조합성(Composability) • [0~15) 에서 5개씩 그룹 지어 각 그룹의 합을 구해라 • 0부터 5개씩 그룹 지어 각 그룹의 합을 6개 구해라? vector<int> v = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); for (auto e : v) cout << e << " "; cout << endl; // 10 35 60 vector<int> v = view::iota(0) | view::group_by(quotient) | view::transform(sum) | view::take(6); for (auto e : v) cout << e << " "; cout << endl; // 10 35 60 85 110 135
  • 41. Ranges : 출력 • 컨테이너 안에 무엇이 들었나 https://docs.python.org/3/tutorial/datastructures.html vector<int> v = { ? };
  • 42. Ranges : 출력 • 옆동네 파이썬에서는.. • 컨테이너 원소 쉽게 확인 가능 https://docs.python.org/3/tutorial/datastructures.html
  • 43. Ranges : 출력 • 우리는..? auto sq = [](int n) { return n * n; }; std::vector<int> v = view::iota(0, 10) | view::transform(sq); for (auto e : v) cout << e << " "; cout << endl; // 0 1 4 9 16 25 36 49 64 81
  • 44. Ranges : 출력 • 이상과 현실 • 추가적인 코드가 불가피 cout << v << endl; // 0 1 4 9 16 25 36 49 64 81 https://namu.wiki/w/%ED%98%84%EC%8B%A4%EC%9D%80%20%EC%8B%9C%EA%B6%81%EC%B0%BD
  • 45. Ranges : 출력 • view 연산의 결과는 (대부분) 그대로 출력 가능 cout << view::iota(0, 10) << endl;
  • 46. Ranges : 출력 • 파이프라인을 auto로 받아서 출력 • 컨테이너를 “범위”로 만들어 출력 auto rng = view::iota(0, 10) | view::transform(sq); cout << rng << endl; cout << view::all(v) << endl;
  • 47. Ranges : 출력 • 그룹 지어진 범위 출력 auto quotient = [](int a, int b) { return a / 5 == b / 5; }; auto rng = view::iota(0, 15) | view::group_by(quotient); cout << rng << endl; // [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]]
  • 48. Ranges : 출력 • Insertion Operator 오버로딩 *it 는 하위 range 일 수 있음 //struct view_interface friend Stream &operator<<(Stream &sout, Derived &rng){ auto it = ranges::begin(rng); auto const e = ranges::end(rng); if(it == e) return sout << "[]"; sout << '[' << *it; while(++it != e) sout << ',' << *it; sout << ']'; return sout; }
  • 50. 의문의 rng auto quotient = [](int a, int b) { return a / 5 == b / 5; }; auto rng = view::iota(0) | view::group_by(quotient); vector<int> v = rng | view::take(10); cout << view::all(v) << endl;
  • 51. 대체 타입이 뭐지? 삐리리 불어봐 재규어
  • 52. Ranges : 타입 • 개념적 타입 • Range<T> 로 표현 • 중첩 가능 cppcon 2015 에릭 니블러 // Range<Range<int>> auto rng = view::iota(0, 15) | view::group_by(quotient); // [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14]]
  • 53. Ranges : 타입 • 실제 타입 • take_exactly_view_<iota_view<int,void>,0> auto rng = view::iota(0) | view::take(10); cout << typeid(rng).name() << endl; //take_exactly_view_<iota_view<int,void>,0> * 가독성을 위해 간략화함
  • 54. Ranges : 기본 구조 • 생각해 봅시다 template <class T> struct simple_iota_view; simple_iota(0); template <class T> struct simple_take_view; simple_take(10) simple_take_view<simple_iota_view>
  • 55. Ranges : 타입 • 표현식 템플릿 (expression template) • 수식 전체가 하나의 타입 • 값이 실제로 필요할 때 계산 auto sum = v1 + v2 + v3; //우항 : VecSum<VecSum<Vec, Vec>, Vec> cout << sum[0] << endl; struct VecSum { double operator[](size_t i) { return _u[i] + _v[i]; } }
  • 56. Ranges : 타입 • 표현식 별 타입 • iota | cycle | take auto rng = view::iota(0, 5) | view::cycle | view::take(15); cout << typeid(rng).name() << endl; // take_exactly_view_<cycled_view<iota_view<int>>> * 가독성을 위해 간략화함
  • 57. Ranges : 타입 • 표현식 별 타입 • iota | group_by | transform * 가독성을 위해 간략화함 auto rng = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); cout << typeid(rng).name() << endl; ?
  • 58. Ranges : 타입 • ?AU?$transform_view@U?$group_by_view@U?$tak e_exactly_view_@U?...생략 이말년 시리즈
  • 59. Ranges : 타입 • 대체 왜…? • iota | cycle | take • iota | group_by | transform auto rng = view::iota(0, 5) | view::cycle | view::take(15); // take_exactly_view_<cycled_view<iota_view<int>>> auto rng = view::iota(0, 15) | view::group_by(quotient) | view::transform(sum); // ?AU?$transform_view@U?$group_by_view@U?$take_exa ctly_view_@U?...생략
  • 60. Ranges : 타입 • Type Displayer • 컴파일러 오류 메시지로 타입 확인 Effective Modern C++ : 항목 4 template <class T> struct TD; /***/ TD<decltype(rng)>();
  • 61. Ranges : 타입 • Type Displayer 로 확인한 타입 • iota | group_by(람다) | transform(람다) * 가독성을 위해 간략화함 error : implicit instantiation of undefined template 'TD<transform_view<group_by_view<iota_view<int>, (lambda at type_of_range.cpp:32:18)>, (lambda at type_of_range.cpp:36:13)>>
  • 62. Ranges : 타입 • 실제 타입은 매번 바뀌는데 어떻게 모두 Range<T> 처럼 사용할 수 있죠? Effective Modern C++ : 항목 4
  • 63. Ranges : 타입 • 개념적 타입 Range<T> 가 가능한 이유 • 공통 인터페이스 상속 • Range<T>는 일종의 인터페이스 • view_façade 를 상속받아 User-Define View 쉽게 정의 가능 https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern iota_view iota_view iota_view view_interface view_facade iota_view
  • 65. Ranges : 자연수 표현 • 자연수 • [명사] <수학> 1부터 시작하여 하나씩 더하여 얻는 수를 통틀어 이르 는 말. 1, 2, 3 따위이다. (후략) • 𝑥 ∈ ℕ • 기존에 자연수를 표현하던 방법 네이버 국어사전 void foo(vector<int> v) { for(auto i : v) { /***/ } } // v는 자연수로 이루어진 벡터 assert(0 < i);
  • 66. Ranges : 자연수 표현 • Ranges 자연수 표현 • 무한 수열 표현 • 반드시 view::take() 와 같은 한정 함수와 함께 사용 auto naturals = view::ints(1); auto s = naturals | view::take(10); cout << s << endl; //[1,2,3,4,5,6,7,8,9,10]
  • 67. Range Comprehension • 수학 시간에 배운 것처럼 Range 만들기 • 지금까지 소개한 방식 // 집합 S = { 2 * x | x는 자연수 } auto twice = [](int i) { return 2 * i; }; auto s = view::ints(1) | view::transform(twice);
  • 68. Range Comprehension • 수학 시간에 배운 것처럼 Range 만들기 • view::for_each 를 이용한 방식 // 집합 S = { 2 * x | x는 자연수 } auto s = view::ints(1) | view::for_each([](int i){ return ranges::yield(i * 2); });
  • 69. Range Comprehension • view::for_each 연산자 오버로딩 • view::for_each • operator extraction equal (>>=) 오버로딩 auto s = view::ints(1) | view::for_each([](int i){ return ranges::yield(i * 2); // }); auto s = view::ints(1) >>= [](int i) { return ranges::yield(i * 2); };
  • 70. Range Comprehension • 피타고라스 수 • 𝑎2 + 𝑏2 = 𝑐2 을 만족하는 세 자연수 쌍 𝑎, 𝑏, 𝑐 100개를 구하시오. auto triples = view::ints(1) >>= [] (int z) { return view::ints(1, z+1) >>= [=](int x) { return view::ints(x, z+1) >>= [=](int y) { return yield_if(x*x + y*y == z*z, std::make_tuple(x, y, z)); };};}; http://ericniebler.com/2014/04/27/range-comprehensions/
  • 72. Ranges : View / Action namespace • ranges::view • 데이터를 특정 시점으로 보기 위한 연산들 • 원본 데이터의 복사나 변환 X • Lazy Evaluation 가능 • 표현식 템플릿 • ranges::action • 데이터 원본을 직접 수정하는 연산들 (STL처럼) auto sum = v1 + v2 + v3; //우항 : VecSum<VecSum<Vec, Vec>, Vec> http://www.chippostudy.com/subview/23255
  • 73. Ranges : View namespace lazy_yield_if adjacent_filter adjacent_remove_if all bounded const counted cycle delimit drop indirect intersperse ints iota iter_take_while move partial_sum remove_if repeat repeat_n split stride tail take_exactly take_while c_str chunk closed_ints closed_iota concat drop_while for_each generate generate_n group_by iter_transform iter_zip_with join keys make_view replace replace_if reverse single slice tokenize transform unbounded unique values
  • 74. Ranges : Action namespace drop drop_while join split stable_sort make_action remove_if shuffle slice sort stride take take_while transform unique
  • 75. Ranges : Action • 컨테이너 직접 수정 std::default_random_engine gen; std::vector<int> v = view::iota(0, 10); action::shuffle(v, gen); cout << view::all(v) << endl; //[2,7,8,4,0,6,1,9,3,5] v |= action::shuffle(gen);
  • 76. Ranges : Action • 복사 • 이동 auto v2 = v | ranges::copy | action::sort; cout << view::all(v2) << endl; //[0,1,2,3,4,5,6,7,8,9] auto v2 = v | ranges::move | action::sort; cout << view::all(v2) << endl; //[0,1,2,3,4,5,6,7,8,9]
  • 77. Ranges : Action • View 와 함께 파이프라이닝 • 특정한 순회 방식에 알고리즘 적용 • 추가 컨테이너 필요 X // 짝수만 셔플 v = view::ints(0, 10); v | view::stride(2) | action::shuffle(gen); cout << view::all(v) << endl; //[6,1,8,3,2,5,0,7,4,9]
  • 79. Summary • 장점 • 간결하고 강력한 표현 • 범위기반 알고리즘 • 파이프라인 • Lazy Evaluation • Range Comprehension • 표준 컨테이너를 매개로 기존 코드에 쉽게 이식 가능 • 단점 • 컴파일 시간 (개선 중)
  • 81. QnA
  • 82. Reference • 라이브러리 : https://github.com/ericniebler/range-v3 • 매뉴얼 : https://ericniebler.github.io/range-v3/ • 기타 참조 • http://ericniebler.com/ • Effective Modern C++ • http://stackoverflow.com/search?q=range-v3 • https://en.wikipedia.org/ • http://www.r-statistics.com/ • http://en.cppreference.com/

Editor's Notes

  1. 그럼 발표 시작하겠습니다. 차기 C++ 표준으로 제안된 Ranges 라이브러리에 대해서 발표할 최동민입니다.
  2. 먼저 발표 내용에 대해서 간략하게 설명을 잠깐 드릴게요. 이번 발표에서는 Ranges 라이브러리가 어떤 것인가, 와 Ranges를 이루는 문법들에 대한 내용을 다룰 예정이구요, 안타깝게도 Ranges만 하기에도 시간이 모잘라서 Ranges가 기반하고 있는 Concept 에 대해서는 다루지 않습니다. 그리고 발표 중간 중간에 나오는 예제 코드들은 가독성을 위해 여러가지 키워드들이 생략되어 있으니 보기 불편하시더라도 양해 부탁드립니다.
  3. 자 그럼 발표를 시작해 볼게요. 짧은 이야기를 하나 들려드릴게요.
  4. 태초에 이터레이터가 있었습니다. 그런데 이터레이터는 타입 이름이 너무 길었어요. 그래서 포문을 한줄에 쓸 수가 없었습니다. 매우 불편했습니다.
  5. 그러자 그 모습을 본 C++표준 위원회에서는 “내 너희에게 auto와 range-based for를 주노라” 하셨으니
  6. 코드가 매우 짧아졌고 C++프로그래머들이 보시기에 매우 좋았습니다.
  7. 이야기는 여기서 끝나지만, 현실에서는 또 다른 문제가 남아있습니다.
  8. 혹시 이런 의문을 가져본 적 있나요? stl 알고리즘 함수를 쓸 때, 십중팔구는 begin 이랑 end를 넣을텐데 왜 컨테이너를 직접 못넘길까, 이런 생각, 저만 가졌었나요? auto와 범위기반 포문이 들어왔지만 아직도 불편함의 씨앗이 남아있습니다. 이 이야기가 시사하는 점이 뭘까요? 이 이야기가 시사하는 점은, 나온지 30년이 넘은 C++이 아직도 발전할 여지가 있고, 실제로도 계속 발전하고 있다는 점입니다. 오늘 소개해드릴 Ranges 라이브러리를 사용하면
  9. 이와 같이 지긋지긋한 begin과 end를 없앨 수 있습니다.
  10. 그럼 ranges 라이브러리가 무엇인지 본격적으로 알아볼까요?
  11. range라고 하면 아마 파이썬을 떠올리는 분들이 있을텐데요 이거랑은 조금 많이 다릅니다.
  12. ranges는 이름에서 알 수 있듯이, 범위를 다루는 제네릭 라이브러리 구요 범위를 <Begin, End> 페어로 이루어진 객체로 다루면서 여러가지 편리하고 신기한 기능들을 제공합니다. 현재 제안된 라이브러리의 소스 코드는 깃허브에 올라와 있구요, 이 소스를 지원되는 컴파일러에서 컴파일하면 지금도 라이브러리를 사용해볼 수 있습니다. 이 라이브러리는 차기 c++표준으로 제안된 상태구요, 다음달에 있을 표준 위원회 회의에서 들어갈지 안들어갈지가 결정될 예정입니다. 그런데 보시면 지원 컴파일러가 clang 이랑 gcc밖에 없는데요
  13. 그럼 비주얼 스튜디오로는 못하느냐, 그건 아닙니다. VS2015에서 프로젝트를 하나 여시고, 프로젝트 속성에 가시면 플랫폼 도구 집합이라는 옵션이 있잖아요? 이걸 Clang 3.7 with 마이크로소프트 코드젠으로 변경하시면 VS에서 Clang 컴파일러를 사용하실 수 있습니다.
  14. 만약 Clang 3.7 항목이 안보이면, 도구-> 확장 및 업데이트로 가셔서 저걸 깔아주시면 됩니다.
  15. ranges는 아까 보여드렸던 것처럼 범위 기반 알고리즘 함수를 제공하는데요,
  16. 이게 다일까요? 당연히 (s) 아니겠죠?
  17. 이 라이브러리를 만든 에릭 니블러 씨는 ranges의 장점을 이렇게 서술하고 있습니다. 첫번째는 편의성입니다. 일단 코드가 더 짧아져요. 굉장한 장점이죠? 둘째는 느긋한 계산이 가능해집니다. 느긋한 계산이 뭐냐면, 어떤 연산이 있을 때, 아 이건 언젠간 해야되겠구나 기억만 하고 있다가, 나중에 사용자가 값을 달라고 요청을 할때 그 때에서야 부랴 부랴 연산을 수행하는 것을 말합니다. 사람으로 치면 참 안좋은건데, 프로그래밍에 이 개념이 도입되면, 무한한 범위를 표현할 수 있다던지, 실제로는 안해도 되는 계산들을 회피할 수 있다던지 하는 장점을 얻을 수 있습니다. 그리고 세번째는 조합성인데요, 이건 뭐냐면 코드를 마치 레고 블록으로 짜듯이 조립할 수 있는 성질을 말합니다. 언어에 조합성이 있으면 더 직관적인 표현이 가능해지고, 그로 인해 코드생산성이 향상되는 이점을 얻을 수가 있습니다.
  18. 대체 어떤 기능이 있길래 이런 장점들이 있다는 건지, 지금부터 알아보겠습니다.
  19. 여러분은 컨테이너를 0 이상 10 미만으로 채워넣을 때 어떤 방법을 사용을 하시나요? 저는 2009년에 프로그래밍을 처음 배웠는데요, 이 때 저는 배열을 선언을 해서, 원소를 0부터 9까지 일일이 적어넣는 식으로 배열을 초기화를 했었지요. 그 이듬해인 2010년에는 벡터라는 걸 알게되어서 와 이렇게 좋은게 있었구나 하면서 사이즈가 10인 벡터를 선언해서 각각의 원소를 for문을 통해 초기화를 하는 방식을 사용했었습니다. 이랬던 제가, 2016년 현재는 이렇게 벡터를 초기화 하고 있습니다. 역시 이니셜라이저 리스트가 최고시다 하면서
  20. 사실은 이것보다 조금 더 고오오오급진 방법이 있는데요, 바로, numeric 헤더에 있는 iota() 라는 함수를 사용하는 것입니다. 이함수는 begin() 부터 end()를 0으로부터 ++한 값으로 채워넣는 함수구요, ranges 라이브러리 내에 정의된 범위 기반 알고리즘을 사용을 하면, begin 과 end를 빼버리고 v만 넣어서 사용할 수도 있습니다. 그런데 보시면, v는 선언과 초기화가 분리되어 있는 걸 보실 수가 있는데요, 별도의 함수를 만들지 않는 이상, C++에서 권장하는 선언과 동시에 초기화를 할 수가 없다는 단점이 있습니다.
  21. 그런데, ranges 라이브리는 이것에 대한 해답을 제공해주고 있는데요, 바로 view 네임스페이스 안에 있는 iota() 함수를 호출함으로써 선언과 동시에 초기화가 가능해집니다. 무슨 의미인지 아시겠죠? 0부터 ++해서 10 미만까지로 v를 초기화해라. 라는 뜻입니다. 그런데 이 iota() 리턴하는 타입은 사실 벡터가 아니에요.
  22. 사실, 범위를 뜻하는 객체가 리턴이 됩니다. 그럼 어떻게 이 범위 객체가 vector로 변환이 되는 걸까요? 그 원리는
  23. 바로 User-Defined Conversion 을 사용하는 것입니다. User Defined Conversion 을 모르시는 분들을 위해 간단히 설명을 드릴게요 이렇게 분수 클래스가 있으면, 분자랑 분모를 뜻하는 멤버변수가 있겠죠? 그런데 이 객체가 가지는 값을 부동 소수점 형태로 출력을 할려면 분자 나누기 분모한 값을 리턴을 하는 getter를 만들 수 밖에 없습니다. 사용시에도 이렇게 f.get() 이라고 함수를 호출해줘야 하는데요, 이 객체를 진짜 double 처럼 사용하고 싶으면 이렇게 해주면 됩니다.
  24. 이렇게 operator double을 선언해주면, 이 객체 f가 double 이 사용될 수 있는 곳에서 double 로 암시적 형변환이 일어나게 됩니다. 물론 형변환 후 리턴되는 값은 operator double 함수가 리턴하는 값이겠죠? 이것을 이용하면, 아까 보셨던 범위 타입에서 벡터T로의 암시적 변환이 가능해집니다.
  25. ranges라이브러리 구조도 파악할 겸 간단하게 한번 구현을 해볼게요. iota함수는 시작값과 끝값을 넣었었죠? 그 두 값을 저장할 멤버변수를 만들구요, 방금 보셨던 User-defined conversio을 이용해서 벡터T로의 암시적 변환을 구현해줍니다. 내용은 간단해요. 암시적 변환이 일어날 때, 벡터를 하나 선언을 해서, from 부터 to까지로 채운 뒤 v를 리턴하면 됩니다.
  26. 이렇게 하면 현재 보시는 것과 같은 결과를 얻으실 수가 있습니다. 그리고 우리가 원하는 최종 결과는 아래와 같은데요. 이 두 개가 같은 형태인가요? 아니죠? 뭔가가 좀 다릅니다. 보시면 이 심플 아이오타 뷰에는 템플릿 파라미터를 넣어줘야 해요. 자 여기서 퀴즈, 아래 표현식처럼 벡터를 초기화하려면, 이제 우린 무얼 해주어야 할까요. 그렇습니다. 이 simple_iota_view를 리턴하는 함수를 만들어 줘야 합니다.
  27. 그런데 그 함수를 그냥 제네릭 템플릿 함수로 만들면, 함수의 주소값을 취해야하는 상황에서는 아무리 짧은 함수더라도 인라인이 안되죠? 때문에 팩토리 함수 대신, 팩토리 펑터를 만듭니다. 이 펑터의 함수 호출 오퍼레이터를 템플릿으로 만들면, 인라인이 가능한 템플릿 함수가 되는거죠? 그래서 이 펑터는, 파라미터로 beg, end를 받아서, 이니셜라이저 리스트로 만들어서, simple_iota_view를 초기화 하여 리턴하게 됩니다. 그런데 이 simple_iota_fn 이라는 클래스는, 클래스기 때문에 호출이 불가능하죠? 때문에 이 클래스의 인스턴스를 이렇게 전역 변수로 선언해주게 됩니다. 이것이 ranges라이브러리 내의 거의 모든 함수들의 구조이구요, 이 구조를 따르면, 다음과 같이 또 각 클래스마다 조금씩 차이가 있지만, 기본 구조는 이렇게 구현체 클래스 하나 당 펑터 하나씩이 대응된다는 점입니다.
  28. 벡터를 함수호출 하나만으로 선언과 동시에 초기화를 할 수가 있습니다. 이 구조가 단지 벡터 초기화 하려고 이렇게 만든 건 아니겠죠? 굳이 이렇게 복잡한 과정을 거쳐서 벡터를 초기화 할 필요가 없어요.
  29. 이 구조는 ranges가 제공하는 여러가지 기능들을 위해 고안된 구조인데요, 크게 세 개의 구성요소로 이루어져 있는 것을 보실 수 있습니다. functor instance, functor class, 그리고 구현체 클래스로 이루어져 있는데요, 보시면 이 functor instance 뒤에 _fn 을 붙이면, functor 클래스가 나오고 뒤에 _view를 붙이면 구현체 클래스가 나오게 됩니다. 나중에 혹시 라이브러리 분석하실 일이 있으시면, 이렇게 접미어를 붙어서 검색하시면 되니 참고해주세요.
  30. 다음으로 설명드릴 것은 파이프라인 입니다. 내가 아는 그 파이프라인인가 하시는 분들도 계실 텐데요. 여러분께서 생각하시는 그 파이프라인이 맞습니다. 파이프라인은 이미 여러 곳에서 사용하고 있는 개념인데요 대표적으로 리눅스/유닉스의 파이프라인이 있지요. 리눅스 파이프라인은 바를 구분자로 삼아서, 바 왼쪽에 연산의 결과가 오른쪽 연산의 입력으로 사용되는 것을 말합니다. 오른쪽 예시를 보시면 맨 위에 cat이라는 명령어를 보실수가 있는데요, 이건 샘플이라는 파일의 내용을 문자열로 출력해라 라는 명령입니다. 그리고 그 아래 grep 이라는 명령어는 입력된 문자열 중에서 특정 패턴을 찾는 명령어입니다. 이 두개를 바로 이으면, cat으로 출력한 문자열을 grep의 입력으로 사용할 수 있는거죠. 이렇게 파이프라인으로 된 명령어들은 순서대로 읽을 수가 있습니다. 샘플의 내용 중에서 애플이란 단어를 찾아라. 이걸 기존의 C++로 표현하면 이렇게 되겠죠?
  31. 파이프라인은 Linux에만 있는 건 아닙니다. R언어의 예를 한번 더 들어볼게요. 이것도 그대로 한번 읽어보겠습니다. 옥션 데이터를 타입별로 그룹지어서 입찰금의 총 합을 구해서 퀵플롯, 이건 그래프를 만들어주는 함수에요. 그래프를 만들어서 그려라 라고 읽을 수가 있구요 이 연산의 결과는
  32. 이렇게 멋진 그래프가 됩니다. 연산들을 연결하는게 마치 블록을 쌓는 것 같죠? 이것이 바로 조합성입니다. 이제 파이프라인에 대해 어느정도 감을 잡으셨을 텐데요 C++에서 제공하는 파이프라인을 보여드리겠습니다. 바로
  33. 이것입니다. 왠지 C++같지 않죠?
  34. ranges에서 제공하는 파이프라인은 리눅스처럼 버티컬바를 사용해서, 왼쪽에서 오른쪽으로 각 연산의 결과물을 전달합니다. 이 예시는 0, 1, 2, 3, 4, 0, 1, 2, 3, 4를 반복하는 수열을 구하는 예제 이구요. 이것도 그대로 한번 읽어보죠 iota는 이미 보셨죠? 0 이상, 5미만의 수열을 만들고, 그걸 무한이 반복하게 한 뒤 그 중 앞에서 15개만 추려라 이런 뜻입니다.
  35. 기존 C++에서 사용하던 방식과 한번 비교해볼까요? for루프를 사용한 방식과 std::generate()를 사용한 방식과 한번 비교해 보겠습니다. 여기서 주의 깊게 보셔야 하는 점은, 코드가 예쁜 게 아니라요. 차이점 1. 컨테이너 이름의 개수 차이점 2. 사람의 의식의 흐름과 비슷한 // for루프를 이용한 방식 // 컨테이너를 원하는 크기로 만들고, 반복문을 이용해 내용을 직접 채워넣는다. // std::generate() 를 이용한 방식 // 컨테이너를 원하는 크기로 만들고, 내용을 채워넣을 방법을 지정한다. // ranges 를 이용한 방식 // 원하는 수열을 만들고, 그 수열로 컨테이너를 초기화한다. 그리고 컨테이너 이름 개수도 비교
  36. 파이프라인 예시를 하나만 더 들어보죠. 0이상 15미만의 범위에서, 숫자를 5개씩 그룹지어서 각 그룹의 합을 구하는 예제입니다. 코드를 보시면, 제가 방금 말씀드렸던 그대로죠.
  37. 만약 요구사항이 이와 같이 바뀌었다고 생각해보죠. 기존에는 입력값의 범위가 주어졌었는데, 이제는 출력값의 개수만 주고 알아서 구하라고 하네요. 다행히 저희는 기존에 숫자가 5개씩 그룹을 지어 합을 구한다는 것을 알고 있으므로, 그냥 입력값을 15에서 30으로 증가시키면 됩니다. 그런데 이 코드를 처음 보는 사람이라면, 숫자가 몇 개씩 그룹을 짓는지 무엇을 보고 알 수 있을까요? 아마 group_by 와 transform 함수들을 찬찬히 살펴보아야 의도를 파악할 수 있을 것입니다. ranges는 이런 요구사항의 변화가 있을 때 그렇게 해결하지 않죠.
  38. ranges 를 이용하면 이렇게 수정이 가능합니다. 첫번째 빨간 네모를 보시면, iota의 인자가 하나인 것을 보실 수가 있습니다. 이렇게 iota 의 인자가 하나이면, 인자로부터 ++를 무한히 하는 범위를 표현하게 됩니다. 지금 보시는 네모는 0부터 시작해서, ++ 하여 얻을 수 있는 무한한 수열을 의미합니다. 이것에 대해서는 조금 있다가 다시 설명 드리겠습니다. 이 오른쪽 파이프라인들의 의미를 그대로 읽자면 “0부터 시작하는 무한한 수열을, 5로 나눈 몫으로 그룹지어서, 그 그룹의 합을 구한 것을, 6개 가져와라“ 라고 읽을 수 있습니다. 여기서 중요한 점은, 구하고자 하는 값에 입력 범위가 종속되지 않았다는 점입니다. 대신, 구하고자 하는 값을 직접 넣어 원하는 결과를 얻을 수 있습니다. 왼쪽 코드와 비교해보면, 하나의 코드 블록을 더 얹은 것으로 원하는 결과를 얻을 수 있었다는 걸 보실 수 있습니다. 이것이 바로 차기 C++ 표준에서 보실 수 있는, ranges의 파이프라인을 통한 조합성 입니다. 다음은 컨테이너 출력에 대해서 다시 한번 생각해보는 시간을 가져볼까 합니다.
  39. 코드 작성 도중 특정 런타임 시점에 컨테이너 안 원소를 확인하고 싶을 때? 디버거를 붙인다 --; 컨테이너 안의 내용물을 출력한다.
  40. 이게 바로, 현시창이죠. 적어도 지금까지는 그래왔습니다.
  41. 보시면서 계속 이 auto로 받은 알엔지 라는 객체가 대체 무슨 타입일까 뭐길래 파이프라인이나 출력 같은 게 가능할까
  42. 대체 타입이 뭘까요?
  43. 먼저 개념적 타입부터 설명을 해 드릴게요. ranges 의 view 네임스페이스 안에 있는 연산들은 모두 Range<T> 라는 타입을 리턴합니다. 그런데 이 Range<T>는 실제 타입은 아니구요, 그냥 “범위”라는 개념입니다.
  44. 한번 생각을 해 보죠. 왜 이렇게 되는지? 저희가 처음에 만들었던 simple_iota() 펑터를 떠올려 보겠습니다. simple_iota()는 simple_iota_view<T>를 리턴했죠? 그럼 같은 방식으로 이런 클래스들도 만들었다고 생각해 보겠습니다. simple_take()는 simple_take_view<T>를 리턴하겠죠? 그런데 만약에 simple_iota() 의 결과가 simple_take()의 입력으로 들어가게 된다면 어떻게 될까요? simple_take_view의 T에는 simple_iota_view가 들어가게 됩니다. 이렇게 되면 무엇이 가능해지느냐, 보시면 simple_take_view와 simple_iota_view 의 조합으로 타입만으로 지금 무엇을 하려고 하는지를 알 수 있는데요. 이런것을
  45. 표현식 템플릿이라고 합니다. 좀 더 자세히 설명 드리자면, 수식 전체가 하나의 타입이 되는 것인데요, 이게 표현식 템플릿이 아니라 일반 컨테이터 벡터 타입이라고 생각해보겠습니다. 벡터는 벡턴데, 더하기 연산이 오버라이드 되어있어서 각각의 원소들, 0번원소끼리, 1번원소끼리, 2번 원소끼리 끼리끼리 더할 수 있고, 각 벡터의 사이즈는 모두 같다고 가정하겠습니다. 이 때, 이 결과물인 sum의 원소를 모두 접근하는게 아니라, 랜덤으로 몇 개만 접근한다고 해보죠. 그 몇 개를 위해서 v1 v2 v3의 모든 원소를 더하는 건 낭비죠. 이런 상황에 표현식 템플릿을 사용하면, 가독성을 trade off 하지 않고도 효율적으로 계산을 할 수가 있습니다. 바로, 참조 연산자를 오버라이드해서, 실제 원소를 참조할 때에만 더하기 연산을 하는 것입니다.
  46. ranges::view 연산들은 이렇게 표현식 템플릿으로 구현되어있습니다. 각 파이프라인에 따른 실제 타입들을 한번 typeid로 찍어보겠습니다.
  47. 자, 이 표현식도 한번 예상해 볼까요? 당연히 각 연산의 구현체 클래스, transform_view, group_by_view, iota_view 로 구성된 클래스를 볼 수 있겠죠? 그래서 이 range를 typeid로 출력하면
  48. 이렇게 나옵니다. 오.. 뭐지? 아까는 예쁘게 나왔는데.
  49. 위에는 없고 아래는 있는게 무엇일까요? 선착순 한명 책 선물 드리겠습니다. 바로 람다입니다. 기억하실지 모르겠지만, 코션트랑 썸은 람다를 auto로 받은 것입니다. 이 람다들이 group_by에 템플릿 파라미터로 들어가면서, 타입이 굉장히 복잡해지게 되는데요, clang typeid 에서는 이런 경우에 이렇게 출력 형식이 깨지는 경우가 생기더라구요? 이럴때에는
  50. 이펙티브 모던 C++ 항목 4에 설명된 TypeDisplayer 를 이용해서 컴파일 에러로 타입을 찍어보면
  51. 이렇게 예쁘게 타입이 나오는 것을 확인하실 수 있습니다. 심지어 람다가 선언된 파일이랑 몇번째 줄 몇번째 열에 선언 되었는지 까지 알 수 있습니다.
  52. 그럼 여기서 또하나의 의문점을 던져볼게요. 조합성은 코드를 조립할 수 있는 성질이라고 말씀드렸는데요, 어떤 함수들을 조합하느냐에 따라서 range 의 실제타입은 매번 바뀌는데 어떻게 Range<T> 라는 추상적 타입을 계속 유지할 수 있을까요?
  53. 그 해답은 모든 구현체 클래스가, 공통된 인터페이스를 상속받기 때문입니다. 즉, range<T>는 일종의 인터페이스인 셈이죠.
  54. 마지막으로 ranges를 통해 가능해지는 표현식에 대해 알아보겠습니다.
  55. 먼저 자연수, 초등학생의 마음가짐으로 자연수의 정의에 대해서 다시 한번 생각해보겠습니다. (정의를 읽고) (수학표현을 읽고) 이런식으로 표현을 하죠? c++에서 기존에 자연수를 표현하던 방법은 이러했습니다. 주석으로 표시를 하거나, 아니면 코드에 어설트를 넣는거죠. 그래도 이 “무한한 범위“ 라는 정의를 충족시키지는 못하죠?
  56. ranges는 앞서 보셨다시피, iota나 cycle 등의 view연산들을 통해서 무한한 범위를 표현할 수가 있습니다. 지금 보시는 ints 는 iota를 int 로 특수화 한, 그냥 특수화만 한 함수입니다. int 밖에 못넣어요. 이 ints에 1을 넣으면, 1부터 무한히 증가하는 정수를 표현하게 되는데요, 이게 바로 자연수죠? 1부터 1씩 더해서 얻을 수 있는 수들의 집합이 됩니다. 그럼 이 naturals를 cout으로 출력해버리면 어떻게 될까요? 무한루프를 돌게 되겠죠? 그러므로 이런 무한 수열은 반드시 take와 같은 한정함수와 함께 사용해야 합니다.
  57. 제가 마지막으로 소개해 드릴 것은 range 컴프리헨션인데요. 파이썬이나 하스켈의 list 컴프리헨션과 대응되는 개념입니다. 수학시간에 배웠던 것처럼 range를 한번 표현해볼게요. 집합 S는 2 * x 바 x는 자연수. 이러면 집합 S는 자연수의 각각의 원소를 2배 한 수들의 집합이 되는거죠? 이걸 지금까지 소개했던 방식을 사용하면, 위와 같이 표현을 할 수가 있습니다. 각각의 원소를 직접 transform 하는 방식이죠. 그런데 컴프리헨션이란 보통, 이미 존재하는 리스트에서 새로운 리스트를 만드는 것을 의미합니다. 위에건 새로운 리스트를 만든다기보단, 기존 리스트를 수정한다는 개념에 가깝죠? 이럴 때 사용하는 것이 view::for_each입니다.
  58. view::for_each 는 입력으로 들어온 범위를 순회하면서 새로운 범위를 만드는 함수입니다. ranges::yield 로 리턴된 값이 새로운 범위의 원소가 되게 됩니다. 이것을 range comprehension 이라고 부릅니다.
  59. 그리고 이 view::for_each 는 특별하기 때문에, 아래처럼 연산자 오버로딩을 해두었는데요 이 두 코드는 같은 동작을 하는 코드입니다.
  60. 이 range comprehension 을 이용하면 무엇을 할 수 있냐면, 파이썬이랑 똑같아요, 기존에 존재하는 범위로부터, 새로운 범위를 쉽게 만들수가 있습니다. 이 예제는 피타고라스 수를 구하는 예제인데요, 아까 yield()로 리턴한 값들이 새로운 범위의 원소가 된다고 말씀 드렸었죠? yield_if() 는 이 앞에 있는 표현식이 참이면, 뒤에 있는 값을 새로운 범위에 추가하고 앞에 표현식이 거짓이면 그냥 건너뛰는 동작을 합니다. 이렇게 만들어진 triples 객체에 take 로 몇 개를 가져올지만 지정해주면 원하는 개수의 피타고라스 수를 구할 수가 있습니다. 이것을 삼중 for문을 이용해서 구현하는 과정을 생각해보면, 이 표현식이 얼마나 간단한지 어렴풋이 느끼실 것입니다. 에릭 니블러씨는 이 예제를 구현하고 Haskell and Python have list comprehensions. C# has LINQ and yield. And now C++ has range comprehensions. 하스켈과 파이썬은 리스트 컴프리헨션이 있고, C#은 링큐와 yield가 있다. 그리고 지금, C++에는 레인지 컴프리헨션이 있다. 라고 말했었습니다. 얼마나 유용한지는 여러분의 판단에 맡기겠습니다.
  61. 지금까지 저는 ranges 라이브러리 내에 있는 View namespace 에 대해서만 설명을 드렸었는데요 사실 중요한 namespace 가 하나 더 있습니다. 바로 Action 입니다.
  62. view namespace 는 데이터를 특정 --- 연산들이 들어있습니다. 시점만 변경하는 거기 때문에, 데이터 원본을 직접 수정하거나, 복사하지 않는 것이 특징입니다. 주로 컨테이너 순회 방법을 바꾸거나, 각 원소를 조금 다른 방식으로 읽는다거나 하는 연산들이 이 namespace 에 들어있습니다. 그리고 아까 설명드렸던 것처럼, 표현식 템플릿을 통해서 느긋한 계산이 가능합니다. 반면에 action namespace 에는 데이터 원본을 직접 수정하는 연산들이 들어있습니다.
  63. 이건 view namespace 의 연산들이구요. 많죠?
  64. 이건 action namespace 의 연산들입니다. 그냥 나중에 참고하시라고 넣었습니다.
  65. action에 대해서 좀더 설명드리면, 이렇게 stl 처럼 v의 원소를 직접 셔플한다거나 할 때 사용하는 함수들의 집합입니다. 그런데 stl과 다른점은, 이렇게 파이프라이닝이 가능하다는 점입니다.
  66. 데이터 원본을 직접 수정할 수 있으니까, 복사나 이동에 민감할 수 밖에 없는데요, 파이프라인을 통해서도 복사나 이동을 할 수 있구요
  67. action의 진면목은, view와 함께 사용했을 때 나오죠. view는 특정 데이터를 순회하는 방법을 바꿀 수 있다고 말씀드렸었는데요, 기존의 데이터에 대한 특정 순회방식을 하나의 “범위“ 로 인식하기 때문에 마치 이 범위가 하나의 컨테이너인 것 처럼 action함수를 사용할 수 있습니다. 이 예제를 보시면 v를 0에서 10으로 초기화를 하고 있는데요 이 v를 , stride, 성큼성큼 걷다 라는 뜻인데요, 이렇게 쓰면 2개씩 건너뛰면서 순회하게 됩니다. 0, 2, 4, 8 이런 식으로 순회하겠죠? 이 0, 2, 4, 8을 하나의 범위로 인식해서 액션의 셔플을 돌리면 이렇게 짝수만 셔플된 컨테이너 v를 얻을 수가 있습니다. 기존에 이런 구현을 하기 위해서는 추가적인 컨테이너가 꼭 필요했는데요, range를 사용하면 훨씬 더 간편하게 구현할 수 있습니다.
  68. 드디어 서머리네요
  69. Range를 사용함으로써의 장점은 명확합니다. 범위 기반 알고리즘이나 파이프라인, 느긋한 계산, range 컴프리헨션 등을 이용한 간결하고 강력한 표현이 가능해집니다. 또 표준 컨…. ---- 반면, 단점은 view같은 경우 표현식 템플릿은 결국 컴파일 타임에 만들어지기 때문에 상대적으로 긴 컴파일 타임을 가질 수 밖에 없습니다. 이는 현재 개선해야 할 사항으로 작업큐에 올라가 있는 것으로 알고 있습니다.
  70. 마지막으로 표준화 작업으로 지금도 고생중이실 에릭 니블러씨께 감사하다는 말씀을 전하면서 이 발표를 마치겠습니다. 감사합니다.