From a90735c5aae1618275b5d846962c99a81d22ca6a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Mon, 29 Dec 2025 16:51:09 -0500 Subject: [PATCH 01/43] Return released pointer from `observer_ptr::release` and adjust deduction guides formatting. --- include/daw/daw_observer_ptr.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/daw/daw_observer_ptr.h b/include/daw/daw_observer_ptr.h index 3087388b4..e1240d43d 100644 --- a/include/daw/daw_observer_ptr.h +++ b/include/daw/daw_observer_ptr.h @@ -44,8 +44,10 @@ namespace daw { return *this; } - constexpr void release( ) { + constexpr pointer release( ) { + auto tmp = m_ptr; m_ptr = nullptr; + return tmp; } constexpr void reset( pointer p = nullptr ) { @@ -126,10 +128,10 @@ namespace daw { }; template - requires std::is_array_v - observer_ptr( A ) -> observer_ptr>; + requires std::is_array_v observer_ptr( A ) + -> observer_ptr>; template - requires std::is_pointer_v

- observer_ptr( P ) -> observer_ptr>; + requires std::is_pointer_v

observer_ptr( P ) + -> observer_ptr>; } // namespace daw From 4735887a9edc2cd2f6725ce8d2fcc4ee7e02ef1a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Mon, 29 Dec 2025 21:32:54 -0500 Subject: [PATCH 02/43] Add `get_out` method to `observer_ptr` to retrieve pointer address. --- include/daw/daw_observer_ptr.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/daw/daw_observer_ptr.h b/include/daw/daw_observer_ptr.h index e1240d43d..3a33bbb86 100644 --- a/include/daw/daw_observer_ptr.h +++ b/include/daw/daw_observer_ptr.h @@ -58,6 +58,10 @@ namespace daw { return m_ptr; } + constexpr pointer *get_out( ) const { + return &m_ptr; + } + constexpr explicit operator bool( ) const { return static_cast( m_ptr ); } From e7d6aa75e2dcbfd103ae3d7e8787910b63d0088f Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Mon, 29 Dec 2025 21:41:44 -0500 Subject: [PATCH 03/43] Make `get_out` method in `observer_ptr` non-const for consistent mutability. --- include/daw/daw_observer_ptr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/daw/daw_observer_ptr.h b/include/daw/daw_observer_ptr.h index 3a33bbb86..117b897a3 100644 --- a/include/daw/daw_observer_ptr.h +++ b/include/daw/daw_observer_ptr.h @@ -58,7 +58,7 @@ namespace daw { return m_ptr; } - constexpr pointer *get_out( ) const { + constexpr pointer *get_out( ) { return &m_ptr; } From 45ed3dfe3ce9ea89008de6a882fd382825cb84af Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Thu, 1 Jan 2026 23:46:59 -0500 Subject: [PATCH 04/43] Refactor concept definitions for clarity and add `explicitly_convertible_to`. Simplify and standardize projection operator formatting. --- include/daw/daw_concepts.h | 47 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/include/daw/daw_concepts.h b/include/daw/daw_concepts.h index f12a24a0c..3ccdf15b6 100644 --- a/include/daw/daw_concepts.h +++ b/include/daw/daw_concepts.h @@ -26,14 +26,12 @@ namespace daw { /*** * @brief Given types From and To and an expression E whose type and value * category are the same as those of std::declval(), - * convertible_to requires E to be both implicitly and explicitly - * convertible to type To. The implicit and explicit conversions are required - * to produce equal results. + * explicitly_convertible_to requires E to be explicitly + * convertible to type To. */ template - concept convertible_to = std::is_convertible_v and requires { - { static_cast( std::declval( ) ) }; - }; + concept explicitly_convertible_to = + requires{ static_cast( std::declval( ) ) }; /*** * @brief Given types From and To and an expression E whose type and value @@ -44,6 +42,17 @@ namespace daw { template concept implicitly_convertible_to = std::is_convertible_v; + /*** + * @brief Given types From and To and an expression E whose type and value + * category are the same as those of std::declval(), + * convertible_to requires E to be both implicitly and explicitly + * convertible to type To. The implicit and explicit conversions are required + * to produce equal results. + */ + template + concept convertible_to = + implicitly_convertible_to and explicitly_convertible_to; + /*** * @brief Satisfied when Lhs and Rhs name the same type (taking into account * const/volatile qualifications) @@ -113,14 +122,14 @@ namespace daw { template concept ContiguousContainer = requires( T && container ) { - { std::data( container ) } -> Pointers; - { std::size( container ) } -> convertible_to; + { std::data( container ) }->Pointers; + { std::size( container ) }->convertible_to; }; template concept ContiguousContainerOf = ContiguousContainer and requires( T container ) { - { *std::data( container ) } -> convertible_to; + { *std::data( container ) }->convertible_to; }; template @@ -182,7 +191,7 @@ namespace daw { std::assignable_from; #else std::is_lvalue_reference_v and requires( LHS lhs, RHS &&rhs ) { - { lhs = DAW_FWD( rhs ) } -> std::same_as; + { lhs = DAW_FWD( rhs ) }->std::same_as; }; #endif @@ -238,7 +247,7 @@ namespace daw { movable and requires( I i ) { typename iter_difference_t; requires SignedStd>; - { ++i } -> same_as; + { ++i }->same_as; i++; }; #endif @@ -253,9 +262,7 @@ namespace daw { */ template concept invocable_result = requires( Func && f, Args &&...args ) { - { - std::invoke( DAW_FWD( f ), DAW_FWD( args )... ) - } -> convertible_to; + { std::invoke( DAW_FWD( f ), DAW_FWD( args )... ) }->convertible_to; }; template @@ -358,7 +365,7 @@ namespace daw { template concept boolean_testable = concept_details::boolean_testable_impl and requires( B && b ) { - { not DAW_FWD( b ) } -> concept_details::boolean_testable_impl; + { not DAW_FWD( b ) }->concept_details::boolean_testable_impl; }; namespace concept_details { @@ -366,10 +373,10 @@ namespace daw { concept weakly_equality_comparable_with = requires( std::remove_reference_t const &t, std::remove_reference_t const &u ) { - { t == u } -> boolean_testable; - { t != u } -> boolean_testable; - { u == t } -> boolean_testable; - { u != t } -> boolean_testable; + { t == u }->boolean_testable; + { t != u }->boolean_testable; + { u == t }->boolean_testable; + { u != t }->boolean_testable; }; } // namespace concept_details #endif @@ -439,7 +446,7 @@ namespace daw { template concept Callable_r = requires( Fn fn, Args... args ) { - { fn( args... ) } -> convertible_to; + { fn( args... ) }->convertible_to; }; template From b3996de38c9139a81349466a307486fa81c482a9 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Thu, 1 Jan 2026 23:49:15 -0500 Subject: [PATCH 05/43] Standardize formatting in `explicitly_convertible_to` concept definition. --- include/daw/daw_concepts.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/daw/daw_concepts.h b/include/daw/daw_concepts.h index 3ccdf15b6..24d2cf7d8 100644 --- a/include/daw/daw_concepts.h +++ b/include/daw/daw_concepts.h @@ -30,8 +30,9 @@ namespace daw { * convertible to type To. */ template - concept explicitly_convertible_to = - requires{ static_cast( std::declval( ) ) }; + concept explicitly_convertible_to = requires { + static_cast( std::declval( ) ); + }; /*** * @brief Given types From and To and an expression E whose type and value From cda298612441eb08743b8556f6a05e15f7e3ee1a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Thu, 1 Jan 2026 23:51:51 -0500 Subject: [PATCH 06/43] newline at end of file in `daw_concepts.h`. --- include/daw/daw_concepts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/daw/daw_concepts.h b/include/daw/daw_concepts.h index 24d2cf7d8..46bdf1568 100644 --- a/include/daw/daw_concepts.h +++ b/include/daw/daw_concepts.h @@ -465,4 +465,4 @@ namespace daw { { fn( args... ) } noexcept; }; } // namespace daw -#endif \ No newline at end of file +#endif From 41d9c7171e46f8479354d79b16954c5c1e232095 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 01:21:28 -0500 Subject: [PATCH 07/43] Add `span_writer` utility for writing to spans with variadic arguments or contiguous ranges. --- include/daw/daw_span_writer.h | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 include/daw/daw_span_writer.h diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h new file mode 100644 index 000000000..46e79b4d9 --- /dev/null +++ b/include/daw/daw_span_writer.h @@ -0,0 +1,60 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/daw_concepts.h" +#include "daw/daw_restrict.h" + +#include +#include +#include + +namespace daw { + /// + /// @tparam T Destination type + /// @param out output span to write elements to + /// @param values parameters that are convertible to T to write to out + /// @return out.subspan( sizeof...( values ) ) + template + [[nodiscard]] constexpr std::span + span_writer( std::span out, + daw::explicitly_convertible_to auto &&...values ) { + if constexpr( sizeof...( values ) == 0 ) { + return out; + } + daw_ensure( sizeof...( values ) <= out.size( ) ); + return [&]( std::index_sequence ) { + ( (void)( out[Is] = static_cast( values ) ), ... ); + return out.subspan( sizeof...( values ) ); + }( std::make_index_sequence{ } ); + } + + /// + /// @tparam T Destination type + /// @tparam R Range of elements convertible to T + /// @param out output span to write elements to + /// @param r input range values to copy write to out + /// @return out.subspan( std::ranges::size( r ) ) + /// The two ranges cannot overlap + template + requires( + std::ranges::contiguous_range + and daw::explicitly_convertible_to, T> ) + [[nodiscard]] constexpr std::span span_writer( std::span out, + R const &r ) { + auto const sz = std::ranges::size( r ); + daw_ensure( sz <= out.size( ) ); + T *DAW_RESTRICT out_ptr = out.data( ); + auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); + for( std::size_t n = 0; n < sz; ++n ) { + out_ptr[n] = static_cast( in_ptr[n] ); + } + return out.subspan( sz ); + } +} // namespace daw From 5c04f74fbab7fae6014360f8d1bc4d8147b4c54a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 01:22:45 -0500 Subject: [PATCH 08/43] Include `daw_ensure.h` in `daw_span_writer.h` to ensure precondition checks. --- include/daw/daw_span_writer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index 46e79b4d9..1df3fffa0 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -9,6 +9,7 @@ #pragma once #include "daw/daw_concepts.h" +#include "daw/daw_ensure.h" #include "daw/daw_restrict.h" #include From 9fd8e313447e4d70ae432b5cb40b76ac5b14645c Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 02:22:36 -0500 Subject: [PATCH 09/43] Add `span_writer_ntz` utility to `daw_span_writer.h` for writing string literals without the trailing null and corresponding tests. Standardize `CMakeLists.txt` formatting. --- include/daw/daw_span_writer.h | 22 ++ tests/CMakeLists.txt | 505 +++++++++++++++++---------------- tests/daw_span_writer_test.cpp | 38 +++ 3 files changed, 313 insertions(+), 252 deletions(-) create mode 100644 tests/daw_span_writer_test.cpp diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index 1df3fffa0..8d9042ca4 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -58,4 +58,26 @@ namespace daw { } return out.subspan( sz ); } + + /// Write a string literal, minus trailing zero to span + /// @tparam T Destination type + /// @tparam N size, including trailing zero, of string literal + /// @param out output span to write elements to + /// @param str input string literal to write to out + /// @return out.subspan( N-1 ) + /// The two ranges cannot overlap and str must have a trailing 0 + template + requires( daw::explicitly_convertible_to ) + [[nodiscard]] constexpr std::span span_writer_ntz( + std::span out, char const ( &str )[N] ) { + daw_ensure( str[N - 1] == '\0' ); + auto const sz = N - 1; + daw_ensure( sz <= out.size( ) ); + T *DAW_RESTRICT out_ptr = out.data( ); + auto *DAW_RESTRICT in_ptr = str; + for( std::size_t n = 0; n < sz; ++n ) { + out_ptr[n] = static_cast( in_ptr[n] ); + } + return out.subspan( sz ); + } } // namespace daw diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 91c796a27..2e368b3a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,282 +5,283 @@ # #Official repository : https: // github.com/beached/header_libraries # -cmake_policy(SET CMP0065 NEW) +cmake_policy( SET CMP0065 NEW ) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +set( CMAKE_CXX_STANDARD_REQUIRED ON ) +set( CMAKE_CXX_EXTENSIONS OFF ) -if (CMAKE_BUILD_TYPE EQUAL "Debug") - add_compile_definitions(DEBUG=1) -endif () +if( CMAKE_BUILD_TYPE EQUAL "Debug" ) + add_compile_definitions( DEBUG=1 ) +endif() -set(TEST_SOURCES - InputIterator_test.cpp - cpp_17_test.cpp - daw_algorithm_test.cpp - daw_arith_traits_test.cpp - daw_array_compare_test.cpp - daw_array_test.cpp - daw_ascii_test.cpp - daw_assume_test.cpp - daw_attributes_test.cpp - daw_benchmark_test.cpp - daw_bounded_vector_test.cpp - daw_constant_test.cpp - daw_container_algorithm_test.cpp - daw_contract_test.cpp - daw_cx_offset_of_test.cpp - daw_cxmath_test.cpp - daw_endian_test.cpp - daw_exception_test.cpp - daw_expected_test.cpp - daw_fnv1a_hash_test.cpp - daw_function_ref_test.cpp - daw_function_table_test.cpp - daw_function_test.cpp - daw_function_view_test.cpp - daw_fwd_pack_apply_test.cpp - daw_generic_hash_test.cpp - daw_graph_algorithm_test.cpp - daw_graph_test.cpp - daw_hash_set_test.cpp - daw_is_any_of_test.cpp - daw_iterator_argument_iterator_test.cpp - daw_iterator_back_inserter_test.cpp - daw_iterator_checked_iterator_proxy_test.cpp - daw_iterator_chunk_iterator_test.cpp - daw_iterator_circular_iterator_test.cpp - daw_iterator_counting_iterators_test.cpp - daw_iterator_end_inserter_test.cpp - daw_iterator_find_iterator_test.cpp - daw_iterator_indexed_iterator_test.cpp - daw_iterator_inserter_test.cpp - daw_iterator_integer_iterator_test.cpp - daw_iterator_iota_iterator_test.cpp - daw_iterator_output_stream_iterator_test.cpp - daw_iterator_random_iterator_test.cpp - daw_iterator_repeat_n_char_iterator_test.cpp - daw_iterator_reverse_iterator_test.cpp - daw_iterator_sorted_insert_iterator_test.cpp - daw_iterator_zipiter_test.cpp - daw_keep_n_test.cpp - daw_lift_test.cpp - daw_logic_test.cpp - daw_math_test.cpp - daw_memory_mapped_file_test.cpp - daw_metro_hash_test.cpp - daw_natural_test.cpp - daw_not_null_test.cpp - daw_nth_pack_element_test.cpp - daw_optional_test.cpp - daw_ordered_map_test.cpp - daw_overload_test.cpp - daw_parse_args_test.cpp - daw_parse_to_test.cpp - daw_parser_helper_sv_test.cpp - daw_poly_var_test.cpp - daw_prop_const_ptr_test.cpp - daw_random_test.cpp - daw_read_file_test.cpp - daw_read_only_test.cpp - daw_ref_counted_pointer_test.cpp - daw_ring_adaptor_test.cpp - daw_rw_ref_test.cpp - daw_scope_guard_test.cpp - daw_simple_array_test.cpp - daw_sip_hash_test.cpp - daw_size_literals_test.cpp - daw_span_test.cpp - daw_stack_function_test.cpp - daw_string_concat_test.cpp - daw_string_view2_test.cpp - daw_take_test.cpp - daw_traits_test.cpp - daw_tuple_helper_test.cpp - daw_tuple_test.cpp - daw_uint_buffer_test.cpp - daw_uninitialized_storage_test.cpp - daw_union_pair_test.cpp - daw_unique_array_test.cpp - daw_unique_ptr_test.cpp - daw_utility_test.cpp - daw_validated_test.cpp - daw_value_ptr_test.cpp - daw_variant_cast_test.cpp - daw_view_test.cpp - daw_virtual_base_test.cpp - daw_visit_test.cpp - daw_visit_as_test.cpp - daw_zipcontainer_test.cpp - sbo_test.cpp - static_hash_table_test.cpp -) +set( TEST_SOURCES + InputIterator_test.cpp + cpp_17_test.cpp + daw_algorithm_test.cpp + daw_arith_traits_test.cpp + daw_array_compare_test.cpp + daw_array_test.cpp + daw_ascii_test.cpp + daw_assume_test.cpp + daw_attributes_test.cpp + daw_benchmark_test.cpp + daw_bounded_vector_test.cpp + daw_constant_test.cpp + daw_container_algorithm_test.cpp + daw_contract_test.cpp + daw_cx_offset_of_test.cpp + daw_cxmath_test.cpp + daw_endian_test.cpp + daw_exception_test.cpp + daw_expected_test.cpp + daw_fnv1a_hash_test.cpp + daw_function_ref_test.cpp + daw_function_table_test.cpp + daw_function_test.cpp + daw_function_view_test.cpp + daw_fwd_pack_apply_test.cpp + daw_generic_hash_test.cpp + daw_graph_algorithm_test.cpp + daw_graph_test.cpp + daw_hash_set_test.cpp + daw_is_any_of_test.cpp + daw_iterator_argument_iterator_test.cpp + daw_iterator_back_inserter_test.cpp + daw_iterator_checked_iterator_proxy_test.cpp + daw_iterator_chunk_iterator_test.cpp + daw_iterator_circular_iterator_test.cpp + daw_iterator_counting_iterators_test.cpp + daw_iterator_end_inserter_test.cpp + daw_iterator_find_iterator_test.cpp + daw_iterator_indexed_iterator_test.cpp + daw_iterator_inserter_test.cpp + daw_iterator_integer_iterator_test.cpp + daw_iterator_iota_iterator_test.cpp + daw_iterator_output_stream_iterator_test.cpp + daw_iterator_random_iterator_test.cpp + daw_iterator_repeat_n_char_iterator_test.cpp + daw_iterator_reverse_iterator_test.cpp + daw_iterator_sorted_insert_iterator_test.cpp + daw_iterator_zipiter_test.cpp + daw_keep_n_test.cpp + daw_lift_test.cpp + daw_logic_test.cpp + daw_math_test.cpp + daw_memory_mapped_file_test.cpp + daw_metro_hash_test.cpp + daw_natural_test.cpp + daw_not_null_test.cpp + daw_nth_pack_element_test.cpp + daw_optional_test.cpp + daw_ordered_map_test.cpp + daw_overload_test.cpp + daw_parse_args_test.cpp + daw_parse_to_test.cpp + daw_parser_helper_sv_test.cpp + daw_poly_var_test.cpp + daw_prop_const_ptr_test.cpp + daw_random_test.cpp + daw_read_file_test.cpp + daw_read_only_test.cpp + daw_ref_counted_pointer_test.cpp + daw_ring_adaptor_test.cpp + daw_rw_ref_test.cpp + daw_scope_guard_test.cpp + daw_simple_array_test.cpp + daw_sip_hash_test.cpp + daw_size_literals_test.cpp + daw_span_test.cpp + daw_stack_function_test.cpp + daw_string_concat_test.cpp + daw_string_view2_test.cpp + daw_take_test.cpp + daw_traits_test.cpp + daw_tuple_helper_test.cpp + daw_tuple_test.cpp + daw_uint_buffer_test.cpp + daw_uninitialized_storage_test.cpp + daw_union_pair_test.cpp + daw_unique_array_test.cpp + daw_unique_ptr_test.cpp + daw_utility_test.cpp + daw_validated_test.cpp + daw_value_ptr_test.cpp + daw_variant_cast_test.cpp + daw_view_test.cpp + daw_virtual_base_test.cpp + daw_visit_test.cpp + daw_visit_as_test.cpp + daw_zipcontainer_test.cpp + sbo_test.cpp + static_hash_table_test.cpp + ) -set(DEPRECATED_TEST_SOURCES - daw_cstring_test.cpp - daw_bounded_graph_test.cpp - daw_bounded_hash_map_test.cpp - daw_bounded_hash_set_test.cpp - daw_bounded_string_test.cpp - daw_carray_test.cpp - daw_checked_expected_test.cpp - daw_clumpy_sparsy_test.cpp - daw_copiable_unique_ptr_test.cpp - daw_fixed_array_test.cpp - daw_fixed_lookup_test.cpp - daw_heap_array_test.cpp - daw_heap_value_test.cpp - daw_optional_poly_test.cpp - daw_poly_vector_test.cpp - daw_range_collection_test.cpp - daw_range_test.cpp - daw_safe_string_test.cpp - daw_stack_quick_sort_test.cpp - daw_string_split_range_test.cpp - daw_string_test.cpp - daw_string_view1_test.cpp - daw_vector_test.cpp -) +set( DEPRECATED_TEST_SOURCES + daw_cstring_test.cpp + daw_bounded_graph_test.cpp + daw_bounded_hash_map_test.cpp + daw_bounded_hash_set_test.cpp + daw_bounded_string_test.cpp + daw_carray_test.cpp + daw_checked_expected_test.cpp + daw_clumpy_sparsy_test.cpp + daw_copiable_unique_ptr_test.cpp + daw_fixed_array_test.cpp + daw_fixed_lookup_test.cpp + daw_heap_array_test.cpp + daw_heap_value_test.cpp + daw_optional_poly_test.cpp + daw_poly_vector_test.cpp + daw_range_collection_test.cpp + daw_range_test.cpp + daw_safe_string_test.cpp + daw_stack_quick_sort_test.cpp + daw_string_split_range_test.cpp + daw_string_test.cpp + daw_string_view1_test.cpp + daw_vector_test.cpp + ) -set(CPP23_TEST_SOURCES - daw_enumerate_tuple_test.cpp -) +set( CPP23_TEST_SOURCES + daw_enumerate_tuple_test.cpp + ) -set(CPP20_TEST_SOURCES - daw_atomic_wait_test.cpp - daw_any_if_test.cpp - daw_concepts_test.cpp - daw_contiguous_view_test.cpp - daw_formatters_test.cpp - daw_from_string_test.cpp - daw_iter_view_test.cpp - daw_move_only_test.cpp - daw_named_params_test.cpp - daw_observer_ptr_test.cpp - daw_poly_value_test.cpp -) +set( CPP20_TEST_SOURCES + daw_atomic_wait_test.cpp + daw_any_if_test.cpp + daw_concepts_test.cpp + daw_contiguous_view_test.cpp + daw_formatters_test.cpp + daw_from_string_test.cpp + daw_iter_view_test.cpp + daw_move_only_test.cpp + daw_named_params_test.cpp + daw_observer_ptr_test.cpp + daw_poly_value_test.cpp + daw_span_writer_test.cpp + ) -set(CPP20_NOT_MSVC_TEST_SOURCES - daw_pipelines_test.cpp - vector_test.cpp -) +set( CPP20_NOT_MSVC_TEST_SOURCES + daw_pipelines_test.cpp + vector_test.cpp + ) #NOT COMPLETED daw_iterator_split_iterator_test.cpp #NOT COMPLETED daw_static_bitset_test.cpp #NOT COMPLETED daw_string_fmt_test.cpp -set(NOT_MSVC_TEST_SOURCES - daw_can_constant_evaluate_test.cpp - daw_parser_helper_test.cpp - daw_piecewise_factory_test.cpp - daw_tuple2_test.cpp -) +set( NOT_MSVC_TEST_SOURCES + daw_can_constant_evaluate_test.cpp + daw_parser_helper_test.cpp + daw_piecewise_factory_test.cpp + daw_tuple2_test.cpp + ) #not included in CI as they are not ready -set(DEV_TEST_SOURCES - daw_bind_args_at_test.cpp - daw_container_help_test.cpp - daw_bit_queues_test.cpp - daw_bit_test.cpp - daw_min_perfect_hash_test.cpp - daw_range_algorithm_test.cpp - daw_sort_n_test.cpp - daw_tracked_allocator_test.cpp -) +set( DEV_TEST_SOURCES + daw_bind_args_at_test.cpp + daw_container_help_test.cpp + daw_bit_queues_test.cpp + daw_bit_test.cpp + daw_min_perfect_hash_test.cpp + daw_range_algorithm_test.cpp + daw_sort_n_test.cpp + daw_tracked_allocator_test.cpp + ) -include(cmake/test_compiler_options.cmake) +include( cmake/test_compiler_options.cmake ) -find_package(Threads REQUIRED) +find_package( Threads REQUIRED ) #Allows building all in some IDE's -add_custom_target(${PROJECT_NAME}_full) +add_custom_target( ${PROJECT_NAME}_full ) -add_library(daw_test INTERFACE) -target_link_libraries(daw_test INTERFACE daw::header_libraries) -target_compile_options(daw_test INTERFACE $<$:/permissive->) +add_library( daw_test INTERFACE ) +target_link_libraries( daw_test INTERFACE daw::header_libraries ) +target_compile_options( daw_test INTERFACE $<$:/permissive-> ) -set(all_tests ${TEST_SOURCES}) +set( all_tests ${TEST_SOURCES} ) -if (NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - list(APPEND all_tests ${NOT_MSVC_TEST_SOURCES}) -endif () +if( NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC" ) + list( APPEND all_tests ${NOT_MSVC_TEST_SOURCES} ) +endif() -foreach (CUR_TEST IN LISTS all_tests) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - add_executable("${CUR_TEST_NAME}" "${CUR_TEST}") - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) -endforeach () +foreach( CUR_TEST IN LISTS all_tests ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + add_executable( "${CUR_TEST_NAME}" "${CUR_TEST}" ) + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) +endforeach() -option(DAW_ENABLE_DEPRECATED_TESTING "Build unit tests still for deprecated libraries" OFF) -foreach (CUR_TEST IN LISTS DEPRECATED_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - if (DAW_ENABLE_DEPRECATED_TESTING) - add_executable("${CUR_TEST_NAME}" "deprecated/${CUR_TEST}") - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - else () - add_executable("${CUR_TEST_NAME}" EXCLUDE_FROM_ALL "deprecated/${CUR_TEST}") - endif () - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) -endforeach () +option( DAW_ENABLE_DEPRECATED_TESTING "Build unit tests still for deprecated libraries" OFF ) +foreach( CUR_TEST IN LISTS DEPRECATED_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + if( DAW_ENABLE_DEPRECATED_TESTING ) + add_executable( "${CUR_TEST_NAME}" "deprecated/${CUR_TEST}" ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + else() + add_executable( "${CUR_TEST_NAME}" EXCLUDE_FROM_ALL "deprecated/${CUR_TEST}" ) + endif() + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) +endforeach() -if (CMAKE_CXX_STANDARD STRGREATER_EQUAL "20") - foreach (CUR_TEST IN LISTS CPP20_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - add_executable("${CUR_TEST_NAME}" "${CUR_TEST}") - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) - target_compile_features(${CUR_TEST_NAME} PUBLIC cxx_std_20) - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - endforeach () - if (NOT MSVC) - foreach (CUR_TEST IN LISTS CPP20_NOT_MSVC_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - add_executable("${CUR_TEST_NAME}" "${CUR_TEST}") - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) - target_compile_features(${CUR_TEST_NAME} PUBLIC cxx_std_20) - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - endforeach () - endif () -else () - message(STATUS "Disabling C++20 tests") -endif () +if( CMAKE_CXX_STANDARD STRGREATER_EQUAL "20" ) + foreach( CUR_TEST IN LISTS CPP20_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + add_executable( "${CUR_TEST_NAME}" "${CUR_TEST}" ) + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) + target_compile_features( ${CUR_TEST_NAME} PUBLIC cxx_std_20 ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + endforeach() + if( NOT MSVC ) + foreach( CUR_TEST IN LISTS CPP20_NOT_MSVC_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + add_executable( "${CUR_TEST_NAME}" "${CUR_TEST}" ) + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) + target_compile_features( ${CUR_TEST_NAME} PUBLIC cxx_std_20 ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + endforeach() + endif() +else() + message( STATUS "Disabling C++20 tests" ) +endif() -if (CMAKE_CXX_STANDARD STRGREATER_EQUAL "23") - foreach (CUR_TEST IN LISTS CPP23_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - add_executable("${CUR_TEST_NAME}" "${CUR_TEST}") - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) - target_compile_features(${CUR_TEST_NAME} PUBLIC cxx_std_23) - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - endforeach () - if (NOT MSVC) - foreach (CUR_TEST IN LISTS CPP23_NOT_MSVC_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - add_executable("${CUR_TEST_NAME}" "${CUR_TEST}") - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) - target_compile_features(${CUR_TEST_NAME} PUBLIC cxx_std_23) - add_test(${CUR_TEST_NAME}_test ${CUR_TEST_NAME}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - endforeach () - endif () -else () - message(STATUS "Disabling C++23 tests") -endif () +if( CMAKE_CXX_STANDARD STRGREATER_EQUAL "23" ) + foreach( CUR_TEST IN LISTS CPP23_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + add_executable( "${CUR_TEST_NAME}" "${CUR_TEST}" ) + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) + target_compile_features( ${CUR_TEST_NAME} PUBLIC cxx_std_23 ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + endforeach() + if( NOT MSVC ) + foreach( CUR_TEST IN LISTS CPP23_NOT_MSVC_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + add_executable( "${CUR_TEST_NAME}" "${CUR_TEST}" ) + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) + target_compile_features( ${CUR_TEST_NAME} PUBLIC cxx_std_23 ) + add_test( ${CUR_TEST_NAME}_test ${CUR_TEST_NAME} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + endforeach() + endif() +else() + message( STATUS "Disabling C++23 tests" ) +endif() -option(DAW_ENABLE_DEV_TESTING "Build unit tests still in development" OFF) -foreach (CUR_TEST IN LISTS DEV_TEST_SOURCES) - string(REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST}) - if (DAW_ENABLE_DEV_TESTING) - add_executable(${CUR_TEST_NAME} ${CUR_TEST}) - add_dependencies(${PROJECT_NAME}_full ${CUR_TEST_NAME}) - else () - add_executable(${CUR_TEST_NAME} EXCLUDE_FROM_ALL ${CUR_TEST}) - endif () - target_link_libraries(${CUR_TEST_NAME} PRIVATE daw_test) -endforeach () +option( DAW_ENABLE_DEV_TESTING "Build unit tests still in development" OFF ) +foreach( CUR_TEST IN LISTS DEV_TEST_SOURCES ) + string( REPLACE ".cpp" "" CUR_TEST_NAME ${CUR_TEST} ) + if( DAW_ENABLE_DEV_TESTING ) + add_executable( ${CUR_TEST_NAME} ${CUR_TEST} ) + add_dependencies( ${PROJECT_NAME}_full ${CUR_TEST_NAME} ) + else() + add_executable( ${CUR_TEST_NAME} EXCLUDE_FROM_ALL ${CUR_TEST} ) + endif() + target_link_libraries( ${CUR_TEST_NAME} PRIVATE daw_test ) +endforeach() diff --git a/tests/daw_span_writer_test.cpp b/tests/daw_span_writer_test.cpp new file mode 100644 index 000000000..b7cf433b1 --- /dev/null +++ b/tests/daw_span_writer_test.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#include "daw/daw_span_writer.h" + +#include +#include +#include + +#include +#include +#include + +DAW_ATTRIB_NOINLINE void foo( std::array &buff, + std::size_t &out_size ) { + auto sp = std::span( buff ); + + sp = daw::span_writer_ntz( sp, "Hello World.......Hello little letters:" ); + sp = daw::span_writer( sp, 'a', 'b', 'c', 0 ); + constexpr auto const expected_sz = + ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + + 4 /*second span_writer*/ ); + daw_ensure( sp.size( ) == ( buff.size( ) - expected_sz ) ); + out_size = sp.size( ); +} + +int main( ) { + auto buff = std::array( ); + std::size_t out_size = 0; + foo( buff, out_size ); + daw::do_not_optimize( buff ); + daw::do_not_optimize( out_size ); +} \ No newline at end of file From a810b01522ebb170214e9e50a4cfabf4a7354969 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 21:43:14 -0500 Subject: [PATCH 10/43] Add `output_span` utility to `daw_span_writer.h` for enhanced span writing functionality and update tests. Adjust `CMakeLists.txt` to include new test file. --- include/daw/daw_span_writer.h | 279 +++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 2 +- tests/daw_span_writer_test.cpp | 39 ++++- 3 files changed, 312 insertions(+), 8 deletions(-) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index 8d9042ca4..a09761cd3 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -8,15 +8,20 @@ #pragma once +#include "daw/daw_attributes.h" #include "daw/daw_concepts.h" +#include "daw/daw_cpp_feature_check.h" #include "daw/daw_ensure.h" #include "daw/daw_restrict.h" #include +#include #include +#include #include namespace daw { + /// /// @tparam T Destination type /// @param out output span to write elements to @@ -80,4 +85,278 @@ namespace daw { } return out.subspan( sz ); } + + template + requires( not std::is_const_v and not std::is_reference_v ) // + struct output_span { + using pointer = T *; + + private: + pointer m_first = nullptr; + std::size_t m_size = 0; + + constexpr void remove_prefix_unsafe( std::size_t n ) { + m_first += static_cast( n ); + m_size -= n; + } + + public: + output_span( ) = default; + + constexpr output_span( std::ranges::contiguous_range auto &r ) + : m_first( std::ranges::data( r ) ) + , m_size( std::ranges::size( r ) ) {} + + constexpr pointer data( ) const { + return m_first; + } + + constexpr std::size_t size( ) const { + return m_size; + } + +#if not defined( DAW_ATTRIB_ENABLE_IF ) + constexpr output_span subspan( std::size_t n ) const { + daw_ensure( n <= m_size ); + return output_span{ .m_first = m_first + static_cast( n ), + .m_size = m_size - n }; + } + + constexpr output_span &remove_prefix( std::size_t n ) { + daw_ensure( n <= m_size ); + m_first += static_cast( n ); + m_size -= n; + return *this; + } + + constexpr output_span & + write( this auto &self, + daw::explicitly_convertible_to auto &&...values ) { + if constexpr( sizeof...( values ) == 0 ) { + return self; + } + daw_ensure( sizeof...( values ) <= self.m_size ); + [&]( std::index_sequence ) { + ( (void)( self.m_first[Is] = static_cast( values ) ), ... ); + }( std::make_index_sequence{ } ); + self.remove_prefix_unsafe( sizeof...( values ) ); + return self; + } + + template + requires( + std::ranges::contiguous_range and daw::explicitly_convertible_to< + std::ranges::range_reference_t, T> ) // + constexpr output_span &write( this auto &self, R const &r ) { + auto const sz = std::ranges::size( r ); + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + + template + requires( daw::explicitly_convertible_to ) // + constexpr output_span &write_ntz( this auto &self, + char const ( &str )[N] ) { + daw_ensure( str[N - 1] == '\0' ); + auto const sz = N - 1; + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = str; + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + +#else +#if defined( DAW_HAS_CPP26_DELETED_REASON ) + constexpr output_span subspan( this auto const &self, std::size_t n ) + DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and n > self.m_size ) = + delete( "Attempt to create a subspan larger than size( )" ); +#endif + + constexpr output_span subspan( this auto const &self, std::size_t n ) + DAW_ATTRIB_ENABLE_IF( + __builtin_constant_p( self ) and n <= self.m_size, + "Attempt to create a subspan larger than size( )" ) { + return output_span{ .m_first = m_first + static_cast( n ), + .m_size = m_size - n }; + } + + constexpr output_span subspan( this auto const &self, std::size_t n ) + DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { + { + daw_ensure( n <= self.m_size ); + return output_span{ .m_first = + self.m_first + static_cast( n ), + .m_size = self.m_size - n }; + } + +#if defined( DAW_HAS_CPP26_DELETED_REASON ) + constexpr output_span &remove_prefix( std::size_t n ) + DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( n ) and n <= self.m_size, + " " ) = + delete( + "Attempt to call remove_prefix with a value larger than size( )" ); +#endif + constexpr output_span &remove_prefix( std::size_t n ) + DAW_ATTRIB_ENABLE_IF( + __builtin_constant_p( n ) and n <= self.m_size, + "Attempt to call remove_prefix with a value larger than size( )" ) { + m_first += static_cast( n ); + m_size -= n; + return *this; + } + + constexpr output_span &remove_prefix( std::size_t n ) + DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( n ), " " ) { + daw_ensure( n <= m_size ); + m_first += static_cast( n ); + m_size -= n; + return *this; + } + +#if defined( DAW_HAS_CPP26_DELETED_REASON ) + constexpr output_span &write( + this auto &self, daw::explicitly_convertible_to auto &&...values ) + DAW_ATTRIB_ENABLE_IF( + __builtin_constant_p( self ) and sizeof...( values ) > self.m_size, + " " ) = delete( "Attempt to write more items than size( )" ); +#endif + constexpr output_span &write( + this auto &self, daw::explicitly_convertible_to auto &&...values ) + DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and + sizeof...( values ) <= self.m_size, + "Attempt to write more items than size( )" ) { + if constexpr( sizeof...( values ) == 0 ) { + return self; + } + [&]( std::index_sequence ) { + ( (void)( self.m_first[Is] = static_cast( values ) ), ... ); + }( std::make_index_sequence{ } ); + self.remove_prefix_unsafe( sizeof...( values ) ); + return self; + } + + constexpr output_span &write( + this auto &self, daw::explicitly_convertible_to auto &&...values ) + DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { + if constexpr( sizeof...( values ) == 0 ) { + return self; + } + daw_ensure( sizeof...( values ) <= self.m_size ); + [&]( std::index_sequence ) { + ( (void)( self.m_first[Is] = static_cast( values ) ), ... ); + }( std::make_index_sequence{ } ); + self.remove_prefix_unsafe( sizeof...( values ) ); + return self; + } + +#if defined( DAW_HAS_CPP26_DELETED_REASON ) + template + requires( daw::explicitly_convertible_to ) // + constexpr output_span &write_ntz( this auto &self, + char const( &str )[N] ) + DAW_ATTRIB_ENABLE_IF( + __builtin_constant_p( self ) and ( N - 1 ) < self.m_size, " " ) = + delete( "Attempt to write more items than size( )" ); +#endif + template + requires( daw::explicitly_convertible_to ) // + constexpr output_span &write_ntz( this auto &self, + char const( &str )[N] ) + DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and + ( N - 1 ) < self.m_size, + "Attempt to write more items than size( )" ) { + daw_ensure( str[N - 1] == '\0' ); + auto const sz = N - 1; + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = str; + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + + template + requires( daw::explicitly_convertible_to ) // + constexpr output_span &write_ntz( this auto &self, + char const( &str )[N] ) + DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { + daw_ensure( str[N - 1] == '\0' ); + auto const sz = N - 1; + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = str; + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + +#if defined( DAW_HAS_CPP26_DELETED_REASON ) + template + requires( + std::ranges::contiguous_range and + daw::explicitly_convertible_to, + T> ) // + constexpr output_span &write( this auto &self, R const &r ) + DAW_ATTRIB_ENABLE_IF( + __builtin_constant_p( self ) and std::ranges::size( r ) <= self.m_size, + " " ) = delete( "Attempt to write more items than size( )" ); +#endif + template + requires( + std::ranges::contiguous_range and + daw::explicitly_convertible_to, + T> ) // + constexpr output_span &write( this auto &self, R const &r ) + DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( r ) and + std::ranges::size( r ) <= self.m_size, + "Attempt to write more items than size( )" ) { + auto const sz = std::ranges::size( r ); + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + + template + requires( + std::ranges::contiguous_range and + daw::explicitly_convertible_to, + T> ) // + constexpr output_span &write( this auto &self, R const &r ) + DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { + auto const sz = std::ranges::size( r ); + daw_ensure( sz <= self.m_size ); + T *DAW_RESTRICT self_ptr = self.m_first; + auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); + for( std::size_t n = 0; n < sz; ++n ) { + self_ptr[n] = static_cast( in_ptr[n] ); + } + self.remove_prefix_unsafe( sz ); + return self; + } + +#endif + }; + + template + output_span( R ) -> output_span>; + } // namespace daw diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2e368b3a3..cc36c518a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -145,6 +145,7 @@ set( DEPRECATED_TEST_SOURCES set( CPP23_TEST_SOURCES daw_enumerate_tuple_test.cpp + daw_span_writer_test.cpp ) set( CPP20_TEST_SOURCES @@ -159,7 +160,6 @@ set( CPP20_TEST_SOURCES daw_named_params_test.cpp daw_observer_ptr_test.cpp daw_poly_value_test.cpp - daw_span_writer_test.cpp ) set( CPP20_NOT_MSVC_TEST_SOURCES diff --git a/tests/daw_span_writer_test.cpp b/tests/daw_span_writer_test.cpp index b7cf433b1..790c9846a 100644 --- a/tests/daw_span_writer_test.cpp +++ b/tests/daw_span_writer_test.cpp @@ -16,12 +16,32 @@ #include #include -DAW_ATTRIB_NOINLINE void foo( std::array &buff, - std::size_t &out_size ) { - auto sp = std::span( buff ); - - sp = daw::span_writer_ntz( sp, "Hello World.......Hello little letters:" ); +DAW_ATTRIB_NOINLINE void test1_impl( std::array &buff, + std::size_t &out_size ) { + auto sp = daw::span_writer_ntz( + buff, "Hello World.......Hello little letters:" ); sp = daw::span_writer( sp, 'a', 'b', 'c', 0 ); + constexpr auto const expected_sz = + buff.size( ) - + ( ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + + 4 /*second span_writer*/ ) ); + daw_ensure( sp.size( ) == expected_sz ); + out_size = sp.size( ); +} + +void test1( ) { + auto buff = std::array( ); + std::size_t out_size = 0; + test1_impl( buff, out_size ); + daw::do_not_optimize( buff ); + daw::do_not_optimize( out_size ); +} + +DAW_ATTRIB_NOINLINE void test2_impl( std::array &buff, + std::size_t &out_size ) { + auto sp = daw::output_span( buff ); + sp.write_ntz( "Hello World.......Hello little letters:" ); + sp.write( 'a', 'b', 'c', 0 ); constexpr auto const expected_sz = ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + 4 /*second span_writer*/ ); @@ -29,10 +49,15 @@ DAW_ATTRIB_NOINLINE void foo( std::array &buff, out_size = sp.size( ); } -int main( ) { +void test2( ) { auto buff = std::array( ); std::size_t out_size = 0; - foo( buff, out_size ); + test2_impl( buff, out_size ); daw::do_not_optimize( buff ); daw::do_not_optimize( out_size ); +} + +int main( ) { + test1( ); + test2( ); } \ No newline at end of file From c39f9ff0cb343bcad8e7afa83ac74945a7f7f1dc Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 22:02:39 -0500 Subject: [PATCH 11/43] Remove unused `DAW_ATTRIB_ENABLE_IF`-dependent code and related directives from `daw_span_writer.h`. Simplify implementation by eliminating conditional compilation paths. --- include/daw/daw_span_writer.h | 182 ---------------------------------- 1 file changed, 182 deletions(-) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index a09761cd3..db34b4523 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -8,7 +8,6 @@ #pragma once -#include "daw/daw_attributes.h" #include "daw/daw_concepts.h" #include "daw/daw_cpp_feature_check.h" #include "daw/daw_ensure.h" @@ -115,7 +114,6 @@ namespace daw { return m_size; } -#if not defined( DAW_ATTRIB_ENABLE_IF ) constexpr output_span subspan( std::size_t n ) const { daw_ensure( n <= m_size ); return output_span{ .m_first = m_first + static_cast( n ), @@ -174,186 +172,6 @@ namespace daw { self.remove_prefix_unsafe( sz ); return self; } - -#else -#if defined( DAW_HAS_CPP26_DELETED_REASON ) - constexpr output_span subspan( this auto const &self, std::size_t n ) - DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and n > self.m_size ) = - delete( "Attempt to create a subspan larger than size( )" ); -#endif - - constexpr output_span subspan( this auto const &self, std::size_t n ) - DAW_ATTRIB_ENABLE_IF( - __builtin_constant_p( self ) and n <= self.m_size, - "Attempt to create a subspan larger than size( )" ) { - return output_span{ .m_first = m_first + static_cast( n ), - .m_size = m_size - n }; - } - - constexpr output_span subspan( this auto const &self, std::size_t n ) - DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { - { - daw_ensure( n <= self.m_size ); - return output_span{ .m_first = - self.m_first + static_cast( n ), - .m_size = self.m_size - n }; - } - -#if defined( DAW_HAS_CPP26_DELETED_REASON ) - constexpr output_span &remove_prefix( std::size_t n ) - DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( n ) and n <= self.m_size, - " " ) = - delete( - "Attempt to call remove_prefix with a value larger than size( )" ); -#endif - constexpr output_span &remove_prefix( std::size_t n ) - DAW_ATTRIB_ENABLE_IF( - __builtin_constant_p( n ) and n <= self.m_size, - "Attempt to call remove_prefix with a value larger than size( )" ) { - m_first += static_cast( n ); - m_size -= n; - return *this; - } - - constexpr output_span &remove_prefix( std::size_t n ) - DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( n ), " " ) { - daw_ensure( n <= m_size ); - m_first += static_cast( n ); - m_size -= n; - return *this; - } - -#if defined( DAW_HAS_CPP26_DELETED_REASON ) - constexpr output_span &write( - this auto &self, daw::explicitly_convertible_to auto &&...values ) - DAW_ATTRIB_ENABLE_IF( - __builtin_constant_p( self ) and sizeof...( values ) > self.m_size, - " " ) = delete( "Attempt to write more items than size( )" ); -#endif - constexpr output_span &write( - this auto &self, daw::explicitly_convertible_to auto &&...values ) - DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and - sizeof...( values ) <= self.m_size, - "Attempt to write more items than size( )" ) { - if constexpr( sizeof...( values ) == 0 ) { - return self; - } - [&]( std::index_sequence ) { - ( (void)( self.m_first[Is] = static_cast( values ) ), ... ); - }( std::make_index_sequence{ } ); - self.remove_prefix_unsafe( sizeof...( values ) ); - return self; - } - - constexpr output_span &write( - this auto &self, daw::explicitly_convertible_to auto &&...values ) - DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { - if constexpr( sizeof...( values ) == 0 ) { - return self; - } - daw_ensure( sizeof...( values ) <= self.m_size ); - [&]( std::index_sequence ) { - ( (void)( self.m_first[Is] = static_cast( values ) ), ... ); - }( std::make_index_sequence{ } ); - self.remove_prefix_unsafe( sizeof...( values ) ); - return self; - } - -#if defined( DAW_HAS_CPP26_DELETED_REASON ) - template - requires( daw::explicitly_convertible_to ) // - constexpr output_span &write_ntz( this auto &self, - char const( &str )[N] ) - DAW_ATTRIB_ENABLE_IF( - __builtin_constant_p( self ) and ( N - 1 ) < self.m_size, " " ) = - delete( "Attempt to write more items than size( )" ); -#endif - template - requires( daw::explicitly_convertible_to ) // - constexpr output_span &write_ntz( this auto &self, - char const( &str )[N] ) - DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( self ) and - ( N - 1 ) < self.m_size, - "Attempt to write more items than size( )" ) { - daw_ensure( str[N - 1] == '\0' ); - auto const sz = N - 1; - daw_ensure( sz <= self.m_size ); - T *DAW_RESTRICT self_ptr = self.m_first; - auto *DAW_RESTRICT in_ptr = str; - for( std::size_t n = 0; n < sz; ++n ) { - self_ptr[n] = static_cast( in_ptr[n] ); - } - self.remove_prefix_unsafe( sz ); - return self; - } - - template - requires( daw::explicitly_convertible_to ) // - constexpr output_span &write_ntz( this auto &self, - char const( &str )[N] ) - DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { - daw_ensure( str[N - 1] == '\0' ); - auto const sz = N - 1; - daw_ensure( sz <= self.m_size ); - T *DAW_RESTRICT self_ptr = self.m_first; - auto *DAW_RESTRICT in_ptr = str; - for( std::size_t n = 0; n < sz; ++n ) { - self_ptr[n] = static_cast( in_ptr[n] ); - } - self.remove_prefix_unsafe( sz ); - return self; - } - -#if defined( DAW_HAS_CPP26_DELETED_REASON ) - template - requires( - std::ranges::contiguous_range and - daw::explicitly_convertible_to, - T> ) // - constexpr output_span &write( this auto &self, R const &r ) - DAW_ATTRIB_ENABLE_IF( - __builtin_constant_p( self ) and std::ranges::size( r ) <= self.m_size, - " " ) = delete( "Attempt to write more items than size( )" ); -#endif - template - requires( - std::ranges::contiguous_range and - daw::explicitly_convertible_to, - T> ) // - constexpr output_span &write( this auto &self, R const &r ) - DAW_ATTRIB_ENABLE_IF( __builtin_constant_p( r ) and - std::ranges::size( r ) <= self.m_size, - "Attempt to write more items than size( )" ) { - auto const sz = std::ranges::size( r ); - daw_ensure( sz <= self.m_size ); - T *DAW_RESTRICT self_ptr = self.m_first; - auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); - for( std::size_t n = 0; n < sz; ++n ) { - self_ptr[n] = static_cast( in_ptr[n] ); - } - self.remove_prefix_unsafe( sz ); - return self; - } - - template - requires( - std::ranges::contiguous_range and - daw::explicitly_convertible_to, - T> ) // - constexpr output_span &write( this auto &self, R const &r ) - DAW_ATTRIB_ENABLE_IF( not __builtin_constant_p( self ), " " ) { - auto const sz = std::ranges::size( r ); - daw_ensure( sz <= self.m_size ); - T *DAW_RESTRICT self_ptr = self.m_first; - auto *DAW_RESTRICT in_ptr = std::ranges::data( r ); - for( std::size_t n = 0; n < sz; ++n ) { - self_ptr[n] = static_cast( in_ptr[n] ); - } - self.remove_prefix_unsafe( sz ); - return self; - } - -#endif }; template From af166cea8a9bbfc0ccb53c73cc53ca9d9411b9aa Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Fri, 2 Jan 2026 22:26:36 -0500 Subject: [PATCH 12/43] Expand `output_span` with additional type aliases, iterator support, and subscript operator. Update tests to validate new functionality. --- include/daw/daw_span_writer.h | 35 +++++++++++++++++++++++++++++++++- tests/daw_span_writer_test.cpp | 3 +++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index db34b4523..a1a0475b9 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -89,6 +89,9 @@ namespace daw { requires( not std::is_const_v and not std::is_reference_v ) // struct output_span { using pointer = T *; + using const_pointer = T const *; + using reference = T &; + using const_reference = T const &; private: pointer m_first = nullptr; @@ -106,14 +109,44 @@ namespace daw { : m_first( std::ranges::data( r ) ) , m_size( std::ranges::size( r ) ) {} - constexpr pointer data( ) const { + constexpr pointer data( ) { return m_first; } + constexpr const_pointer data( ) const { + return m_first; + } + + constexpr pointer begin( ) { + return m_first; + } + + constexpr const_pointer begin( ) const { + return m_first; + } + + constexpr pointer end( ) { + return std::next( m_first, static_cast( m_size ) ); + } + + constexpr const_pointer end( ) const { + return std::next( m_first, static_cast( m_size ) ); + } + constexpr std::size_t size( ) const { return m_size; } + constexpr reference operator[]( std::size_t n ) { + daw_ensure( n < m_size ); + return m_first[n]; + } + + constexpr const_reference operator[]( std::size_t n ) const { + daw_ensure( n < m_size ); + return m_first[n]; + } + constexpr output_span subspan( std::size_t n ) const { daw_ensure( n <= m_size ); return output_span{ .m_first = m_first + static_cast( n ), diff --git a/tests/daw_span_writer_test.cpp b/tests/daw_span_writer_test.cpp index 790c9846a..cf5f3f98b 100644 --- a/tests/daw_span_writer_test.cpp +++ b/tests/daw_span_writer_test.cpp @@ -39,7 +39,10 @@ void test1( ) { DAW_ATTRIB_NOINLINE void test2_impl( std::array &buff, std::size_t &out_size ) { + static_assert( std::ranges::contiguous_range> ); auto sp = daw::output_span( buff ); + std::span s = sp; + (void)s; sp.write_ntz( "Hello World.......Hello little letters:" ); sp.write( 'a', 'b', 'c', 0 ); constexpr auto const expected_sz = From 15550847276a28e91b680233f1bd2774f5631ef1 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 19:23:17 -0500 Subject: [PATCH 13/43] Add overload to `output_span` constructor with size constraint validation --- include/daw/daw_span_writer.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index a1a0475b9..5f7849d56 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -109,6 +109,13 @@ namespace daw { : m_first( std::ranges::data( r ) ) , m_size( std::ranges::size( r ) ) {} + constexpr output_span( std::ranges::contiguous_range auto &r, + std::size_t count ) + : m_first( std::ranges::data( r ) ) + , m_size( count ) { + daw_ensure( count <= std::ranges::size( r ) ); + } + constexpr pointer data( ) { return m_first; } From 8ae09696ab80818e3a468723d621bf7de02fd9eb Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 19:30:55 -0500 Subject: [PATCH 14/43] Rename `output_span` to `span_writer` and update associated methods for consistency. Adjust tests accordingly. --- include/daw/daw_span_writer.h | 34 ++++++++++++++++++---------------- tests/daw_span_writer_test.cpp | 12 ++++++------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/include/daw/daw_span_writer.h b/include/daw/daw_span_writer.h index 5f7849d56..6a41afba8 100644 --- a/include/daw/daw_span_writer.h +++ b/include/daw/daw_span_writer.h @@ -28,8 +28,8 @@ namespace daw { /// @return out.subspan( sizeof...( values ) ) template [[nodiscard]] constexpr std::span - span_writer( std::span out, - daw::explicitly_convertible_to auto &&...values ) { + write_to_span( std::span out, + daw::explicitly_convertible_to auto &&...values ) { if constexpr( sizeof...( values ) == 0 ) { return out; } @@ -51,8 +51,8 @@ namespace daw { requires( std::ranges::contiguous_range and daw::explicitly_convertible_to, T> ) - [[nodiscard]] constexpr std::span span_writer( std::span out, - R const &r ) { + [[nodiscard]] constexpr std::span write_to_span( std::span out, + R const &r ) { auto const sz = std::ranges::size( r ); daw_ensure( sz <= out.size( ) ); T *DAW_RESTRICT out_ptr = out.data( ); @@ -72,7 +72,7 @@ namespace daw { /// The two ranges cannot overlap and str must have a trailing 0 template requires( daw::explicitly_convertible_to ) - [[nodiscard]] constexpr std::span span_writer_ntz( + [[nodiscard]] constexpr std::span write_to_span_ntz( std::span out, char const ( &str )[N] ) { daw_ensure( str[N - 1] == '\0' ); auto const sz = N - 1; @@ -87,7 +87,7 @@ namespace daw { template requires( not std::is_const_v and not std::is_reference_v ) // - struct output_span { + struct span_writer { using pointer = T *; using const_pointer = T const *; using reference = T &; @@ -103,13 +103,13 @@ namespace daw { } public: - output_span( ) = default; + span_writer( ) = default; - constexpr output_span( std::ranges::contiguous_range auto &r ) + constexpr span_writer( std::ranges::contiguous_range auto &r ) : m_first( std::ranges::data( r ) ) , m_size( std::ranges::size( r ) ) {} - constexpr output_span( std::ranges::contiguous_range auto &r, + constexpr span_writer( std::ranges::contiguous_range auto &r, std::size_t count ) : m_first( std::ranges::data( r ) ) , m_size( count ) { @@ -154,20 +154,20 @@ namespace daw { return m_first[n]; } - constexpr output_span subspan( std::size_t n ) const { + constexpr span_writer subspan( std::size_t n ) const { daw_ensure( n <= m_size ); - return output_span{ .m_first = m_first + static_cast( n ), + return span_writer{ .m_first = m_first + static_cast( n ), .m_size = m_size - n }; } - constexpr output_span &remove_prefix( std::size_t n ) { + constexpr span_writer &remove_prefix( std::size_t n ) { daw_ensure( n <= m_size ); m_first += static_cast( n ); m_size -= n; return *this; } - constexpr output_span & + constexpr span_writer & write( this auto &self, daw::explicitly_convertible_to auto &&...values ) { if constexpr( sizeof...( values ) == 0 ) { @@ -185,7 +185,7 @@ namespace daw { requires( std::ranges::contiguous_range and daw::explicitly_convertible_to< std::ranges::range_reference_t, T> ) // - constexpr output_span &write( this auto &self, R const &r ) { + constexpr span_writer &write( this auto &self, R const &r ) { auto const sz = std::ranges::size( r ); daw_ensure( sz <= self.m_size ); T *DAW_RESTRICT self_ptr = self.m_first; @@ -199,7 +199,7 @@ namespace daw { template requires( daw::explicitly_convertible_to ) // - constexpr output_span &write_ntz( this auto &self, + constexpr span_writer &write_ntz( this auto &self, char const ( &str )[N] ) { daw_ensure( str[N - 1] == '\0' ); auto const sz = N - 1; @@ -215,6 +215,8 @@ namespace daw { }; template - output_span( R ) -> output_span>; + span_writer( R ) -> span_writer>; + template + span_writer( R, std::size_t ) -> span_writer>; } // namespace daw diff --git a/tests/daw_span_writer_test.cpp b/tests/daw_span_writer_test.cpp index cf5f3f98b..a294c0a53 100644 --- a/tests/daw_span_writer_test.cpp +++ b/tests/daw_span_writer_test.cpp @@ -18,13 +18,13 @@ DAW_ATTRIB_NOINLINE void test1_impl( std::array &buff, std::size_t &out_size ) { - auto sp = daw::span_writer_ntz( + auto sp = daw::write_to_span_ntz( buff, "Hello World.......Hello little letters:" ); - sp = daw::span_writer( sp, 'a', 'b', 'c', 0 ); + sp = daw::write_to_span( sp, 'a', 'b', 'c', 0 ); constexpr auto const expected_sz = buff.size( ) - ( ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + - 4 /*second span_writer*/ ) ); + 4 /*second write_to_span*/ ) ); daw_ensure( sp.size( ) == expected_sz ); out_size = sp.size( ); } @@ -39,15 +39,15 @@ void test1( ) { DAW_ATTRIB_NOINLINE void test2_impl( std::array &buff, std::size_t &out_size ) { - static_assert( std::ranges::contiguous_range> ); - auto sp = daw::output_span( buff ); + static_assert( std::ranges::contiguous_range> ); + auto sp = daw::span_writer( buff ); std::span s = sp; (void)s; sp.write_ntz( "Hello World.......Hello little letters:" ); sp.write( 'a', 'b', 'c', 0 ); constexpr auto const expected_sz = ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + - 4 /*second span_writer*/ ); + 4 /*second write_to_span*/ ); daw_ensure( sp.size( ) == ( buff.size( ) - expected_sz ) ); out_size = sp.size( ); } From 33a50c8b3dcb26cce926dc04901abc65d3f2372a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 21:05:37 -0500 Subject: [PATCH 15/43] Add `create_bitset_from_set_positions` utility to `daw_bitset_helper.h` for creating bitsets from position ranges. Add corresponding tests and update `CMakeLists.txt`. --- include/daw/daw_bitset_helper.h | 49 +++++++++++++++++++++++++++++ include/daw/daw_cpp_feature_check.h | 14 ++++++++- tests/CMakeLists.txt | 1 + tests/daw_bitset_helper_test.cpp | 22 +++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 include/daw/daw_bitset_helper.h create mode 100644 tests/daw_bitset_helper_test.cpp diff --git a/include/daw/daw_bitset_helper.h b/include/daw/daw_bitset_helper.h new file mode 100644 index 000000000..d8cd4364b --- /dev/null +++ b/include/daw/daw_bitset_helper.h @@ -0,0 +1,49 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw_cxmath.h" + +#include +#include + +#include +#include +#include + +namespace daw { + namespace create_bs_impl { + template + DAW_CPP23_CX_BITSET_CX auto + create_bitset_from_set_positions( auto const &r ) { + auto result = std::bitset{ }; + using value_t = std::ranges::range_value_t; + for( auto const &position : r ) { + assert( ( std::is_unsigned_v or position > 0 ) ); + assert( static_cast( position ) < N ); + result.set( static_cast( position ) ); + } + return result; + } + + } // namespace create_bs_impl + + template + requires( std::ranges::range + and std::is_integral_v> ) // + DAW_CPP23_CX_BITSET_CX auto create_bitset_from_set_positions( R const &r ) { + return create_bs_impl::create_bitset_from_set_positions( r ); + } + + template + DAW_CPP23_CX_BITSET_CX auto + create_bitset_from_set_positions( std::initializer_list r ) { + return create_bs_impl::create_bitset_from_set_positions( r ); + } +} // namespace daw diff --git a/include/daw/daw_cpp_feature_check.h b/include/daw/daw_cpp_feature_check.h index f9f382952..77b3a0fec 100644 --- a/include/daw/daw_cpp_feature_check.h +++ b/include/daw/daw_cpp_feature_check.h @@ -227,4 +227,16 @@ inline constexpr bool daw_has_cx_cmath = false; #if __cpp_lib_move_only_function >= 202110L #define DAW_HAS_CPP23_MOVE_ONLY_FUNCTION #endif -#endif \ No newline at end of file +#endif + +#if defined( __cpp_lib_constexpr_bitset ) +#if __cpp_lib_constexpr_bitset >= 202207L +#define DAW_HAS_CPP23_CONSTEXPR_BITSET 1 +#endif +#endif + +#if defined( DAW_HAS_CPP23_CONSTEXPR_BITSET ) +#define DAW_CPP23_CONSTEXPR_BITSET_CX constexpr +#else +#define DAW_CPP23_CX_BITSET_CX inline +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc36c518a..949e35e60 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -151,6 +151,7 @@ set( CPP23_TEST_SOURCES set( CPP20_TEST_SOURCES daw_atomic_wait_test.cpp daw_any_if_test.cpp + daw_bitset_helper_test.cpp daw_concepts_test.cpp daw_contiguous_view_test.cpp daw_formatters_test.cpp diff --git a/tests/daw_bitset_helper_test.cpp b/tests/daw_bitset_helper_test.cpp new file mode 100644 index 000000000..8e598998c --- /dev/null +++ b/tests/daw_bitset_helper_test.cpp @@ -0,0 +1,22 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#include "daw/daw_bitset_helper.h" + +#include + +#include + +int main( ) { + auto const bs = daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); + daw_ensure( bs.count( ) == 4 ); + daw_ensure( bs.test( 1 ) ); + daw_ensure( bs.test( 2 ) ); + daw_ensure( bs.test( 5 ) ); + daw_ensure( bs.test( 10 ) ); +} From 2ac01919a93c3da1aefded7218beee5afc5db4ca Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 21:27:52 -0500 Subject: [PATCH 16/43] Refactor `create_bitset_from_set_positions` to support enums with `IntegerEnum` concept. Add `integral_type_t` helper and corresponding tests. Adjust `daw_cpp_feature_check.h` for constexpr bitset detection. --- include/daw/daw_bitset_helper.h | 17 +++++----- include/daw/daw_cpp_feature_check.h | 4 +-- include/daw/traits/daw_traits_integer_type.h | 22 ++++++++++++ tests/daw_bitset_helper_test.cpp | 35 ++++++++++++++++---- 4 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 include/daw/traits/daw_traits_integer_type.h diff --git a/include/daw/daw_bitset_helper.h b/include/daw/daw_bitset_helper.h index d8cd4364b..5073a1043 100644 --- a/include/daw/daw_bitset_helper.h +++ b/include/daw/daw_bitset_helper.h @@ -8,10 +8,9 @@ #pragma once -#include "daw_cxmath.h" - -#include -#include +#include "daw/daw_cpp_feature_check.h" +#include "daw/daw_ensure.h" +#include "daw/traits/daw_traits_integer_type.h" #include #include @@ -23,9 +22,11 @@ namespace daw { DAW_CPP23_CX_BITSET_CX auto create_bitset_from_set_positions( auto const &r ) { auto result = std::bitset{ }; - using value_t = std::ranges::range_value_t; + using value_t = + daw::traits::integral_type_t>; for( auto const &position : r ) { - assert( ( std::is_unsigned_v or position > 0 ) ); + assert( ( std::is_unsigned_v or + static_cast( position ) >= 0 ) ); assert( static_cast( position ) < N ); result.set( static_cast( position ) ); } @@ -36,12 +37,12 @@ namespace daw { template requires( std::ranges::range - and std::is_integral_v> ) // + and daw::traits::IntegerEnum> ) // DAW_CPP23_CX_BITSET_CX auto create_bitset_from_set_positions( R const &r ) { return create_bs_impl::create_bitset_from_set_positions( r ); } - template + template DAW_CPP23_CX_BITSET_CX auto create_bitset_from_set_positions( std::initializer_list r ) { return create_bs_impl::create_bitset_from_set_positions( r ); diff --git a/include/daw/daw_cpp_feature_check.h b/include/daw/daw_cpp_feature_check.h index 77b3a0fec..029209113 100644 --- a/include/daw/daw_cpp_feature_check.h +++ b/include/daw/daw_cpp_feature_check.h @@ -230,13 +230,13 @@ inline constexpr bool daw_has_cx_cmath = false; #endif #if defined( __cpp_lib_constexpr_bitset ) -#if __cpp_lib_constexpr_bitset >= 202207L +#if __cpp_lib_constexpr_bitset >= 202202L #define DAW_HAS_CPP23_CONSTEXPR_BITSET 1 #endif #endif #if defined( DAW_HAS_CPP23_CONSTEXPR_BITSET ) -#define DAW_CPP23_CONSTEXPR_BITSET_CX constexpr +#define DAW_CPP23_CX_BITSET_CX constexpr #else #define DAW_CPP23_CX_BITSET_CX inline #endif diff --git a/include/daw/traits/daw_traits_integer_type.h b/include/daw/traits/daw_traits_integer_type.h new file mode 100644 index 000000000..d65d76343 --- /dev/null +++ b/include/daw/traits/daw_traits_integer_type.h @@ -0,0 +1,22 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include + +namespace daw::traits { + template + concept IntegerEnum = std::is_integral_v or std::is_enum_v; + // Get the underlying type or integral type passed + template + using integral_type_t = + typename std::conditional_t, std::underlying_type, + std::type_identity>::type; + +} // namespace daw::traits diff --git a/tests/daw_bitset_helper_test.cpp b/tests/daw_bitset_helper_test.cpp index 8e598998c..20e4fc4dd 100644 --- a/tests/daw_bitset_helper_test.cpp +++ b/tests/daw_bitset_helper_test.cpp @@ -8,15 +8,38 @@ #include "daw/daw_bitset_helper.h" +#include #include #include int main( ) { - auto const bs = daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); - daw_ensure( bs.count( ) == 4 ); - daw_ensure( bs.test( 1 ) ); - daw_ensure( bs.test( 2 ) ); - daw_ensure( bs.test( 5 ) ); - daw_ensure( bs.test( 10 ) ); + auto const bs0 = daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); + daw_ensure( bs0.count( ) == 4 ); + daw_ensure( bs0.test( 1 ) ); + daw_ensure( bs0.test( 2 ) ); + daw_ensure( bs0.test( 5 ) ); + daw_ensure( bs0.test( 10 ) ); + + enum class EPos { a, b, c, d, e, f }; + auto const bs1 = + daw::create_bitset_from_set_positions<4>( { EPos::a, EPos::d } ); + daw_ensure( bs1.count( ) == 2 ); + daw_ensure( bs1.test( static_cast( EPos::a ) ) ); + daw_ensure( bs1.test( static_cast( EPos::d ) ) ); +#if defined( DAW_HAS_CPP23_CONSTEXPR_BITSET ) + constexpr auto bs2 = + daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); + static_assert( bs2.count( ) == 4 ); + static_assert( bs2.test( 1 ) ); + static_assert( bs2.test( 2 ) ); + static_assert( bs2.test( 5 ) ); + static_assert( bs2.test( 10 ) ); + + constexpr auto bs3 = + daw::create_bitset_from_set_positions<4>( { EPos::a, EPos::d } ); + static_assert( bs3.count( ) == 2 ); + static_assert( bs3.test( static_cast( EPos::a ) ) ); + static_assert( bs3.test( static_cast( EPos::d ) ) ); +#endif } From b9c70115fdb0e32a87bf84a19ac4dc892b32bbc9 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 21:30:33 -0500 Subject: [PATCH 17/43] Align `DAW_CPP23_CX_BITSET_CX` macro usage and replace with `DAW_CPP23_CX_BITSET_FN`. Update `daw_cpp_feature_check.h` definitions and adjust `daw_bitset_helper.h` and tests accordingly. --- include/daw/daw_bitset_helper.h | 6 +++--- include/daw/daw_cpp_feature_check.h | 6 ++++-- tests/daw_bitset_helper_test.cpp | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/daw/daw_bitset_helper.h b/include/daw/daw_bitset_helper.h index 5073a1043..eae11b5d9 100644 --- a/include/daw/daw_bitset_helper.h +++ b/include/daw/daw_bitset_helper.h @@ -19,7 +19,7 @@ namespace daw { namespace create_bs_impl { template - DAW_CPP23_CX_BITSET_CX auto + DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( auto const &r ) { auto result = std::bitset{ }; using value_t = @@ -38,12 +38,12 @@ namespace daw { template requires( std::ranges::range and daw::traits::IntegerEnum> ) // - DAW_CPP23_CX_BITSET_CX auto create_bitset_from_set_positions( R const &r ) { + DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( R const &r ) { return create_bs_impl::create_bitset_from_set_positions( r ); } template - DAW_CPP23_CX_BITSET_CX auto + DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( std::initializer_list r ) { return create_bs_impl::create_bitset_from_set_positions( r ); } diff --git a/include/daw/daw_cpp_feature_check.h b/include/daw/daw_cpp_feature_check.h index 029209113..79de0db5e 100644 --- a/include/daw/daw_cpp_feature_check.h +++ b/include/daw/daw_cpp_feature_check.h @@ -236,7 +236,9 @@ inline constexpr bool daw_has_cx_cmath = false; #endif #if defined( DAW_HAS_CPP23_CONSTEXPR_BITSET ) -#define DAW_CPP23_CX_BITSET_CX constexpr +#define DAW_CPP23_CX_BITSET_FN constexpr +#define DAW_CPP23_CX_BITSET constexpr #else -#define DAW_CPP23_CX_BITSET_CX inline +#define DAW_CPP23_CX_BITSET_FN inline +#define DAW_CPP23_CX_BITSET #endif diff --git a/tests/daw_bitset_helper_test.cpp b/tests/daw_bitset_helper_test.cpp index 20e4fc4dd..820512f70 100644 --- a/tests/daw_bitset_helper_test.cpp +++ b/tests/daw_bitset_helper_test.cpp @@ -14,7 +14,8 @@ #include int main( ) { - auto const bs0 = daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); + DAW_CPP23_CX_BITSET auto const bs0 = + daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); daw_ensure( bs0.count( ) == 4 ); daw_ensure( bs0.test( 1 ) ); daw_ensure( bs0.test( 2 ) ); @@ -22,7 +23,7 @@ int main( ) { daw_ensure( bs0.test( 10 ) ); enum class EPos { a, b, c, d, e, f }; - auto const bs1 = + DAW_CPP23_CX_BITSET auto const bs1 = daw::create_bitset_from_set_positions<4>( { EPos::a, EPos::d } ); daw_ensure( bs1.count( ) == 2 ); daw_ensure( bs1.test( static_cast( EPos::a ) ) ); From 44acbfd22de6b004feb52d1f40e52382db69a2c5 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 22:44:54 -0500 Subject: [PATCH 18/43] Add `First` pipeline utility and corresponding tests. Update feature checks for optional references. --- include/daw/daw_cpp_feature_check.h | 4 +++ include/daw/daw_pipelines.h | 1 + include/daw/pipelines/first.h | 43 +++++++++++++++++++++++++++++ tests/daw_pipelines_test.cpp | 22 +++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 include/daw/pipelines/first.h diff --git a/include/daw/daw_cpp_feature_check.h b/include/daw/daw_cpp_feature_check.h index 79de0db5e..90c76dbcf 100644 --- a/include/daw/daw_cpp_feature_check.h +++ b/include/daw/daw_cpp_feature_check.h @@ -242,3 +242,7 @@ inline constexpr bool daw_has_cx_cmath = false; #define DAW_CPP23_CX_BITSET_FN inline #define DAW_CPP23_CX_BITSET #endif + +#if __cpp_lib_optional >= 202506L +#define DAW_HAS_CPP26_OPTIONAL_REFS +#endif \ No newline at end of file diff --git a/include/daw/daw_pipelines.h b/include/daw/daw_pipelines.h index ab0f42029..9d39c2ba7 100644 --- a/include/daw/daw_pipelines.h +++ b/include/daw/daw_pipelines.h @@ -16,6 +16,7 @@ #include "daw/pipelines/every.h" #include "daw/pipelines/filter.h" #include "daw/pipelines/find.h" +#include "daw/pipelines/first.h" #include "daw/pipelines/flatten.h" #include "daw/pipelines/foreach.h" #include "daw/pipelines/generate.h" diff --git a/include/daw/pipelines/first.h b/include/daw/pipelines/first.h new file mode 100644 index 000000000..98e3d1f31 --- /dev/null +++ b/include/daw/pipelines/first.h @@ -0,0 +1,43 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/daw_cpp_feature_check.h" +#include "daw/daw_iterator_traits.h" + +#include +#include +#include +#include +#include + +namespace daw::pipelines { + constexpr auto First( ) { + return []( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP { + auto f = std::begin( r ); + auto l = std::end( r ); + using ref_t = daw::iter_reference_t; +#if defined( DAW_HAS_CPP26_OPTIONAL_REFS ) + using result_t = + std::conditional_t::value, + ref_t, + std::remove_cvref_t>; +#else + using result_t = std::conditional_t< + std::is_lvalue_reference::value, + std::reference_wrapper>, + std::remove_cvref_t>; +#endif + if( f == l ) { + return std::optional( ); + } + return std::optional( std::in_place, *f ); + }; + } +} // namespace daw::pipelines diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index a8b182b42..049bb15d8 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -605,6 +606,25 @@ namespace tests { constexpr auto s2 = daw::string_view{ std::data( p ), std::size( p ) }; daw_ensure( s2 == "Hello" ); } + + DAW_ATTRIB_NOINLINE void test034( ) { + constexpr daw::string_view s = " Hello "; + constexpr auto p = pipeline( s, + DropWhile( is_control_or_space ), + Reverse, + DropWhile( is_control_or_space ), + Reverse, + First ); + daw_ensure( p.has_value( ) and p.value( ) == 'H' ); + } + + DAW_ATTRIB_NOINLINE void test035( ) { + char buff[] = "Hello"; + auto p = pipeline( buff, First ); + daw_ensure( p.has_value( ) and p.value( ) == 'H' ); + buff[0] = 'h'; + daw_ensure( p.value( ) == 'h' ); + } } // namespace tests int main( ) { @@ -641,6 +661,8 @@ int main( ) { tests::test031( ); tests::test032( ); tests::test033( ); + tests::test034( ); + tests::test035( ); daw::println( "Done" ); } From 889dce16edd6ee9f5c9e84391244d78d5d19452d Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 22:52:00 -0500 Subject: [PATCH 19/43] Refactor `First` pipeline utility to use `pimpl::FirstFn` implementation for improved error messages --- include/daw/pipelines/first.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/include/daw/pipelines/first.h b/include/daw/pipelines/first.h index 98e3d1f31..c0b2afaeb 100644 --- a/include/daw/pipelines/first.h +++ b/include/daw/pipelines/first.h @@ -17,9 +17,11 @@ #include #include -namespace daw::pipelines { - constexpr auto First( ) { - return []( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP { +namespace daw::pipelines::pimpl { + struct FirstFn { + explicit FirstFn( ) = default; + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { auto f = std::begin( r ); auto l = std::end( r ); using ref_t = daw::iter_reference_t; @@ -38,6 +40,12 @@ namespace daw::pipelines { return std::optional( ); } return std::optional( std::in_place, *f ); - }; + } + }; +} // namespace daw::pipelines::pimpl + +namespace daw::pipelines { + constexpr pimpl::FirstFn First( ) { + return pimpl::FirstFn{ }; } } // namespace daw::pipelines From cf7df498fad9c0bd394608a30bfb14016c7a8c85 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 23:12:39 -0500 Subject: [PATCH 20/43] Introduce `FirstRef` utility to `daw::pipelines` and update tests to validate reference support. Adjust `First` implementation for consistency. --- include/daw/pipelines/first.h | 24 ++++++++++++++++++++++-- tests/daw_pipelines_test.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/include/daw/pipelines/first.h b/include/daw/pipelines/first.h index c0b2afaeb..adac835e0 100644 --- a/include/daw/pipelines/first.h +++ b/include/daw/pipelines/first.h @@ -18,8 +18,9 @@ #include namespace daw::pipelines::pimpl { - struct FirstFn { - explicit FirstFn( ) = default; + struct FirstRefFn { + explicit FirstRefFn( ) = default; + DAW_CPP23_STATIC_CALL_OP constexpr auto operator( )( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { auto f = std::begin( r ); @@ -42,9 +43,28 @@ namespace daw::pipelines::pimpl { return std::optional( std::in_place, *f ); } }; + struct FirstFn { + explicit FirstFn( ) = default; + + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { + auto f = std::begin( r ); + auto l = std::end( r ); + using ref_t = daw::iter_reference_t; + using result_t = std::remove_cvref_t; + if( f == l ) { + return std::optional( ); + } + return std::optional( std::in_place, *f ); + } + }; } // namespace daw::pipelines::pimpl namespace daw::pipelines { + constexpr pimpl::FirstRefFn FirstRef( ) { + return pimpl::FirstRefFn{ }; + } + constexpr pimpl::FirstFn First( ) { return pimpl::FirstFn{ }; } diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 049bb15d8..0415104d8 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -614,17 +614,36 @@ namespace tests { Reverse, DropWhile( is_control_or_space ), Reverse, - First ); + FirstRef ); daw_ensure( p.has_value( ) and p.value( ) == 'H' ); } DAW_ATTRIB_NOINLINE void test035( ) { char buff[] = "Hello"; - auto p = pipeline( buff, First ); + auto p = pipeline( buff, FirstRef ); daw_ensure( p.has_value( ) and p.value( ) == 'H' ); buff[0] = 'h'; daw_ensure( p.value( ) == 'h' ); } + + DAW_ATTRIB_NOINLINE void test036( ) { + constexpr daw::string_view s = " Hello "; + constexpr auto p = pipeline( s, + DropWhile( is_control_or_space ), + Reverse, + DropWhile( is_control_or_space ), + Reverse, + First ); + daw_ensure( p.has_value( ) and p.value( ) == 'H' ); + } + + DAW_ATTRIB_NOINLINE void test037( ) { + char buff[] = "Hello"; + auto p = pipeline( buff, First ); + daw_ensure( p.has_value( ) and p.value( ) == 'H' ); + buff[0] = 'h'; + daw_ensure( p.value( ) == 'H' ); + } } // namespace tests int main( ) { @@ -663,6 +682,8 @@ int main( ) { tests::test033( ); tests::test034( ); tests::test035( ); + tests::test036( ); + tests::test037( ); daw::println( "Done" ); } From 0fd6757f4ff4d39a3aa7250c1e53c14fce51730d Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 3 Jan 2026 23:29:05 -0500 Subject: [PATCH 21/43] Refactor `contiguous_view` with updated constructors to allow a subset of range passed in --- include/daw/daw_contiguous_view.h | 47 +++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/include/daw/daw_contiguous_view.h b/include/daw/daw_contiguous_view.h index e5e8e179a..fd76dfd6d 100644 --- a/include/daw/daw_contiguous_view.h +++ b/include/daw/daw_contiguous_view.h @@ -8,12 +8,12 @@ #pragma once -#include "daw_concepts.h" -#include "daw_consteval.h" -#include "daw_data_end.h" -#include "daw_utility.h" -#include "impl/daw_view_tags.h" -#include "wrap_iter.h" +#include "daw/daw_concepts.h" +#include "daw/daw_consteval.h" +#include "daw/daw_data_end.h" +#include "daw/daw_utility.h" +#include "daw/impl/daw_view_tags.h" +#include "daw/wrap_iter.h" #include #include @@ -45,7 +45,8 @@ namespace daw { pointer m_last = nullptr; public: - contiguous_view( ) = default; + explicit contiguous_view( ) = default; + constexpr contiguous_view( contiguous_view &other ) noexcept : m_first( other.m_first ) , m_last( other.m_last ) {} @@ -70,6 +71,15 @@ namespace daw { : m_first( std::data( c ) ) , m_last( daw::data_end( c ) ) {} + template Container> + requires( not_cvref_of ) explicit( + ExplicitConv ) constexpr contiguous_view( Container &&c, + std::size_t count ) noexcept + : m_first( std::data( c ) ) + , m_last( std::next( m_first, static_cast( count ) ) ) { + daw_ensure( count <= std::size( c ) ); + } + template Container> explicit constexpr operator Container( ) const noexcept { return Container{ data( ), size( ) }; @@ -226,7 +236,7 @@ namespace daw { } constexpr void remove_prefix( std::size_t count ) noexcept { - count = ( std::min )( count, size( ) ); + count = (std::min)( count, size( ) ); m_first += count; } @@ -241,7 +251,7 @@ namespace daw { } constexpr void remove_suffix( std::size_t count ) noexcept { - count = ( std::min )( count, size( ) ); + count = (std::min)( count, size( ) ); m_last -= count; } @@ -397,8 +407,8 @@ namespace daw { [[nodiscard]] friend constexpr bool operator<( contiguous_view const &x, contiguous_view const &y ) { - return std::lexicographical_compare( x.data( ), x.data_end( ), y.data( ), - y.data_end( ) ); + return std::lexicographical_compare( + x.data( ), x.data_end( ), y.data( ), y.data_end( ) ); } [[nodiscard]] friend constexpr bool operator>( contiguous_view const &x, @@ -593,7 +603,7 @@ namespace daw { } constexpr void remove_prefix( std::size_t count ) noexcept { - count = ( std::min )( count, size( ) ); + count = (std::min)( count, size( ) ); m_first += count; } @@ -608,7 +618,7 @@ namespace daw { } constexpr void remove_suffix( std::size_t count ) noexcept { - count = ( std::min )( count, size( ) ); + count = (std::min)( count, size( ) ); m_last -= count; } @@ -764,8 +774,8 @@ namespace daw { [[nodiscard]] friend constexpr bool operator<( contiguous_view const &x, contiguous_view const &y ) { - return std::lexicographical_compare( x.data( ), x.data_end( ), y.data( ), - y.data_end( ) ); + return std::lexicographical_compare( + x.data( ), x.data_end( ), y.data( ), y.data_end( ) ); } [[nodiscard]] friend constexpr bool operator>( contiguous_view const &x, @@ -794,8 +804,15 @@ namespace daw { contiguous_view( Container &&c ) -> contiguous_view>; + template + contiguous_view( Container &&c, std::size_t ) + -> contiguous_view>; + template contiguous_view( T ( & )[N] ) -> contiguous_view; + template + contiguous_view( T ( & )[N], std::size_t ) -> contiguous_view; + } // namespace daw DAW_UNSAFE_BUFFER_FUNC_STOP From a24653429d98bacb4c19c4528d58afe42214121a Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 4 Jan 2026 00:10:27 -0500 Subject: [PATCH 22/43] Add variadic overload to `create_bitset_from_set_positions` and improve debug checks. Update tests for enhanced coverage. --- include/daw/daw_bitset_helper.h | 36 ++++++++++++++++++++++++-------- tests/daw_bitset_helper_test.cpp | 11 +++++++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/include/daw/daw_bitset_helper.h b/include/daw/daw_bitset_helper.h index eae11b5d9..23387b4f4 100644 --- a/include/daw/daw_bitset_helper.h +++ b/include/daw/daw_bitset_helper.h @@ -13,7 +13,6 @@ #include "daw/traits/daw_traits_integer_type.h" #include -#include #include namespace daw { @@ -22,29 +21,48 @@ namespace daw { DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( auto const &r ) { auto result = std::bitset{ }; - using value_t = - daw::traits::integral_type_t>; for( auto const &position : r ) { - assert( ( std::is_unsigned_v or - static_cast( position ) >= 0 ) ); - assert( static_cast( position ) < N ); +#ifndef NDEBUG + using value_t = daw::traits::integral_type_t< + std::ranges::range_value_t>; + daw_ensure( ( std::is_unsigned_v or + static_cast( position ) >= 0 ) ); + daw_ensure( static_cast( position ) < N ); +#endif result.set( static_cast( position ) ); } return result; } - } // namespace create_bs_impl template requires( std::ranges::range and daw::traits::IntegerEnum> ) // - DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( R const &r ) { + DAW_ATTRIB_FLATTEN DAW_CPP23_CX_BITSET_FN + auto create_bitset_from_set_positions( R const &r ) { return create_bs_impl::create_bitset_from_set_positions( r ); } template - DAW_CPP23_CX_BITSET_FN auto + DAW_ATTRIB_FLATTEN DAW_CPP23_CX_BITSET_FN auto create_bitset_from_set_positions( std::initializer_list r ) { return create_bs_impl::create_bitset_from_set_positions( r ); } + + template + DAW_ATTRIB_FLATTEN DAW_CPP23_CX_BITSET_FN auto + create_bitset_from_set_positions( traits::IntegerEnum auto... positions ) { + auto result = std::bitset{ }; + ( result.set( [&]( auto position ) { +#ifndef NDEBUG + using value_t = daw::traits::integral_type_t; + daw_ensure( ( std::is_unsigned_v or + static_cast( position ) >= 0 ) ); + daw_ensure( static_cast( position ) < N ); +#endif + return static_cast( position ); + }( positions ) ), + ... ); + return result; + } } // namespace daw diff --git a/tests/daw_bitset_helper_test.cpp b/tests/daw_bitset_helper_test.cpp index 820512f70..8801d0926 100644 --- a/tests/daw_bitset_helper_test.cpp +++ b/tests/daw_bitset_helper_test.cpp @@ -7,6 +7,7 @@ // #include "daw/daw_bitset_helper.h" +#include "daw/daw_do_not_optimize.h" #include #include @@ -14,8 +15,7 @@ #include int main( ) { - DAW_CPP23_CX_BITSET auto const bs0 = - daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); + auto const bs0 = daw::create_bitset_from_set_positions<16>( { 1, 2, 5, 10 } ); daw_ensure( bs0.count( ) == 4 ); daw_ensure( bs0.test( 1 ) ); daw_ensure( bs0.test( 2 ) ); @@ -23,7 +23,7 @@ int main( ) { daw_ensure( bs0.test( 10 ) ); enum class EPos { a, b, c, d, e, f }; - DAW_CPP23_CX_BITSET auto const bs1 = + auto const bs1 = daw::create_bitset_from_set_positions<4>( { EPos::a, EPos::d } ); daw_ensure( bs1.count( ) == 2 ); daw_ensure( bs1.test( static_cast( EPos::a ) ) ); @@ -43,4 +43,9 @@ int main( ) { static_assert( bs3.test( static_cast( EPos::a ) ) ); static_assert( bs3.test( static_cast( EPos::d ) ) ); #endif + auto const bs4 = daw::create_bitset_from_set_positions<4>( EPos::a, EPos::d ); + daw::do_not_optimize( ( bs4 ) ); + daw_ensure( bs4.count( ) == 2 ); + daw_ensure( bs4.test( static_cast( EPos::a ) ) ); + daw_ensure( bs4.test( static_cast( EPos::d ) ) ); } From b8629420a3a89516f5ba054922ad40c4c2906930 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 4 Jan 2026 00:41:11 -0500 Subject: [PATCH 23/43] Update `contiguous_view` to improve type deduction for const containers and enhance tests --- include/daw/daw_contiguous_view.h | 9 ++++++--- tests/daw_contiguous_view_test.cpp | 11 ++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/daw/daw_contiguous_view.h b/include/daw/daw_contiguous_view.h index fd76dfd6d..0ef9e053c 100644 --- a/include/daw/daw_contiguous_view.h +++ b/include/daw/daw_contiguous_view.h @@ -801,12 +801,15 @@ namespace daw { contiguous_view( T *, std::size_t ) -> contiguous_view; template - contiguous_view( Container &&c ) - -> contiguous_view>; + contiguous_view( Container &&c ) -> contiguous_view>, + daw::range_value_t const, daw::range_value_t>>; template contiguous_view( Container &&c, std::size_t ) - -> contiguous_view>; + -> contiguous_view>, + daw::range_value_t const, daw::range_value_t>>; template contiguous_view( T ( & )[N] ) -> contiguous_view; diff --git a/tests/daw_contiguous_view_test.cpp b/tests/daw_contiguous_view_test.cpp index 48104e02f..5331734e0 100644 --- a/tests/daw_contiguous_view_test.cpp +++ b/tests/daw_contiguous_view_test.cpp @@ -6,7 +6,9 @@ // Official repository: https://github.com/beached/header_libraries // -#include +#include "daw/daw_contiguous_view.h" + +#include #include @@ -15,6 +17,9 @@ DAW_CONSTEVAL int foo( daw::contiguous_view vals ) { } int main( ) { - constexpr int vals[] = { 1, 2, 3 }; - return foo( vals ) == 6 ? 0 : foo( vals ); + static constexpr int vals[] = { 1, 2, 3 }; + constexpr auto cv = daw::contiguous_view( vals ); + static_assert( foo( cv ) == 6 ); + constexpr auto vals2 = std::array{ 1, 2, 3 }; + static_assert( daw::contiguous_view( vals2 )[0] == 1 ); } \ No newline at end of file From 12bf6ed098745e9344785cb39bf6233728dbcbfa Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 4 Jan 2026 00:49:15 -0500 Subject: [PATCH 24/43] Refactor `contiguous_view` to add constructor for subset ranges with bounds check and enhance tests for improved coverage --- include/daw/daw_contiguous_view.h | 17 ++++++++++++++--- tests/daw_contiguous_view_test.cpp | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/include/daw/daw_contiguous_view.h b/include/daw/daw_contiguous_view.h index 0ef9e053c..c9b0071e5 100644 --- a/include/daw/daw_contiguous_view.h +++ b/include/daw/daw_contiguous_view.h @@ -11,6 +11,7 @@ #include "daw/daw_concepts.h" #include "daw/daw_consteval.h" #include "daw/daw_data_end.h" +#include "daw/daw_ensure.h" #include "daw/daw_utility.h" #include "daw/impl/daw_view_tags.h" #include "daw/wrap_iter.h" @@ -27,7 +28,8 @@ namespace daw { struct contiguous_view; template - requires( not std::is_const_v ) struct contiguous_view { + requires( not std::is_const_v ) // + struct contiguous_view { using value_type = T; using reference = value_type &; using const_reference = value_type const &; @@ -428,8 +430,8 @@ namespace daw { }; template - requires( std::is_const_v ) struct contiguous_view { - + requires( std::is_const_v ) // + struct contiguous_view { using value_type = T; using reference = value_type &; using const_reference = value_type const &; @@ -468,6 +470,15 @@ namespace daw { : m_first( std::data( c ) ) , m_last( daw::data_end( c ) ) {} + template Container> + requires( not_cvref_of ) explicit( + ExplicitConv ) constexpr contiguous_view( Container &&c, + std::size_t count ) noexcept + : m_first( std::data( c ) ) + , m_last( std::next( m_first, static_cast( count ) ) ) { + daw_ensure( count <= std::size( c ) ); + } + template Container> explicit constexpr operator Container( ) const noexcept { return Container{ data( ), size( ) }; diff --git a/tests/daw_contiguous_view_test.cpp b/tests/daw_contiguous_view_test.cpp index 5331734e0..d54bf4f6d 100644 --- a/tests/daw_contiguous_view_test.cpp +++ b/tests/daw_contiguous_view_test.cpp @@ -12,14 +12,26 @@ #include -DAW_CONSTEVAL int foo( daw::contiguous_view vals ) { +int foo( daw::contiguous_view vals ) { + return std::accumulate( vals.begin( ), vals.end( ), 0 ); +} + +int foo( daw::contiguous_view vals ) { return std::accumulate( vals.begin( ), vals.end( ), 0 ); } int main( ) { static constexpr int vals[] = { 1, 2, 3 }; constexpr auto cv = daw::contiguous_view( vals ); - static_assert( foo( cv ) == 6 ); + daw_ensure( foo( cv ) == 6 ); constexpr auto vals2 = std::array{ 1, 2, 3 }; static_assert( daw::contiguous_view( vals2 )[0] == 1 ); + static_assert( daw::contiguous_view( vals2, 1 )[0] == 1 ); + + int vals3[] = { 1, 2, 3 }; + auto cv3 = daw::contiguous_view( vals3 ); + daw_ensure( foo( cv3 ) == 6 ); + constexpr auto vals4 = std::array{ 1, 2, 3 }; + daw_ensure( daw::contiguous_view( vals4 )[0] == 1 ); + daw_ensure( daw::contiguous_view( vals4, 1 )[0] == 1 ); } \ No newline at end of file From 4eb4e5bc16c283432b05df403136822755ef3a26 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 4 Jan 2026 14:45:41 -0500 Subject: [PATCH 25/43] Add `Copy` pipeline utility and `unique_range` implementation, refactor `unique_view` to use `unique_range`, and introduce test for unique sorted range --- include/daw/daw_contiguous_view.h | 18 +++++----- include/daw/daw_pipelines.h | 1 + include/daw/pipelines/algorithm.h | 58 ++++++++++++++++++------------- include/daw/pipelines/copy.h | 32 +++++++++++++++++ include/daw/pipelines/pipeline.h | 15 ++++++-- include/daw/pipelines/to.h | 4 +-- include/daw/pipelines/unique.h | 44 ++++++++++++++++++++++- tests/daw_pipelines_test.cpp | 11 ++++++ 8 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 include/daw/pipelines/copy.h diff --git a/include/daw/daw_contiguous_view.h b/include/daw/daw_contiguous_view.h index c9b0071e5..24e374965 100644 --- a/include/daw/daw_contiguous_view.h +++ b/include/daw/daw_contiguous_view.h @@ -805,28 +805,26 @@ namespace daw { } }; - template - contiguous_view( T *, T * ) -> contiguous_view; + template + contiguous_view( P, P ) -> contiguous_view>; - template - contiguous_view( T *, std::size_t ) -> contiguous_view; + template + contiguous_view( P, SizeT ) -> contiguous_view>; template contiguous_view( Container &&c ) -> contiguous_view>, daw::range_value_t const, daw::range_value_t>>; - template - contiguous_view( Container &&c, std::size_t ) - -> contiguous_view>, - daw::range_value_t const, daw::range_value_t>>; + template + contiguous_view( Container &&c, SizeT ) -> contiguous_view>, + daw::range_value_t const, daw::range_value_t>>; template contiguous_view( T ( & )[N] ) -> contiguous_view; template contiguous_view( T ( & )[N], std::size_t ) -> contiguous_view; - } // namespace daw DAW_UNSAFE_BUFFER_FUNC_STOP diff --git a/include/daw/daw_pipelines.h b/include/daw/daw_pipelines.h index 9d39c2ba7..9146f53ad 100644 --- a/include/daw/daw_pipelines.h +++ b/include/daw/daw_pipelines.h @@ -11,6 +11,7 @@ #include "daw/pipelines/algorithm.h" #include "daw/pipelines/chunk.h" #include "daw/pipelines/concat.h" +#include "daw/pipelines/copy.h" #include "daw/pipelines/drop.h" #include "daw/pipelines/enumerate.h" #include "daw/pipelines/every.h" diff --git a/include/daw/pipelines/algorithm.h b/include/daw/pipelines/algorithm.h index 35f620c79..1682b51d5 100644 --- a/include/daw/pipelines/algorithm.h +++ b/include/daw/pipelines/algorithm.h @@ -34,9 +34,9 @@ namespace daw::pipelines { return Sort_t{ DAW_FWD( compare ) }; } - [[nodiscard]] constexpr decltype( auto ) - operator( )( Sortable auto &&r ) const { - std::sort( std::begin( r ), std::end( r ), + [[nodiscard]] constexpr auto operator( )( Sortable auto &&r ) const { + std::sort( std::begin( r ), + std::end( r ), [&]( auto const &lhs, auto const &rhs ) { return std::invoke( m_compare, std::invoke( m_projection, lhs ), @@ -61,19 +61,22 @@ namespace daw::pipelines { template requires( not Range ) // - [[nodiscard]] DAW_CPP23_STATIC_CALL_OP constexpr auto - operator( )( C &&compare, P &&projection = Projection{ } ) + [[nodiscard]] DAW_CPP23_STATIC_CALL_OP + constexpr auto operator( )( C &&compare, + P &&projection = Projection{ } ) DAW_CPP23_STATIC_CALL_OP_CONST { return Max_t{ DAW_FWD( compare ), DAW_FWD( projection ) }; } [[nodiscard]] constexpr auto operator( )( Range auto &&r ) const { - return std::max_element( - std::begin( r ), std::end( r ), - [&]( auto const &lhs, auto const &rhs ) { - return std::invoke( m_compare, std::invoke( m_projection, lhs ), - std::invoke( m_projection, rhs ) ); - } ); + return std::max_element( std::begin( r ), + std::end( r ), + [&]( auto const &lhs, auto const &rhs ) { + return std::invoke( + m_compare, + std::invoke( m_projection, lhs ), + std::invoke( m_projection, rhs ) ); + } ); } }; Max_t( ) -> Max_t<>; @@ -92,19 +95,22 @@ namespace daw::pipelines { template requires( not Range ) // - [[nodiscard]] DAW_CPP23_STATIC_CALL_OP constexpr auto - operator( )( C &&compare, P &&projection = Projection{ } ) + [[nodiscard]] DAW_CPP23_STATIC_CALL_OP + constexpr auto operator( )( C &&compare, + P &&projection = Projection{ } ) DAW_CPP23_STATIC_CALL_OP_CONST { return Min_t{ DAW_FWD( compare ), DAW_FWD( projection ) }; } [[nodiscard]] constexpr auto operator( )( Range auto &&r ) const { - return std::min_element( - std::begin( r ), std::end( r ), - [&]( auto const &lhs, auto const &rhs ) { - return std::invoke( m_compare, std::invoke( m_projection, lhs ), - std::invoke( m_projection, rhs ) ); - } ); + return std::min_element( std::begin( r ), + std::end( r ), + [&]( auto const &lhs, auto const &rhs ) { + return std::invoke( + m_compare, + std::invoke( m_projection, lhs ), + std::invoke( m_projection, rhs ) ); + } ); } }; Min_t( ) -> Min_t<>; @@ -123,17 +129,20 @@ namespace daw::pipelines { template requires( not Range ) // - [[nodiscard]] DAW_CPP23_STATIC_CALL_OP constexpr auto - operator( )( C &&compare, P &&projection = Projection{ } ) + [[nodiscard]] DAW_CPP23_STATIC_CALL_OP + constexpr auto operator( )( C &&compare, + P &&projection = Projection{ } ) DAW_CPP23_STATIC_CALL_OP_CONST { return MinMax_t{ DAW_FWD( compare ), DAW_FWD( projection ) }; } [[nodiscard]] constexpr auto operator( )( Range auto &&r ) const { auto result = std::minmax_element( - std::begin( r ), std::end( r ), + std::begin( r ), + std::end( r ), [&]( auto const &lhs, auto const &rhs ) { - return std::invoke( m_compare, std::invoke( m_projection, lhs ), + return std::invoke( m_compare, + std::invoke( m_projection, lhs ), std::invoke( m_projection, rhs ) ); } ); return std::tuple{ result.first, result.second }; @@ -197,7 +206,8 @@ namespace daw::pipelines { [[nodiscard]] constexpr auto Contains( auto &&needle, auto &&compare, auto &&projection ) { return pimpl::Contains_t{ - DAW_FWD( needle ), DAW_FWD( compare ), + DAW_FWD( needle ), + DAW_FWD( compare ), DAW_FWD( projection ) }; // namespace daw::pipelines } } // namespace daw::pipelines \ No newline at end of file diff --git a/include/daw/pipelines/copy.h b/include/daw/pipelines/copy.h new file mode 100644 index 000000000..da0e1f5a3 --- /dev/null +++ b/include/daw/pipelines/copy.h @@ -0,0 +1,32 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/daw_attributes.h" +#include "daw/daw_iterator_traits.h" +#include "daw/daw_move.h" +#include "daw/pipelines/range.h" + +namespace daw::pipelines::pimpl { + struct Copy_t { + explicit Copy_t( ) = default; + + template + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( T &&value ) DAW_CPP23_STATIC_CALL_OP_CONST { + return DAW_FWD( value ); + } + }; +} // namespace daw::pipelines::pimpl + +namespace daw::pipelines { + constexpr auto Copy( ) { + return pimpl::Copy_t{ }; + } +} // namespace daw::pipelines diff --git a/include/daw/pipelines/pipeline.h b/include/daw/pipelines/pipeline.h index f3c4b5ae9..c9610f66f 100644 --- a/include/daw/pipelines/pipeline.h +++ b/include/daw/pipelines/pipeline.h @@ -38,12 +38,21 @@ namespace daw::pipelines::pimpl { [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr auto pipeline( std::tuple const &tpfns, R &&r, Ranges &&...ranges ) { using std::get; + /* + if constexpr( Idx > 0 ) { + return std::invoke( + get( tpfns ), + pipeline( tpfns, DAW_FWD( r ), DAW_FWD( ranges )... ) ); + } else { + return std::invoke( get<0>( tpfns ), DAW_FWD( r ), DAW_FWD( ranges )... + ); + } + */ if constexpr( Idx > 0 ) { - return std::invoke( - get( tpfns ), + return get( tpfns )( pipeline( tpfns, DAW_FWD( r ), DAW_FWD( ranges )... ) ); } else { - return std::invoke( get<0>( tpfns ), DAW_FWD( r ), DAW_FWD( ranges )... ); + return get<0>( tpfns )( DAW_FWD( r ), DAW_FWD( ranges )... ); } } diff --git a/include/daw/pipelines/to.h b/include/daw/pipelines/to.h index d981a5080..b42a4b29a 100644 --- a/include/daw/pipelines/to.h +++ b/include/daw/pipelines/to.h @@ -48,9 +48,7 @@ namespace daw::pipelines::pimpl { operator( )( R &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { #if defined( __cpp_lib_containers_ranges ) #if __cpp_lib_containers_ranges >= 202202L - if constexpr( requires { - Container( std::from_range, DAW_FWD( r ) ); - } ) { + if constexpr( requires { Container( std::from_range, DAW_FWD( r ) ); } ) { return Container( std::from_range, DAW_FWD( r ) ); } else { #endif diff --git a/include/daw/pipelines/unique.h b/include/daw/pipelines/unique.h index b63c5c489..a4d150af7 100644 --- a/include/daw/pipelines/unique.h +++ b/include/daw/pipelines/unique.h @@ -100,11 +100,53 @@ namespace daw::pipelines { template unique_view( I ) -> unique_view; + template + struct unique_range { + using value_type = daw::range_value_t; + static_assert( requires( value_type const &v ) { v == v; } ); + using iterator = unique_view>; + + private: + R m_range; + + public: + explicit constexpr unique_range( R const &r ) noexcept + : m_range{ r } {} + + explicit constexpr unique_range( R &&r ) noexcept + : m_range{ std::move( r ) } {} + + [[nodiscard]] constexpr iterator begin( ) { + return iterator{ std::begin( m_range ), std::end( m_range ) }; + } + + [[nodiscard]] constexpr iterator begin( ) const { + return iterator{ std::begin( m_range ), std::end( m_range ) }; + } + + [[nodiscard]] constexpr iterator end( ) { + return iterator{ std::end( m_range ), std::end( m_range ) }; + } + + [[nodiscard]] constexpr iterator end( ) const { + return iterator{ std::end( m_range ), std::end( m_range ) }; + } + + [[nodiscard]] constexpr friend bool + operator==( unique_range const &lhs, unique_range const &rhs ) = default; + + [[nodiscard]] constexpr friend bool + operator!=( unique_range const &lhs, unique_range const &rhs ) = default; + }; + + template + unique_range( R ) -> unique_range; + namespace pimpl { struct Unique_t { [[nodiscard]] DAW_CPP23_STATIC_CALL_OP constexpr auto operator( )( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { - return unique_view( std::begin( r ), std::end( r ) ); + return unique_range{ DAW_FWD( r ) }; } }; } // namespace pimpl diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 0415104d8..c0bb3eece 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -644,9 +644,18 @@ namespace tests { buff[0] = 'h'; daw_ensure( p.value( ) == 'H' ); } + + DAW_ATTRIB_NOINLINE void test038( ) { + constexpr auto values = std::array{ 1, 5, 5, 10, 32 }; + auto const unique_values = + pipeline( values, Copy, Sort, Unique, To ); + daw_ensure( unique_values.size( ) == 4 ); + daw_ensure( unique_values == std::vector{ 1, 5, 10, 32 } ); + } } // namespace tests int main( ) { + /* tests::test001( ); tests::test002( ); tests::test003( ); @@ -684,6 +693,8 @@ int main( ) { tests::test035( ); tests::test036( ); tests::test037( ); + */ + tests::test038( ); daw::println( "Done" ); } From ea6fb756ddc02cc7e2e5712ac77f569a30d460f0 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 4 Jan 2026 15:06:30 -0500 Subject: [PATCH 26/43] Add `forward_lvalue` utility to improve handling of rvalue references in pipelines, refactor `unique_t` and `Sort_t` to utilize it, and adjust tests --- include/daw/daw_forward_lvalue.h | 22 ++++++++++++++++++++++ include/daw/pipelines/algorithm.h | 6 ++++-- include/daw/pipelines/unique.h | 6 +++++- tests/daw_pipelines_test.cpp | 2 -- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 include/daw/daw_forward_lvalue.h diff --git a/include/daw/daw_forward_lvalue.h b/include/daw/daw_forward_lvalue.h new file mode 100644 index 000000000..7f118b8c2 --- /dev/null +++ b/include/daw/daw_forward_lvalue.h @@ -0,0 +1,22 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include + +namespace daw { + template + DAW_ATTRIB_INLINE constexpr decltype( auto ) forward_lvalue( T &&r ) { + if constexpr( std::is_lvalue_reference_v ) { + return std::forward( r ); + } else { + return static_cast>( r ); + } + } +} // namespace daw diff --git a/include/daw/pipelines/algorithm.h b/include/daw/pipelines/algorithm.h index 1682b51d5..73a1e877d 100644 --- a/include/daw/pipelines/algorithm.h +++ b/include/daw/pipelines/algorithm.h @@ -9,6 +9,7 @@ #pragma once #include "daw/daw_attributes.h" +#include "daw/daw_forward_lvalue.h" #include "daw/daw_iterator_traits.h" #include "daw/daw_move.h" #include "daw/pipelines/range.h" @@ -34,7 +35,8 @@ namespace daw::pipelines { return Sort_t{ DAW_FWD( compare ) }; } - [[nodiscard]] constexpr auto operator( )( Sortable auto &&r ) const { + [[nodiscard]] constexpr decltype( auto ) + operator( )( Sortable auto &&r ) const { std::sort( std::begin( r ), std::end( r ), [&]( auto const &lhs, auto const &rhs ) { @@ -42,7 +44,7 @@ namespace daw::pipelines { std::invoke( m_projection, lhs ), std::invoke( m_projection, rhs ) ); } ); - return DAW_FWD( r ); + return daw::forward_lvalue( r ); } }; Sort_t( ) -> Sort_t, std::identity>; diff --git a/include/daw/pipelines/unique.h b/include/daw/pipelines/unique.h index a4d150af7..5b5b10b46 100644 --- a/include/daw/pipelines/unique.h +++ b/include/daw/pipelines/unique.h @@ -146,7 +146,11 @@ namespace daw::pipelines { struct Unique_t { [[nodiscard]] DAW_CPP23_STATIC_CALL_OP constexpr auto operator( )( Range auto &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { - return unique_range{ DAW_FWD( r ) }; + if constexpr( std::is_rvalue_reference_v ) { + return unique_range{ DAW_FWD( r ) }; + } else { + return unique_view{ DAW_FWD( r ) }; + } } }; } // namespace pimpl diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index c0bb3eece..73b79ae7c 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -655,7 +655,6 @@ namespace tests { } // namespace tests int main( ) { - /* tests::test001( ); tests::test002( ); tests::test003( ); @@ -693,7 +692,6 @@ int main( ) { tests::test035( ); tests::test036( ); tests::test037( ); - */ tests::test038( ); daw::println( "Done" ); From 8b00adb9a0bc0d663d601ed2a3cb94503c0e48f5 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Tue, 6 Jan 2026 21:52:37 -0500 Subject: [PATCH 27/43] Add `maybe_unique_ptr` implementation, corresponding helper functions, and tests. Enhance `unique_ptr` deleter access methods. --- include/daw/daw_maybe_unique_ptr.h | 312 ++++++++++++++++++++++++++++ include/daw/daw_unique_ptr.h | 14 +- tests/CMakeLists.txt | 3 +- tests/daw_maybe_unique_ptr_test.cpp | 89 ++++++++ 4 files changed, 414 insertions(+), 4 deletions(-) create mode 100644 include/daw/daw_maybe_unique_ptr.h create mode 100644 tests/daw_maybe_unique_ptr_test.cpp diff --git a/include/daw/daw_maybe_unique_ptr.h b/include/daw/daw_maybe_unique_ptr.h new file mode 100644 index 000000000..a42ff11fe --- /dev/null +++ b/include/daw/daw_maybe_unique_ptr.h @@ -0,0 +1,312 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/daw_concepts.h" +#include "daw/daw_move.h" +#include "daw/daw_unique_ptr.h" + +namespace daw { + template> + struct maybe_unique_ptr : private Deleter { + using value_type = T; + using pointer = unique_ptr_del::pointer_type_t; + using reference = std::add_lvalue_reference_t; + using deleter_t = Deleter; + + private: + daw::unique_ptr m_ptr = nullptr; + bool m_owned = true; + + template + friend struct maybe_unique_ptr; + + public: + maybe_unique_ptr( ) = default; + + explicit constexpr maybe_unique_ptr( pointer ptr ) noexcept + : m_ptr( ptr ) {} + + template + requires( not std::is_pointer_v ) // + explicit constexpr maybe_unique_ptr( pointer ptr, B owned ) noexcept + : m_ptr( ptr ) + , m_owned( owned ) {} + + template D> + explicit constexpr maybe_unique_ptr( pointer ptr, D &&deleter ) noexcept + : m_ptr( ptr, DAW_FWD( deleter ) ) {} + + template D, typename B> + requires( not std::is_pointer_v ) // + explicit constexpr maybe_unique_ptr( pointer ptr, D &&deleter, + B owned ) noexcept + : m_ptr( ptr, DAW_FWD( deleter ) ) + , m_owned( owned ) {} + + template + requires( not std::is_same_v ) // + constexpr maybe_unique_ptr( U *p ) noexcept + : m_ptr( p ) { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + } + + template + requires( not std::is_same_v and not std::is_pointer_v ) // + constexpr maybe_unique_ptr( U *p, B owned ) noexcept + : m_ptr( p ) + , m_owned( owned ) { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + } + + constexpr maybe_unique_ptr( std::nullptr_t ) noexcept {} + + constexpr maybe_unique_ptr( maybe_unique_ptr &&other ) noexcept + : m_ptr( other.release( ) ) + , m_owned( std::exchange( other.m_owned, true ) ) {} + + template + requires( not std::is_same_v ) // + constexpr maybe_unique_ptr( maybe_unique_ptr &&other ) noexcept + : Deleter( static_cast( other ) ) + , m_ptr( other.release( ) ) + , m_owned( std::exchange( other.m_owned, true ) ) { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + } + + template + requires( not std::is_same_v ) // + constexpr maybe_unique_ptr( unique_ptr &&other ) noexcept + : Deleter( std::move( other ).get_deleter( ) ) + , m_ptr( other.release( ) ) { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + } + + constexpr maybe_unique_ptr &operator=( maybe_unique_ptr &&rhs ) noexcept { + if( this != &rhs ) { + reset( ); + m_ptr = rhs.release( ); + m_owned = std::exchange( rhs.m_owned, true ); + } + return *this; + } + + template + requires( not std::is_same_v ) // + constexpr maybe_unique_ptr & + operator=( maybe_unique_ptr &&rhs ) noexcept { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + m_ptr = std::move( rhs.m_ptr ); + m_owned = std::exchange( rhs.m_owned, true ); + return *this; + } + + template + requires( not std::is_same_v ) // + constexpr maybe_unique_ptr &operator=( unique_ptr &&rhs ) noexcept { + static_assert( unique_ptr_details::is_safe_child_type_v, + "One cannot destruct U, if T does not have a virtual " + "destructor, and U being a child of T" ); + m_ptr = std::move( rhs ); + m_owned = std::exchange( rhs.m_owned, true ); + return *this; + } + + constexpr maybe_unique_ptr( maybe_unique_ptr const & ) = delete; + constexpr maybe_unique_ptr &operator=( maybe_unique_ptr const & ) = delete; + + DAW_CPP20_CX_ALLOC ~maybe_unique_ptr( ) noexcept { + reset( ); + } + + constexpr pointer release( ) noexcept { + m_owned = true; + return m_ptr.release( ); + } + + constexpr deleter_t &get_deleter( ) & { + return m_ptr.get_deleter( ); + } + + constexpr deleter_t const &get_deleter( ) const & { + return m_ptr.get_deleter( ); + } + + constexpr deleter_t &&get_deleter( ) && { + return std::move( m_ptr.get_deleter( ) ); + } + + constexpr deleter_t const &&get_deleter( ) const && { + return std::move( m_ptr.get_deleter( ) ); + } + + constexpr void reset( ) noexcept { + if( m_owned ) { + m_ptr.reset( ); + } else { + (void)m_ptr.release( ); + m_owned = true; + } + } + + constexpr void reset( pointer p, bool owned = true ) noexcept { + if( m_owned ) { + m_ptr.reset( std::move( p ) ); + } else { + (void)m_ptr.release( ); + } + m_ptr.reset( std::move( p ) ); + m_owned = owned; + } + + constexpr pointer get( ) const { + return m_ptr.get( ); + } + + constexpr pointer *get_out( ) noexcept { + return m_ptr.get_out( ); + } + + constexpr pointer operator->( ) const { + return m_ptr.get( ); + } + + constexpr reference operator*( ) const noexcept { + return *m_ptr; + } + + explicit constexpr operator bool( ) const noexcept { + return static_cast( m_ptr ); + } + + constexpr void swap( maybe_unique_ptr &other ) noexcept { + using std::swap; + swap( m_ptr, other.m_ptr ); + swap( m_owned, other.m_owned ); + } + + constexpr decltype( auto ) operator[]( std::size_t index ) const noexcept + requires( requires { m_ptr[std::size_t{ }]; } ) { + return m_ptr[index]; + } + + template + constexpr auto and_then( F &&func ) && { + return monadic_ptr::and_then( std::move( *this ), DAW_FWD( func ) ); + } + + template + constexpr auto or_else( F &&func ) && { + return monadic_ptr::or_else( std::move( *this ), DAW_FWD( func ) ); + } + + template + constexpr auto transform( F &&func ) & { + return monadic_ptr::transform( *this, DAW_FWD( func ) ); + } + + template + constexpr auto transform( F &&func ) const & { + return monadic_ptr::transform( *this, DAW_FWD( func ) ); + } + + template + constexpr auto transform( F &&func ) && { + return monadic_ptr::transform( std::move( *this ), DAW_FWD( func ) ); + } + + template + constexpr auto transform( F &&func ) const && { + return monadic_ptr::transform( std::move( *this ), DAW_FWD( func ) ); + } + }; + + template + DAW_CPP20_CX_ALLOC auto make_maybe_unique( Args &&...args ) + -> std::enable_if_t, maybe_unique_ptr> { + if constexpr( std::is_aggregate_v ) { + return maybe_unique_ptr( new T{ DAW_FWD( args )... } ); + } else { + return maybe_unique_ptr( new T( DAW_FWD( args )... ) ); + } + } + + template + DAW_CPP20_CX_ALLOC auto make_maybe_unique_for_overwrite( ) + -> std::enable_if_t, maybe_unique_ptr> { + return maybe_unique_ptr( new T ); + } + + template + DAW_CPP20_CX_ALLOC auto make_maybe_unique( std::size_t count ) + -> std::enable_if_t, maybe_unique_ptr> { + using value_type = std::remove_extent_t; + static_assert( daw::is_unbounded_array_v, + "It is an error to specify a bound on the array type. e.g " + "T[3], use T[] instead" ); + return maybe_unique_ptr( new value_type[count]{ } ); + } + + template + DAW_CPP20_CX_ALLOC auto make_maybe_unique_array( Elements &&...elements ) + -> std::enable_if_t, maybe_unique_ptr> { + static_assert( daw::is_unbounded_array_v, + "It is an error to specify a bound on the array type. e.g " + "T[3], use T[] instead" ); + using value_type = std::remove_extent_t; + return maybe_unique_ptr( + new value_type[sizeof...( Elements )]{ DAW_FWD( elements )... } ); + } + + template + constexpr bool operator==( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return lhs.get( ) == rhs.get( ); + } + + template + constexpr bool operator!=( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return lhs.get( ) != rhs.get( ); + } + + template + constexpr bool operator<( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return std::less{ }( lhs.get( ), rhs.get( ) ); + } + + template + constexpr bool operator<=( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return not( lhs < rhs ); + } + + template + constexpr bool operator>( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return rhs < lhs; + } + + template + constexpr bool operator>=( maybe_unique_ptr const &lhs, + maybe_unique_ptr const &rhs ) noexcept { + return not( lhs < rhs ); + } +} // namespace daw diff --git a/include/daw/daw_unique_ptr.h b/include/daw/daw_unique_ptr.h index 0d6e439ef..8cef9448b 100644 --- a/include/daw/daw_unique_ptr.h +++ b/include/daw/daw_unique_ptr.h @@ -176,14 +176,22 @@ namespace daw { m_ptr, unique_ptr_del::default_pointer_value_v ); } - constexpr deleter_t &get_deleter( ) { + constexpr deleter_t &get_deleter( ) & { return *static_cast( this ); } - constexpr deleter_t const &get_deleter( ) const { + constexpr deleter_t const &get_deleter( ) const & { return *static_cast( this ); } + constexpr deleter_t &&get_deleter( ) && { + return std::move( *static_cast( this ) ); + } + + constexpr deleter_t const &&get_deleter( ) const && { + return std::move( *static_cast( this ) ); + } + constexpr void reset( ) noexcept { if( unique_ptr_del::does_validity_check_v or m_ptr ) { get_deleter( )( daw::exchange( @@ -200,7 +208,7 @@ namespace daw { return m_ptr; } - constexpr pointer * get_out( ) noexcept { + constexpr pointer *get_out( ) noexcept { return std::addressof( m_ptr ); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 949e35e60..f48a703e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -157,10 +157,11 @@ set( CPP20_TEST_SOURCES daw_formatters_test.cpp daw_from_string_test.cpp daw_iter_view_test.cpp + daw_poly_value_test.cpp + daw_maybe_unique_ptr_test.cpp daw_move_only_test.cpp daw_named_params_test.cpp daw_observer_ptr_test.cpp - daw_poly_value_test.cpp ) set( CPP20_NOT_MSVC_TEST_SOURCES diff --git a/tests/daw_maybe_unique_ptr_test.cpp b/tests/daw_maybe_unique_ptr_test.cpp new file mode 100644 index 000000000..707fbb9be --- /dev/null +++ b/tests/daw_maybe_unique_ptr_test.cpp @@ -0,0 +1,89 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include + +#include +#include + +struct Base { + int x = 55; + Base( ) = default; + Base( int v ) + : x( v ) {} + virtual ~Base( ) = default; +}; + +struct Child : Base { + Child( ) = default; + Child( int v ) + : Base( v ) {} +}; + +int main( ) { + auto a1 = daw::make_maybe_unique( 5 ); + assert( a1 ); + assert( *a1 == 5 ); + auto a2 = daw::make_maybe_unique( 5 ); + assert( a2 ); + assert( *a2 == 5 ); + assert( a1 != a2 ); + auto b = daw::make_maybe_unique( 5 ); + assert( b ); + b[0] = 0; + b[1] = 1; + b[2] = 2; + b[3] = 3; + b[4] = 4; + assert( b[1] == 1 ); + auto c = daw::make_maybe_unique_array( 1, 2, 3, 4, 5 ); + assert( c ); + assert( c[1] == 2 ); + c.reset( ); + assert( not c ); + c = std::move( b ); + assert( c ); + assert( not b ); + assert( c[1] == 1 ); + auto d = std::move( a1 ); + assert( d ); + assert( not a1 ); + assert( *d == 5 ); + a2.reset( ); + assert( not a2 ); + + std::default_delete{ }; + daw::maybe_unique_ptr my_base = daw::make_maybe_unique( ); + int x = -1; + my_base = std::move( my_base ).and_then( [&]( auto p ) { + x = p->x; + return p; + } ); + assert( x == my_base->x ); + my_base = std::move( my_base ).and_then( []( auto p ) { + return daw::make_maybe_unique( p->x ); + } ); + daw::maybe_unique_ptr my_child2 = nullptr; + auto x2 = std::move( my_child2 ) + .or_else( []( ) { + return daw::make_maybe_unique( 55 ); + } ) + .and_then( []( daw::maybe_unique_ptr p ) { + return daw::make_maybe_unique( 42 + p->x ); + } ) + .or_else( []( ) { + return daw::make_maybe_unique( 23 ); + } ) + .transform( []( auto const &p ) { + return p->x; + } ); + assert( x2 == 97 ); +} From 583840bffc375f0d014d8edc1aa5e1969937441c Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Tue, 6 Jan 2026 22:08:34 -0500 Subject: [PATCH 28/43] Add `print_log` utility to log formatted output to `std::clog` and update `print` implementation formatting --- include/daw/daw_print_ostream.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/daw/daw_print_ostream.h b/include/daw/daw_print_ostream.h index 74dcd4b36..7af1d6783 100644 --- a/include/daw/daw_print_ostream.h +++ b/include/daw/daw_print_ostream.h @@ -35,7 +35,17 @@ namespace daw { static_assert( ( has_std_formatter_specialization_v and ... ), "Print requires a std::formatter specialization for each " "Arg type" ); - return daw::print( os, "{}\n", - std::format( std::move( fmt ), DAW_FWD( args )... ) ); + return daw::print( + os, "{}\n", std::format( std::move( fmt ), DAW_FWD( args )... ) ); } + + template + std::ostream &print_log( std::format_string fmt, Args &&...args ) { + static_assert( ( has_std_formatter_specialization_v and ... ), + "print_log requires a std::formatter specialization for each " + "Arg type" ); + return daw::print( + std::clog, "{}\n", std::format( std::move( fmt ), DAW_FWD( args )... ) ); + } + } // namespace daw From 17842e7876eaed4c74de9184e57265e475f6e467 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 24 Jan 2026 16:30:36 -0500 Subject: [PATCH 29/43] Add `elements` pipeline utility with iterator implementation, update `pipeline` function internals, and introduce corresponding tests --- include/daw/daw_pipelines.h | 1 + include/daw/pipelines/elements.h | 219 +++++++++++++++++++++++++++++++ include/daw/pipelines/pipeline.h | 19 +-- tests/daw_pipelines_test.cpp | 48 +++++-- 4 files changed, 260 insertions(+), 27 deletions(-) create mode 100644 include/daw/pipelines/elements.h diff --git a/include/daw/daw_pipelines.h b/include/daw/daw_pipelines.h index 9146f53ad..0093c6ea3 100644 --- a/include/daw/daw_pipelines.h +++ b/include/daw/daw_pipelines.h @@ -13,6 +13,7 @@ #include "daw/pipelines/concat.h" #include "daw/pipelines/copy.h" #include "daw/pipelines/drop.h" +#include "daw/pipelines/elements.h" #include "daw/pipelines/enumerate.h" #include "daw/pipelines/every.h" #include "daw/pipelines/filter.h" diff --git a/include/daw/pipelines/elements.h b/include/daw/pipelines/elements.h new file mode 100644 index 000000000..8dcba0284 --- /dev/null +++ b/include/daw/pipelines/elements.h @@ -0,0 +1,219 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/cpp_17.h" +#include "daw/daw_iterator_traits.h" +#include "daw/daw_move.h" +#include "daw/iterator/daw_arrow_proxy.h" + +#include + +namespace daw::pipelines::pimpl { + template + struct elements_iterator { + using iterator_category = daw::iter_category_t; + using value_type = std::tuple< + std::tuple_element_t>...>; + using reference = value_type; + using const_reference = std::tuple< + std::tuple_element_t>...>; + using pointer = arrow_proxy; + using const_pointer = arrow_proxy; + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + + private: + Iterator m_iter{ }; + + public: + explicit constexpr elements_iterator( ) = default; + explicit constexpr elements_iterator( Iterator it ) + : m_iter( std::move( it ) ) {} + + private: + template + [[nodiscard]] DAW_ATTRIB_INLINE static constexpr auto raw_get( I &&iter ) { + using result_t = + std::tuple>...>; + auto &&r = *DAW_FWD( iter ); + return result_t{ std::get( r )... }; + } + + public: + [[nodiscard]] constexpr auto &base( ) { + return m_iter; + } + + [[nodiscard]] constexpr auto const &base( ) const { + return m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr reference + operator[]( size_type n ) requires( RandomIterator ) { + return raw_get( std::next( m_iter, static_cast( n ) ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_reference + operator[]( size_type n ) const requires( RandomIterator ) { + return raw_get( std::next( m_iter, static_cast( n ) ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr reference operator*( ) { + return raw_get( m_iter ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_reference + operator*( ) const { + return raw_get( m_iter ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr pointer operator->( ) { + return pointer( raw_get( m_iter ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_pointer + operator->( ) const { + return const_pointer( raw_get( m_iter ) ); + } + + DAW_ATTRIB_INLINE constexpr elements_iterator &operator++( ) { + ++m_iter; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr elements_iterator + operator++( int ) { + elements_iterator result = *this; + ++m_iter; + return result; + } + + DAW_ATTRIB_INLINE constexpr elements_iterator &operator--( ) + requires( BidirectionalIterator ) { + --m_iter; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr elements_iterator + operator--( int ) requires( BidirectionalIterator ) { + elements_iterator result = *this; + --m_iter; + return result; + } + + DAW_ATTRIB_INLINE constexpr elements_iterator & + operator+=( difference_type n ) requires( RandomIterator ) { + m_iter += n; + return *this; + } + + DAW_ATTRIB_INLINE constexpr elements_iterator & + operator-=( difference_type n ) requires( RandomIterator ) { + m_iter -= n; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr elements_iterator + operator+( difference_type n ) const noexcept + requires( RandomIterator ) { + elements_iterator result = *this; + m_iter += n; + return result; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr elements_iterator + operator-( difference_type n ) const noexcept + requires( RandomIterator ) { + elements_iterator result = *this; + m_iter -= n; + return result; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr difference_type + operator-( elements_iterator const &rhs ) + requires( RandomIterator ) { + return m_iter - rhs.m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend bool + operator==( elements_iterator const &lhs, elements_iterator const &rhs ) { + return lhs.m_iter == rhs.m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend bool + operator!=( elements_iterator const &lhs, elements_iterator const &rhs ) { + return lhs.m_iter != rhs.m_iter; + } + + // clang-format off + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend auto + operator<=>( elements_iterator const &lhs, elements_iterator const &rhs ) + requires( RandomIterator ) { + return lhs.m_iter <=> rhs.m_iter; + } + // clang-format on + }; + template + elements_iterator( I ) -> elements_iterator; + + template + struct elements_view + : range_base_t< + elements_iterator>, + Indices...>, + elements_iterator>, + Indices...>> { + using range_t = std::remove_reference_t; + using daw_range_base_t = + range_base_t, Indices...>, + elements_iterator, Indices...>>; + + using typename daw_range_base_t::iterator_first_t; + using typename daw_range_base_t::iterator_last_t; + + using value_type = daw::iter_value_t; + + iterator_first_t m_first = iterator_first_t{ }; + iterator_last_t m_last = iterator_last_t{ }; + + explicit elements_view( ) = default; + + template + explicit constexpr elements_view( U &&r ) + : m_first( std::begin( daw::forward_lvalue( r ) ) ) + , m_last( std::end( daw::forward_lvalue( r ) ) ) {} + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr iterator_first_t begin( ) const { + return m_first; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr iterator_last_t end( ) const { + return m_last; + } + }; + + template + struct elements_t { + explicit elements_t( ) = default; + + template + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( R &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { + return elements_view, Indices...>{ + daw::forward_lvalue( r ) }; + } + }; + +} // namespace daw::pipelines::pimpl + +namespace daw::pipelines { + template + inline constexpr auto elements = pimpl::elements_t{ }; +} diff --git a/include/daw/pipelines/pipeline.h b/include/daw/pipelines/pipeline.h index c9610f66f..8d4419998 100644 --- a/include/daw/pipelines/pipeline.h +++ b/include/daw/pipelines/pipeline.h @@ -36,23 +36,14 @@ namespace daw::pipelines::pimpl { template [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr auto - pipeline( std::tuple const &tpfns, R &&r, Ranges &&...ranges ) { + pipeline( std::tuple const &functions, R &&r, Ranges &&...ranges ) { using std::get; - /* - if constexpr( Idx > 0 ) { - return std::invoke( - get( tpfns ), - pipeline( tpfns, DAW_FWD( r ), DAW_FWD( ranges )... ) ); - } else { - return std::invoke( get<0>( tpfns ), DAW_FWD( r ), DAW_FWD( ranges )... - ); - } - */ if constexpr( Idx > 0 ) { - return get( tpfns )( - pipeline( tpfns, DAW_FWD( r ), DAW_FWD( ranges )... ) ); + auto &¶m = + pipeline( functions, DAW_FWD( r ), DAW_FWD( ranges )... ); + return get( functions )( DAW_FWD( param ) ); } else { - return get<0>( tpfns )( DAW_FWD( r ), DAW_FWD( ranges )... ); + return get<0>( functions )( DAW_FWD( r ), DAW_FWD( ranges )... ); } } diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 73b79ae7c..9b12e33e9 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -54,10 +54,10 @@ namespace tests { DAW_ATTRIB_NOINLINE void test001( ) { auto m1 = pipeline( zip_view( prices, costs ), To ); - daw_ensure( m1[prices[0]] == costs[0] ); - daw::println( - "\ntest001: pipeline( zip_view( prices, costs ), To )\n{}", - daw::fmt_range{ m1 } ); + daw_ensure( m1.size( ) == prices.size( ) ); + for( std::size_t n = 0; n < prices.size( ); ++n ) { + daw_ensure( m1[prices[n]] == costs[n] ); + } } struct ToLetter_t { @@ -68,7 +68,11 @@ namespace tests { } else if( i < 52 ) { return static_cast( 'A' + ( i - 26 ) ); } - throw std::out_of_range( "i is out of range" ); + daw::println( stderr, + "{}:{} is out of range", + static_cast( i ), + static_cast( i ) ); + std::terminate( ); } }; inline constexpr auto to_letter = ToLetter_t{ }; @@ -76,17 +80,16 @@ namespace tests { DAW_ATTRIB_NOINLINE void test002( ) { constexpr auto pm2 = pipeline( Map( to_letter ), Enumerate, To ); auto const m2 = pm2( iota_view( 0, 26 ) ); - daw::println( - "\ntest002: pipeline( Map( to_letter ), Enumerate, To )\n{}", - daw::fmt_range( m2 ) ); + for( auto const &[k, v] : m2 ) { + daw_ensure( v == static_cast( 'a' + k ) ); + } constexpr auto pm3 = pipeline( Map( to_letter ), EnumerateWith, To ); auto const m3 = pm3( iota_view( 0, 26 ) ); - daw::println( - "\ntest002: pipeline( Map( to_letter ), EnumerateWith, To " - ")\n{}", - daw::fmt_range( m3 ) ); + for( auto const &[k, v] : m3 ) { + daw_ensure( v == static_cast( 'a' + k ) ); + } for( auto vec = std::vector{ 1, 2, 3 }; auto [index, value] : EnumerateWith( vec ) ) { @@ -637,7 +640,7 @@ namespace tests { daw_ensure( p.has_value( ) and p.value( ) == 'H' ); } - DAW_ATTRIB_NOINLINE void test037( ) { + DAW_ATTRIB_NOINLINE DAW_CONSTEVAL void test037( ) { char buff[] = "Hello"; auto p = pipeline( buff, First ); daw_ensure( p.has_value( ) and p.value( ) == 'H' ); @@ -647,11 +650,29 @@ namespace tests { DAW_ATTRIB_NOINLINE void test038( ) { constexpr auto values = std::array{ 1, 5, 5, 10, 32 }; + daw::do_not_optimize( values ); auto const unique_values = pipeline( values, Copy, Sort, Unique, To ); daw_ensure( unique_values.size( ) == 4 ); daw_ensure( unique_values == std::vector{ 1, 5, 10, 32 } ); } + + DAW_ATTRIB_NOINLINE void test039( ) { + auto z0 = Zip( prices, costs ); + + auto e0 = elements<1>( z0 ); + daw_ensure( std::ranges::distance( e0 ) == costs.size( ) ); + auto eb0 = std::begin( e0 ); + for( std::size_t n = 0; n < costs.size( ); ++n ) { + daw_ensure( std::get<0>( eb0[n] ) == costs[n] ); + } + auto e1 = elements<0>( z0 ); + auto eb1 = std::begin( e1 ); + daw_ensure( std::ranges::distance( e1 ) == prices.size( ) ); + for( std::size_t n = 0; n < prices.size( ); ++n ) { + daw_ensure( std::get<0>( eb1[n] ) == prices[n] ); + } + } } // namespace tests int main( ) { @@ -693,6 +714,7 @@ int main( ) { tests::test036( ); tests::test037( ); tests::test038( ); + tests::test039( ); daw::println( "Done" ); } From 7947234cc6bdd68de25a089d92efa2540b6109ee Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sat, 24 Jan 2026 17:29:38 -0500 Subject: [PATCH 30/43] Add `Element` pipeline utility with iterator/view implementations, update tests to validate functionality, and introduce `Element` shorthand --- include/daw/pipelines/elements.h | 199 ++++++++++++++++++++++++++++++- tests/daw_pipelines_test.cpp | 22 +++- 2 files changed, 215 insertions(+), 6 deletions(-) diff --git a/include/daw/pipelines/elements.h b/include/daw/pipelines/elements.h index 8dcba0284..8ab449954 100644 --- a/include/daw/pipelines/elements.h +++ b/include/daw/pipelines/elements.h @@ -16,6 +16,196 @@ #include namespace daw::pipelines::pimpl { + template + struct element_iterator { + using iterator_category = daw::iter_category_t; + using value_type = + std::tuple_element_t>; + using reference = value_type; + using const_reference = + std::tuple_element_t>; + using pointer = arrow_proxy; + using const_pointer = arrow_proxy; + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + + private: + Iterator m_iter{ }; + + public: + explicit constexpr element_iterator( ) = default; + explicit constexpr element_iterator( Iterator it ) + : m_iter( std::move( it ) ) {} + + private: + template + [[nodiscard]] DAW_ATTRIB_INLINE static constexpr decltype( auto ) + raw_get( I &&iter ) { + return std::get( *DAW_FWD( iter ) ); + } + + public: + [[nodiscard]] constexpr auto &base( ) { + return m_iter; + } + + [[nodiscard]] constexpr auto const &base( ) const { + return m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr reference + operator[]( size_type n ) requires( RandomIterator ) { + return raw_get( std::next( m_iter, static_cast( n ) ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_reference + operator[]( size_type n ) const requires( RandomIterator ) { + return raw_get( std::next( m_iter, static_cast( n ) ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr reference operator*( ) { + return raw_get( m_iter ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_reference + operator*( ) const { + return raw_get( m_iter ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr pointer operator->( ) { + return pointer( raw_get( m_iter ) ); + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr const_pointer + operator->( ) const { + return const_pointer( raw_get( m_iter ) ); + } + + DAW_ATTRIB_INLINE constexpr element_iterator &operator++( ) { + ++m_iter; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr element_iterator + operator++( int ) { + element_iterator result = *this; + ++m_iter; + return result; + } + + DAW_ATTRIB_INLINE constexpr element_iterator &operator--( ) + requires( BidirectionalIterator ) { + --m_iter; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr element_iterator operator--( int ) + requires( BidirectionalIterator ) { + element_iterator result = *this; + --m_iter; + return result; + } + + DAW_ATTRIB_INLINE constexpr element_iterator & + operator+=( difference_type n ) requires( RandomIterator ) { + m_iter += n; + return *this; + } + + DAW_ATTRIB_INLINE constexpr element_iterator & + operator-=( difference_type n ) requires( RandomIterator ) { + m_iter -= n; + return *this; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr element_iterator + operator+( difference_type n ) const noexcept + requires( RandomIterator ) { + element_iterator result = *this; + m_iter += n; + return result; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr element_iterator + operator-( difference_type n ) const noexcept + requires( RandomIterator ) { + element_iterator result = *this; + m_iter -= n; + return result; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr difference_type + operator-( element_iterator const &rhs ) + requires( RandomIterator ) { + return m_iter - rhs.m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend bool + operator==( element_iterator const &lhs, element_iterator const &rhs ) { + return lhs.m_iter == rhs.m_iter; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend bool + operator!=( element_iterator const &lhs, element_iterator const &rhs ) { + return lhs.m_iter != rhs.m_iter; + } + + // clang-format off + [[nodiscard]] DAW_ATTRIB_INLINE constexpr friend auto + operator<=>( element_iterator const &lhs, element_iterator const &rhs ) + requires( RandomIterator ) { + return lhs.m_iter <=> rhs.m_iter; + } + // clang-format on + }; + + template + struct element_view + : range_base_t< + element_iterator>, Index>, + element_iterator>, + Index>> { + using range_t = std::remove_reference_t; + using daw_range_base_t = + range_base_t, Index>, + element_iterator, Index>>; + + using typename daw_range_base_t::iterator_first_t; + using typename daw_range_base_t::iterator_last_t; + + using value_type = daw::iter_value_t; + + iterator_first_t m_first = iterator_first_t{ }; + iterator_last_t m_last = iterator_last_t{ }; + + explicit element_view( ) = default; + + template + explicit constexpr element_view( U &&r ) + : m_first( std::begin( daw::forward_lvalue( r ) ) ) + , m_last( std::end( daw::forward_lvalue( r ) ) ) {} + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr iterator_first_t begin( ) const { + return m_first; + } + + [[nodiscard]] DAW_ATTRIB_INLINE constexpr iterator_last_t end( ) const { + return m_last; + } + }; + + template + struct element_t { + explicit element_t( ) = default; + + template + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( R &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { + return element_view, Index>{ + daw::forward_lvalue( r ) }; + } + }; + template struct elements_iterator { using iterator_category = daw::iter_category_t; @@ -160,8 +350,6 @@ namespace daw::pipelines::pimpl { } // clang-format on }; - template - elements_iterator( I ) -> elements_iterator; template struct elements_view @@ -214,6 +402,9 @@ namespace daw::pipelines::pimpl { } // namespace daw::pipelines::pimpl namespace daw::pipelines { + template + inline constexpr auto Element = pimpl::element_t{ }; + template - inline constexpr auto elements = pimpl::elements_t{ }; -} + inline constexpr auto Elements = pimpl::elements_t{ }; +} // namespace daw::pipelines diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 9b12e33e9..476215da2 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -660,19 +660,36 @@ namespace tests { DAW_ATTRIB_NOINLINE void test039( ) { auto z0 = Zip( prices, costs ); - auto e0 = elements<1>( z0 ); + auto e0 = Elements<1>( z0 ); daw_ensure( std::ranges::distance( e0 ) == costs.size( ) ); auto eb0 = std::begin( e0 ); for( std::size_t n = 0; n < costs.size( ); ++n ) { daw_ensure( std::get<0>( eb0[n] ) == costs[n] ); } - auto e1 = elements<0>( z0 ); + auto e1 = Elements<0>( z0 ); auto eb1 = std::begin( e1 ); daw_ensure( std::ranges::distance( e1 ) == prices.size( ) ); for( std::size_t n = 0; n < prices.size( ); ++n ) { daw_ensure( std::get<0>( eb1[n] ) == prices[n] ); } } + + DAW_ATTRIB_NOINLINE void test040( ) { + auto z0 = Zip( prices, costs ); + + auto e0 = Element<1>( z0 ); + daw_ensure( std::ranges::distance( e0 ) == costs.size( ) ); + auto eb0 = std::begin( e0 ); + for( std::size_t n = 0; n < costs.size( ); ++n ) { + daw_ensure( eb0[n] == costs[n] ); + } + auto e1 = Element<0>( z0 ); + auto eb1 = std::begin( e1 ); + daw_ensure( std::ranges::distance( e1 ) == prices.size( ) ); + for( std::size_t n = 0; n < prices.size( ); ++n ) { + daw_ensure( eb1[n] == prices[n] ); + } + } } // namespace tests int main( ) { @@ -715,6 +732,7 @@ int main( ) { tests::test037( ); tests::test038( ); tests::test039( ); + tests::test040( ); daw::println( "Done" ); } From 1898b5ef9f07c997f76d287691bd7bd0810cfaa7 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Wed, 28 Jan 2026 01:07:14 -0500 Subject: [PATCH 31/43] Add `CacheLast` pipeline utility with iterator/view implementations, update tests to include `CacheLast` validation, and enhance pipeline functionality --- include/daw/daw_pipelines.h | 1 + include/daw/pipelines/cache_last_view.h | 149 ++++++++++++++++++++++++ tests/daw_pipelines_test.cpp | 20 ++++ 3 files changed, 170 insertions(+) create mode 100644 include/daw/pipelines/cache_last_view.h diff --git a/include/daw/daw_pipelines.h b/include/daw/daw_pipelines.h index 0093c6ea3..4f1fd339c 100644 --- a/include/daw/daw_pipelines.h +++ b/include/daw/daw_pipelines.h @@ -9,6 +9,7 @@ #pragma once #include "daw/pipelines/algorithm.h" +#include "daw/pipelines/cache_last_view.h" #include "daw/pipelines/chunk.h" #include "daw/pipelines/concat.h" #include "daw/pipelines/copy.h" diff --git a/include/daw/pipelines/cache_last_view.h b/include/daw/pipelines/cache_last_view.h new file mode 100644 index 000000000..7250bc2fc --- /dev/null +++ b/include/daw/pipelines/cache_last_view.h @@ -0,0 +1,149 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/header_libraries +// + +#pragma once + +#include "daw/daw_iterator_traits.h" +#include "daw/iterator/daw_arrow_proxy.h" +#include "daw/pipelines/range.h" + +#include +#include +#include +#include + +namespace daw::pipelines { + template + struct cache_last_iterator { + using difference_type = daw::iter_difference_t; + using value_type = std::remove_reference_t>; + using reference = value_type &; + using pointer = daw::arrow_proxy; + using iterator_category = + daw::common_iterator_category_t, + std::bidirectional_iterator_tag>; + + private: + It m_iterator = It{ }; + std::optional m_cached = std::nullopt; + + public: + cache_last_iterator( ) = default; + + explicit constexpr cache_last_iterator( It i ) + : m_iterator( std::move( i ) ) {} + + constexpr reference value( ) { + if( not m_cached ) { + m_cached.emplace( *m_iterator ); + } + return *m_cached; + } + + constexpr reference operator*( ) { + return value( ); + } + + constexpr pointer operator->( ) { + return pointer{ value( ) }; + } + + constexpr cache_last_iterator &operator++( ) { + m_cached.reset( ); + ++m_iterator; + return *this; + } + + constexpr cache_last_iterator operator++( int ) { + auto result = *this; + operator++( ); + return result; + } + + constexpr cache_last_iterator &operator--( ) requires( + std::is_same_v ) { + + m_cached.reset( ); + --m_iterator; + return *this; + } + + constexpr cache_last_iterator operator--( int ) requires( + std::is_same_v ) { + + auto result = *this; + operator--( ); + return result; + } + constexpr bool operator==( cache_last_iterator const &rhs ) const { + return m_iterator == rhs.m_iterator; + } + + constexpr bool operator!=( cache_last_iterator const &rhs ) const { + return m_iterator != rhs.m_iterator; + } + + constexpr bool operator==( It const &rhs ) const { + return m_iterator == rhs; + } + + constexpr bool operator!=( It const &rhs ) const { + return m_iterator != rhs; + } + }; + + template + struct cache_last_view { + using iterator_first_t = cache_last_iterator>; + using iterator_last_t = cache_last_iterator>; + + private: + iterator_first_t m_first = iterator_first_t{ }; + iterator_last_t m_last = iterator_last_t{ }; + + public: + cache_last_view( ) = default; + + constexpr cache_last_view( std::convertible_to auto &&r ) + : m_first( std::begin( r ) ) + , m_last( std::end( r ) ) {} + + constexpr iterator_first_t begin( ) { + return m_first; + } + + constexpr iterator_first_t begin( ) const { + return m_first; + } + + constexpr iterator_last_t end( ) { + return m_last; + } + + constexpr iterator_last_t end( ) const { + return m_last; + } + }; + + template + cache_last_view( R ) -> cache_last_view; + + namespace pimpl { + struct CacheLast_t { + explicit CacheLast_t( ) = default; + + template + DAW_CPP23_STATIC_CALL_OP constexpr auto + operator( )( R &&r ) DAW_CPP23_STATIC_CALL_OP_CONST { + return cache_last_view{ DAW_FWD( r ) }; + } + }; + } // namespace pimpl + + inline constexpr auto CacheLast = pimpl::CacheLast_t{ }; +} // namespace daw::pipelines diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 476215da2..b5c200daa 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -690,6 +690,25 @@ namespace tests { daw_ensure( eb1[n] == prices[n] ); } } + + DAW_ATTRIB_NOINLINE void test041( ) { + constexpr auto values = std::array{ 1, 5, 5, 10, 32 }; + auto cp = pipeline( values, + Map( []( int x ) { + static int map_count = 0; + ++map_count; + daw::println( "MapCount = {}", map_count ); + return x * x; + } ), + CacheLast ); + auto first = std::begin( cp ); + auto last = std::end( cp ); + while( first != last ) { + daw::println( "{}", *first ); + daw::println( "{}", *first ); + ++first; + } + } } // namespace tests int main( ) { @@ -733,6 +752,7 @@ int main( ) { tests::test038( ); tests::test039( ); tests::test040( ); + tests::test041( ); daw::println( "Done" ); } From e70afe14498dd3e6fece16f4c53447281c890f3e Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Wed, 28 Jan 2026 17:31:04 -0500 Subject: [PATCH 32/43] Refactor span writer and pipelines tests to enhance size calculations and ensure type correctness --- tests/daw_pipelines_test.cpp | 3 ++- tests/daw_span_writer_test.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index b5c200daa..ff1181254 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -661,7 +661,8 @@ namespace tests { auto z0 = Zip( prices, costs ); auto e0 = Elements<1>( z0 ); - daw_ensure( std::ranges::distance( e0 ) == costs.size( ) ); + daw_ensure( static_cast( std::ranges::distance( e0 ) ) == + costs.size( ) ); auto eb0 = std::begin( e0 ); for( std::size_t n = 0; n < costs.size( ); ++n ) { daw_ensure( std::get<0>( eb0[n] ) == costs[n] ); diff --git a/tests/daw_span_writer_test.cpp b/tests/daw_span_writer_test.cpp index a294c0a53..35e2042b4 100644 --- a/tests/daw_span_writer_test.cpp +++ b/tests/daw_span_writer_test.cpp @@ -22,7 +22,7 @@ DAW_ATTRIB_NOINLINE void test1_impl( std::array &buff, buff, "Hello World.......Hello little letters:" ); sp = daw::write_to_span( sp, 'a', 'b', 'c', 0 ); constexpr auto const expected_sz = - buff.size( ) - + 256 - ( ( daw::string_view( "Hello World.......Hello little letters:" ).size( ) + 4 /*second write_to_span*/ ) ); daw_ensure( sp.size( ) == expected_sz ); From a345c377709b177075e1f8d46d8403d11bef137b Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Wed, 28 Jan 2026 20:16:04 -0500 Subject: [PATCH 33/43] Add `forward_like` utility with type transformation traits and refactor iterator traits for clarity --- include/daw/daw_forward_like.h | 45 +++++++++++++++++++++++++++++++ include/daw/daw_iterator_traits.h | 5 ++-- include/daw/daw_traits.h | 3 +++ 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 include/daw/daw_forward_like.h diff --git a/include/daw/daw_forward_like.h b/include/daw/daw_forward_like.h new file mode 100644 index 000000000..2b17b7a62 --- /dev/null +++ b/include/daw/daw_forward_like.h @@ -0,0 +1,45 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/ +// + +#pragma once + +#include +#include + +namespace daw { + template + constexpr auto &&forward_like( U &&x ) noexcept { + constexpr bool is_adding_const = + std::is_const_v>; + if constexpr( std::is_lvalue_reference_v ) { + if constexpr( is_adding_const ) { + return std::as_const( x ); + } else { + return static_cast( x ); + } + } else { + if constexpr( is_adding_const ) { + return std::move( std::as_const( x ) ); + } else { + return std::move( x ); + } + } + } + + template + using forward_like_t = std::conditional_t< + std::is_lvalue_reference_v, + std::conditional_t< + std::is_const_v>, + std::add_lvalue_reference_t>>, + std::add_lvalue_reference_t>>, + std::conditional_t< + std::is_const_v>, + std::add_rvalue_reference_t>>, + std::add_rvalue_reference_t>>>; +} // namespace daw diff --git a/include/daw/daw_iterator_traits.h b/include/daw/daw_iterator_traits.h index ca702a5d2..0b7bf4290 100644 --- a/include/daw/daw_iterator_traits.h +++ b/include/daw/daw_iterator_traits.h @@ -35,8 +35,7 @@ namespace daw { typename std::iterator_traits>::difference_type; template - using iter_const_reference_t = - std::common_reference_t const &&, iter_reference_t>; + using iter_const_reference_t = decltype( *std::declval( ) ); template using iter_category_t = @@ -44,7 +43,7 @@ namespace daw { template concept Iterator = requires( It & it ) { - { ++it } -> std::same_as; + { ++it }->std::same_as; { it++ }; { *it }; }; diff --git a/include/daw/daw_traits.h b/include/daw/daw_traits.h index 761e71f6e..0280c4dbf 100644 --- a/include/daw/daw_traits.h +++ b/include/daw/daw_traits.h @@ -937,6 +937,9 @@ namespace daw { std::get<0>( std::declval( ) ); typename std::tuple_element_t<0, daw::remove_cvref_t>; }; + + template + concept TupleLike = is_tuple_like_v; #else template inline constexpr bool is_tuple_like_v = false; From 76705826013fc9d6f1d9a1a6e851d04d249f89bb Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Wed, 28 Jan 2026 21:23:11 -0500 Subject: [PATCH 34/43] Fixed `zip_iterator` to use `zip_indices` for decrement operations --- include/daw/pipelines/zip.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/daw/pipelines/zip.h b/include/daw/pipelines/zip.h index e2893c7cd..be2e09eb8 100644 --- a/include/daw/pipelines/zip.h +++ b/include/daw/pipelines/zip.h @@ -148,14 +148,14 @@ namespace daw::pipelines { // bidirectional iterator interface constexpr zip_iterator &operator--( ) requires( BidirectionalIteratorTag ) { - decrement( m_iters ); + decrement( zip_indices( ) ); return *this; } [[nodiscard]] constexpr zip_iterator operator--( int ) requires( BidirectionalIteratorTag ) { auto tmp = *this; - decrement( m_iters ); + decrement( zip_indices( ) ); return tmp; } From fdf1b2f87bf6403cdd56a3f2a02b18e9c1a36ba6 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Wed, 28 Jan 2026 22:45:40 -0500 Subject: [PATCH 35/43] Ensure `std::ranges::distance` conversions are explicitly cast to `std::size_t` in pipeline tests --- tests/daw_pipelines_test.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index ff1181254..8317878e0 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -669,7 +669,8 @@ namespace tests { } auto e1 = Elements<0>( z0 ); auto eb1 = std::begin( e1 ); - daw_ensure( std::ranges::distance( e1 ) == prices.size( ) ); + daw_ensure( static_cast( std::ranges::distance( e1 ) ) == + prices.size( ) ); for( std::size_t n = 0; n < prices.size( ); ++n ) { daw_ensure( std::get<0>( eb1[n] ) == prices[n] ); } @@ -679,14 +680,16 @@ namespace tests { auto z0 = Zip( prices, costs ); auto e0 = Element<1>( z0 ); - daw_ensure( std::ranges::distance( e0 ) == costs.size( ) ); + daw_ensure( static_cast( std::ranges::distance( e0 ) ) == + costs.size( ) ); auto eb0 = std::begin( e0 ); for( std::size_t n = 0; n < costs.size( ); ++n ) { daw_ensure( eb0[n] == costs[n] ); } auto e1 = Element<0>( z0 ); auto eb1 = std::begin( e1 ); - daw_ensure( std::ranges::distance( e1 ) == prices.size( ) ); + daw_ensure( static_cast( std::ranges::distance( e1 ) ) == + prices.size( ) ); for( std::size_t n = 0; n < prices.size( ); ++n ) { daw_ensure( eb1[n] == prices[n] ); } From 1cc0fc2cf028c58e8f781f99341e6adcd9bea013 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 1 Feb 2026 12:39:53 -0500 Subject: [PATCH 36/43] Add `daw_dbg_ensure` macro and integrate validations in `c_str_proxy` constructors --- include/daw/daw_ensure.h | 13 +++++++++++++ include/daw/daw_string_view.h | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/daw/daw_ensure.h b/include/daw/daw_ensure.h index e36c567d8..f19686c9f 100644 --- a/include/daw/daw_ensure.h +++ b/include/daw/daw_ensure.h @@ -44,3 +44,16 @@ namespace daw::ensure { ::daw::ensure::ensure_error( not( __VA_ARGS__ ) ); \ } \ } while( false ) + +#if not defined( NDEBUG ) +#define daw_dbg_ensure( ... ) \ + do { \ + if( not( __VA_ARGS__ ) ) { \ + ::daw::ensure::ensure_error( not( __VA_ARGS__ ) ); \ + } \ + } while( false ) +#else +#define daw_dbg_ensure( ... ) \ + do { \ + } while( false ) +#endif diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index 04b4dd1a8..14ef2589c 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -491,10 +491,14 @@ namespace daw { DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N, zero_terminated_t ) noexcept - : m_str{ buff_t{ str, N } } {} + : m_str{ buff_t{ str, N } } { + daw_dbg_ensure( N == sv2_details::strlen( str ) ); + } DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N ) noexcept - : m_str{ std::basic_string( str, N ) } {} + : m_str{ std::basic_string( str, N ) } { + daw_dbg_ensure( N == sv2_details::strlen( str ) ); + } template static DAW_CPP20_CX_ALLOC std::basic_string From 278f77308cff24d1b703857803258f5f8411756f Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 1 Feb 2026 13:00:50 -0500 Subject: [PATCH 37/43] Fixed bug --- include/daw/daw_string_view.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index 14ef2589c..f411a98ff 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -461,6 +461,7 @@ namespace daw { struct zero_terminated_t { explicit zero_terminated_t( ) = default; }; + inline constexpr zero_terminated_t zero_terminated{ }; // tag type for non zero-terminated overloads @@ -492,12 +493,12 @@ namespace daw { DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N, zero_terminated_t ) noexcept : m_str{ buff_t{ str, N } } { - daw_dbg_ensure( N == sv2_details::strlen( str ) ); + daw_dbg_ensure( N == sv2_details::strlen( str ) ); } DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N ) noexcept : m_str{ std::basic_string( str, N ) } { - daw_dbg_ensure( N == sv2_details::strlen( str ) ); + daw_dbg_ensure( N == sv2_details::strlen( str ) ); } template From ff48d5a383540d1c86337a836bf77feeed300b0e Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 1 Feb 2026 13:09:40 -0500 Subject: [PATCH 38/43] Added define switch to disable debug checks of zterm string length matching reported length --- include/daw/daw_string_view.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index f411a98ff..b7a935daa 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -493,12 +493,16 @@ namespace daw { DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N, zero_terminated_t ) noexcept : m_str{ buff_t{ str, N } } { +#if not defined( DAW_STRINGVIEW_NOZTERM_CHECK ) daw_dbg_ensure( N == sv2_details::strlen( str ) ); +#endif } DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N ) noexcept : m_str{ std::basic_string( str, N ) } { +#if not defined( DAW_STRINGVIEW_NOZTERM_CHECK ) daw_dbg_ensure( N == sv2_details::strlen( str ) ); +#endif } template From 329d1493bdf722ce37f30439e5f37f27b669846c Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 1 Feb 2026 15:11:39 -0500 Subject: [PATCH 39/43] Was checking the wrong string in new debug check. Fixed --- include/daw/daw_string_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index b7a935daa..871d1ecdf 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -501,7 +501,7 @@ namespace daw { DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N ) noexcept : m_str{ std::basic_string( str, N ) } { #if not defined( DAW_STRINGVIEW_NOZTERM_CHECK ) - daw_dbg_ensure( N == sv2_details::strlen( str ) ); + daw_dbg_ensure( N == sv2_details::strlen( std::get<1>( m_str ).c_str( ) ) ); #endif } From 9c23a7e4149593ceea45490bc6f0e25a34aeca7c Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Sun, 1 Feb 2026 15:59:50 -0500 Subject: [PATCH 40/43] Added missing include `daw_ensure.h` to string_view --- include/daw/daw_string_view.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index 871d1ecdf..36be0f1e3 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -24,6 +24,7 @@ #include "daw/daw_cpp_feature_check.h" #include "daw/daw_data_end.h" #include "daw/daw_enable_requires.h" +#include "daw/daw_ensure.h" #include "daw/daw_fnv1a_hash.h" #include "daw/daw_is_constant_evaluated.h" #include "daw/daw_likely.h" @@ -501,7 +502,8 @@ namespace daw { DAW_CPP20_CX_ALLOC c_str_proxy( CharT const *str, std::size_t N ) noexcept : m_str{ std::basic_string( str, N ) } { #if not defined( DAW_STRINGVIEW_NOZTERM_CHECK ) - daw_dbg_ensure( N == sv2_details::strlen( std::get<1>( m_str ).c_str( ) ) ); + daw_dbg_ensure( N == sv2_details::strlen( + std::get<1>( m_str ).c_str( ) ) ); #endif } From 67ddaa96b5b3fe001b211a728565b6155397a27b Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Tue, 3 Feb 2026 17:47:22 -0500 Subject: [PATCH 41/43] Include `` header in `daw_safe_pointer.h` --- include/daw/daw_safe_pointer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/daw/daw_safe_pointer.h b/include/daw/daw_safe_pointer.h index 817b49b39..053f82737 100644 --- a/include/daw/daw_safe_pointer.h +++ b/include/daw/daw_safe_pointer.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #if not defined( DAW_NO_SAFE_POINTER_CHECKS ) From 600f270131e3ab0ef778508a4ee843f46502b84d Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Thu, 12 Feb 2026 00:04:49 -0500 Subject: [PATCH 42/43] Refactor pipelines and iterator traits to improve const-correctness and type handling --- include/daw/daw_iterator_traits.h | 16 ++--- include/daw/pipelines/chunk.h | 9 ++- include/daw/pipelines/concat.h | 4 +- include/daw/pipelines/flatten.h | 15 +++-- include/daw/pipelines/map.h | 102 ++++++++++++++---------------- include/daw/pipelines/slide.h | 12 ++-- include/daw/pipelines/swizzle.h | 4 +- include/daw/pipelines/unique.h | 11 ++-- tests/daw_pipelines_test.cpp | 16 ++++- 9 files changed, 101 insertions(+), 88 deletions(-) diff --git a/include/daw/daw_iterator_traits.h b/include/daw/daw_iterator_traits.h index 0b7bf4290..1c1e022f9 100644 --- a/include/daw/daw_iterator_traits.h +++ b/include/daw/daw_iterator_traits.h @@ -19,12 +19,13 @@ namespace daw { template - using iter_value_t = - typename std::iterator_traits>::value_type; + using iter_reference_t = decltype( *std::declval( ) ); template - using iter_reference_t = - typename std::iterator_traits>::reference; + using iter_const_reference_t = decltype( *std::declval( ) ); + + template + using iter_value_t = std::remove_cvref_t>; template using iter_pointer_t = @@ -34,9 +35,6 @@ namespace daw { using iter_difference_t = typename std::iterator_traits>::difference_type; - template - using iter_const_reference_t = decltype( *std::declval( ) ); - template using iter_category_t = typename std::iterator_traits>::iterator_category; @@ -169,10 +167,10 @@ namespace daw { }; template - using iterator_t = DAW_TYPEOF( std::begin( std::declval( ) ) ); + using iterator_t = DAW_TYPEOF( std::begin( std::declval( ) ) ); template - using iterator_end_t = DAW_TYPEOF( std::end( std::declval( ) ) ); + using iterator_end_t = DAW_TYPEOF( std::end( std::declval( ) ) ); template using const_iterator_t = diff --git a/include/daw/pipelines/chunk.h b/include/daw/pipelines/chunk.h index d27a68ebe..246b438e0 100644 --- a/include/daw/pipelines/chunk.h +++ b/include/daw/pipelines/chunk.h @@ -8,6 +8,7 @@ #pragma once +#include "daw/cpp_17.h" #include "daw/daw_attributes.h" #include "daw/daw_iterator_traits.h" #include "daw/daw_move.h" @@ -54,7 +55,9 @@ namespace daw::pipelines::pimpl { public: explicit chunk_view( ) = default; - explicit chunk_view( Range auto &&r ) + template + requires( not std::same_as, chunk_view> ) // + explicit chunk_view( U &&r ) : m_range{ DAW_FWD( r ) } , m_iter{ std::begin( m_range ) } {} @@ -119,9 +122,9 @@ namespace daw::pipelines::pimpl { operator!=( chunk_view const &rhs ) const noexcept = default; }; template - chunk_view( R &&r ) -> chunk_view; + chunk_view( R &&r ) -> chunk_view>; template - chunk_view( R &&r, std::size_t ) -> chunk_view; + chunk_view( R &&r, std::size_t ) -> chunk_view>; struct Chunk_t { std::size_t chunk_size; diff --git a/include/daw/pipelines/concat.h b/include/daw/pipelines/concat.h index 448761310..09c867ee9 100644 --- a/include/daw/pipelines/concat.h +++ b/include/daw/pipelines/concat.h @@ -8,6 +8,7 @@ #pragma once +#include "daw/cpp_17.h" #include "daw/daw_iterator_traits.h" #include "daw/daw_move.h" #include "daw/daw_traits.h" @@ -115,7 +116,8 @@ namespace daw::pipelines { } }; template - concat_view( Ranges &&... ) -> concat_view; + concat_view( Ranges &&... ) + -> concat_view...>; namespace pimpl { struct Concat_t { diff --git a/include/daw/pipelines/flatten.h b/include/daw/pipelines/flatten.h index d17d9f568..8fab762ec 100644 --- a/include/daw/pipelines/flatten.h +++ b/include/daw/pipelines/flatten.h @@ -25,7 +25,7 @@ namespace daw::pipelines::pimpl { template struct flatten_view_end_t { using iterator_t = daw::iterator_t; - using sub_iterator_t = daw::iterator_t>; + using sub_iterator_t = daw::iterator_t>; using iterator_category = std::forward_iterator_tag; using value_type = daw::iter_value_t; using reference = daw::iter_reference_t; @@ -50,7 +50,7 @@ namespace daw::pipelines::pimpl { using end_t = flatten_view_end_t; using m_range_t = daw::remove_cvrvref_t; - m_range_t m_range; + mutable m_range_t m_range; iterator_t m_range_first{ }; sub_iterator_t m_cur_first{ }; @@ -62,11 +62,11 @@ namespace daw::pipelines::pimpl { return std::begin( m_range ); } - [[nodiscard]] constexpr iterator_end_t raend( ) { + [[nodiscard]] constexpr auto raend( ) { return std::end( m_range ); } - [[nodiscard]] constexpr iterator_end_t raend( ) const { + [[nodiscard]] constexpr auto raend( ) const { return std::end( m_range ); } @@ -93,7 +93,10 @@ namespace daw::pipelines::pimpl { public: explicit flatten_view( ) = default; - explicit constexpr flatten_view( auto &&r ) + template + requires( not std::same_as, + flatten_view> ) // + explicit constexpr flatten_view( U &&r ) : m_range( DAW_FWD( r ) ) , m_range_first( rabegin( ) ) , m_cur_first( m_range_first == raend( ) @@ -152,7 +155,7 @@ namespace daw::pipelines::pimpl { } }; template - flatten_view( R && ) -> flatten_view; + flatten_view( R && ) -> flatten_view>; struct Flatten_t { [[nodiscard]] DAW_ATTRIB_INLINE DAW_CPP23_STATIC_CALL_OP constexpr auto diff --git a/include/daw/pipelines/map.h b/include/daw/pipelines/map.h index 7b5cb62cf..e46a6008b 100644 --- a/include/daw/pipelines/map.h +++ b/include/daw/pipelines/map.h @@ -27,7 +27,7 @@ namespace daw::pipelines { struct map_iterator { using iterator_category = daw::iter_category_t; using value_type = daw::remove_cvref_t>>>; + Fn, std::invoke_result_t>>>; using reference = value_type; using const_reference = value_type; using pointer = arrow_proxy; @@ -172,38 +172,37 @@ namespace daw::pipelines { template map_iterator( I, F, P ) -> map_iterator; - template - struct map_view : range_base_t> { - using daw_range_base_t = range_base_t>; + template + struct map_view + : range_base_t, Fn, Projection>, + map_iterator, Fn, Projection>> { + using daw_range_base_t = + range_base_t, Fn, Projection>, + map_iterator, Fn, Projection>>; + using typename daw_range_base_t::iterator_first_t; using typename daw_range_base_t::iterator_last_t; - using value_type = daw::iter_value_t>; + using value_type = daw::iter_value_t; - iterator_first_t m_first = iterator_first_t{ }; - iterator_last_t m_last = iterator_last_t{ }; + daw::remove_rvalue_ref_t m_range{ }; + iterator_first_t m_first{ }; + iterator_last_t m_last{ }; explicit map_view( ) = default; - explicit constexpr map_view( First first, Fn const &fn ) - requires( not Iterator ) - : m_first( first, fn ) {} - - explicit constexpr map_view( First first, Fn const &fn, - Projection const &projection ) - requires( not Iterator and not Iterator ) - : m_first( first, fn, projection ) {} - - explicit constexpr map_view( First first, First last, Fn const &fn ) + explicit constexpr map_view( Range auto &&r, Fn const &fn ) requires( not Iterator ) - : m_first( first, fn ) - , m_last( last, fn ) {} + : m_range( DAW_FWD( r ) ) + , m_first{ std::begin( m_range ), fn } + , m_last{ std::end( m_range ), fn } {} - explicit constexpr map_view( First first, First last, Fn const &fn, + explicit constexpr map_view( Range auto &&r, Fn const &fn, Projection const &projection ) requires( not Iterator and not Iterator ) - : m_first( first, fn, projection ) - , m_last( last, fn, projection ) {} + : m_range( DAW_FWD( r ) ) + , m_first{ std::begin( m_range ), fn, projection } + , m_last{ std::end( m_range ), fn, projection } {} [[nodiscard]] DAW_ATTRIB_INLINE constexpr iterator_first_t begin( ) const { return m_first; @@ -213,21 +212,13 @@ namespace daw::pipelines { return m_last; } }; - template - requires( not Iterator ) // - map_view( I, F ) -> map_view; - - template - requires( not Iterator and not Iterator

) // - map_view( I, F, P ) -> map_view; - - template + template requires( not Iterator ) // - map_view( I, I, F ) -> map_view; + map_view( R &&, F ) -> map_view, F>; - template + template requires( not Iterator and not Iterator

) // - map_view( I, I, F, P ) -> map_view; + map_view( R &&, F, P ) -> map_view, F, P>; namespace pimpl { template @@ -236,23 +227,27 @@ namespace daw::pipelines { DAW_NO_UNIQUE_ADDRESS Projection m_projection = Projection{ }; [[nodiscard]] constexpr auto operator( )( auto &&r ) const { - using R = DAW_TYPEOF( r ); - static_assert( - std::invocable>, - "Projection must be invocable with the range_value_t" ); - using projected_t = std::invoke_result_t>; - + using R = decltype( r ); if constexpr( Range ) { + static_assert( + std::invocable>, + "Projection must be invocable with the range_value_t" ); + using projected_t = + std::invoke_result_t>; + static_assert( std::invocable, "Map requires the function to be able to be called " "with invoke and the range_reference_t(e.g. invoke( " "MapFn, *it ) )" ); static_assert( traits::NoVoidResults, "Map requires the result to not be void" ); - return map_view, Fn, Projection>( - std::begin( DAW_FWD( r ) ), std::end( DAW_FWD( r ) ), m_func, - m_projection ); + return map_view( + DAW_FWD( r ), m_func, m_projection ); } else { + static_assert( std::invocable, + "Projection must be invocable with R" ); + using projected_t = std::invoke_result_t; + static_assert( std::invocable, "Map requires the function to be able to be called " "with invoke and passed value" ); @@ -273,15 +268,15 @@ namespace daw::pipelines { struct MapApply_t { DAW_NO_UNIQUE_ADDRESS Fn m_func; - [[nodiscard]] constexpr auto operator( )( auto &&r ) const { - using R = DAW_TYPEOF( r ); + template + [[nodiscard]] constexpr auto operator( )( R &&r ) const { static_assert( traits::is_applicable_v>, "MapApply requires the function to be able to be called " "with apply and the range_reference_t" ); auto func = m_func; - return map_view( std::begin( r ), std::end( r ), [=]( auto &&tp ) { - return std::apply( func, tp ); - } ); + return map_view{ DAW_FWD( r ), [=]( auto &&tp ) { + return std::apply( func, tp ); + } }; } }; template @@ -293,8 +288,8 @@ namespace daw::pipelines { T hi; DAW_NO_UNIQUE_ADDRESS Compare compare; - [[nodiscard]] constexpr auto operator( )( auto &&r ) const { - using R = DAW_TYPEOF( r ); + template + [[nodiscard]] constexpr auto operator( )( R &&r ) const { auto h = hi; auto l = lo; auto c = compare; @@ -303,10 +298,9 @@ namespace daw::pipelines { static_assert( std::convertible_to, "Clamp requires a lo/hi values convertible to the " "range value type" ); - return map_view( std::begin( r ), std::end( r ), - [=]( value_type const &v ) { - return std::clamp( v, l, h, c ); - } ); + return map_view{ DAW_FWD( r ), [=]( value_type const &v ) { + return std::clamp( v, l, h, c ); + } }; } else { static_assert( std::convertible_to>, diff --git a/include/daw/pipelines/slide.h b/include/daw/pipelines/slide.h index 8be552291..c4bae70f7 100644 --- a/include/daw/pipelines/slide.h +++ b/include/daw/pipelines/slide.h @@ -21,11 +21,11 @@ namespace daw::pipelines::pimpl { template struct slide_view { using iterator = daw::iterator_t; - using const_iterator = daw::iterator_t>; + using const_iterator = daw::const_iterator_t; using iterator_category = daw::range_category_t; using value_type = range_t; using reference = range_t; - using const_reference = range_t; + using const_reference = range_t; using difference_type = std::ptrdiff_t; using i_am_a_daw_slide_view_iterator_class = void; @@ -55,7 +55,9 @@ namespace daw::pipelines::pimpl { public: explicit slide_view( ) = default; - explicit slide_view( Range auto &&r ) + template + requires( not std::same_as, slide_view> ) // + explicit slide_view( U &&r ) : m_range{ DAW_FWD( r ) } , m_iter{ std::begin( m_range ) } {} @@ -120,9 +122,9 @@ namespace daw::pipelines::pimpl { operator!=( slide_view const &rhs ) const noexcept = default; }; template - slide_view( R &&r ) -> slide_view; + slide_view( R &&r ) -> slide_view>; template - slide_view( R &&r, std::size_t ) -> slide_view; + slide_view( R &&r, std::size_t ) -> slide_view>; struct Slide_t { std::size_t slide_size = 1; diff --git a/include/daw/pipelines/swizzle.h b/include/daw/pipelines/swizzle.h index 62cdd1da4..986d33405 100644 --- a/include/daw/pipelines/swizzle.h +++ b/include/daw/pipelines/swizzle.h @@ -26,7 +26,7 @@ namespace daw::pipelines::pimpl { typename daw::remove_cvref_t< R>::i_am_a_daw_zip_iterator_class; } ) { - return map_view{ std::begin( r ), std::end( r ), []( auto &&tp ) { + return map_view{ DAW_FWD( r ), []( auto &&tp ) { return daw::forward_nonrvalue_as_tuple( get( DAW_FWD( tp ) )... ); } }; @@ -35,7 +35,7 @@ namespace daw::pipelines::pimpl { static_assert( std::max( { Indices... } ) < std::tuple_size_v>, "Swizzle - Index out of range of tuple type" ); - return map_view{ std::begin( r ), std::end( r ), []( auto &&v ) { + return map_view{ DAW_FWD( r ), []( auto &&v ) { return daw::forward_nonrvalue_as_tuple( get( DAW_FWD( v ) )... ); } }; diff --git a/include/daw/pipelines/unique.h b/include/daw/pipelines/unique.h index 5b5b10b46..7483a0477 100644 --- a/include/daw/pipelines/unique.h +++ b/include/daw/pipelines/unique.h @@ -105,6 +105,7 @@ namespace daw::pipelines { using value_type = daw::range_value_t; static_assert( requires( value_type const &v ) { v == v; } ); using iterator = unique_view>; + using const_iterator = unique_view>; private: R m_range; @@ -120,16 +121,16 @@ namespace daw::pipelines { return iterator{ std::begin( m_range ), std::end( m_range ) }; } - [[nodiscard]] constexpr iterator begin( ) const { - return iterator{ std::begin( m_range ), std::end( m_range ) }; + [[nodiscard]] constexpr const_iterator begin( ) const { + return const_iterator{ std::begin( m_range ), std::end( m_range ) }; } [[nodiscard]] constexpr iterator end( ) { return iterator{ std::end( m_range ), std::end( m_range ) }; } - [[nodiscard]] constexpr iterator end( ) const { - return iterator{ std::end( m_range ), std::end( m_range ) }; + [[nodiscard]] constexpr const_iterator end( ) const { + return const_iterator{ std::end( m_range ), std::end( m_range ) }; } [[nodiscard]] constexpr friend bool @@ -140,7 +141,7 @@ namespace daw::pipelines { }; template - unique_range( R ) -> unique_range; + unique_range( R && ) -> unique_range>; namespace pimpl { struct Unique_t { diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index 8317878e0..a50051345 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -536,11 +536,11 @@ namespace tests { } DAW_ATTRIB_NOINLINE void test030( ) { - constexpr auto comma_splitter = + static constexpr auto comma_splitter = pipeline( Split( ',' ), Map( []( daw::Range auto r ) { return daw::string_view( r ).trim( ); } ) ); - constexpr auto values = "1a, 2b,3c, 4d ,5e"_sv; + static constexpr auto values = "1a, 2b,3c, 4d ,5e"_sv; constexpr auto parts = comma_splitter( values ); daw::println( @@ -713,6 +713,16 @@ namespace tests { ++first; } } + + DAW_ATTRIB_NOINLINE void test042( ) { + auto v = std::vector{ "Hello", "World" }; + + auto mapper = Map( +[]( std::string &s ) -> char * { + return s.data( ); + } ); + auto v2 = std::vector( std::from_range, mapper( v ) ); + daw_ensure( v2.size( ) == v.size( ) ); + } } // namespace tests int main( ) { @@ -757,6 +767,6 @@ int main( ) { tests::test039( ); tests::test040( ); tests::test041( ); - + tests::test042( ); daw::println( "Done" ); } From dcd3b06aa06950feec3e6b62c56e9498269b7978 Mon Sep 17 00:00:00 2001 From: Darrell Wright Date: Thu, 12 Feb 2026 00:09:59 -0500 Subject: [PATCH 43/43] Refactor pipeline test to replace `std::from_range` with explicit iterator range usage --- tests/daw_pipelines_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/daw_pipelines_test.cpp b/tests/daw_pipelines_test.cpp index a50051345..d6ec6226d 100644 --- a/tests/daw_pipelines_test.cpp +++ b/tests/daw_pipelines_test.cpp @@ -720,7 +720,8 @@ namespace tests { auto mapper = Map( +[]( std::string &s ) -> char * { return s.data( ); } ); - auto v2 = std::vector( std::from_range, mapper( v ) ); + auto m = mapper( v ); + auto v2 = std::vector( std::begin( m ), std::end( m ) ); daw_ensure( v2.size( ) == v.size( ) ); } } // namespace tests