From 9e3a0dc3eb81f0e9b30eb34930239fb1cadcbb7f Mon Sep 17 00:00:00 2001 From: Patrick Roberts Date: Sun, 9 Feb 2025 01:58:23 -0600 Subject: [PATCH] Add some documentation to readme --- README.md | 421 ++++++++++++++++++++ include/beman/any_view/any_view_options.hpp | 4 +- tests/beman/any_view/constexpr.test.cpp | 17 +- 3 files changed, 436 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 665a7c1..a5de473 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,38 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Usage +```cpp +#include + +namespace bav = beman::any_view; +using opt = bav::any_view_options; + +template +using proxy_any_view = bav::any_view; + +constexpr auto sum(proxy_any_view> views) { + auto result = 0; + + for (auto view : views) { + for (const auto value : view) { + result += value; + } + } + + return result; +} + +static_assert(10 == sum(std::vector{std::vector{1, 2}, std::vector{3, 4}})); + +constexpr auto iota(int n) { return std::views::iota(1) | std::views::take(n); }; + +static_assert(35 == sum(iota(5) | std::views::transform(iota))); + +static_assert(22 == sum(std::vector{iota(1), iota(3), iota(5)})); +``` + +Full code can be found in [./tests/beman/any_view/constexpr.test.cpp](./tests/beman/any_view/constexpr.test.cpp). + `std::ranges::any_view` is a class template that provides a type-erased interface for `std::ranges::view`. It may additionally model other concepts like `std::ranges::contiguous_range`, `std::ranges::sized_range`, `std::ranges::borrowed_range`, and `std::copyable` depending on the instantiation. @@ -45,6 +77,395 @@ target_link_libraries(yourlib PUBLIC beman::any_view) +## Reference + +### Designs + +
+ENUM (default) + +Synopsis: + +```cpp +#define BEMAN_ANY_VIEW_USE_ENUM() 1 +#define BEMAN_ANY_VIEW_USE_TRAITS() 0 +#define BEMAN_ANY_VIEW_USE_NAMED() 0 + +namespace beman::any_view { + +enum class any_view_options { + input = 0b0000000, + forward = 0b0000001, + bidirectional = 0b0000011, + random_access = 0b0000111, + contiguous = 0b0001111, + sized = 0b0010000, + borrowed = 0b0100000, +#if BEMAN_ANY_VIEW_USE_COPYABLE() + copyable = 0b1000000, +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + move_only = 0b1000000, +#endif +}; + +constexpr auto operator|(any_view_options l, any_view_options r) noexcept -> any_view_options; +constexpr auto operator&(any_view_options l, any_view_options r) noexcept -> any_view_options; +constexpr auto operator^(any_view_options l, any_view_options r) noexcept -> any_view_options; + +constexpr auto operator~(any_view_options o) noexcept -> any_view_options; + +constexpr auto operator|=(any_view_options& l, any_view_options r) noexcept -> any_view_options&; +constexpr auto operator&=(any_view_options& l, any_view_options r) noexcept -> any_view_options&; +constexpr auto operator^=(any_view_options& l, any_view_options r) noexcept -> any_view_options&; + +template , + class DiffT = std::ptrdiff_t> +class any_view : public std::ranges::view_interface> { + class iterator; // exposition-only + class sentinel; // exposition-only + + using size_type = /*make-unsigned-like-t*/; // exposition-only + + static constexpr bool copyable = // exposition-only +#if BEMAN_ANY_VIEW_USE_COPYABLE() + (OptionsV & any_view_options::copyable) == any_view_options::copyable; +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + (OptionsV & any_view_options::move_only) != any_view_options::move_only; +#endif + + static constexpr bool sized = // exposition-only + (OptionsV & any_view_options::sized) == any_view_options::sized; + + public: + template RangeT> + constexpr any_view(RangeT&& range); + + constexpr any_view(const any_view&) requires copyable; + + constexpr any_view(any_view&&) noexcept; + + constexpr auto operator=(const any_view&) -> any_view& requires copyable; + + constexpr auto operator=(any_view&&) noexcept -> any_view&; + + constexpr auto begin() -> iterator; + + constexpr auto end() -> sentinel; + + constexpr auto size() const -> size_type requires sized; +}; + +} // namespace beman::any_view + +template +inline constexpr bool std::ranges::enable_borrowed_range< + beman::any_view::any_view> = + (OptionsV & beman::any_view::any_view_options::borrowed) == beman::any_view::any_view_options::borrowed; +``` + +
+ +
+TRAITS + +Synopsis: + +```cpp +#define BEMAN_ANY_VIEW_USE_ENUM() 0 +#define BEMAN_ANY_VIEW_USE_TRAITS() 1 +#define BEMAN_ANY_VIEW_USE_NAMED() 0 + +namespace beman::any_view { + +struct default_range_traits {}; + +template +struct range_traits { + using iterator_concept = /*range-concept-t*/; + using reference_type = std::ranges::range_reference_t; + using rvalue_reference_type = std::ranges::range_rvalue_reference_t; + using difference_type = std::ranges::range_difference_t; + + static constexpr bool sized = std::ranges::sized_range; + static constexpr bool borrowed = std::ranges::borrowed_range; +#if BEMAN_ANY_VIEW_USE_COPYABLE() + static constexpr bool copyable = std::copyable; +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + static constexpr bool move_only = not std::copyable; +#endif +}; + +template +class any_view : public std::ranges::view_interface> { + class iterator; // exposition-only + class sentinel; // exposition-only + + using difference_type = /*difference-type-or-t*/; // exposition-only + using size_type = /*make-unsigned-like-t*/; // exposition-only + + static constexpr bool copyable = // exposition-only +#if BEMAN_ANY_VIEW_USE_COPYABLE() + /*copyable-or-v*/; +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + not /*move-only-or-v*/; +#endif + + static constexpr bool sized = /*sized-or-v*/; // exposition-only + + public: + template RangeT> + constexpr any_view(RangeT&& range); + + constexpr any_view(const any_view&) requires copyable; + + constexpr any_view(any_view&&) noexcept; + + constexpr auto operator=(const any_view&) -> any_view& requires copyable; + + constexpr auto operator=(any_view&&) noexcept -> any_view&; + + constexpr auto begin() -> iterator; + + constexpr auto end() -> sentinel; + + constexpr auto size() const -> size_type requires sized; +}; + +} // namespace beman::any_view + +template +inline constexpr bool std::ranges::enable_borrowed_range> = + /*borrowed-or-v*/; +``` + +
+ +
+NAMED + +Synopsis: + +```cpp +#define BEMAN_ANY_VIEW_USE_ENUM() 0 +#define BEMAN_ANY_VIEW_USE_TRAITS() 0 +#define BEMAN_ANY_VIEW_USE_NAMED() 1 + +namespace beman::any_view { + +template +struct type_t { + using type = T; +}; + +template +inline constexpr type_t type{}; + +template , + class DiffT = std::ptrdiff_t> +struct any_view_options { + type_t reference_type; + type_t iterator_concept = {}; + bool sized = false; +#if BEMAN_ANY_VIEW_USE_COPYABLE() + bool copyable = false; +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + bool move_only = false; +#endif + bool borrowed = false; + type_t rvalue_reference_type = {}; + type_t difference_type = {}; +}; + +template }> +class any_view : public std::ranges::view_interface> { + class iterator; // exposition-only + class sentinel; // exposition-only + + using difference_type = decltype(OptionsV.difference_type)::type; // exposition-only + using size_type = /*make-unsigned-like-t*/; // exposition-only + + static constexpr bool copyable = // exposition-only +#if BEMAN_ANY_VIEW_USE_COPYABLE() + OptionsV.copyable; +#elif BEMAN_ANY_VIEW_USE_MOVE_ONLY() + not OptionsV.move_only; +#endif + + static constexpr bool sized = OptionsV.sized; // exposition-only + + public: + template RangeT> + constexpr any_view(RangeT&& range); + + constexpr any_view(const any_view&) requires copyable; + + constexpr any_view(any_view&&) noexcept; + + constexpr auto operator=(const any_view&) -> any_view& requires copyable; + + constexpr auto operator=(any_view&&) noexcept -> any_view&; + + constexpr auto begin() -> iterator; + + constexpr auto end() -> sentinel; + + constexpr auto size() const -> size_type requires sized; +}; + +} // namespace beman::any_view + +template +inline constexpr bool std::ranges::enable_borrowed_range> = + OptionsV.borrowed; +``` + +
+ +### Options + +
+COPYABLE (default) + +Synopsis: + +```cpp +#define BEMAN_ANY_VIEW_USE_COPYABLE() 1 +#define BEMAN_ANY_VIEW_USE_MOVE_ONLY() 0 +``` + +
+ +
+MOVE_ONLY + +Synopsis: + +```cpp +#define BEMAN_ANY_VIEW_USE_COPYABLE() 0 +#define BEMAN_ANY_VIEW_USE_MOVE_ONLY() 1 +``` + +
+ +## Building + +### CMake configuration variables + +```text +-DBEMAN_ANY_VIEW_DESIGN=ENUM +-DBEMAN_ANY_VIEW_DESIGN=TRAITS +-DBEMAN_ANY_VIEW_DESIGN=NAMED + +-DBEMAN_ANY_VIEW_OPTION=COPYABLE +-DBEMAN_ANY_VIEW_OPTION=MOVE_ONLY +``` + +There are workflows available in CMake presets such as `gcc-debug`: + +```bash +cmake --workflow --preset gcc-debug +``` + +Alternatively you can manually configure, build, and test with CMake and CTest: + +```bash +cmake -B build +cmake --build build +ctest --test-dir build +``` + +Possible output: + +```text +Executing workflow step 1 of 3: configure preset "gcc-debug" + +Preset CMake variables: + + BEMAN_BUILDSYS_SANITIZER="MaxSan" + CMAKE_BUILD_TYPE="Debug" + CMAKE_EXPORT_COMPILE_COMMANDS:BOOL="TRUE" + CMAKE_TOOLCHAIN_FILE="cmake/gnu-toolchain.cmake" + +-- The CXX compiler identification is GNU 15.0.0 +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/g++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- The C compiler identification is GNU 15.0.0 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/gcc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Found Python3: /usr/bin/python3.12 (found version "3.12.7") found components: Interpreter +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success +-- Found Threads: TRUE +-- Configuring done (2.7s) +-- Generating done (0.0s) +-- Build files have been written to: /home/patrick/projects/any_view/build/gcc-debug + +Executing workflow step 2 of 3: build preset "gcc-debug" + +[12/12] Linking CXX executable tests/beman/any_view/beman.any_view.tests.constexpr + +Executing workflow step 3 of 3: test preset "gcc-debug" + +Test project /home/patrick/projects/any_view/build/gcc-debug + Start 1: ConceptsTest.iterator_concept + 1/21 Test #1: ConceptsTest.iterator_concept ................... Passed 0.01 sec + Start 2: ConceptsTest.sized_concept + 2/21 Test #2: ConceptsTest.sized_concept ...................... Passed 0.01 sec + Start 3: ConceptsTest.borrowed_concept + 3/21 Test #3: ConceptsTest.borrowed_concept ................... Passed 0.01 sec + Start 4: ConceptsTest.copyable_concept + 4/21 Test #4: ConceptsTest.copyable_concept ................... Passed 0.01 sec + Start 5: ConstexprTest.sum_vector_of_vector + 5/21 Test #5: ConstexprTest.sum_vector_of_vector .............. Passed 0.01 sec + Start 6: ConstexprTest.sum_transform_view_of_iota_view + 6/21 Test #6: ConstexprTest.sum_transform_view_of_iota_view ... Passed 0.01 sec + Start 7: ConstexprTest.sum_vector_of_iota_view + 7/21 Test #7: ConstexprTest.sum_vector_of_iota_view ........... Passed 0.01 sec + Start 8: ConstexprTest.sort_vector + 8/21 Test #8: ConstexprTest.sort_vector ....................... Passed 0.01 sec + Start 9: SfinaeTest.istream_view + 9/21 Test #9: SfinaeTest.istream_view ......................... Passed 0.01 sec + Start 10: SfinaeTest.forward_list +10/21 Test #10: SfinaeTest.forward_list ......................... Passed 0.01 sec + Start 11: SfinaeTest.list +11/21 Test #11: SfinaeTest.list ................................. Passed 0.01 sec + Start 12: SfinaeTest.deque +12/21 Test #12: SfinaeTest.deque ................................ Passed 0.01 sec + Start 13: SfinaeTest.vector +13/21 Test #13: SfinaeTest.vector ............................... Passed 0.01 sec + Start 14: SfinaeTest.vector_of_bool +14/21 Test #14: SfinaeTest.vector_of_bool ....................... Passed 0.01 sec + Start 15: SfinaeTest.span +15/21 Test #15: SfinaeTest.span ................................. Passed 0.01 sec + Start 16: TypeTraitsTest.value_type +16/21 Test #16: TypeTraitsTest.value_type ....................... Passed 0.01 sec + Start 17: TypeTraitsTest.reference_type +17/21 Test #17: TypeTraitsTest.reference_type ................... Passed 0.01 sec + Start 18: TypeTraitsTest.rvalue_reference_type +18/21 Test #18: TypeTraitsTest.rvalue_reference_type ............ Passed 0.01 sec + Start 19: TypeTraitsTest.difference_type +19/21 Test #19: TypeTraitsTest.difference_type .................. Passed 0.01 sec + Start 20: TypeTraitsTest.size_type +20/21 Test #20: TypeTraitsTest.size_type ........................ Passed 0.01 sec + Start 21: TypeTraitsTest.borrowed_iterator_type +21/21 Test #21: TypeTraitsTest.borrowed_iterator_type ........... Passed 0.01 sec + +100% tests passed, 0 tests failed out of 21 + +Total Test time (real) = 0.15 sec +``` + ## Contributing Please do! Issues and pull requests are appreciated. diff --git a/include/beman/any_view/any_view_options.hpp b/include/beman/any_view/any_view_options.hpp index d283564..016e039 100644 --- a/include/beman/any_view/any_view_options.hpp +++ b/include/beman/any_view/any_view_options.hpp @@ -44,8 +44,8 @@ enum class any_view_options { return any_view_options(static_cast(l) ^ static_cast(r)); } -[[nodiscard]] constexpr auto operator~(any_view_options k) noexcept -> any_view_options { - return any_view_options(~static_cast(k)); +[[nodiscard]] constexpr auto operator~(any_view_options o) noexcept -> any_view_options { + return any_view_options(~static_cast(o)); } constexpr auto operator|=(any_view_options& l, any_view_options r) noexcept -> any_view_options& { return l = l | r; } diff --git a/tests/beman/any_view/constexpr.test.cpp b/tests/beman/any_view/constexpr.test.cpp index 3dd5a1e..2113851 100644 --- a/tests/beman/any_view/constexpr.test.cpp +++ b/tests/beman/any_view/constexpr.test.cpp @@ -61,13 +61,14 @@ constexpr auto sum(proxy_any_view> views) { TEST(ConstexprTest, sum_vector_of_vector) { #ifndef _MSC_VER // ICE on MSVC - static_assert(15 == sum(std::vector{std::vector{1, 2}, std::vector{3, 4}, std::vector{5}})); + static_assert(10 == sum(std::vector{std::vector{1, 2}, std::vector{3, 4}})); #endif - EXPECT_EQ(15, sum(std::vector{std::vector{1, 2}, std::vector{3, 4}, std::vector{5}})); + EXPECT_EQ(10, sum(std::vector{std::vector{1, 2}, std::vector{3, 4}})); } -TEST(ConstexprTest, sum_transform_view_of_iota) { - constexpr auto iota = [](int n) { return std::views::iota(1) | std::views::take(n); }; +constexpr auto iota(int n) { return std::views::iota(1) | std::views::take(n); }; + +TEST(ConstexprTest, sum_transform_view_of_iota_view) { constexpr auto view = iota(5) | std::views::transform(iota); #ifndef _MSC_VER @@ -77,6 +78,14 @@ TEST(ConstexprTest, sum_transform_view_of_iota) { EXPECT_EQ(35, sum(view)); } +TEST(ConstexprTest, sum_vector_of_iota_view) { +#ifndef _MSC_VER + // ICE on MSVC + static_assert(22 == sum(std::vector{iota(1), iota(3), iota(5)})); +#endif + EXPECT_EQ(22, sum(std::vector{iota(1), iota(3), iota(5)})); +} + constexpr auto sort(any_view view) { std::ranges::sort(view); return std::ranges::is_sorted(view);