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
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.6k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
710
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.3k
Making Projects Easy
brettharned
120
6.8k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
720
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
Claude Code のすすめ
schroneko
67
230k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
560
Ruling the World: When Life Gets Gamed
codingconduct
0
380
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.3k
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.1k
Leo the Paperboy
mayatellez
10
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 となる。