Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Rangeアダプタを作る
Search
Tetsuro Matsumura
December 21, 2024
310
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Rangeアダプタを作る
C++MIX #12
https://cppmix.connpass.com/event/337028/
Tetsuro Matsumura
December 21, 2024
Featured
See All Featured
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
260
From π to Pie charts
rasagy
0
360
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
510
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
65k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Documentation Writing (for coders)
carmenintech
77
5.5k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
270
WCS-LA-2024
lcolladotor
0
820
The Invisible Side of Design
smashingmag
301
52k
Being A Developer After 40
akosma
91
590k
Leo the Paperboy
mayatellez
8
2.3k
Transcript
Rangeアダプタを作る 2024-12-20 C++ MIX #12 Tetsuro Matsumura
目次 1. Rangeアダプタ 2. Rangeアダプタの性質 3. Rangeアダプタオブジェクト 4. Rangeアダプタクロージャオブジェクトとなる要件 5.
Rangeアダプタクロージャオブジェクトを作る 6. Rangeアダプタオブジェクトを作る 7. ジェネレーターでviewを作る 1
Rangeアダプタ 2 C++20でRangeアダプタが導入され、様々なRangeを簡単に加工できるようになった。 using namespace std; vector v {1, 2,
3, 4, 5}; // reverse: 逆順にする println("{}", v | views::reverse) // [5, 4, 3, 2, 1] // take(n): 先頭からn個とる println("{}", v | views::reverse | views::take(3)) // [5, 4, 3] これを作りたい C++20ではRangeアダプタの性質をすべて満たす汎用的な実装は不可能 ⚫ 何がRangeアダプタとなるのかが規定されていない ⚫ 特定の処理系における実装は、標準ライブラリを真似すればできる C++23では可能
Rangeアダプタの性質 3 Rangeアダプタクロージャオブジェクト(RACO): ⚫ rangeを受け取る単項関数オブジェクト (C++23) ⚫ パイプライン記法で、 | 演算子の右側に現れるオブジェクト
⚫ r をrange, raco をRangeアダプタクロージャオブジェクトとするとき、 r | raco と raco® は等しい Rangeアダプタオブジェクト(RAO): ⚫ 第1引数に viewable_range をとり view を返すカスタマイゼーションポイントオブ ジェクト ⚫ range 以外の引数をすべて渡すと、Rangeアダプタクロージャオブジェクトを返す ⚫ r をrange, rao をRangeアダプタオブジェクトとするとき、 r | rao(…) と rao(r, …) と rao(…)(r) は等しい
Rangeアダプタオブジェクト 4 using namespace std; vector v {1, 2, 3,
4, 5}; // 追加の引数を受け取る前のtakeはRAO、take(3)の値はRACO v | views::take(3); // RACO take(3) に対して関数記法でvを渡す views::take(3)(v); // RAO take に対してすべての引数を渡す views::take(v, 3); // reverse は追加の引数がなく、初めからRACO v | views::reverse; views::reverse(v);
Rangeアダプタクロージャオブジェクトとなる要件 5 C++23では、型 T のオブジェクト t がRACOとなる要件が定められた。 ⚫ t はRangeを受け取る単項関数オブジェクトである
⚫ T は std::ranges::range_adaptor_closure<T> の派生クラスである ⚫ T 以外の型 U に対して、 T は std::ranges::range_adaptor_closure<U> の 派生クラスではない ⚫ T は range のモデルではない 以上の要件を満たせば、自作したクラスのオブジェクトでもRACOになる。 range_adaptor_closure 自体は空のクラス: namespace std::ranges { template<class D> requires is_class_v<D> && same_as<D, remove_cv_t<D>> class range_adaptor_closure { }; }
Rangeアダプタクロージャオブジェクトを作る 6 RACOの実装例: class my_adaptor_closure : public std::ranges::range_adaptor_closure<my_adaptor_closure> { public:
template <std::ranges::viewable_range R> constexpr auto operator()(R&& r) const { return my_adaptor_view(r); // rをラップしたviewを作って返す処理 } }; constexpr my_adaptor_closure my_adaptor; // 使用例 // v | my_adaptor my_adptor_viewクラスはあらかじめ定義しておく
Rangeアダプタクロージャオブジェクトを作る 7 Rangeを受け取る単項関数オブジェクトをRACOに変換する例(P2387R3): template <typename F> class closure_t : public
std::ranges::range_adaptor_closure<closure_t<F>> { F f; public: constexpr closure_t(F f) : f(f) { } template <std::ranges::viewable_range R> constexpr auto operator()(R&& r) const { return f(std::forward<R>(r)); } }; このようなものを用意すれば、ラムダ式で簡単にRACOを定義できる。
Rangeアダプタクロージャオブジェクトを作る 8 例: 標準の join_view を作る別のRACOを定義する (viewまで作るのは大変なので) inline constexpr closure_t
user_defined_join = []<std::ranges::viewable_range R> (R&& r) { return std::ranges::join_view(std::forward<R>(r)); }; // 使用例 // v | user_defined_join;
Rangeアダプタオブジェクトを作る 9 追加の引数があるビューの場合、それらを受け取ってRACOを構築するためには Rangeアダプタオブジェクト(RAO) を定義する必要がある。 関数オブジェクトをラップしてRAOを定義する例 (P2387R3, closure_t は前述の定義): template
<typename F> class adaptor_t { F f; public: constexpr adaptor_t(F f) : f(f) { } template <typename... Args> constexpr auto operator()(Args&&... args) const { if constexpr (std::invocable<F const&, Args...>) { // すべての引数が渡されたら、持っている関数を呼ぶ return f(std::forward<Args>(args)...); } else { // range 以外の引数が渡されたら、後ろから部分適用してRACOを作る return closure_t(std::bind_back(f, std::forward<Args>(args)...)); } } };
Rangeアダプタオブジェクトを作る 10 例: 標準の take_view を作る別のRAOを定義する inline constexpr adaptor_t user_defined_take
= []<std::ranges::viewable_range R> (R&& r, std::range_difference_t<R> d) { return std::ranges::take_view(std::forward<R>(r), d); }; // 使用例 // v | user_defined_take(3);
ジェネレーターでviewを作る 11 std::generator は view であるため、ジェネレーターを使えば簡単に実装できる。 inline constexpr closure_t my_all
= []<std::ranges::viewable_range R> (R&& r) -> std::generator<std::ranges::range_value_t<R>> { // 受け取ったRangeの要素をそのまま出力する for(auto&& i : r1) co_yield i; }; // 使用例 // v | my_all; ここでは、ラムダ式でジェネレーターを定義し、 前述のclosure_tでラップしてRACOにしている。 ジェネレーターはinput_rangeにしかならないので、Rangeの性質としてはそれほど良く ない。元のRangeの性質を温存するにはviewクラスの実装を頑張るしかない。
ジェネレーターでviewを作る 12 concat の実装例 inline constexpr adaptor_t concat = []<std::ranges::viewable_range
R1, std::ranges::viewable_range R2> requires std::same_as< std::ranges::range_value_t<R1>, std::ranges::range_value_t<R2>> (R1&& r1, R2&& r2) -> std::generator<std::ranges::range_value_t<R1>> { // 受け取った2つのRangeの要素を出力する for(auto&& i : r1) co_yield i; for(auto&& i : r2) co_yield i; }; // 使用例 // v1 | concat(v2);
13 まとめ ⚫ C++23では、オブジェクトがRangeアダプタクロージャオブジェクトと なるための要件が定められた。 ⚫ std::ranges::range_adaptor_closure を継承することで、 Rangeアダプタクロージャオブジェクトを自作できる。 ⚫
ジェネレーターはviewなので、Rangeアダプタの自作において便利である。 ただし、input_range となる。