diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 858b592..3c58bf3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,6 +37,7 @@ jobs: make concat.html make P2483R0.html make ref_wrapper_common_ref.html + make any_view.html - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@v4.2.2 diff --git a/README.md b/README.md index dd245fc..a173b60 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # cpp_papers - [`views::concat`](https://huixie90.github.io/cpp_papers/generated/concat) -- [`common_reference_t` of `reference_wrapper` should be a reference type](https://huixie90.github.io/cpp_papers/generated/ref_wrapper_common_ref) \ No newline at end of file +- [`common_reference_t` of `reference_wrapper` should be a reference type](https://huixie90.github.io/cpp_papers/generated/ref_wrapper_common_ref) +- [`any_view`](https://huixie90.github.io/cpp_papers/generated/any_view) diff --git a/any_view.md b/any_view.md new file mode 100644 index 0000000..dda20e8 --- /dev/null +++ b/any_view.md @@ -0,0 +1,171 @@ +--- +title: "`any_view`" +document: PXXXXR0 +date: 2024-05-27 +audience: SG9, LEWG +author: + - name: Hui Xie + email: + - name: S. Levent Yilmaz + email: + - name: Louis Dionne + email: +toc: true +--- + +# Revision History + +## R0 + +- Initial revision. + +# Abstract + +This paper proposes a new type-erased `view`: `any_view`. + +# Motivation and Examples + +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, + +```cpp +// in MyClass.hpp +class MyClass{ + std::unordered_map widgets_; +public: + 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: + +```cpp +// in MyClass.hpp +class MyClass{ + std::unordered_map widgets_; +public: + /* 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`, 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`, 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`. + +# Design + +## What Parameters can Users Configure? + +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: + +- Is it `copyable`? +- Does it own the function + +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: + +- Is it an `input_range`, `forward_range`, `bidirectional_range`, `random_access_range`, or a `contiguous_range` ? +- Is the range `copyable` ? +- Is the iterator `copyable` ? +- Is it a `sized_range` ? +- Is it a `borrowed_range` ? +- Is it a `common_range` ? +- What is the `range_reference_t` ? +- What is the `range_value_t` ? +- What is the `range_rvalue_reference_t` ? +- What is the `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.Range `boost::ranges::any_range` + +Here is the type declaration + +```cpp +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. + +### range-v3 `ranges::views::any_view` + +Here is the type declaration + +```cpp +template +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. + +# Implementation Experience + +# Wording + +## Feature Test Macro + +``` + +--- +references: +--- + + diff --git a/impl/CMakeLists.txt b/impl/CMakeLists.txt index 6980575..c7440ae 100644 --- a/impl/CMakeLists.txt +++ b/impl/CMakeLists.txt @@ -11,7 +11,7 @@ endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CXX_STANDARD_REQUIRED TRUE) - #add_compile_options(-Wall -Wextra -Werror -stdlib=libc++ -std=c++2b) + add_compile_options(-Wall -Wextra -Werror -stdlib=libc++ -std=c++23) #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CXX_STANDARD_REQUIRED TRUE) @@ -49,6 +49,15 @@ file(GLOB_RECURSE ref_wrapper_hdr RELATIVE ${CMAKE_SOURCE_DIR} ref_wrapper/*.hp add_library(ref_wrapper INTERFACE) # header-only library target_sources(ref_wrapper INTERFACE ${ref_wrapper_hdr}) +# +----------+ +# | ANY_VIEW | +# +----------+ +# for now, all in one file. ideally if things grow, consider splitting CMake logic to individual directories, via +# add_subdirectory(any_view) etc. +file(GLOB_RECURSE any_view_hdr RELATIVE ${CMAKE_SOURCE_DIR} any_view/*.hpp) +add_library(any_view INTERFACE) # header-only library +target_sources(any_view INTERFACE ${any_view_hdr}) + # +---------------+ # | CONCAT-TEST | @@ -74,4 +83,16 @@ add_executable(ref_wrapper-test) target_sources(ref_wrapper-test PRIVATE ${ref_wrapper_test_src}) target_link_libraries(ref_wrapper-test PRIVATE Catch2::Catch2WithMain) target_include_directories(ref_wrapper-test PRIVATE ref_wrapper) -#target_link_libraries(ref_wrapper-test PRIVATE range-v3) \ No newline at end of file +#target_link_libraries(ref_wrapper-test PRIVATE range-v3) + + +# +---------------+ +# | ANY-VIEW-TEST | +# +---------------+ + +file(GLOB_RECURSE any_view_test_src RELATIVE ${CMAKE_SOURCE_DIR} CONFIGURE_DEPENDS any_view/test/*.cpp) +add_executable(any_view-test) +target_sources(any_view-test PRIVATE ${any_view_test_src}) +target_link_libraries(any_view-test PRIVATE Catch2::Catch2WithMain) +target_include_directories(any_view-test PRIVATE any_view ref_wrapper) +target_link_libraries(any_view-test PRIVATE range-v3) diff --git a/impl/any_view/any_view.hpp b/impl/any_view/any_view.hpp new file mode 100644 index 0000000..25ee6a0 --- /dev/null +++ b/impl/any_view/any_view.hpp @@ -0,0 +1,6 @@ +#ifndef LIBCPP__RANGE_ANY_VIEW_HPP +#define LIBCPP__RANGE_ANY_VIEW_HPP + +namespace std::ranges {} + +#endif diff --git a/impl/any_view/test/example.cpp b/impl/any_view/test/example.cpp new file mode 100644 index 0000000..e69de29