diff --git a/.gitignore b/.gitignore index ea60d45..a8a3d14 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ cxx-bucket-vs*/ .vs/ .vscode/ .cache/ -generated/ out/ .gdb_history build-* diff --git a/generated/any_view.html b/generated/any_view.html new file mode 100644 index 0000000..4e0b27c --- /dev/null +++ b/generated/any_view.html @@ -0,0 +1,659 @@ + + +
+ + + + +any_view
Document #: | +PXXXXR0 | +
Date: | +2024-05-27 | +
Project: | +Programming Language C++ | +
Audience: | +
+ SG9, LEWG + |
+
Reply-to: | +
+ Hui Xie <hui.xie1990@gmail.com> + S. Levent Yilmaz <levent.yilmaz@gmail.com> + Louis Dionne <ldionne.2@gmail.com> + |
+
This paper proposes a new type-erased
+view
:
+any_view
.
From C++20, a lot of view
s
+have been introduced into the standard library. With these
+view
s, it is quite easy to
+create a range of objects. For example,
// in MyClass.hpp
+class MyClass{
+::unordered_map<Key, Widget> widgets_;
+ stdpublic:
+auto getWidgets () const {
+ return widgets_ | std::views::values
+ | std::views::filter(myFilter);
+ }
+
+// other members
+ };
This works if one writes everything in the header. However, in +practice, in user’s non-templated code bases, headers usually contain +the declarations, and implementation details are hidden in the +implementation cpp files:
+// in MyClass.hpp
+class MyClass{
+::unordered_map<Key, Widget> widgets_;
+ stdpublic:
+/* what should be the return type? */ getWidgets() const;
+
+// other members
+ };
+
+// in MyClass.cpp
+
+/* what should be the return type? */ MyClass::getWidgets() const {
+return widgets_ | std::views::values
+ | std::views::filter(myFilter);
+ }
However, it is almost impossible to spell the correct type of the
+return value. And in fact, to allow the flexibility of future changes,
+we don’t actually want to spell that particular type of the
+view
. We need some type-erased
+helper that can easily written in the header and can accept any concrete
+type of view
.
There is something very similar: Lambdas are extremely useful but one
+cannot spell their types. When we need a type in the API boundary, we
+often use the type-erased type
+std::function
.
Prior to C++20, that return type is often
+std::vector<Widget>
, which
+enforces ownership. This also enforces implementations to make copy of
+all the Widget
s. On the other
+hand, the caller may not care about the ownership at all and all it
+wants is to iterate through them.
After C++20, that return type is sometimes
+std::span<Widget>
, which
+explicitly says the caller does not want the ownership. However, one
+major caveat is that this enforces contiguous memory. As a result,
+implementations cannot return the
+view
pipelines as shown in the
+example.
This paper proposes a new type-erased view
+any_view
so that the above
+function’s return type can be spelled as any_view<const Widget&>
.
Let’s take std::function
as
+an example. Its interface seems extremely simple: the
+operator()
and users only need
+to configure the return type and argument types. Well, it is a bit more
+than that:
copyable
?After answering all these questions we ended up with three types +now:
+function
move_only_function
function_ref
For any_view
, it is much much
+more complex than that:
input_range
,
+forward_range
,
+bidirectional_range
,
+random_access_range
, or a
+contiguous_range
?copyable
?copyable
+?sized_range
?borrowed_range
+?common_range
?range_reference_t
?range_value_t
+?range_rvalue_reference_t
?range_difference_t
?We can easily get combinatorial explosion of types if we follow the
+same approach of std::function
.
+So let’s look at the prior arts.
boost::ranges::any_range
Here is the type declaration
+template<
+class Value
+ class Traversal
+ , class Reference
+ , class Difference
+ , class Buffer = any_iterator_default_buffer
+ , >
+class any_range;
It asks users to put
+range_reference_t
,
+range_value_t
and
+range_difference_t
.
+Traversal
is equivalent to
+iterator_concept
so it decides
+the traversal category of the range. It does not need to configure
+copyable
,
+borrowed_range
and
+common_range
because all
+BOOST.Range ranges are copyable
,
+borrowed_range
and
+common_range
.
+sized_range
and
+range_rvalue_reference_t
are not
+considered.
ranges::views::any_view
Here is the type declaration
+template<typename Ref, category Cat = category::input>
+struct any_view;
Here Cat
handles both the
+traversal category and
+sized_range
.
+Ref
is the
+range_reference_t
. It does not
+allow users to configure the
+range_value_t
,
+range_difference_t
,
+borrowed_range
and
+common_range
.
+copyable
is mandatory in
+range-v3.
```
+ +