diff --git a/tc/algorithm/accumulate.h b/tc/algorithm/accumulate.h index f71e4be..c1412ac 100644 --- a/tc/algorithm/accumulate.h +++ b/tc/algorithm/accumulate.h @@ -46,7 +46,7 @@ namespace tc { template< typename S > constexpr auto operator()( S&& s ) const& MAYTHROW { - return CONDITIONAL_PRVALUE_AS_VAL( + return tc_conditional_prvalue_as_val( m_t, tc::continue_if_not_break( m_accuop, *m_t, std::forward(s) ), TC_FWD(tc::optional_emplace(m_t, std::forward(s)), tc::constant()) diff --git a/tc/algorithm/accumulator.h b/tc/algorithm/accumulator.h deleted file mode 100644 index 01dd491..0000000 --- a/tc/algorithm/accumulator.h +++ /dev/null @@ -1,47 +0,0 @@ - -// think-cell public library -// -// Copyright (C) 2016-2023 think-cell Software GmbH -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt - -#pragma once - -#include "../base/assert_defs.h" -#include "../base/functors.h" -#include "../base/return_decltype.h" -#include "../base/casts.h" -#include "../base/derivable.h" -#include "../base/assign.h" - -#include - -namespace tc { - -template -struct [[nodiscard]] FAccumulator final : tc::derivable_t> { - using value_t = tc::decay_t; - - FAccumulator(Value&& value, Accumulate&& accumulate) noexcept - : tc::derivable_t(std::forward(value)) - , m_accumulate(std::forward(accumulate)) - {} - - template - void operator() (Args&& ... args) & noexcept { - m_accumulate( tc::base_cast(*this), std::forward(args)... ); - } - -private: - tc::decay_t m_accumulate; -}; - -/////////////////////////////////////////////// -// accumulators - -template -auto make_accumulator(Value&& value, Accumulate&& accumulate) - return_ctor_noexcept( TC_FWD(FAccumulator), (std::forward(value),std::forward(accumulate)) ) - -} // namespace tc diff --git a/tc/algorithm/algorithm.h b/tc/algorithm/algorithm.h index 5653ff8..3fcc03d 100644 --- a/tc/algorithm/algorithm.h +++ b/tc/algorithm/algorithm.h @@ -72,38 +72,12 @@ namespace tc { }); } template< typename Rng, typename Less = tc::fn_less> - [[nodiscard]] constexpr bool is_sorted(Rng const& rng, Less less = Less()) noexcept { - return !tc::any_of(tc::adjacent<2>(rng), [&](auto const& first, auto const& second) noexcept { - return less(second, first); + [[nodiscard]] constexpr bool is_sorted(Rng const& rng, Less less = Less()) MAYTHROW { + return !tc::any_of(tc::adjacent<2>(rng), [&](auto const& first, auto const& second) MAYTHROW { + return less(second, first); // MAYTHROW }); } - template< typename Rng, typename Equal = tc::fn_equal_to > - [[nodiscard]] constexpr bool all_same(Rng const& rng, Equal equal = Equal()) noexcept { - if constexpr( tc::range_with_iterators ) { - auto const itBegin=tc::begin(rng); - auto const itEnd=tc::end(rng); - if(itBegin==itEnd) return true; - auto const itNext=tc_modified(itBegin, ++_); - if(itNext==itEnd) return true; - tc_auto_cref( front, *itBegin ); - return all_of( - tc::drop(rng, itNext), - [&](auto const& _) noexcept { return equal(front, _); } - ); - } else { - std::optional> oFirst; - return tc::continue_ == tc::for_each(rng, [&](auto&& val) noexcept { - if (oFirst) { - return tc::continue_if(equal(*oFirst, val)); - } else { - oFirst.emplace(tc_move_if_owned(val)); - return tc::continue_; - } - }); - } - } - ///////////////////////////////// // associative containers @@ -266,7 +240,7 @@ namespace tc { ); } - sorted_index_adaptor(sorted_index_adaptor&& rng) noexcept requires tc::is_index_valid_for_move_constructed_range::value // reference_or_value is movable for const Rng as well + sorted_index_adaptor(sorted_index_adaptor&& rng) noexcept requires tc::stable_index_on_move // reference_or_value is movable for const Rng as well : range_adaptor_base_range(tc_move(rng)) , m_vecidx(tc_move(rng).m_vecidx) { @@ -347,11 +321,11 @@ namespace tc { struct [[nodiscard]] untransform_adaptor : tc::index_range_adaptor< untransform_adaptor, - Rng + Rng, tc::index_range_adaptor_flags::inherit_begin_end | tc::index_range_adaptor_flags::inherit_traversal > { private: - using base_ = tc::index_range_adaptor, Rng>; + using base_ = typename untransform_adaptor::index_range_adaptor; public: using typename base_::tc_index; @@ -464,7 +438,7 @@ namespace tc { } template - constexpr void ordered_unique_inplace( Cont& cont, Less less=Less() ) noexcept { + constexpr void ordered_unique_inplace( Cont& cont, Less less=Less() ) MAYTHROW { _ASSERTDEBUG( tc::is_sorted( cont, less ) ); tc::adjacent_unique_inplace( cont, std::not_fn( tc_move(less) ) ); } @@ -481,34 +455,30 @@ namespace tc { tc::ordered_unique_inplace( cont, tc_move(less) ); } - template - auto ordered_for_each_occurrence(Rng&& rng, Less&& less, Func func) noexcept { - return tc::for_each(tc::ordered_unique_range( std::forward(rng), std::forward(less)), [&](auto const& rngSub) noexcept { - return tc::continue_if_not_break( func, std::make_pair( + template + auto ordered_unique_begin_and_count(Rng&& rng, Less&& less) noexcept { + return tc::transform(tc::ordered_unique_range( std::forward(rng), std::forward(less)), [&](auto const& rngSub) noexcept { + return std::make_pair( tc::begin(rngSub), tc::implicit_cast >::type >(tc::size_linear(rngSub)) - ) ); + ); }); } template [[nodiscard]] auto plurality_element(Rng&& rng) noexcept { - if(tc::empty(rng)) { + auto vecitSorted = tc::sorted_iterator_range(rng, tc::fn_less()); // do not inline, oit->first points into vecitSorted + if(auto oit = tc::max_element( + tc::ordered_unique_begin_and_count( + vecitSorted, + tc::projected(tc::fn_less(), fn_indirection()) + ), + tc_member(.second) + )) { + return RangeReturn::pack_element(tc_move_always(*oit->first), std::forward(rng)); + } else { return RangeReturn::pack_no_element(std::forward(rng)); } - auto const rng2=tc::sorted_iterator_range(rng, tc::fn_less()); - - auto it = *( tc::accumulate( - [&](auto const& func) noexcept { - return tc::ordered_for_each_occurrence(rng2, tc::projected(tc::fn_less(), fn_indirection()), func); - }, - std::pair< - tc::iterator_t, - typename boost::range_size::type - >(), // value-initialized, second=0 - tc::fn_assign_better(tc::projected(tc::fn_greater(), tc_member(.second))) - ).first ); - return RangeReturn::pack_element(it, std::forward(rng)); } template< typename RangeReturn, typename Rng, typename Pred > @@ -525,8 +495,14 @@ namespace tc { template< typename Rng, typename Pred > [[nodiscard]] decltype(auto) trim_if(Rng&& rng, Pred&& pred) MAYTHROW { - auto rngTrimmed = tc::trim_right_if( std::forward(rng), pred ); - return tc::trim_left_if( tc_move(rngTrimmed), std::forward(pred) ); + decltype(auto) rngTrimmed = tc::trim_right_if( tc_move_if_owned(rng), pred ); + if constexpr (std::is_reference::value) { + _ASSERTEQUAL(std::addressof(rngTrimmed), std::addressof(rng)); + return tc::trim_left_if( tc_move_if_owned(rngTrimmed), tc_move_if_owned(pred) ); + } else { + static_assert(tc::instance); + return tc::decay_copy(tc::trim_left_if( tc_move(rngTrimmed), tc_move_if_owned(pred) )); + } } template< typename RangeReturn, typename Rng, typename RngTrim > @@ -685,7 +661,7 @@ namespace tc { template Cont get_truncating_buffer_allowing_nulls(Func func) noexcept { - return tc::get_buffer_detail::get_buffer_allowing_nulls([&](auto pBuffer, auto nBufferSize) noexcept { + return tc::get_buffer_detail::get_buffer_allowing_nulls([&](auto pBuffer, auto const nBufferSize) noexcept { auto nSize = func(pBuffer, nBufferSize); if (nSize == nBufferSize) { return nSize+1; // Any return value larger than nBufferSize causes a retry with a larger buffer @@ -863,7 +839,7 @@ namespace tc { template [[nodiscard]] constexpr auto constexpr_sort_unique(Rng&& rng, Less&& less=Less()) noexcept { return tc_modified( - tc::make_static_vector::value>(std::forward(rng)), + tc::make_static_vector()>(std::forward(rng)), tc::sort_unique_inplace(_, std::forward(less)) ); } @@ -884,64 +860,63 @@ namespace tc { template< typename RangeReturn, typename SetType, typename T, typename Less > void binary_find_unique(tc::unordered_set const& rng, T const& t, Less less) noexcept = delete; - template< typename RangeReturn, typename Rng, typename T, typename Pred > - [[nodiscard]] decltype(auto) binary_find_unique(Rng&& rng, T const& t, Pred predLessOr3way) noexcept { - // The result of tc::binary_find_unique must be unambiguous. In general, this means that rng is strictly - // ordered. In some cases, it is convenient to allow multiple occurrences of the same item in - // rng, which is not a problem as long as these items are not searched for. - - // preserve order of arguments for 3way predicates - static constexpr bool c_b3way = tc::is_comparison_category; - auto it=[&]() noexcept { - if constexpr(c_b3way) { - return tc::lower_bound( rng, t, tc::lessfrom3way(std::ref(predLessOr3way)) ); - } else { - _ASSERTDEBUG( tc::is_sorted(rng, predLessOr3way) ); - return tc::lower_bound( rng, t, std::ref(predLessOr3way) ); - } - }(); - if( it==tc::end( rng ) ) { - return RangeReturn::pack_no_element(std::forward(rng)); - } else { - auto Greater = [&](auto const& elem) noexcept { + namespace binary_find_first_or_unique_adl { + template< typename RangeReturn, bool c_bAssertUniqueness, typename Rng, typename T, typename Pred > + [[nodiscard]] decltype(auto) binary_find_first_or_unique(Rng&& rng, T const& t, Pred predLessOr3way) noexcept { + // The result of tc::binary_find_unique must be unambiguous. In general, this means that rng is strictly + // ordered. In some cases, it is convenient to allow multiple occurrences of the same item in + // rng, which is not a problem as long as these items are not searched for. + + // preserve order of arguments for 3way predicates + static constexpr bool c_b3way = tc::is_comparison_category; + auto it=[&]() noexcept { if constexpr(c_b3way) { - return std::is_gt(predLessOr3way(elem, t)); + _ASSERTDEBUG( tc::is_sorted(rng, tc::lessfrom3way(std::ref(predLessOr3way))) ); + return tc::lower_bound( rng, t, tc::lessfrom3way(std::ref(predLessOr3way)) ); } else { - return predLessOr3way(t, elem); + _ASSERTDEBUG( tc::is_sorted(rng, predLessOr3way) ); + return tc::lower_bound( rng, t, std::ref(predLessOr3way) ); } - }; - auto && ref=*it; - if (Greater(tc::as_const(ref))) { + }(); + if( it==tc::end( rng ) ) { return RangeReturn::pack_no_element(std::forward(rng)); } else { - #ifdef _CHECKS - auto itNext = tc_modified(it, ++_); - _ASSERT( tc::end(rng)==itNext || Greater(tc::as_const(*itNext))); - #endif - return RangeReturn::pack_element(it,std::forward(rng),tc_move_if_owned(ref)); + auto const Greater = [&](auto const& elem) noexcept { + if constexpr(c_b3way) { + return std::is_gt(predLessOr3way(elem, t)); + } else { + return predLessOr3way(t, elem); + } + }; + auto && ref=*it; + if (Greater(tc::as_const(ref))) { + return RangeReturn::pack_no_element(std::forward(rng)); + } else { +#ifdef _CHECKS + if constexpr(c_bAssertUniqueness) { + auto itNext = tc_modified(it, ++_); + _ASSERT( tc::end(rng)==itNext || Greater(tc::as_const(*itNext))); + } +#endif + return RangeReturn::pack_element(it,std::forward(rng),tc_move_if_owned(ref)); + } } } } - + + template< typename RangeReturn, typename Rng, typename T, typename Pred > + [[nodiscard]] decltype(auto) binary_find_unique(Rng&& rng, T const& t, Pred predLessOr3way) noexcept { + return binary_find_first_or_unique_adl::binary_find_first_or_unique(tc_move_if_owned(rng), t, tc_move(predLessOr3way)); + } + template< typename RangeReturn, typename Rng, typename T > [[nodiscard]] decltype(auto) binary_find_unique(Rng&& rng, T const& t) noexcept { return tc::binary_find_unique( std::forward(rng), t, tc::fn_less() ); } - template< typename RangeReturn, typename Rng, typename T, typename Less > - [[nodiscard]] decltype(auto) binary_find_first(Rng&& rng, T const& t, Less less) noexcept { - _ASSERTDEBUG( tc::is_sorted(rng, less) ); - auto it=tc::lower_bound( rng, t, less ); - if (it == tc::end(rng)) { - return RangeReturn::pack_no_element(std::forward(rng)); - } else { - auto && ref = *it; - if (less(t, tc::as_const(ref))) { - return RangeReturn::pack_no_element(std::forward(rng)); - } else { - return RangeReturn::pack_element(it, std::forward(rng), tc_move_if_owned(ref)); - } - } + template< typename RangeReturn, typename Rng, typename T, typename Pred > + [[nodiscard]] decltype(auto) binary_find_first(Rng&& rng, T const& t, Pred predLessOr3way) noexcept { + return binary_find_first_or_unique_adl::binary_find_first_or_unique(tc_move_if_owned(rng), t, tc_move(predLessOr3way)); } template< typename RangeReturn, typename Rng, typename T > @@ -1043,7 +1018,7 @@ namespace tc { }; } - template< typename RngA, typename RngB, typename Comp, typename SinkA, typename SinkB, typename SinkAB > + template< typename RngA, tc::range_with_iterators RngB, typename Comp, typename SinkA, typename SinkB, typename SinkAB > auto interleave_2(RngA&& rnga, RngB&& rngb, Comp const comp, SinkA const sinka, SinkB const sinkb, SinkAB const sinkab) MAYTHROW -> tc::common_type_t< decltype(tc::for_each(rnga, std::declval>() @@ -1065,6 +1040,23 @@ namespace tc { return tc::constant(); } + template< tc::range_with_iterators RngA, typename RngB, typename Comp, typename SinkA, typename SinkB, typename SinkAB > + requires (!tc::range_with_iterators) + auto interleave_2(RngA&& rnga, RngB&& rngb, Comp&& comp, SinkA&& sinka, SinkB&& sinkb, SinkAB&& sinkab) MAYTHROW { + return tc::interleave_2( + std::forward(rngb), + std::forward(rnga), + [comp=tc::decay_copy(std::forward(comp))](auto const& elemb, auto const& elema) noexcept { + return tc::negate(comp(elema, elemb)); + }, + std::forward(sinkb), + std::forward(sinka), + [sinkab=tc::decay_copy(std::forward(sinkab))](auto&& elemb, auto&& elema) noexcept { + return sinkab(tc_move_if_owned(elema), tc_move_if_owned(elemb)); + } + ); + } + namespace no_adl { template struct SInterleaveImpl { diff --git a/tc/algorithm/algorithm.t.cpp b/tc/algorithm/algorithm.t.cpp index 1cf9382..7f21943 100644 --- a/tc/algorithm/algorithm.t.cpp +++ b/tc/algorithm/algorithm.t.cpp @@ -28,33 +28,6 @@ namespace { void(tc::explicit_cast(tc::vector{})); } -UNITTESTDEF( quantifiers ) { - - tc::vector v{1,2,3,4,5,6,7}; - - tc::vector all_even{2,4,6,8}; - tc::vector all_odd{3,5,7,9}; - - auto even = [](int i) noexcept { return i%2==0; }; - - int const existing_value = 5; - int const non_existing_value = 9; - - auto const_range = tc::slice(tc::as_const(v)); TEST_RANGE_LENGTH(const_range, 7); - - _ASSERT( tc::find_first(const_range, existing_value)); - _ASSERT(!tc::find_first(const_range, non_existing_value)); - - _ASSERT(! tc::all_of(const_range, even)); - _ASSERT( tc::any_of(const_range, even)); - - _ASSERT( tc::all_of(all_even, even)); - _ASSERT( tc::any_of(all_even, even)); - - _ASSERT(! tc::all_of(all_odd, even)); - _ASSERT(! tc::any_of(all_odd, even)); -} - UNITTESTDEF( sort_accumulate_each_unique_range_2 ) { struct SValAccu final { SValAccu(int val, int accu) noexcept : m_val(val), m_accu(accu) {} @@ -102,10 +75,10 @@ UNITTESTDEF(filter_no_self_assignment_of_rvalues) { UNITTESTDEF( trim_leftright_if ) { tc::vector v{1,2,3,4,5,6,7,7,7}; - auto rng = tc::trim_left_if(v, [] (int n) noexcept {return n<4;}); + auto rng = tc::trim_left_if(v, [] (int const n) noexcept {return n<4;}); _ASSERT(tc::begin(rng) != tc::end(rng)); _ASSERTEQUAL(tc::size(rng), 6); - _ASSERTEQUAL(tc::size(tc::trim_right_if(rng, [] (int n) noexcept {return n==7;})), 3); + _ASSERTEQUAL(tc::size(tc::trim_right_if(rng, [] (int const n) noexcept {return n==7;})), 3); } UNITTESTDEF( is_sorted ) { @@ -145,43 +118,10 @@ UNITTESTDEF( is_sorted ) { UNITTESTDEF( make_vector_on_r_vector_is_identity ) { tc::vector v{1,2,3}; - auto pvecdata = v.data(); + auto pvecdata = tc::ptr_begin(v); auto vNew = tc::make_vector(tc_move(v)); - _ASSERTEQUAL(vNew.data(), pvecdata); -} - -UNITTESTDEF(rangefilter_on_subrange) { - tc::vector vecn={2,3,3,0, /*rngn starts here*/ 3,4,5,6,4,5,6,7}; - auto rngn=tc::begin_next(vecn, 4); - - { - tc::sort_unique_inplace(rngn); // uses range_filter&> > internally - int const anExpected[]={2,3,3,0, /*rngn starts here*/ 3,4,5,6,7}; - _ASSERT(tc::equal(vecn, anExpected)); - _ASSERTEQUAL(tc::begin(rngn), tc::begin_next(vecn,4)); - _ASSERTEQUAL(tc::end(rngn), tc::end(vecn)); - } - - { - { - tc::range_filter&> > filter(rngn); - auto it=tc::begin(rngn); - filter.keep(it++); - filter.keep(it++); - filter.keep(it++); - int i=0; - while(tc::begin(filter)!=tc::end(filter)) { - tc::drop_last_inplace(filter); - ++i; - } - _ASSERTEQUAL(i,3); - } - int const anExpected[]={2,3,3,0, /*rngn starts here*/}; - _ASSERT(tc::equal(vecn, anExpected)); - _ASSERTEQUAL(tc::begin(rngn), tc::begin_next(vecn,4)); - _ASSERTEQUAL(tc::end(rngn), tc::end(vecn)); - } + _ASSERTEQUAL(tc::ptr_begin(vNew), pvecdata); } UNITTESTDEF(is_strictly_sorted){ @@ -231,7 +171,7 @@ UNITTESTDEF(Naryinterleave) { ); _ASSERT(tc::equal( - tc::transform(vecnBCopy, [](int n) noexcept {return n+100;}), + tc::transform(vecnBCopy, [](int const n) noexcept {return n+100;}), vecnB )); } @@ -273,7 +213,7 @@ UNITTESTDEF(NaryinterleaveBreak) { ); _ASSERT(tc::equal( - tc::transform(vecnBCopy, [](int n) noexcept {return n < 7 ? n + 100 : n; }), + tc::transform(vecnBCopy, [](int const n) noexcept {return n < 7 ? n + 100 : n; }), vecnB )); } @@ -337,7 +277,7 @@ UNITTESTDEF(plurality_element_test) { auto const str3 = "a"; _ASSERTEQUAL(tc::begin(str3),tc::plurality_element(str3)); int an[] = {1,2,3,4,3,4,3,5,6}; - _ASSERTEQUAL(std::optional(4), tc::plurality_element(tc::filter(an, [](auto n) noexcept { return n % 2 == 0; }))); + _ASSERTEQUAL(std::optional(4), tc::plurality_element(tc::filter(an, [](auto const n) noexcept { return n % 2 == 0; }))); } static_assert(std::is_move_constructible const&>()))>::value); @@ -361,7 +301,7 @@ UNITTESTDEF(constexpr_sort_test) { std::mt19937 gen; // same sequence of numbers each time for reproducibility std::uniform_int_distribution<> dist(0, 63); - auto Test = [](auto rngn) noexcept { + static auto constexpr Test = [](auto rngn) noexcept { auto vecn = tc::explicit_cast>(rngn); _ASSERTEQUAL(tc_modified(vecn, tc::sort_inplace(_)), tc_modified(vecn, tc::constexpr_sort_inplace_detail::constexpr_sort_inplace(tc::begin(_), tc::end(_), tc::fn_less()))); }; diff --git a/tc/algorithm/any_accu.h b/tc/algorithm/any_accu.h index 12c2b66..e5a9dc6 100644 --- a/tc/algorithm/any_accu.h +++ b/tc/algorithm/any_accu.h @@ -25,7 +25,7 @@ namespace tc { return m_b; } - constexpr void operator()(bool b) & noexcept { + constexpr void operator()(bool const b) & noexcept { tc_inplace(m_b) || b; } diff --git a/tc/algorithm/append.h b/tc/algorithm/append.h index 23c0460..fab1e31 100644 --- a/tc/algorithm/append.h +++ b/tc/algorithm/append.h @@ -10,6 +10,7 @@ #include "../base/assert_defs.h" #include "../base/construction_restrictiveness.h" +#include "../base/inside_unwinding.h" #include "../container/container_traits.h" #include "../container/insert.h" @@ -143,7 +144,7 @@ namespace tc { } else { // assume iterators are stable to get iterator to first inserted element auto const it = tc::back(cont); - auto FirstAppendedElement = [&]() noexcept { + auto const FirstAppendedElement = [&]() noexcept { return it ? tc_modified(it, ++_) : tc::begin(cont); }; try { @@ -163,6 +164,36 @@ namespace tc { return tc::append(std::forward(cont), tc::concat(std::forward(rng)...)); } + namespace no_adl { + template + struct append_on_dtor_t final : tc::noncopyable, tc::inside_unwinding { + tc::optional m_ocont; + tc::reference_or_value m_rng; + + append_on_dtor_t(Cont& cont, Rng&& rng) noexcept + : m_ocont(cont), m_rng(tc::aggregate_tag, std::forward(rng)) + {} + + append_on_dtor_t(append_on_dtor_t&& other) noexcept + : m_ocont(tc_move(other.m_ocont)), m_rng(tc_move(other.m_rng)) + { + other.m_ocont = std::nullopt; + } + ASSIGN_BY_RENEW(append_on_dtor_t, append_on_dtor_t&&); + + ~append_on_dtor_t() MAYTHROW { + if(m_ocont && !inside_stack_unwinding()) { + tc::append(*m_ocont, *tc_move(m_rng)); // MAYTHROW + } + } + }; + } + + template Rng> + constexpr decltype(auto) make_append_on_dtor(Cont& cont, Rng&& rng) MAYTHROW { + return no_adl::append_on_dtor_t(cont, std::forward(rng)); + } + namespace explicit_convert_to_container_detail { template using use_ctor=tc::constant< diff --git a/tc/algorithm/append.t.cpp b/tc/algorithm/append.t.cpp index 6ab1490..029cb64 100644 --- a/tc/algorithm/append.t.cpp +++ b/tc/algorithm/append.t.cpp @@ -14,7 +14,6 @@ #include "../static_vector.h" #include "../range/filter_adaptor.h" - static_assert(tc::appendable&>); static_assert(!tc::appendable&>); static_assert(tc::appendable&>); @@ -25,85 +24,27 @@ static_assert(!tc::appendable, tc::string&>); UNITTESTDEF(nonappendable) { tc::vector vecnTest{1, 2, 3}; - auto rngTest1=tc::transform(vecnTest, [](auto n) noexcept { return n + 1; }); + auto rngTest1=tc::transform(vecnTest, [](auto const n) noexcept { return n + 1; }); static_assert(!tc::appendable&>); auto rngTest2=tc::filter(vecnTest, [](auto const n) noexcept { return n%2==1; }); static_assert(!tc::appendable&>); } -namespace { - struct SNonReportAppendable final { - friend bool operator==(SNonReportAppendable const&, int) noexcept { - return true; - } - }; -} - -UNITTESTDEF(nonreportappendable) { - int n = 5; - _ASSERTEQUAL(SNonReportAppendable(), n); - VERIFYEQUAL(SNonReportAppendable(), n); -#ifdef TC_PRIVATE - VERIFYPRED(SNonReportAppendable(), _==n); -#endif -} - -#ifdef TC_PRIVATE -#include "Library/ErrorReporting/decl.h" - -namespace { - struct STestError1 { - friend auto debug_output_impl(STestError1 const&) noexcept { - return tc::concat("STestError1(", tc::debug_output(-1), ")"); +UNITTESTDEF(append_on_dtor) { + { + tc::string str; + tc::append(str, "Hello "); + { + tc::make_append_on_dtor(str, "World!"); } - }; - - struct STestError2 { - DECLARE_FRIEND_FN_DEBUG_OUTPUT_IMPL(STestError2) - }; - - DEFINE_FRIEND_FN_DEBUG_OUTPUT_IMPL(STestError2) { - tc::for_each(tc::concat("STestError2(", tc::debug_output(5), ", ", tc::debug_output(STestError1()), ")"), tc_move(appdr)); + _ASSERT(tc::equal(str, "Hello World!")); } - - struct STestError3 {}; - - auto debug_output_impl(STestError3 const&) noexcept { - return [](tc::report_appender appdr) noexcept { - tc::for_each(tc::concat("STestError3(", tc::debug_output(STestError2()), ", ", tc::debug_output(nullptr), ")"), tc_move(appdr)); - }; + { + tc::string str; + { + tc_auto_cref(a1, tc::make_append_on_dtor(str, "World!")); + tc_auto_cref(a2, tc::make_append_on_dtor(str, "Hello ")); + } + _ASSERT(tc::equal(str, "Hello World!")); } } - -UNITTESTDEF(SReportStreamTest) { - tc::SReportStream rs; - char const* pt = nullptr; - tc::append(rs, UTF16("abc[")); - tc::append(rs, tc::debug_output(10)); - tc::append(rs, "][", tc::debug_output(pt), "]"); - tc::append(rs, tc::debug_output(STestError3())); - _ASSERTEQUAL(rs.m_str, "abc[10][nullptr]STestError3(STestError2(5, STestError1(-1)), nullptr)"); -} - - -UNITTESTDEF(SReportStreamExceptionTest) { - tc::SReportStream rs; - tc::append(rs, - "Generate msg#0: ", [](auto sink) MAYTHROW { - tc::for_each("", sink); - tc::for_each([](auto sink) MAYTHROW { throw 0; }, sink); - throw 1; - }, "\n" - "Generate msg#1: ", [](tc::report_appender appdr) MAYTHROW { - tc::for_each([](auto sink) MAYTHROW { throw 0; }, appdr); - tc::for_each([](auto sink) MAYTHROW { throw 1; }, appdr); - tc::for_each("", appdr); - throw 0; - }, "\n" - ); - _ASSERTEQUAL(rs.m_str, - "Generate msg#0: \n" - "Generate msg#1: \n" - ); -} -#endif diff --git a/tc/algorithm/best_element.h b/tc/algorithm/best_element.h index 503bf98..fe55a76 100644 --- a/tc/algorithm/best_element.h +++ b/tc/algorithm/best_element.h @@ -8,7 +8,6 @@ #pragma once -#include "../range/range_fwd.h" #include "../range/subrange.h" #include "../storage_for.h" #include "../base/assign.h" @@ -52,7 +51,9 @@ namespace tc { } } } - } else if (auto ovalue = tc::accumulate_with_front(std::forward(rng), tc::fn_assign_better(tc_move(better)))) { + } else if (auto ovalue = tc::accumulate_with_front(std::forward(rng), [&](auto& valueBest, auto&& value) noexcept { + return tc::assign_better(better, valueBest, tc_move_if_owned(value)); + })) { return RangeReturn::template pack_element(*tc_move(ovalue)); } else { return RangeReturn::template pack_no_element(); diff --git a/tc/algorithm/break_or_continue.h b/tc/algorithm/break_or_continue.h index 752ce68..c1dc265 100644 --- a/tc/algorithm/break_or_continue.h +++ b/tc/algorithm/break_or_continue.h @@ -15,8 +15,6 @@ #include "../base/derivable.h" #include "../base/invoke.h" -#include "accumulator.h" - #include #include @@ -85,6 +83,11 @@ namespace tc { //// continue_if_not_break /////////////////////////////////////////////////////////////////////////// // Func returns break_or_continue + template + concept sinkable = tc::invocable const&, Args...>; + template + concept nothrow_sinkable = tc::sinkable && tc::nothrow_invocable const&, Args...>; + template constexpr auto continue_if_not_break(Sink const& sink, Args&&... args) return_decltype_MAYTHROW( tc_internal_continue_if_not_break(tc::invoke(sink, std::forward(args)...)) @@ -198,7 +201,7 @@ namespace tc { using no_adl::move_only_function; - inline bool cyclic_improve_impl(int n, int& nSkipRule) noexcept { + inline bool cyclic_improve_impl(int const n, int& nSkipRule) noexcept { return false; } diff --git a/tc/algorithm/compare.h b/tc/algorithm/compare.h index e5b86f7..fd58ab9 100644 --- a/tc/algorithm/compare.h +++ b/tc/algorithm/compare.h @@ -39,24 +39,9 @@ namespace tc { } namespace tc { - // we do not allow std::partial_ordering by tc::explicit_cast it to std::weak_ordering in tc::compare + // we always use tc::compare to perform 3 way comparison and tc::compare tc::explicit_cast std::strong_ordering/partial_ordering result to std::weak_ordering template - concept is_comparison_category = std::same_as, std::weak_ordering>; - - namespace no_adl { - template - struct common_comparison_category; - - template - struct common_comparison_category, std::enable_if_t<(... && tc::is_comparison_category)>> final { - using type = std::common_comparison_category_t; - static_assert(tc::is_comparison_category); - }; - } - - // provides a common type if every T is either std::weak_ordering or std::strong_ordering - template - using common_comparison_category_t = typename tc::no_adl::common_comparison_category>::type; + concept is_comparison_category = std::same_as; namespace no_adl { // class T derives from tc::equal_from_three_way if T has a user defined operator<=> and implements operator== with operator<=> @@ -82,7 +67,7 @@ namespace tc { template requires std::same_as friend constexpr bool operator==(T const&, T const&) noexcept { return true; } template requires std::same_as - friend constexpr auto operator<=>(T const&, T const&) noexcept { return std::strong_ordering::equal; } + friend constexpr auto operator<=>(T const&, T const&) noexcept { return std::weak_ordering::equivalent; } }; } using no_adl::equal_from_three_way; @@ -124,15 +109,12 @@ namespace tc { concept has_operator_3way_compare = requires(Lhs const& lhs, Rhs const& rhs) { lhs<=>rhs; }; template - constexpr auto compare(Lhs const& lhs, Rhs const& rhs) noexcept(noexcept(lhs<=>rhs)) requires has_operator_3way_compare && (!tc::comparable_pointers) { - if constexpr (std::same_asrhs), std::partial_ordering>) { - // 1. floating point comparison - // 2. library types comparison may return std::partial_ordering if they contain floating point values. e.g. std::vector, std::pair, std::variant, std::tuple - return tc::explicit_cast(lhs<=>rhs); - } else { - static_assert(tc::is_comparison_categoryrhs)>); - return lhs<=>rhs; - } + constexpr std::weak_ordering compare(Lhs const& lhs, Rhs const& rhs) noexcept(noexcept(lhs<=>rhs)) requires has_operator_3way_compare && (!tc::comparable_pointers) { + // We tc::explicit_cast std::partial_ordering/std::strong_ordering result to std::weak_ordering. + // Comparisons that return std::partial_ordering: + // 1. floating point comparison + // 2. library types comparison may return std::partial_ordering if they contain floating point values. e.g. std::vector, std::pair, std::variant, std::tuple + tc_return_cast(lhs<=>rhs); } // similar to Synthesized three-way comparison except with tc::less instead of operator< @@ -221,7 +203,7 @@ namespace tc { return tc::is_neq(order); }), std::weak_ordering::equal); #else // constexpr workaround - tc::range_value_t order=std::strong_ordering::equivalent; + tc::range_value_t order=std::weak_ordering::equivalent; tc::for_each(rng, [&](auto const& order2) noexcept { if(tc::is_neq(order2)) { order = order2; @@ -247,19 +229,19 @@ namespace tc { for(;;) { if( itLhs==itLhsEnd ) { if constexpr(eprefixEQUIVALENT==eprefix) { - return std::strong_ordering::equivalent; // if lhs is a prefix of rhs this is considered equivalent + return std::weak_ordering::equivalent; // if lhs is a prefix of rhs this is considered equivalent } else { if( itRhs==itRhsEnd ) { - return std::strong_ordering::equivalent; + return std::weak_ordering::equivalent; } else { _ASSERTE(eprefixALLOW==eprefix); - return std::strong_ordering::less; // lhs shorter than rhs, thus < + return std::weak_ordering::less; // lhs shorter than rhs, thus < } } } if( itRhs==itRhsEnd ) { _ASSERTE(eprefixFORBID!=eprefix); - return std::strong_ordering::greater; // rhs shorter than lhs, thus > + return std::weak_ordering::greater; // rhs shorter than lhs, thus > } tc_return_if_not_equal( fnCompare( *itLhs, *itRhs ) ); ++itLhs; @@ -299,13 +281,13 @@ namespace std { // ADL } template< typename First, typename Second > - [[nodiscard]] constexpr auto operator<=>(std::pair const& lhs, std::pair const& rhs ) noexcept -> tc::common_comparison_category_t< - decltype(tc::compare(lhs.first, rhs.first)), - decltype(tc::compare(lhs.second, rhs.second)) - > { + [[nodiscard]] constexpr auto operator<=>(std::pair const& lhs, std::pair const& rhs) noexcept requires requires { + tc::compare(lhs.first, rhs.first); + tc::compare(lhs.second, rhs.second); + } { tc_compare_expr( _.first ); tc_compare_expr( _.second ); - return std::strong_ordering::equivalent; + return std::weak_ordering::equivalent; } template @@ -320,22 +302,21 @@ namespace std { // ADL template [[nodiscard]] constexpr auto operator<=>(std::variant const& lhs, std::variant const& rhs) noexcept - -> tc::common_comparison_category_t(), std::declval()))...> + requires requires { typename tc::type::list(), std::declval()))...>; } { if(lhs.valueless_by_exception() && rhs.valueless_by_exception()) { - return std::strong_ordering::equal; + return std::weak_ordering::equivalent; } else if(lhs.valueless_by_exception()) { - return std::strong_ordering::less; + return std::weak_ordering::less; } else if(rhs.valueless_by_exception()) { - return std::strong_ordering::greater; + return std::weak_ordering::greater; } else { tc_compare_expr(_.index()); - using result_t = tc::common_comparison_category_t(), std::declval()))...>; return tc::fn_visit( - [](TVal const& valLhs, TVal const& valRhs) noexcept -> result_t { + [](TVal const& valLhs, TVal const& valRhs) noexcept { return tc::compare(valLhs, valRhs); }, - tc::never_called() + tc::never_called() )(lhs, rhs); } } @@ -346,13 +327,13 @@ namespace std { // ADL } template - constexpr std::strong_ordering operator<=>(std::optional const& opt, std::nullopt_t) noexcept { + constexpr std::weak_ordering operator<=>(std::optional const& opt, std::nullopt_t) noexcept { return tc::compare(static_cast(opt), false); } template>* = nullptr> constexpr auto operator<=>(std::optional const& opt, U const& value) noexcept -> decltype(tc::compare(*opt, value)) { - return opt ? tc::compare(*opt, value) : std::strong_ordering::less; + return opt ? tc::compare(*opt, value) : std::weak_ordering::less; } } #endif @@ -418,11 +399,11 @@ namespace tc { } namespace tuple_adl { - template + template requires requires { typename tc::type::list(), std::declval()))...>; } constexpr auto operator<=>(tc::tuple const& lhs, tc::tuple const& rhs) noexcept { STATICASSERTEQUAL(sizeof...(T), sizeof...(U)); return tc::lexicographical_compare_3way( - tc::transform(std::index_sequence_for(), [&](auto nconstIndex) noexcept -> tc::common_comparison_category_t const&>(), std::declval const&>()))...> { + tc::transform(std::index_sequence_for(), [&](auto const nconstIndex) noexcept -> std::weak_ordering { return tc::compare(tc::get(lhs), tc::get(rhs)); }) ); diff --git a/tc/algorithm/element.h b/tc/algorithm/element.h index 4f03e4a..87461e0 100644 --- a/tc/algorithm/element.h +++ b/tc/algorithm/element.h @@ -9,7 +9,7 @@ #pragma once #include "find.h" -#include "../range/subrange.h" +#include "../range/range_return.h" namespace tc { template diff --git a/tc/algorithm/empty.h b/tc/algorithm/empty.h index c066d77..fd59ae4 100644 --- a/tc/algorithm/empty.h +++ b/tc/algorithm/empty.h @@ -18,7 +18,7 @@ namespace tc { template constexpr auto empty(Rng&& rng) noexcept { if constexpr(has_constexpr_size) { - return [&]() return_decltype_noexcept(0==constexpr_size::value); + return [&]() return_decltype_noexcept(0==constexpr_size()); } else if constexpr(has_mem_fn_empty) { return [&]() return_MAYTHROW(tc_move_if_owned(rng).empty()); } else if constexpr(tc::range_with_iterators) { diff --git a/tc/algorithm/equal.t.cpp b/tc/algorithm/equal.t.cpp index 60995b4..63ff8e1 100644 --- a/tc/algorithm/equal.t.cpp +++ b/tc/algorithm/equal.t.cpp @@ -38,7 +38,7 @@ UNITTESTDEF( equal_vec_int_pred ) { TEST_init_hack(tc::vector, int, v123, {1,2,3}); TEST_init_hack(tc::vector, int, v234, {2,3,4}); - auto ofByOne = [](int lhs, int rhs) noexcept { return (lhs + 1) == rhs; }; // unsymmetrical to uncover wrong order of argument application to the predicate + auto const ofByOne = [](int const lhs, int const rhs) noexcept { return (lhs + 1) == rhs; }; // unsymmetrical to uncover wrong order of argument application to the predicate _ASSERT(tc::equal(v123, v234, ofByOne)); _ASSERT(!tc::equal(v234, v123, ofByOne)); @@ -89,7 +89,7 @@ UNITTESTDEF( equal_generator_pred ) { auto g123 = tc::make_generator_range(v123); auto g234 = tc::make_generator_range(v234); - auto ofByOne = [](int lhs, int rhs) noexcept { return (lhs + 1) == rhs; }; // unsymmetrical to uncover wrong order of argument application to the predicate + auto const ofByOne = [](int const lhs, int const rhs) noexcept { return (lhs + 1) == rhs; }; // unsymmetrical to uncover wrong order of argument application to the predicate //_ASSERT(tc::equal(g123, g234, ofByOne)); // Fails to compile with proper msg, as it should be. diff --git a/tc/algorithm/filter_inplace.h b/tc/algorithm/filter_inplace.h index c3d95f9..c142adb 100644 --- a/tc/algorithm/filter_inplace.h +++ b/tc/algorithm/filter_inplace.h @@ -10,7 +10,7 @@ #include "../base/assert_defs.h" #include "../range/meta.h" -#include "../range/subrange.h" +#include "../range/range_return.h" #include "../container/container_traits.h" #include "../storage_for.h" #include "restrict_size_decrement.h" @@ -31,12 +31,10 @@ namespace tc { has_mem_fn_hash_function struct range_filter : tc::noncopyable { static_assert(tc::decayed); - using iterator = tc::iterator_t; - using const_iterator = iterator; // no deep constness (analog to subrange) private: Cont& m_cont; - iterator m_itOutputEnd; + tc::iterator_t m_itOutputEnd; public: explicit constexpr range_filter(Cont& cont) noexcept @@ -44,7 +42,7 @@ namespace tc { , m_itOutputEnd(tc::begin(cont)) {} - constexpr range_filter(Cont& cont, iterator const& itStart) noexcept + constexpr range_filter(Cont& cont, tc::iterator_t const& itStart) noexcept : m_cont(cont) , m_itOutputEnd(itStart) {} @@ -53,7 +51,7 @@ namespace tc { tc::take_inplace(m_cont, m_itOutputEnd); } - constexpr void keep(iterator it) & noexcept { + constexpr void keep(tc::iterator_t it) & noexcept { #ifdef _CHECKS auto const nDistance = std::distance(m_itOutputEnd, it); _ASSERTE( 0<=nDistance ); @@ -67,11 +65,11 @@ namespace tc { // range interface for output range // no deep constness (analog to subrange) - constexpr iterator begin() const& noexcept { - return tc::begin(tc::as_mutable(m_cont)); + constexpr auto begin() const& noexcept { + return tc::begin(m_cont); } - constexpr iterator end() const& noexcept { + constexpr auto end() const& noexcept { return m_itOutputEnd; } @@ -83,58 +81,16 @@ namespace tc { } }; - template - struct range_filter : Cont, private tc::noncopyable { - static_assert(tc::dependent_false::value, "Careful: currently unused and without unit test"); - - static_assert(tc::decayed); - using typename Cont::iterator; - using const_iterator = iterator; // no deep constness (analog to subrange) - - private: - Cont& m_contInput; - iterator m_itLastOutput; - - public: - explicit constexpr range_filter(Cont& cont) noexcept - : m_contInput(cont) - , m_itLastOutput(cont.before_begin()) - {} - - explicit constexpr range_filter(Cont& cont, iterator const& itStart) noexcept - : range_filter(cont) - { - for(;;) { - auto it=tc::begin(m_contInput); - if( it==itStart ) break; - this->splice_after(m_itLastOutput,m_contInput.before_begin()); - m_itLastOutput=it; - } - } - - constexpr ~range_filter() { - m_contInput=tc_move_always( tc::base_cast(*this) ); - } - - constexpr void keep(iterator it) & noexcept { - while( it!=tc::begin(m_contInput) ) m_contInput.pop_front(); - this->splice_after(m_itLastOutput,m_contInput.before_begin()); - m_itLastOutput=it; - } - }; - template struct range_filter : Cont, private tc::noncopyable { static_assert(tc::decayed); Cont& m_contInput; - using typename Cont::iterator; - using const_iterator = iterator; // no deep constness (analog to subrange) explicit constexpr range_filter(Cont& cont) noexcept : m_contInput(cont) {} - constexpr range_filter(Cont& cont, iterator const& itStart) noexcept + constexpr range_filter(Cont& cont, tc::iterator_t const& itStart) noexcept : m_contInput(cont) { this->splice( tc::end(*this), m_contInput, tc::begin(m_contInput), itStart ); @@ -144,7 +100,7 @@ namespace tc { m_contInput=tc_move_always( tc::base_cast(*this) ); } - constexpr void keep(iterator it) & noexcept { + constexpr void keep(tc::iterator_t it) & noexcept { _ASSERTE( it!=tc::end(m_contInput) ); this->splice( tc::end(*this), @@ -157,16 +113,14 @@ namespace tc { template requires range_filter_by_move_element::value struct range_filter : tc::noncopyable { static_assert(tc::decayed); - using iterator = tc::iterator_t; - using const_iterator = iterator; // no deep constness (analog to subrange) protected: Cont& m_cont; - iterator m_itOutput; + tc::iterator_t m_itOutput; private: #ifdef _CHECKS - iterator m_itFirstValid; + tc::iterator_t m_itFirstValid; #endif public: @@ -178,7 +132,7 @@ namespace tc { #endif {} - explicit constexpr range_filter(Cont& cont, iterator itStart) noexcept + explicit constexpr range_filter(Cont& cont, tc::iterator_t itStart) noexcept : m_cont(cont) , m_itOutput(itStart) #ifdef _CHECKS @@ -190,7 +144,7 @@ namespace tc { tc::take_inplace( m_cont, m_itOutput ); } - constexpr void keep(iterator it) & noexcept { + constexpr void keep(tc::iterator_t it) & noexcept { #ifdef _CHECKS // Filter without reordering _ASSERTE( 0<=std::distance(m_itFirstValid, it) ); @@ -207,11 +161,11 @@ namespace tc { // range interface for output range // no deep constness (analog to subrange) - constexpr iterator begin() const& noexcept { - return tc::begin(tc::as_mutable(m_cont)); + constexpr auto begin() const& noexcept { + return tc::begin(m_cont); } - constexpr iterator end() const& noexcept { + constexpr auto end() const& noexcept { return m_itOutput; } @@ -228,48 +182,6 @@ namespace tc { } }; - template requires range_filter_by_move_element::value - struct range_filter> { - using iterator = tc::iterator_t; - using const_iterator = iterator; // no deep constness (analog to subrange) - - explicit constexpr range_filter(tc::subrange< Cont& >& rng) noexcept : m_rng(rng) { - _ASSERTE(tc::end(m_rng) == tc::end(Container())); // otherwise, we would need to keep [ end(m_rng), end(Container()) ) inside dtor - m_orngfilter.ctor(Container(), tc::begin(rng)); - } - - constexpr void keep(iterator it) & noexcept { - m_orngfilter->keep(it); - } - - constexpr iterator begin() const& noexcept { - return tc::begin(m_rng); - } - - constexpr iterator end() const& noexcept { - return tc::end(*m_orngfilter); - } - - constexpr void pop_back() & noexcept { - _ASSERTE(tc::end(*this) != tc::begin(*this)); - m_orngfilter->pop_back(); - } - - constexpr ~range_filter() { - auto& cont = Container(); - auto const nIndexBegin = tc::begin(m_rng) - tc::begin(cont); - m_orngfilter.dtor(); // erases cont tail and invalidates iterators in m_rng - m_rng = tc::begin_next(cont, nIndexBegin); - } - - constexpr Cont& Container() const& noexcept { - return tc::implicit_cast(m_rng.base_range()); - } - - tc::subrange< Cont& >& m_rng; - tc::storage_for< tc::range_filter > m_orngfilter; - }; - ///////////////////////////////////////////////////// // filter_inplace diff --git a/tc/algorithm/find.h b/tc/algorithm/find.h index bb6f37c..31dab1e 100644 --- a/tc/algorithm/find.h +++ b/tc/algorithm/find.h @@ -12,6 +12,7 @@ #include "../range/meta.h" #include "../range/iterator_cache.h" #include "../base/scope.h" +#include "../base/trivial_functors.h" #include "../storage_for.h" #include "equal.h" diff --git a/tc/algorithm/find_closest_if.h b/tc/algorithm/find_closest_if.h index 6e7a00b..985f88f 100644 --- a/tc/algorithm/find_closest_if.h +++ b/tc/algorithm/find_closest_if.h @@ -34,7 +34,7 @@ namespace tc { return tc::constant(); } if (tc::back(aitLimit) == tc::back(ait)) { - for (; tc::front(ait) != tc::front(aitLimit); ) { + while (tc::front(ait) != tc::front(aitLimit)) { --tc::front(ait); tc_yield(func, tc::begin_next(tc::as_const(ait))); } @@ -48,7 +48,7 @@ namespace tc { template < typename RangeReturn, typename Rng, typename It, typename Pred = tc::identity> - [[nodiscard]] constexpr decltype(auto) find_closest_if(Rng&& rng, It it, bool bSkipSelf, Pred pred = Pred()) noexcept { + [[nodiscard]] constexpr decltype(auto) find_closest_if(Rng&& rng, It it, bool const bSkipSelf, Pred pred = Pred()) noexcept { tc::storage_for> oitc; if (tc::break_ == tc::for_each_iterator_pair_outwards(rng, tc_move(it), bSkipSelf, [&](auto const& rngit) noexcept { return tc::for_each(rngit, [&](auto const& it) noexcept { @@ -66,7 +66,7 @@ namespace tc { } template < typename RangeReturn, typename Rng, typename Index, typename Pred = tc::identity> - [[nodiscard]] decltype(auto) find_closest_if_with_index(Rng&& rng, Index&& n, bool bSkipSelf, Pred&& pred = Pred()) noexcept { + [[nodiscard]] decltype(auto) find_closest_if_with_index(Rng&& rng, Index&& n, bool const bSkipSelf, Pred&& pred = Pred()) noexcept { return find_closest_if(std::forward(rng), tc::begin_next(rng, std::forward(n)), bSkipSelf, std::forward(pred)); } } diff --git a/tc/algorithm/find_closest_if.t.cpp b/tc/algorithm/find_closest_if.t.cpp index bc0b562..1ddfa73 100644 --- a/tc/algorithm/find_closest_if.t.cpp +++ b/tc/algorithm/find_closest_if.t.cpp @@ -12,7 +12,7 @@ UNITTESTDEF(find_closest_if) { struct IntCompareOnce final : boost::noncopyable { IntCompareOnce(int n) noexcept :m_n(n) { } - bool operator==(int n) const& noexcept { + bool operator==(int const n) const& noexcept { _ASSERT( tc::change(m_bCompared, true) ); return m_n==n; } @@ -21,7 +21,7 @@ UNITTESTDEF(find_closest_if) { bool mutable m_bCompared = false; }; - auto find=[](auto const& rngn, int iStart, int nTarget, int nComparisonsMax) noexcept { + auto find=[](auto const& rngn, int const iStart, int const nTarget, int const nComparisonsMax) noexcept { int nComparisons = 0; return tc::find_closest_if_with_index(rngn, iStart, /*bSkipSelf*/false, [&](IntCompareOnce const& n) noexcept { _ASSERT(++nComparisons<=nComparisonsMax); diff --git a/tc/algorithm/for_each.h b/tc/algorithm/for_each.h index 6059d40..ef5a2db 100644 --- a/tc/algorithm/for_each.h +++ b/tc/algorithm/for_each.h @@ -270,7 +270,7 @@ namespace tc { ) template>* = nullptr, + std::enable_if_t, std::tuple>>* = nullptr, typename IndexList = typename for_each_detail::integer_sequence_to_type_list>::value>>::type > constexpr auto for_each_impl(adl_tag_t, Tuple&& tuple, Sink&& sink) return_decltype_MAYTHROW( @@ -300,7 +300,7 @@ namespace tc { template typename TupleT, typename Tuple> using type = tc::type::unique_t::arguments, + typename tc::is_instance_or_derived, TupleT>::arguments, tc::type::rcurry::template type >, tc::remove_rvalue_reference_t diff --git a/tc/algorithm/for_each.t.cpp b/tc/algorithm/for_each.t.cpp index d2d9608..cc9f74b 100644 --- a/tc/algorithm/for_each.t.cpp +++ b/tc/algorithm/for_each.t.cpp @@ -57,7 +57,7 @@ namespace { mock_reset(); } - void mock_reset(tc::vector const& v = tc::vector(), std::size_t break_at = 0, bool expect_break = true) & noexcept { + void mock_reset(tc::vector const& v = tc::vector(), std::size_t const break_at = 0, bool expect_break = true) & noexcept { if(!m_copyed_or_moved_from && !(m_index == tc::min(m_expect.size(), (m_expect_break) ? m_break_at + 1 : m_expect.size()))) { TEST_OUTPUT( << "unexpectedly terminated before index " << m_index << " went to the expected index " << tc::min(m_expect.size(), m_break_at + 1) << '\n'); @@ -70,7 +70,7 @@ namespace { m_copyed_or_moved_from = false; } - tc::break_or_continue operator()(int val) const& noexcept { + tc::break_or_continue operator()(int const val) const& noexcept { if (m_copyed_or_moved_from) { TEST_OUTPUT(<< "used copyed or moved consumer for real work!\n"); } @@ -98,7 +98,7 @@ namespace { all_called_mock g_mock; - void foo(int i) noexcept { g_mock(i); } + void foo(int const i) noexcept { g_mock(i); } //----------------------------------------------------------------------------------------------------------------------------- UNITTESTDEF( for_each ) { @@ -126,8 +126,8 @@ UNITTESTDEF( for_each ) { //g_mock.mock_reset(exp); tc::for_each(gv, &foo); // call with lambda - g_mock.mock_reset(exp); tc::for_each(v, [](int i) noexcept { g_mock(i); }); - g_mock.mock_reset(exp); tc::for_each(gv, [](int i) noexcept { g_mock(i); }); + g_mock.mock_reset(exp); tc::for_each(v, [](int const i) noexcept { g_mock(i); }); + g_mock.mock_reset(exp); tc::for_each(gv, [](int const i) noexcept { g_mock(i); }); // Todo: call with mem func, std::function and bind } @@ -174,7 +174,7 @@ UNITTESTDEF( for_each ) { struct consumer_break /*final*/ { consumer_break(tc::vector const& v, std::size_t break_at = 0, bool expect_break = true) noexcept : m_mock(v, break_at, expect_break) {} - tc::break_or_continue operator()(int i) const& noexcept { return m_mock(i); } + tc::break_or_continue operator()(int const i) const& noexcept { return m_mock(i); } private: all_called_mock m_mock; }; @@ -183,7 +183,7 @@ UNITTESTDEF( for_each ) { consumer_nobreak(tc::vector const& v, std::size_t break_at = 0, bool expect_break = true) noexcept : m_mock(v, break_at, expect_break) {} ~consumer_nobreak() {} - void operator()(int i) const& noexcept { m_mock(i); } + void operator()(int const i) const& noexcept { m_mock(i); } private: all_called_mock m_mock; @@ -230,7 +230,7 @@ UNITTESTDEF(for_each_adjacent_tuple_deref) { TEST_RANGE_EQUAL( vecn, - as_constexpr(tc::make_array(tc::aggregate_tag, 1,2,3,2,1)) + tc_as_constexpr(tc::make_array(tc::aggregate_tag, 1,2,3,2,1)) ); int nTransforms = 0; @@ -238,7 +238,7 @@ UNITTESTDEF(for_each_adjacent_tuple_deref) { tc::adjacent<3>( tc::transform( vecn, - [&](int n) noexcept {++nTransforms; return n;} + [&](int const n) noexcept {++nTransforms; return n;} ) ), [](int n0, int n1, int n2) noexcept { @@ -256,19 +256,19 @@ UNITTESTDEF(for_each_adjacent_tuple_deref) { std::ref(overloads) ); - TEST_RANGE_EQUAL(overloads.m_n, as_constexpr(tc::make_array(tc::aggregate_tag, 3,0,0))); + TEST_RANGE_EQUAL(overloads.m_n, tc_as_constexpr(tc::make_array(tc::aggregate_tag, 3,0,0))); } { lr_overloads overloads; tc::for_each( tc::adjacent<3>( - tc::transform(vecn, [](int n) noexcept {return n;}) + tc::transform(vecn, [](int const n) noexcept {return n;}) ), std::ref(overloads) ); - TEST_RANGE_EQUAL(overloads.m_n, as_constexpr(tc::make_array(tc::aggregate_tag, 0,2,1))); + TEST_RANGE_EQUAL(overloads.m_n, tc_as_constexpr(tc::make_array(tc::aggregate_tag, 0,2,1))); } } diff --git a/tc/algorithm/interleave_ranges.h b/tc/algorithm/interleave_ranges.h index 5d0b0dc..7b8e428 100644 --- a/tc/algorithm/interleave_ranges.h +++ b/tc/algorithm/interleave_ranges.h @@ -29,7 +29,7 @@ namespace tc { template struct SIteratorView : std::conditional_t>::value, tc::copyable, tc::noncopyable> { - static_assert( tc::is_index_valid_for_move_constructed_range>::value ); + static_assert( tc::stable_index_on_move> ); explicit SIteratorView(tc::iterator_t const& itrng) noexcept : m_rng(tc::aggregate_tag, *itrng) @@ -69,6 +69,9 @@ namespace tc { std::size_t m_nLast; static_assert(!tc::is_stashing_element>::value || std::is_copy_constructible>::value); + + interleave_ranges_index(tc::empty_range) noexcept + : m_nLast(0) {} interleave_ranges_index(RngRng const& rng) noexcept : tc_member_init_cast(m_vecview, tc::filter( tc::make_range_of_iterators(rng), @@ -124,7 +127,7 @@ namespace tc { } STATIC_FINAL_MOD(constexpr, end_index)() const& noexcept -> tc_index { - return {tc::empty_range(), 0}; + return tc::empty_range(); } STATIC_FINAL_MOD(constexpr, at_end_index)(tc_index const& idx) const& noexcept -> bool { diff --git a/tc/algorithm/longest_common_prefix.t.cpp b/tc/algorithm/longest_common_prefix.t.cpp index c0af0a2..2d9b721 100644 --- a/tc/algorithm/longest_common_prefix.t.cpp +++ b/tc/algorithm/longest_common_prefix.t.cpp @@ -11,7 +11,7 @@ namespace { UNITTESTDEF( longest_common_prefix ) { - auto CheckLCP = [](auto const& strLhs, auto const& strRhs, auto const& strPrefix, auto const& strSuffixLhs, auto const& strSuffixRhs) noexcept { + auto const CheckLCP = [](auto const& strLhs, auto const& strRhs, auto const& strPrefix, auto const& strSuffixLhs, auto const& strSuffixRhs) noexcept { tc_auto_cref(pairstrstrPrefix, tc::longest_common_prefix(strLhs, strRhs)); _ASSERT(tc::equal(pairstrstrPrefix.first, pairstrstrPrefix.second)); _ASSERT(tc::equal(pairstrstrPrefix.first, strPrefix)); diff --git a/tc/algorithm/minmax.h b/tc/algorithm/minmax.h index d81696c..625e28e 100644 --- a/tc/algorithm/minmax.h +++ b/tc/algorithm/minmax.h @@ -43,7 +43,7 @@ namespace tc { return operator()(std::forward(t0), std::forward(args)...); } else { STATICASSERTSAME(decltype(b), bool); - return CONDITIONAL_PRVALUE_AS_VAL( + return tc_conditional_prvalue_as_val( b, /*t1 is better*/operator()(std::forward(t1), std::forward(args)...), /*t0 is better or equal*/operator()(std::forward(t0), std::forward(args)...) diff --git a/tc/algorithm/partition_iterator.h b/tc/algorithm/partition_iterator.h index c461a19..ca98a3f 100644 --- a/tc/algorithm/partition_iterator.h +++ b/tc/algorithm/partition_iterator.h @@ -30,7 +30,7 @@ namespace tc { namespace adl { // default forward iterator implementation - template + template requires true // to make it more constrained than tc::iterator::middle_point It middle_point( It const& itBegin, It const& itEnd ) noexcept { // SEnumerateGapConstraints::SEnumerateGapConstraints calls intersect on transforms of counting ranges of tree iterators diff --git a/tc/algorithm/partition_range.h b/tc/algorithm/partition_range.h index b9c4d99..5f5e10a 100644 --- a/tc/algorithm/partition_range.h +++ b/tc/algorithm/partition_range.h @@ -90,6 +90,23 @@ namespace tc { pred ); } + + template + [[nodiscard]] decltype(auto) contains_exclusive_range(Rng&& rng, tc::interval const& intvlval) noexcept { + return lower_bound( + upper_bound(std::forward(rng),intvlval[tc::lo]), + intvlval[tc::hi] + ); + } + + template + [[nodiscard]] decltype(auto) contains_exclusive_range(Rng&& rng, tc::interval const& intvlval, SortPredicate&& pred) noexcept { + return lower_bound( + upper_bound(std::forward(rng),intvlval[tc::lo],pred), + intvlval[tc::hi], + pred + ); + } } using namespace range; } diff --git a/tc/algorithm/quantifier.h b/tc/algorithm/quantifier.h index 72c72ad..7059384 100644 --- a/tc/algorithm/quantifier.h +++ b/tc/algorithm/quantifier.h @@ -9,8 +9,8 @@ #pragma once #include "../base/invoke.h" -#include "../range/range_fwd.h" #include "../range/transform_adaptor.h" +#include "../variant.h" #include "for_each.h" #include "find.h" #include "compare.h" @@ -36,7 +36,7 @@ template< typename Rng, typename Pred = tc::identity > template< typename Rng > [[nodiscard]] std::pair all_any_of( Rng const& rng ) MAYTHROW { std::pair pairb(true,false); - tc::for_each(rng, [&](bool b) noexcept { + tc::for_each(rng, [&](bool const b) noexcept { pairb.first=pairb.first && b; pairb.second=pairb.second || b; return continue_if( pairb.first || !pairb.second ); @@ -73,4 +73,114 @@ template< typename Rng, typename Pred > return !tc::eager_any_of(std::forward(rng), std::not_fn(std::forward(pred))); } +// all_same_element +namespace all_same_result { + struct empty_t final {}; + struct not_same_t final {}; + + template + struct never_empty /*final*/ { + constexpr T operator()(empty_t) const& noexcept { _ASSERTFALSE; return tc::construct_default_or_terminate(); } + }; +} + +namespace no_adl { + template + struct [[nodiscard]] accumulator_all_same final { + static_assert(tc::decayed); + + using result_type = std::variant; + + constexpr accumulator_all_same() noexcept = default; + constexpr accumulator_all_same(auto&& equal) noexcept : m_equal(tc_move_if_owned(equal)) {} + + template + constexpr auto operator()(U&& u) & MAYTHROW { + if constexpr( std::is_same>::value ) { + return tc::fn_visit( + [&](all_same_result::empty_t) noexcept { + return tc::continue_if(!std::holds_alternative(m_result)); + }, + [&](auto&& t) MAYTHROW { + return operator()(tc_move_if_owned(t)); // MAYTHROW + }, + [&](all_same_result::not_same_t) noexcept { + m_result=all_same_result::not_same_t(); + return tc::break_; + } + )(std::forward(u).m_result_()); + } else { + return tc::fn_visit( + [&](all_same_result::empty_t) MAYTHROW { + m_result=std::forward(u); // MAYTHROW + return tc::continue_; + }, + [&](T const& t) noexcept { + if(m_equal(t, tc::as_const(u))) { + return tc::continue_; + } else { + m_result=all_same_result::not_same_t(); + return tc::break_; + } + }, + [](all_same_result::not_same_t) noexcept { + return tc::break_; + } + )(m_result); + } + } + + private: + PRIVATE_MEMBER_PUBLIC_ACCESSOR(result_type, m_result) + Equal m_equal; + }; + + struct return_all_same_result; + + template + struct all_same_element_impl; + + template + struct all_same_element_impl final { + static constexpr auto fn(Rng&& rng, Equal&& equal) MAYTHROW { + accumulator_all_same, tc::decay_t> accu(std::forward(equal)); + tc::for_each(std::forward(rng), std::ref(accu)); + return accu.m_result_(); + } + }; + + template + struct all_same_element_impl final { + static constexpr decltype(auto) fn(Rng&& rng, Equal&& equal) MAYTHROW { + // TODO: For RangeReturn return_value, without _CHECKS, the algorithm should not loop but compute the result in O(1) time. + return tc::fn_visit( + [](auto&& t) noexcept -> decltype(auto) { + return RangeReturn::template pack_element(tc_move_if_owned(t)); + }, + [](tc::all_same_result::empty_t) noexcept -> decltype(auto) { + return RangeReturn::template pack_no_element(); + }, + [](tc::all_same_result::not_same_t) noexcept -> decltype(auto) { + return RangeReturn::template pack_no_element(); + } + )(all_same_element_impl::fn(std::forward(rng), std::forward(equal)) /*MAYTHROW*/); + } + }; +} +using no_adl::accumulator_all_same; +using no_adl::return_all_same_result; + +template +[[nodiscard]] constexpr decltype(auto) all_same_element(Rng&& rng, Equal&& equal = Equal()) MAYTHROW { + return no_adl::all_same_element_impl::fn(std::forward(rng), std::forward(equal)); +} + +template< typename Rng, typename Equal = tc::fn_equal_to > +[[nodiscard]] constexpr bool all_same(Rng&& rng, Equal&& equal = Equal()) noexcept { + return tc::fn_visit( + [](auto&&) noexcept { return true; }, + [](tc::all_same_result::empty_t) noexcept { return true; }, + [](tc::all_same_result::not_same_t) noexcept { return false; } + )(tc::all_same_element(std::forward(rng), std::forward(equal))); +} } diff --git a/tc/algorithm/quantifier.t.cpp b/tc/algorithm/quantifier.t.cpp new file mode 100644 index 0000000..b15ae03 --- /dev/null +++ b/tc/algorithm/quantifier.t.cpp @@ -0,0 +1,55 @@ + +// think-cell public library +// +// Copyright (C) 2016-2023 think-cell Software GmbH +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +#include "../base/assert_defs.h" +#include "../container/container.h" // tc::vector +#include "../unittest.h" +#include "quantifier.h" + +UNITTESTDEF( quantifiers ) { + + tc::vector v{1,2,3,4,5,6,7}; + + tc::vector all_even{2,4,6,8}; + tc::vector all_odd{3,5,7,9}; + + auto const even = [](int const i) noexcept { return i%2==0; }; + + int const existing_value = 5; + int const non_existing_value = 9; + + auto const_range = tc::all(tc::as_const(v)); TEST_RANGE_LENGTH(const_range, 7); + + _ASSERT( tc::find_first(const_range, existing_value)); + _ASSERT(!tc::find_first(const_range, non_existing_value)); + + _ASSERT(! tc::all_of(const_range, even)); + _ASSERT( tc::any_of(const_range, even)); + + _ASSERT( tc::all_of(all_even, even)); + _ASSERT( tc::any_of(all_even, even)); + + _ASSERT(! tc::all_of(all_odd, even)); + _ASSERT(! tc::any_of(all_odd, even)); +} + +UNITTESTDEF( all_same ) { + tc::vector const v_empty{}; + tc::vector const v_same{1, 1, 1, 1, 1}; + tc::vector const v_different{1, 1, 2, 1, 1}; + + _ASSERT(tc::all_same(v_empty)); + _ASSERT(!tc::all_same_element(v_empty)); + + _ASSERT(tc::all_same(v_same)); + _ASSERT(tc::all_same_element(v_same)); + _ASSERTEQUAL(tc::all_same_element(v_same), 1); + + _ASSERT(!tc::all_same(v_different)); + _ASSERT(!tc::all_same_element(v_different)); +} diff --git a/tc/algorithm/round.h b/tc/algorithm/round.h index 899a774..590ede5 100644 --- a/tc/algorithm/round.h +++ b/tc/algorithm/round.h @@ -74,7 +74,7 @@ namespace tc { #ifdef __clang__ __attribute__((optnone)) // Disable compiler optimization num/den ---> num*(1/den) resulting in tc::fdiv(49,49)==0.99999999999999989 #endif - double fdiv(double num, double den) noexcept { + double fdiv(double const num, double const den) noexcept { return num/den; } #ifdef _MSC_VER @@ -165,6 +165,9 @@ namespace tc { struct SRoundBanker final { } inline constexpr roundBANKER{}; + struct SNoRounding final { + } inline constexpr norounding{}; + namespace idiv_impl { // The standard guarantees that integer division rounds to zero. // [expr.mul]/4 (oder 5.6/4) @@ -174,7 +177,7 @@ namespace tc { static_assert( tc::actual_integer_like< Denom > ); STATICASSERTEQUAL( std::numeric_limits::is_signed, std::numeric_limits::is_signed ); _ASSERTE( 0::value ) { + if constexpr(std::same_as) { auto result = num / denom; auto remainder = num - result * denom; if(remainder<0) -tc::inplace(remainder); @@ -182,6 +185,9 @@ namespace tc { result += num < 0 ? -1 : 1; } tc_return_cast(result); + } else if constexpr(std::same_as) { + _ASSERTEQUAL(num%denom, 0); + tc_return_cast(num/denom); } else { tc_return_cast( ( num<0 ? num-Round::NegativeOffset(denom) : num+Round::PositiveOffset(denom) )/denom ); } diff --git a/tc/algorithm/size.h b/tc/algorithm/size.h index ee26ff7..55bc12a 100644 --- a/tc/algorithm/size.h +++ b/tc/algorithm/size.h @@ -15,8 +15,7 @@ #include "../base/generic_macros.h" #include "../base/tag_type.h" #include "../container/container_traits.h" - -#include +#include "../range/meta.h" #include @@ -164,16 +163,6 @@ namespace tc { } } - namespace detail { - template - concept has_traversal = tc::range_with_iterators && std::convertible_to::type, Required>; - } - - template - concept bidirectional_range = detail::has_traversal; - template - concept random_access_range = detail::has_traversal; - template< typename T > [[nodiscard]] constexpr auto make_size_proxy(T t) noexcept { if constexpr( tc::actual_integer ) { @@ -215,6 +204,32 @@ namespace tc { struct common_type_decayed> : tc::common_type_decayed, T0> {}; } + //////////////////////////////// + // tc::constexpr_size + namespace no_adl { + template + struct constexpr_size_impl; + + // Rng has a size function that returns an integral_constant. + template requires requires { decltype(std::declval().size())::value; } + struct constexpr_size_impl : decltype(std::declval().size()) {}; + + template + struct constexpr_size_impl : tc::least_uint_constant ? 1 : 0)> {}; + + template + struct constexpr_size_impl> : tc::least_uint_constant {}; + } + + template requires has_constexpr_size + constexpr auto constexpr_size = []() noexcept { + using type = no_adl::constexpr_size_impl>; + static_assert(std::derived_from>); + return tc::least_uint_constant{}; + }(); + + //////////////////////////////// + // tc::size namespace size_raw_internal { // tc::size() requires a range with either: // - a constexpr_size_impl specialization which provide size as a compile time constant @@ -225,7 +240,7 @@ namespace tc { has_constexpr_size || has_mem_fn_size || (random_access_range && tc::common_range) constexpr auto size_raw(Rng&& rng) noexcept { if constexpr( has_constexpr_size ) { - return [&]() return_decltype_noexcept(constexpr_size::value); + return [&]() return_decltype_noexcept(constexpr_size()); } else if constexpr( has_mem_fn_size ) { return [&]() return_MAYTHROW(tc_move_if_owned(rng).size()); // .size() may throw for files } else { @@ -242,6 +257,7 @@ namespace tc { size_raw_internal::size_raw(tc_move_if_owned(rng))() ) + // Note: This overload is only necessary for the assertion - the size is otherwise computed the same by constexpr_size. template [[nodiscard]] constexpr auto size_raw(T const (&ach)[N]) noexcept { _ASSERTE(tc::strlen(ach)==N-1); // VERIFYEQUAL is not constexpr @@ -255,5 +271,16 @@ namespace tc { TC_HAS_EXPR(size, (T), size_raw(std::declval())) DEFINE_FN2(tc::size_raw, fn_size_raw) + + template + [[nodiscard]] constexpr auto compute_range_adaptor_size(Rng&&... rng) MAYTHROW { + if constexpr ((tc::has_constexpr_size && ...)) { + tc::actual_unsigned_integer auto constexpr value = Fn(tc::constexpr_size()...); + return tc::least_uint_constant{}; + } else { + tc::actual_unsigned_integer auto const value = Fn(tc::size_raw(tc_move_if_owned(rng))...); + return value; + } + } } diff --git a/tc/algorithm/size_bounded.h b/tc/algorithm/size_bounded.h index 699b2c3..73e457a 100644 --- a/tc/algorithm/size_bounded.h +++ b/tc/algorithm/size_bounded.h @@ -15,6 +15,31 @@ #include "for_each.h" namespace tc { + template< typename It, typename T, typename Sentinel > + T advance_forward_bounded(It&& it, T n, Sentinel&& itBound) noexcept { + _ASSERT(0 <= n); + if constexpr( std::convertible_to< + typename boost::iterator_traversal>::type, + boost::iterators::random_access_traversal_tag + > && requires { itBound - it; } ) { + if (tc::assign_better(tc::fn_less_equal(), n, tc::make_size_proxy(itBound - it))) { + it = std::forward(itBound); + } else { + it += n; + } + return n; + } else { + // size_proxy does not provide operator++ and the operation cannot fail here, + // because nCount is always inside interval [0,n]. + auto nCount = tc::explicit_cast(0); + while (nCount != n && it != itBound) { + ++nCount; + ++it; + } + tc_return_cast(nCount); + } + } + template< typename Rng, typename T> [[nodiscard]] constexpr auto size_bounded(Rng const& rng, T const nBound) noexcept { if constexpr( tc::has_size ) { @@ -24,7 +49,7 @@ namespace tc { } else { T n = 0; if (0 < nBound) { - auto Enumerate = [&](tc::unused) noexcept { return tc::continue_if(nBound!=++n); }; + auto const Enumerate = [&](tc::unused) noexcept { return tc::continue_if(nBound!=++n); }; STATICASSERTSAME(tc::break_or_continue, decltype(tc::for_each(rng, Enumerate)), "size_bounded only works with interruptible generators"); tc::for_each(rng, Enumerate); } diff --git a/tc/algorithm/sort_streaming.h b/tc/algorithm/sort_streaming.h index e8aec1d..bce94d1 100644 --- a/tc/algorithm/sort_streaming.h +++ b/tc/algorithm/sort_streaming.h @@ -23,7 +23,7 @@ namespace tc { // tc::back(cont) = std::forward(t); // std::ranges::push_heap(cont, less); auto const n = tc::size_raw(cont); - auto IndexAndIterator = [&](decltype(n) i) noexcept { + auto const IndexAndIterator = [&](decltype(n) i) noexcept { return tc::make_tuple(i, tc::at(cont, i)); }; auto nitHole = IndexAndIterator(0); diff --git a/tc/array.h b/tc/array.h index f25fdd3..28b20f1 100644 --- a/tc/array.h +++ b/tc/array.h @@ -56,19 +56,10 @@ namespace tc { typename no_adl::array_storage::type m_a; public: - using value_type = tc::decay_t; - using reference = T&; - using const_reference = T&; // reference semantics == no deep constness - using pointer = T *; - using iterator = boost::indirect_iterator; - using const_iterator = iterator; // reference semantics == no deep constness - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - static constexpr std::size_t size() noexcept { - return N; + using iterator = boost::indirect_iterator, /*CategoryOrTraversal*/boost::use_default, T&>; + + static constexpr auto size() noexcept { + return tc::least_uint_constant{}; } private: @@ -139,7 +130,7 @@ namespace tc { // access (no rvalue-qualified overloads, must not move data out of a reference) // reference semantics == no deep constness - [[nodiscard]] constexpr T& operator[](std::size_t i) const& noexcept { + [[nodiscard]] constexpr T& operator[](std::size_t const i) const& noexcept { _ASSERTDEBUG(i - struct constexpr_size_impl> : tc::constant {}; - } - ///////////////////////////////////////////////////// // std::array namespace no_adl { template - struct constexpr_size_impl> : tc::constant {}; + struct constexpr_size_impl> : tc::least_uint_constant {}; } namespace explicit_convert_std_array_detail { @@ -282,12 +268,12 @@ namespace tc { template [[nodiscard]] constexpr auto make_array(Rng&& rng) return_decltype_MAYTHROW( - tc::make_array::value>(std::forward(rng)) + tc::make_array()>(std::forward(rng)) ) template [[nodiscard]] constexpr auto make_array(Rng&& rng) return_decltype_MAYTHROW( - tc::make_array::value>(std::forward(rng)) + tc::make_array()>(std::forward(rng)) ) template requires (!std::is_reference::value) diff --git a/tc/base/accessors.h b/tc/base/accessors.h index cfc99c9..b9adca0 100644 --- a/tc/base/accessors.h +++ b/tc/base/accessors.h @@ -47,19 +47,19 @@ namespace tc { TC_EXPAND(TC_EXPAND(BOOST_PP_IF(BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), DEFINE_MEMBER_WITH_INIT, DEFINE_MEMBER_WITHOUT_INIT))(TC_FWD(type), __VA_ARGS__)) #define DEFINE_ACCESSORS_BASE(type, funcname, invariant, name) \ - constexpr typename tc::no_adl::accessor_return_type::const_ref_type funcname() const& noexcept { invariant(name); return name; } \ + [[nodiscard]] constexpr typename tc::no_adl::accessor_return_type::const_ref_type funcname() const& noexcept { invariant(name); return name; } \ \ template requires /*dummy constraint making this function preferred in overload resolution over the deleted function below*/true \ - constexpr typename tc::no_adl::accessor_return_type::ref_ref_type funcname() && noexcept { invariant(name); return tc_move(name); } \ + [[nodiscard]] constexpr typename tc::no_adl::accessor_return_type::ref_ref_type funcname() && noexcept { invariant(name); return tc_move(name); } \ \ template /* needed to stay behind non-deleted function in overload resolution */ \ - constexpr type&& funcname() && noexcept = delete; /* Visual Studio gives improper error message if it returns a dummy type */ \ + [[nodiscard]] constexpr type&& funcname() && noexcept = delete; /* Visual Studio gives improper error message if it returns a dummy type */ \ \ template requires true \ - constexpr typename tc::no_adl::accessor_return_type::const_ref_ref_type funcname() const&& noexcept { invariant(name); return std::move(name); } \ + [[nodiscard]] constexpr typename tc::no_adl::accessor_return_type::const_ref_ref_type funcname() const&& noexcept { invariant(name); return std::move(name); } \ \ template \ - constexpr type const&& funcname() const&& noexcept = delete; /* Visual Studio gives improper error message if it returns a dummy type */ + [[nodiscard]] constexpr type const&& funcname() const&& noexcept = delete; /* Visual Studio gives improper error message if it returns a dummy type */ #define DEFINE_MEMBER_INVARIANT(...) ([&](auto const& _) constexpr noexcept { \ _ASSERTINITIALIZED(_); \ diff --git a/tc/base/assert_defs.h b/tc/base/assert_defs.h index 28f891e..e87050c 100644 --- a/tc/base/assert_defs.h +++ b/tc/base/assert_defs.h @@ -46,6 +46,9 @@ #ifndef _ASSERTNORETURN #define _ASSERTNORETURN _ASSERT #endif + #ifndef _ASSERTNORETURNFALSE + #define _ASSERTNORETURNFALSE _ASSERTFALSE + #endif #ifndef _ASSERTNOTIFY #define _ASSERTNOTIFY _ASSERT #endif @@ -97,7 +100,7 @@ return std::forward(expr); } } - #define VERIFY( expr ) ErrorHandling::Verify(expr) + #define VERIFY ErrorHandling::Verify #endif #ifndef VERIFYPRED #include diff --git a/tc/base/assign.h b/tc/base/assign.h index b3e645d..790eb63 100644 --- a/tc/base/assign.h +++ b/tc/base/assign.h @@ -166,7 +166,7 @@ constexpr auto assign_better( Better better, Var&& var, Val&& val ) noexcept { [&](tc::constant b) noexcept { return b; }, - [&](bool b) noexcept { + [&](bool const b) noexcept { if (b) { static_assert( tc::safely_assignable_from ); std::forward(var) = std::forward(val); @@ -238,13 +238,6 @@ void change_with_or(Var&& var, Val&& val, bool& bChanged) noexcept { tc_define_fn( assign_max ); tc_define_fn( assign_min ); -template< typename Better > -[[nodiscard]] constexpr auto fn_assign_better(Better&& better) noexcept { - return [better=std::forward(better)](auto&& var, auto&& val0, auto&&... val) noexcept { - return tc::assign_better(better, tc_move_if_owned(var), tc_move_if_owned(val0), tc_move_if_owned(val)...); - }; -} - //////////////////////////////////// // change for float/double diff --git a/tc/base/bit_cast.h b/tc/base/bit_cast.h index fbad7a9..43e0cc7 100644 --- a/tc/base/bit_cast.h +++ b/tc/base/bit_cast.h @@ -10,14 +10,124 @@ #include "casts.h" #include "../range/subrange.h" +#include "../array.h" #include //----------------------------------------------------------------------------------------------------------------------------- namespace tc { + + //-------------------------------------------------------------------------------------------------------------------------- + // as_blob + // reinterprets a range of items as a range of bytes + + // We use unsigned char for uninterpreted memory. + // - The type used must support aliasing, so the only candidates are char, signed char and unsigned char. + // - char is bad because we interpret char as UTF-8 and char* as zero-terminated range. + // - unsigned char is better than signed char because the binary representation of signs may vary between platforms. + // - char is bad because it is either signed or unsigned, so it has the same problem. + // - unsigned char is better than std::uint8_t because the latter must be 8 bit, but we mean the smallest addressable unit, which is char and may be larger (or smaller?) on other platforms. + + static_assert(!tc::range_with_iterators< void const* >); + + template + [[nodiscard]] auto range_as_blob(Rng&& rng) noexcept { + using cv_value_type = std::remove_pointer_t; + static_assert( std::is_trivially_copyable< cv_value_type >::value, "as_blob only works on std::is_trivially_copyable types" ); + if constexpr(tc::safely_constructible_from, Rng>) { + return tc::make_iterator_range( + reinterpret_cast*>(tc::ptr_begin(rng)), + reinterpret_cast*>(tc::ptr_end(rng)) + ); + } else { + // not inside blob_range_t because templates cannot be declared inside of a local class + static auto constexpr as_blob_ptr=[](auto ptr) noexcept { + return reinterpret_cast>*>(ptr); + }; + struct blob_range_t final { + explicit blob_range_t(Rng&& rng) noexcept : m_rng(tc_move(rng)) {} + auto begin() & noexcept { return as_blob_ptr(tc::ptr_begin(m_rng)); } + auto begin() const& noexcept { return as_blob_ptr(tc::ptr_begin(m_rng)); } + auto end() & noexcept { return as_blob_ptr(tc::ptr_end(m_rng)); } + auto end() const& noexcept { return as_blob_ptr(tc::ptr_end(m_rng)); } + private: + static_assert(!std::is_reference::value); + std::remove_cv_t m_rng; + }; + return blob_range_t(tc_move(rng)); + } + } + + namespace no_adl { + template + struct range_as_blob_sink { // no final: verify_sink_result_impl derives + private: + // range_as_blob_sink is only used inline in range_as_blob below, and m_sink is only passed to tc::for_each, so holding by lvalue reference ok + Sink& m_sink; + + public: + explicit range_as_blob_sink(Sink& sink) noexcept: m_sink(sink) {} + + // chunk must be defined before operator() - otherwise MSVC will not allow it to occur in the return type of operator() + template + auto chunk(Rng&& rng) const& return_decltype_MAYTHROW ( + tc::for_each(tc::range_as_blob(tc_move_if_owned(rng)), m_sink) + ) + + template + auto operator()(T&& t) const& return_decltype_MAYTHROW ( + chunk(tc::single(/* no std::forward */ t)) + ) + }; + } + + template + [[nodiscard]] auto range_as_blob(Rng&& rng) noexcept { + return [rng=tc::make_reference_or_value(tc_move_if_owned(rng))](auto&& sink) MAYTHROW { + return tc::for_each(*rng, no_adl::range_as_blob_sink>(sink)); + }; + } + + template requires std::is_trivially_copyable>::value + [[nodiscard]] auto as_blob(T&& t) noexcept { + return tc::range_as_blob( tc::single( std::forward(t) ) ); + } + + namespace assert_no_overlap_impl { + void assert_no_overlap(auto const& lhs, auto const& rhs) noexcept { + _ASSERT( + reinterpret_cast(tc::ptr_end(lhs)) <= reinterpret_cast(tc::ptr_begin(rhs)) || + reinterpret_cast(tc::ptr_end(rhs)) <= reinterpret_cast(tc::ptr_begin(lhs)) + ); + } + } + template< typename Lhs, typename Rhs> + void assert_no_overlap(Lhs const& lhs, Rhs const& rhs) noexcept { + assert_no_overlap_impl::assert_no_overlap(tc::single(lhs), tc::single(rhs)); + if constexpr( tc::contiguous_range && tc::contiguous_range ) { + assert_no_overlap_impl::assert_no_overlap(lhs, rhs); + } + } + ///////////////////////////////////////////// // bit_cast + namespace no_adl { + struct any_ptr_ref final: tc::nonmovable { + private: + void* m_pv; + public: + explicit any_ptr_ref(void* pv) noexcept: m_pv(pv) {} + + template requires std::same_as, T> && std::is_trivially_copyable::value + operator T() && noexcept { + T t; + std::memcpy(std::addressof(t), m_pv, sizeof(t)); + return t; + } + }; + } + namespace any_ptr_adl { struct any_ptr final { private: @@ -27,6 +137,14 @@ namespace tc { : m_pv(pv) {} + explicit operator bool() const& noexcept { + return m_pv; + } + + auto operator*() const& noexcept { + return tc::no_adl::any_ptr_ref(m_pv); + } + template requires std::is_pointer::value || std::is_member_pointer::value operator T() const& noexcept { STATICASSERTEQUAL(sizeof(T), sizeof(void*)); @@ -187,4 +305,3 @@ namespace tc { return typename aliasing_ptr>::type(reinterpret_cast(src)); } } - diff --git a/tc/base/casts.h b/tc/base/casts.h index de80c87..d658527 100644 --- a/tc/base/casts.h +++ b/tc/base/casts.h @@ -65,16 +65,29 @@ namespace tc { // derived_cast namespace derived_cast_detail { + template + constexpr void check_derived_cast(From const& obj) noexcept { + if constexpr (!std::is_same::value && std::is_polymorphic::value) { + _ASSERT(dynamic_cast(std::addressof(obj))); + } + } + namespace derived_cast_internal_default { - template - [[nodiscard]] constexpr same_cvref_t< To, From&&> derived_cast_internal_impl( tc::type::identity, From&& t ) noexcept { + template + [[nodiscard]] constexpr same_cvref_t< To, From&&> derived_cast_internal_impl(tc::type::identity, From&& t, std::bool_constant) noexcept { static_assert( tc::derived_from>, "derived_cast is for downcasts only."); + if constexpr (bChecked) { + check_derived_cast(t); + } return static_cast< apply_cvref_t< To, From&&> >(t); } - template - [[nodiscard]] constexpr same_cvref_t< To, From>* derived_cast_internal_impl( tc::type::identity, From* pt ) noexcept { + template + [[nodiscard]] constexpr same_cvref_t< To, From>* derived_cast_internal_impl(tc::type::identity, From* pt, std::bool_constant) noexcept { static_assert( tc::derived_from>, "derived_cast is for downcasts only."); + if constexpr (bChecked) { + if (nullptr != pt) check_derived_cast(*pt); + } return static_cast< apply_cvref_t< To, From>* >(pt); } } @@ -85,7 +98,13 @@ namespace tc { template [[nodiscard]] constexpr decltype(auto) derived_cast(From&& t) noexcept { STATICASSERTSAME(std::remove_reference_t, To); - return tc::derived_cast_detail::derived_cast_internal(tc::type::identity(), std::forward(t)); + return tc::derived_cast_detail::derived_cast_internal(tc::type::identity(), std::forward(t), /*bChecked*/tc::constant()); + } + + template + [[nodiscard]] constexpr decltype(auto) unchecked_derived_cast(From&& t) noexcept { + STATICASSERTSAME(std::remove_reference_t, To); + return tc::derived_cast_detail::derived_cast_internal(tc::type::identity(), std::forward(t), /*bChecked*/tc::constant()); } ///////////////////////////////////////////// @@ -259,59 +278,46 @@ MODIFY_WARNINGS_END return std::forward(src); } - ///////////////////////////////////////////// - // reluctant_static_cast - // Returns a reference to its argument whenever possible, otherwise performs an explicit conversion. - - template TSource> - [[nodiscard]] TSource&& reluctant_static_cast(TSource&& src) noexcept { - STATICASSERTSAME(std::remove_cvref_t, TTarget); - return std::forward(src); - } - - template - [[nodiscard]] TTarget reluctant_static_cast(TSource&& src) noexcept { - STATICASSERTSAME(std::remove_cvref_t, TTarget); - return static_cast(std::forward(src)); - } - ///////////////////////////////////////////// // as_c_str + namespace as_c_str_default { + template< typename Char, typename Traits, typename Alloc > + [[nodiscard]] Char const* as_c_str_impl(std::basic_string< Char, Traits, Alloc > const& str) noexcept + { + return str.data(); // since C++ 11, performs same function as c_str(). cannot use tc::ptr_begin to avoid circular dependency + } - template< typename Char, typename Traits, typename Alloc > - [[nodiscard]] Char const* as_c_str(std::basic_string< Char, Traits, Alloc > const& str) noexcept - { - return str.data(); // since C++ 11, performs same function as c_str() - } + template< typename Char, typename Traits, typename Alloc > + [[nodiscard]] Char* as_c_str_impl(std::basic_string< Char, Traits, Alloc >& str) noexcept + { + return str.data(); // since C++ 11, performs same function as c_str(). cannot use tc::ptr_begin to avoid circular dependency + } - template< typename Char, typename Traits, typename Alloc > - [[nodiscard]] Char* as_c_str(std::basic_string< Char, Traits, Alloc >& str) noexcept - { - return str.data(); // since C++ 11, performs same function as c_str() - } + template< typename Char, typename Traits, typename Alloc > + [[nodiscard]] Char* as_c_str_impl(std::basic_string< Char, Traits, Alloc >&& str) noexcept + { + return str.data(); // since C++ 11, performs same function as c_str(). cannot use tc::ptr_begin to avoid circular dependency + } - template< typename Char, typename Traits, typename Alloc > - [[nodiscard]] Char* as_c_str(std::basic_string< Char, Traits, Alloc >&& str) noexcept - { - return str.data(); // since C++ 11, performs same function as c_str() - } + template + [[nodiscard]] constexpr Char const* as_c_str_impl(Char const* psz) noexcept { + return psz; + } - template - [[nodiscard]] constexpr Char const* as_c_str(Char const* psz) noexcept { - return psz; - } + template + [[nodiscard]] constexpr Char* as_c_str_impl(Char* psz) noexcept { + return psz; + } - template - [[nodiscard]] constexpr Char* as_c_str(Char* psz) noexcept { - return psz; + #ifdef TC_PRIVATE + // Prevents implicit conversion from std::vector to boost::filesystem::path on Windows, which causes a dangling pointer to be returned + template requires std::is_same::value + [[nodiscard]] inline boost::filesystem::path::value_type const* as_c_str_impl(T const& fspath) noexcept { + return fspath.c_str(); + } + #endif } -#ifdef TC_PRIVATE - // Prevents implicit conversion from std::vector to boost::filesystem::path on Windows, which causes a dangling pointer to be returned - template requires std::is_same::value - [[nodiscard]] inline boost::filesystem::path::value_type const* as_c_str(T const& fspath) noexcept { - return fspath.c_str(); - } -#endif + DEFINE_TMPL_FUNC_WITH_CUSTOMIZATIONS(as_c_str) } diff --git a/tc/base/conditional.h b/tc/base/conditional.h index 8d27bb0..fb8ea5c 100644 --- a/tc/base/conditional.h +++ b/tc/base/conditional.h @@ -15,8 +15,8 @@ #define TYPED_CONDITIONAL(b, type, lhs, rhs) \ (tc::explicit_cast(b) ? static_cast(lhs) : static_cast(rhs)) // static_cast needed for conversion from const& to const&& -#define CONDITIONAL_RVALUE_AS_REF(b, lhs, rhs) \ +#define tc_conditional_rvalue_as_ref(b, lhs, rhs) \ TYPED_CONDITIONAL(TC_FWD(b), TC_FWD(tc::common_reference_xvalue_as_ref_t), TC_FWD(lhs), TC_FWD(rhs)) -#define CONDITIONAL_PRVALUE_AS_VAL(b, lhs, rhs) \ +#define tc_conditional_prvalue_as_val(b, lhs, rhs) \ TYPED_CONDITIONAL(TC_FWD(b), TC_FWD(tc::common_reference_prvalue_as_val_t), TC_FWD(lhs), TC_FWD(rhs)) diff --git a/tc/base/enum.h b/tc/base/enum.h index c7500c0..ddeeedc 100644 --- a/tc/base/enum.h +++ b/tc/base/enum.h @@ -24,15 +24,16 @@ #include namespace tc { - template - void contiguous_enum_impl(Enum const&) = delete; + template + concept contiguous_enum_type = tc::enum_type && requires(T const& value) { + contiguous_enum_impl(value); + }; namespace no_adl { template struct contiguous_enum : tc::constant {}; - template - requires requires(Enum const& e) { contiguous_enum_impl(e); } + template struct contiguous_enum : tc::constant { using impl = decltype(contiguous_enum_impl(std::declval())); @@ -48,9 +49,6 @@ namespace tc { } using no_adl::contiguous_enum; - template - concept contiguous_enum_type = tc::enum_type && contiguous_enum::value; - template [[nodiscard]] constexpr bool is_enum_value_or_end(Integer const& n) noexcept { // reference to avoid error C4701: potentially uninitialized local variable static_assert( tc::actual_integer ); @@ -134,10 +132,10 @@ constexpr Enum& operator^=(Enum& _Left, Enum _Right) \ \ [[nodiscard]] constexpr bool HasAllOf(Enum _Left, Enum _Right) \ { /* return _Left HasAllOf _Right */\ - return !(~_Left & _Right); \ + return !static_cast(~_Left & _Right); \ }\ \ -std::strong_ordering operator<=>(Enum, Enum) = delete; \ +std::weak_ordering operator<=>(Enum, Enum) = delete; \ bool operator<(Enum, Enum) = delete; \ bool operator<=(Enum, Enum) = delete; \ bool operator>=(Enum, Enum) = delete; \ @@ -184,7 +182,7 @@ namespace tc { [[nodiscard]] inline bool check_initialized_impl(Enum const& e) noexcept { /*reference to avoid error C4701: potentially uninitialized local variable*/ \ return tc::is_enum_value_or_end(tc::to_underlying(e)); \ } \ - [[nodiscard]] constexpr boost::int_max_value_t< tc::enum_count::value >::least operator-(Enum e1, Enum e2) noexcept { \ + [[nodiscard]] constexpr boost::int_max_value_t< tc::enum_count::value >::least operator-(Enum const e1, Enum const e2) noexcept { \ return static_cast::value >::least>(tc::to_underlying(e1)-tc::to_underlying(e2)); \ } \ template \ @@ -260,7 +258,7 @@ namespace tc { namespace tc::enum_detail { template - constexpr auto max_value_for_underlying_type(IntOffset nOffset, int nConstants) noexcept { + constexpr auto max_value_for_underlying_type(IntOffset nOffset, int const nConstants) noexcept { static_assert( std::is_signed::value, "unsigned underlying type not supported" ); // Assume two's complement. // -1 - nOffset, like ~nOffset never overflows. diff --git a/tc/base/fundamental.h b/tc/base/fundamental.h index bb00530..a7043c8 100644 --- a/tc/base/fundamental.h +++ b/tc/base/fundamental.h @@ -81,18 +81,3 @@ namespace tc { #else #define MSVC_TEMPLATE_WORKAROUND template #endif - -#if WCHAR_MAX == 0xFFFF // == 2 bytes unsigned: We want tc::char16 to be unsigned. wchar_t is unsigned on MSVC, but that's implementation-specific. -namespace tc { - using char16 = wchar_t; -} - #define UTF16_(x) L ## x -#else -namespace tc { - using char16 = char16_t; -} - #define UTF16_(x) u ## x -#endif -#define UTF16(x) UTF16_(x) - -static_assert(0 == std::numeric_limits::lowest() && 0xFFFF == std::numeric_limits::max()); diff --git a/tc/base/inside_unwinding.h b/tc/base/inside_unwinding.h new file mode 100644 index 0000000..6486edb --- /dev/null +++ b/tc/base/inside_unwinding.h @@ -0,0 +1,28 @@ +#pragma once + +#include "assert_defs.h" +#include + +namespace tc { + namespace no_adl { + struct inside_unwinding { + inside_unwinding() noexcept + : m_nUncaughtExceptions(std::uncaught_exceptions()) + {} + inside_unwinding(inside_unwinding const&) noexcept + : inside_unwinding() + {} + inside_unwinding& operator=(inside_unwinding const&) & noexcept { + return *this; + } + bool inside_stack_unwinding() const& noexcept { + int const nUncaughtExceptions = std::uncaught_exceptions(); + _ASSERT(m_nUncaughtExceptions<=nUncaughtExceptions); + return m_nUncaughtExceptions #include namespace tc { - template - concept tuple_like = tc::actual_integer>::value)>; - namespace invoke_no_adl { ////////////////////////////////////////////////////////////////////////// // expanded @@ -163,14 +161,29 @@ namespace tc { } ////////////////////////////////////////////////////////////////////////// - // is_invocable - namespace invoke_no_adl { - template - struct is_invocable /*final*/ : tc::constant {}; - - template - struct is_invocable(), std::declval()...))>, Func, Args...> /*final*/ : tc::constant {}; - } + // concepts + template + concept invocable = requires(Func&& f, Args&&... args) { tc::invoke(std::forward(f), std::forward(args)...); }; template - using is_invocable = invoke_no_adl::is_invocable; + concept nothrow_invocable = tc::invocable && noexcept(tc::invoke(std::declval(), std::declval()...)); + + template + concept predicate = tc::invocable const&> + && requires(Func const& f, std::remove_cvref_t const& t) { tc::explicit_cast(tc::invoke(f, t)); }; + template + concept nothrow_predicate = tc::predicate && tc::nothrow_invocable const&>; + + template + concept constant_predicate = tc::predicate + && requires(Func const& f, std::remove_cvref_t const& t) { tc::explicit_cast(decltype(tc::invoke(f, t))::value); }; + + template + concept constant_predicate_true = tc::constant_predicate + && tc::explicit_cast(decltype(tc::invoke(std::declval(), std::declval const&>()))::value); + template + concept constant_predicate_false = tc::constant_predicate + && !tc::explicit_cast(decltype(tc::invoke(std::declval(), std::declval const&>()))::value); + + template + concept runtime_predicate = tc::predicate && (!tc::constant_predicate); } diff --git a/tc/base/invoke.t.cpp b/tc/base/invoke.t.cpp index 7bde1e1..8b5ef9e 100644 --- a/tc/base/invoke.t.cpp +++ b/tc/base/invoke.t.cpp @@ -12,8 +12,8 @@ namespace { [[maybe_unused]] void static_tests() noexcept { auto rvalue_sink = [](int, double&&, char const&) noexcept {}; - static_assert(!tc::is_invocable::value); - static_assert(!tc::is_invocable>::value); + static_assert(!tc::invocable); + static_assert(!tc::invocable>); tc::invoke(rvalue_sink, 0, 0., 'a'); tc::invoke(rvalue_sink, std::make_tuple(0, 0., 'a')); @@ -21,9 +21,9 @@ namespace { tc::invoke(rvalue_sink, std::make_tuple(0, std::make_tuple(std::make_tuple(0.), 'a'))); auto lvalue_sink = [](int, double&, char const&) noexcept {}; - static_assert(!tc::is_invocable::value); - static_assert(!tc::is_invocable>::value); - static_assert(!tc::is_invocable>::value); + static_assert(!tc::invocable); + static_assert(!tc::invocable>); + static_assert(!tc::invocable>); int a0 = 0; double a1 = 0.; diff --git a/tc/base/ref.h b/tc/base/ref.h index 2568668..aa846e3 100644 --- a/tc/base/ref.h +++ b/tc/base/ref.h @@ -10,7 +10,6 @@ #include "assert_defs.h" #include "as_lvalue.h" -#include "../range/range_fwd.h" #include "../algorithm/for_each.h" namespace tc { @@ -89,7 +88,7 @@ namespace tc { template requires (!tc::decayed_derived_from) - && tc::is_invocable&, Args...>::value + && tc::invocable&, Args...> && ( std::convertible_to&>(), std::declval()...)), Ret> || std::is_same::value diff --git a/tc/base/reference_or_value.h b/tc/base/reference_or_value.h index bdd7074..033a6a4 100644 --- a/tc/base/reference_or_value.h +++ b/tc/base/reference_or_value.h @@ -33,15 +33,34 @@ namespace tc { : m_t(std::forward(rhs)) {} - // T may be a proxy with reference behavior (e.g. pair): operator= may not be equivalent to copy ctor. + // reference_or_value is trivially copy assignable if T is trivially copy assignable. + constexpr reference_or_value& operator=(reference_or_value const& other) & requires std::is_trivially_copy_assignable::value = default; + // T may be a proxy with reference behavior (e.g. pair): operator= must be non trivial and may not be equivalent to copy ctor. // operator=() with tc::renew for pointer semantics. - constexpr reference_or_value& operator=(reference_or_value const& other) & noexcept { +#ifdef __clang__ // workaround clang bug on trivially copyable: https://github.com/llvm/llvm-project/issues/63352 + template + constexpr reference_or_value& operator=(reference_or_value const& other) & noexcept requires +#else + constexpr reference_or_value& operator=(reference_or_value const& other) & noexcept requires +#endif + (!std::is_trivially_copy_assignable::value) && + std::is_copy_constructible::value + { _ASSERTE(this != std::addressof(other)); tc::renew(m_t, other.m_t); return *this; } - constexpr reference_or_value& operator=(reference_or_value&& other) & noexcept { + constexpr reference_or_value& operator=(reference_or_value&& other) & requires std::is_trivially_move_assignable::value = default; +#ifdef __clang__ // workaround clang bug on trivially copyable: https://github.com/llvm/llvm-project/issues/63352 + template + constexpr reference_or_value& operator=(reference_or_value&& other) & noexcept requires +#else + constexpr reference_or_value& operator=(reference_or_value&& other) & noexcept requires +#endif + (!std::is_trivially_move_assignable::value) && + std::is_move_constructible::value + { _ASSERTE(this != std::addressof(other)); tc::renew(m_t, tc_move(other).m_t); return *this; @@ -146,8 +165,8 @@ namespace tc { } template using reference_or_value = std::conditional_t< - tc::empty_type, - no_adl::empty_value>, + tc::empty_type>, + no_adl::empty_value>, no_adl::reference_or_value> >; diff --git a/tc/base/reference_or_value.t.cpp b/tc/base/reference_or_value.t.cpp new file mode 100644 index 0000000..f30c15e --- /dev/null +++ b/tc/base/reference_or_value.t.cpp @@ -0,0 +1,29 @@ + +// think-cell public library +// +// Copyright (C) 2016-2023 think-cell Software GmbH +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +#include "reference_or_value.h" +#include "../tuple.h" +#include + +static_assert(std::is_trivially_copyable>::value); +static_assert(std::is_trivially_copy_assignable>::value); +static_assert(std::is_trivially_move_assignable>::value); + +static_assert(!std::is_trivially_copy_assignable>::value); +static_assert(!std::is_trivially_move_assignable>::value); +static_assert(!std::is_trivially_copyable>>::value); +static_assert(!std::is_trivially_copy_assignable>>::value); +static_assert(!std::is_trivially_move_assignable>>::value); + +static_assert(std::is_trivially_copyable>::value); +static_assert(std::is_trivially_copy_assignable>::value); +static_assert(std::is_trivially_move_assignable>::value); + +static_assert(std::is_trivially_copyable>>::value); +static_assert(std::is_trivially_copy_assignable>>::value); +static_assert(std::is_trivially_move_assignable>>::value); diff --git a/tc/base/safe_comparison.h b/tc/base/safe_comparison.h new file mode 100644 index 0000000..ebefe83 --- /dev/null +++ b/tc/base/safe_comparison.h @@ -0,0 +1,81 @@ + +// think-cell public library +// +// Copyright (C) 2016-2023 think-cell Software GmbH +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +#pragma once + +#include "type_traits.h" +#include "casts.h" +#include + +namespace tc { + // By default, a comparison is safe; specialize to mark as unsafe. + // (We can't delegate to safely_convertible_to, as we can't tell whether `T == U` is valid because of implicit conversions, + // or because of an overloaded operator== that takes those types and handles them safely.) + template + constexpr bool safe_comparison = true; + + // Comparison between mixed integral types is only safe as long as neither is a char type and they have the same signedness. + template + requires (!std::same_as) + constexpr bool safe_comparison + = !tc::char_type && !tc::char_type + && std::is_signed::value == std::is_signed::value; + + template + concept safely_equality_comparable_with = std::equality_comparable_with && safe_comparison, tc::decay_t>; + + template + concept safely_totally_ordered_with = std::totally_ordered_with && safe_comparison, tc::decay_t>; +} + +////////////////////////////////////////////////////////////////////////// +// cmp_equal/cmp_less/cmp_greater... + +namespace tc { // TODO c++20: replace these functions with std versions + template< tc::actual_integer T, tc::actual_integer U > + constexpr bool cmp_equal( T t, U u ) noexcept + { + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : static_cast>(t) == u; + else + return u < 0 ? false : t == static_cast>(u); + } + + template< typename T, typename U> + constexpr auto cmp_not_equal( T t, U u ) return_decltype_noexcept( + !cmp_equal(t, u) + ) + + template< tc::actual_integer T, tc::actual_integer U> + constexpr bool cmp_less( T t, U u ) noexcept + { + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : static_cast>(t) < u; + else + return u < 0 ? false : t < static_cast>(u); + } + + template< typename T, typename U> + constexpr auto cmp_greater( T t, U u ) return_decltype_noexcept( + cmp_less(u, t) + ) + + template< typename T, typename U > + constexpr auto cmp_less_equal( T t, U u ) return_decltype_noexcept( + !cmp_greater(t, u) + ) + + template< typename T, typename U > + constexpr auto cmp_greater_equal( T t, U u ) return_decltype_noexcept( + !cmp_less(t, u) + ) +} diff --git a/tc/base/safe_comparison.t.cpp b/tc/base/safe_comparison.t.cpp new file mode 100644 index 0000000..27b316d --- /dev/null +++ b/tc/base/safe_comparison.t.cpp @@ -0,0 +1,53 @@ +// think-cell public library +// +// Copyright (C) 2016-2023 think-cell Software GmbH +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +#include "assert_defs.h" +#include "safe_comparison.h" +#include "../unittest.h" +#include "../string/char.h" + +// Same type is safe. +static_assert(tc::safe_comparison); +static_assert(tc::safe_comparison); +static_assert(tc::safe_comparison); +// Mixed signedness is unsafe. +static_assert(!tc::safe_comparison); +static_assert(!tc::safe_comparison); +// Mixed chars are unsafe. +static_assert(!tc::safe_comparison); +static_assert(!tc::safe_comparison); +// chars and ints are unsafe. +static_assert(!tc::safe_comparison); +static_assert(!tc::safe_comparison); + +UNITTESTDEF(cmp_equal) { + // both signed + _ASSERT(tc::cmp_equal(static_cast(17), static_cast(17))); + _ASSERT(tc::cmp_equal(static_cast(-42), static_cast(-42))); + + // left signed + _ASSERT(tc::cmp_equal(static_cast(17), static_cast(17))); + _ASSERT(!tc::cmp_equal(static_cast(-42), static_cast(42))); + + // right signed + _ASSERT(tc::cmp_equal(static_cast(17), static_cast(17))); + _ASSERT(!tc::cmp_equal(static_cast(42), static_cast(-42))); +} + +UNITTESTDEF(cmp_less) { + // both signed + _ASSERT(tc::cmp_less(static_cast(17), static_cast(42))); + _ASSERT(tc::cmp_less(static_cast(-42), static_cast(-17))); + + // left signed + _ASSERT(tc::cmp_less(static_cast(17), static_cast(42))); + _ASSERT(tc::cmp_less(static_cast(-42), static_cast(42))); + + // right signed + _ASSERT(tc::cmp_less(static_cast(17), static_cast(42))); + _ASSERT(!tc::cmp_less(static_cast(42), static_cast(-42))); +} diff --git a/tc/base/static_polymorphism.h b/tc/base/static_polymorphism.h index c8896d4..e6bd247 100644 --- a/tc/base/static_polymorphism.h +++ b/tc/base/static_polymorphism.h @@ -10,6 +10,11 @@ #include "assert_defs.h" #include +#include "tag_type.h" + +namespace tc { + DEFINE_TAG_TYPE(unchecked_derived_cast_tag) +} #define STATIC_VIRTUAL_METHOD_NAME( Name ) \ Name ## _ImplDoNotCallDirectly @@ -20,50 +25,42 @@ #define STATIC_VIRTUAL_FALLBACK_NAME( Name ) \ Name ## _FallbackDoNotCallDirectly -#define STATIC_VIRTUAL_FORWARD_IMPL( Mod, Name, Decoration ) \ +#define STATIC_VIRTUAL_FORWARD_IMPL( Name, Decoration, DerivedCast, MaybeUncheckedDerivedCastTagWithComma ) \ template \ - Mod \ - auto Name(Args&&... args) Decoration return_decltype_xvalue_by_ref_MAYTHROW( \ - STATIC_VIRTUAL_DISPATCH_IMPL_NAME(Name)(static_cast(tc::derived_cast(*MSVC_WORKAROUND_THIS)), std::forward(args)...) \ + constexpr auto Name(Args&&... args) Decoration return_decltype_xvalue_by_ref_MAYTHROW( \ + STATIC_VIRTUAL_DISPATCH_IMPL_NAME(Name)(static_cast( DerivedCast(*MSVC_WORKAROUND_THIS)), MaybeUncheckedDerivedCastTagWithComma std::forward(args)...) \ ) -#define STATIC_VIRTUAL_FORWARD_ALL_IMPL( Mod, Name ) \ - using Name ## _derived_type = Derived; \ - using Name ## _declaring_type = this_type; \ - STATIC_VIRTUAL_FORWARD_IMPL( Mod, Name, & ) \ - STATIC_VIRTUAL_FORWARD_IMPL( Mod, Name, const& ) \ - STATIC_VIRTUAL_FORWARD_IMPL( Mod, Name, && ) \ - STATIC_VIRTUAL_FORWARD_IMPL( Mod, Name, const&& ) +#define STATIC_VIRTUAL_FORWARD_ALL_IMPL( Name, DerivedCast, MaybeUncheckedDerivedCastTagWithComma ) \ + STATIC_VIRTUAL_FORWARD_IMPL( Name, &, DerivedCast, TC_FWD(MaybeUncheckedDerivedCastTagWithComma) ) \ + STATIC_VIRTUAL_FORWARD_IMPL( Name, const&, DerivedCast, TC_FWD(MaybeUncheckedDerivedCastTagWithComma) ) \ + STATIC_VIRTUAL_FORWARD_IMPL( Name, &&, DerivedCast, TC_FWD(MaybeUncheckedDerivedCastTagWithComma) ) \ + STATIC_VIRTUAL_FORWARD_IMPL( Name, const&&, DerivedCast, TC_FWD(MaybeUncheckedDerivedCastTagWithComma) ) -#define STATIC_VIRTUAL_MOD( Mod, Name ) \ +#define STATIC_VIRTUAL( Name ) \ template \ static \ - Mod \ - auto STATIC_VIRTUAL_DISPATCH_IMPL_NAME(Name)(Derived_&& derived, Args&&... args) return_decltype_xvalue_by_ref_MAYTHROW( \ + constexpr auto STATIC_VIRTUAL_DISPATCH_IMPL_NAME(Name)(Derived_&& derived, Args&&... args) return_decltype_xvalue_by_ref_MAYTHROW( \ std::forward(derived).STATIC_VIRTUAL_METHOD_NAME(Name)(std::forward(args)...) \ ) \ - STATIC_VIRTUAL_FORWARD_ALL_IMPL( Mod, Name ) - -#define STATIC_VIRTUAL( Name ) \ - STATIC_VIRTUAL_MOD( BOOST_PP_EMPTY(), Name ) + using Name ## _derived_type = Derived; \ + using Name ## _declaring_type = this_type; \ + STATIC_VIRTUAL_FORWARD_ALL_IMPL( Name, tc::derived_cast, BOOST_PP_EMPTY() ) -#define STATIC_VIRTUAL_CONSTEXPR( Name ) \ - STATIC_VIRTUAL_MOD( constexpr, Name ) +#define UNCHECKED_STATIC_VIRTUAL( Name ) \ + STATIC_VIRTUAL( Name ) \ + STATIC_VIRTUAL_FORWARD_ALL_IMPL( Name, tc::unchecked_derived_cast, TC_FWD(tc::unchecked_derived_cast_tag,) ) // force implementation as static method, using STATIC_FINAL_MOD(static, Name) -#define STATIC_STATIC_VIRTUAL_MOD( Mod, Name ) \ +#define STATIC_STATIC_VIRTUAL( Name ) \ using Name ## _derived_type = Derived; \ using Name ## _declaring_type = this_type; \ template \ - Mod \ - static auto Name(Args_&& ...args) return_decltype_xvalue_by_ref_MAYTHROW( \ + static constexpr auto Name(Args_&& ...args) return_decltype_xvalue_by_ref_MAYTHROW( \ Derived_:: STATIC_VIRTUAL_METHOD_NAME(Name) (std::forward(args)...) \ ) -#define STATIC_STATIC_VIRTUAL( Name ) \ - STATIC_STATIC_VIRTUAL_MOD( BOOST_PP_EMPTY(), Name ) - -#define STATIC_VIRTUAL_WITH_DEFAULT_IMPL_MOD(Mod, Name) \ +#define STATIC_VIRTUAL_WITH_DEFAULT_IMPL_MOD( Mod, Name ) \ STATIC_VIRTUAL( Name ) \ Mod \ auto STATIC_VIRTUAL_METHOD_NAME( Name ) @@ -94,9 +91,6 @@ __VA_ARGS__ \ auto STATIC_VIRTUAL_METHOD_NAME( Name ) -#define STATIC_OVERRIDE_MOD_BASE(Name, ...) \ - STATIC_OVERRIDE_MOD_DECLARING_BASE(this_type::Name ## _declaring_type, Name, __VA_ARGS__) - #define STATIC_OVERRIDE_MOD_DECLARING(Mod, Declaring, Name) \ STATIC_OVERRIDE_MOD_DECLARING_BASE(TC_FWD(Declaring), Name, \ static_assert( \ @@ -117,7 +111,7 @@ #define STATIC_VIRTUAL_WITH_FALLBACK_MOD(Mod, Name) \ template requires (!requires { std::declval().STATIC_VIRTUAL_METHOD_NAME(Name)( std::declval()... ); }) \ - static \ + static constexpr \ auto STATIC_VIRTUAL_DISPATCH_IMPL_NAME(Name)(Derived_&& derived, Args&&... args) return_decltype_xvalue_by_ref_MAYTHROW( \ std::forward(derived).STATIC_VIRTUAL_FALLBACK_NAME(Name)(std::forward(args)...) \ ) \ diff --git a/tc/base/string_template_param.h b/tc/base/string_template_param.h index 23ce4b0..8707c81 100644 --- a/tc/base/string_template_param.h +++ b/tc/base/string_template_param.h @@ -11,12 +11,25 @@ #include "type_traits_fwd.h" namespace tc { + namespace literal_range_adl { + template + struct literal_range; + } + using literal_range_adl::literal_range; + namespace no_adl { - template requires tc::char_like && (!std::is_volatile::value) + template requires tc::char_like && (!std::is_volatile::value) struct string_template_param final { consteval string_template_param(Char const (&str)[N]) noexcept { for(int i=0; i + consteval string_template_param(literal_range) noexcept { + static_assert(sizeof...(Cs) + 1 == N); + std::size_t idx = 0; + ((m_str[idx++] = Cs), ...); // avoid using library functions to prevent circular dependency on _ASSERT + m_str[idx] = Char(); + } constexpr auto begin() const& noexcept { return &m_str[0]; @@ -26,23 +39,23 @@ namespace tc { return &m_str[N-1]; } + static constexpr auto size() noexcept { + return tc::least_uint_constant{}; + } + constexpr operator auto const&() const& noexcept { return m_str; } - using iterator = Char const*; - using const_iterator = iterator; - using value_type = Char; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; + constexpr Char operator[](std::size_t idx) const& noexcept { + return m_str[idx]; + } Char m_str[N]; }; - template struct constexpr_size_impl; // prevent circular dependency - - template - struct constexpr_size_impl> : tc::constant {}; + template + string_template_param(literal_range) -> string_template_param; } using no_adl::string_template_param; } diff --git a/tc/base/tag_type.h b/tc/base/tag_type.h index 72c36ba..1092537 100644 --- a/tc/base/tag_type.h +++ b/tc/base/tag_type.h @@ -54,6 +54,7 @@ namespace tc { DEFINE_TAG_TYPE(aggregate_tag) // tag to distinguish constructors that aggregate their single argument from templated copy constructors DEFINE_TAG_TYPE(func_tag) + DEFINE_TEMPLATE_TAG_TYPE(type_tag) // tc_define_fn(func) always defines a function void func(define_fn_dummy_t) // If that function did not exist, -> decltype( func(...) ) would not be diff --git a/tc/base/track_instance.h b/tc/base/track_instance.h index d83384a..31b77fb 100644 --- a/tc/base/track_instance.h +++ b/tc/base/track_instance.h @@ -23,7 +23,7 @@ namespace tc { _ASSERT(!c_ptib); c_ptib=this; } - void reset_instance() const & noexcept { + void reset_instance() const& noexcept { _ASSERT(is_instance()); c_ptib=nullptr; }; diff --git a/tc/base/trivial_functors.h b/tc/base/trivial_functors.h index c040650..1877674 100644 --- a/tc/base/trivial_functors.h +++ b/tc/base/trivial_functors.h @@ -40,6 +40,7 @@ namespace tc { using no_adl::noop; using no_adl::never_called; using no_adl::constexpr_function; + using no_adl::identity; } // MAKE_CONSTEXPR_FUNCTION is guaranteed a constexpr function. @@ -53,7 +54,7 @@ namespace tc { }; \ } else { \ return [](auto&& ...) constexpr noexcept -> decltype(auto) { \ - return as_constexpr(__VA_ARGS__); \ + return tc_as_constexpr(__VA_ARGS__); \ }; \ } \ }() diff --git a/tc/base/trivial_functors.t.cpp b/tc/base/trivial_functors.t.cpp index 4cd2ac5..e85c14d 100644 --- a/tc/base/trivial_functors.t.cpp +++ b/tc/base/trivial_functors.t.cpp @@ -24,7 +24,7 @@ namespace { // Fits in a pointer { constexpr auto fn = tc::constexpr_function(); - auto fnOracle = []() constexpr noexcept { + auto const fnOracle = []() constexpr noexcept { constexpr auto _ = int(); return _; }; @@ -36,7 +36,7 @@ namespace { int m_n; }; constexpr auto fn = MAKE_CONSTEXPR_FUNCTION(SFoo{}); - auto fnOracle = []() constexpr noexcept { + auto const fnOracle = []() constexpr noexcept { constexpr auto _ = SFoo{}; return _; }; @@ -45,7 +45,7 @@ namespace { } { constexpr auto fn = MAKE_CONSTEXPR_FUNCTION(tc::constant<10>()); - auto fnOracle = []() constexpr noexcept { + auto const fnOracle = []() constexpr noexcept { constexpr auto _ = tc::constant<10>(); return _; }; @@ -54,7 +54,7 @@ namespace { } { constexpr auto fn = MAKE_CONSTEXPR_FUNCTION(tc::constant()); - auto fnOracle = []() constexpr noexcept { + auto const fnOracle = []() constexpr noexcept { constexpr auto _ = tc::constant(); return _; }; @@ -65,7 +65,7 @@ namespace { // Doesn't fit in a pointer { constexpr auto fn = MAKE_CONSTEXPR_FUNCTION(c_typebiggerthanpointer); - auto fnOracle = []() constexpr noexcept ->decltype(auto) { + auto const fnOracle = []() constexpr noexcept ->decltype(auto) { return (c_typebiggerthanpointer); }; STATICASSERTSAME(decltype(fnOracle()), TypeBiggerThanPointer const&); diff --git a/tc/base/type_list.t.cpp b/tc/base/type_list.t.cpp index e72fc52..6758d0d 100644 --- a/tc/base/type_list.t.cpp +++ b/tc/base/type_list.t.cpp @@ -8,6 +8,7 @@ #include "assert_defs.h" #include "type_list.h" #include "../range/meta.h" +#include "../string/char.h" namespace tc { template diff --git a/tc/base/type_traits.t.cpp b/tc/base/type_traits.t.cpp index 2e44d68..f9847ba 100644 --- a/tc/base/type_traits.t.cpp +++ b/tc/base/type_traits.t.cpp @@ -398,14 +398,22 @@ static_assert( static_assert( std::is_same< tc::common_reference_xvalue_as_ref_t&, tc::string&>, - tc::span + std::conditional_t< + std::same_as>, tc::iterator_t>>, + tc::iterator_range>>, + tc::span + > >::value ); static_assert( std::is_same< tc::common_reference_xvalue_as_ref_t&, tc::string const&>, - tc::span + std::conditional_t< + std::same_as>, tc::iterator_t>>, + tc::iterator_range const>>, + tc::span + > >::value ); @@ -595,28 +603,28 @@ static_assert( static_assert( std::is_same< - tc::common_reference_xvalue_as_ref_t&, tc::subrange&>>, - tc::subrange&> + tc::common_reference_xvalue_as_ref_t&, tc::subrange&>>, + tc::subrange&> >::value ); static_assert( std::is_same < - tc::common_reference_xvalue_as_ref_t&, tc::subrange>&>, - tc::subrange&> + tc::common_reference_xvalue_as_ref_t&, tc::subrange>&>, + tc::subrange&> >::value ); static_assert( std::is_same< - tc::common_reference_xvalue_as_ref_t&>, tc::subrange>&>, - tc::subrange&> + tc::common_reference_xvalue_as_ref_t&>, tc::subrange>&>, + tc::subrange&> >::value ); static_assert( std::is_same< - tc::common_reference_xvalue_as_ref_t, tc::subrange&>>, + tc::common_reference_xvalue_as_ref_t, tc::subrange&>>, tc::span >::value ); @@ -635,40 +643,66 @@ static_assert( static_assert( std::is_same< - tc::common_reference_prvalue_as_val_t&, tc::subrange&>>, - tc::subrange&> + tc::common_reference_prvalue_as_val_t&, tc::subrange&>>, + tc::subrange&> >::value ); static_assert( std::is_same < - tc::common_reference_prvalue_as_val_t&, tc::subrange>&>, - tc::subrange&> + tc::common_reference_prvalue_as_val_t&, tc::subrange>&>, + tc::subrange&> >::value ); static_assert( std::is_same< - tc::common_reference_prvalue_as_val_t&>, tc::subrange>&>, - tc::subrange&> + tc::common_reference_prvalue_as_val_t&>, tc::subrange>&>, + tc::subrange&> >::value ); static_assert( - !tc::has_common_reference_xvalue_as_ref&, tc::subrange>> + !tc::has_common_reference_xvalue_as_ref&, tc::subrange>> ); static_assert( - !tc::has_common_reference_xvalue_as_ref, tc::subrange&>> + !tc::has_common_reference_xvalue_as_ref, tc::subrange&>> ); static_assert( std::is_same< tc::common_reference_prvalue_as_val_t&, tc::string&>, - tc::span + std::conditional_t< + std::same_as>, tc::iterator_t>>, + tc::iterator_range>>, + tc::span + > + >::value +); + +namespace +{ + struct my_vector + { + tc::vector impl; + + auto begin() & { return tc::begin(impl); } + auto end() & { return tc::begin(impl); } + }; +} + +static_assert( + std::is_same< + tc::common_reference_prvalue_as_val_t&, my_vector&>, + tc::iterator_range>> >::value ); +static_assert( + !tc::has_common_reference_prvalue_as_val&, my_vector> +); + namespace { struct S; tc::unordered_set g_sets; @@ -698,11 +732,11 @@ struct S{ _ASSERT(tc::end(g_sets) != g_sets.find(this)); } - void foo() const & { + void foo() const& { _ASSERT(tc::end(g_sets) != g_sets.find(this)); } - void foo() const && { + void foo() const&& { _ASSERT(tc::end(g_sets) != g_sets.find(this)); } @@ -846,7 +880,7 @@ UNITTESTDEF(minTest) { tc::transform( tc::transform( tc::iota(0,1), - [&](int n) noexcept -> S&& { + [&](int const n) noexcept -> S&& { return std::move(s2[n]); } ), @@ -873,7 +907,7 @@ UNITTESTDEF(minTest) { S s2[2]; tc::projected( tc::fn_min(), - [&](int n) noexcept -> S&& { + [&](int const n) noexcept -> S&& { return std::move(s2[n]); } )(0,1).foo(); @@ -976,6 +1010,8 @@ namespace is_instance_or_derived_test { using CInstantiation2 = CTemplate2; static_assert(tc::instance_or_derived); + static_assert(!tc::instance_or_derived); + static_assert(!tc::is_instance_or_derived::value); STATICASSERTSAME((CTemplate1), (typename tc::is_instance_or_derived::base_instance)); STATICASSERTSAME((tc::type::list), (typename tc::is_instance_or_derived::arguments)); diff --git a/tc/base/type_traits_fwd.h b/tc/base/type_traits_fwd.h index 617475d..23f67f6 100644 --- a/tc/base/type_traits_fwd.h +++ b/tc/base/type_traits_fwd.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -187,17 +188,14 @@ namespace tc { template using remove_rvalue_reference_t=typename tc::remove_rvalue_reference::type; - ////////////////////////////////////////////////////////////////////////// - // constant - - template - using constant = std::integral_constant; - ////////////////////////// // type classification concepts template - concept char_type = tc::type::find_unique, std::remove_cv_t>::found; + concept char_type = tc::type::find_unique, std::remove_cv_t>::found; + + template< typename T > + concept char_ptr = std::is_pointer::value && tc::char_type>; namespace char_like_detail { template @@ -208,13 +206,17 @@ namespace tc { concept char_like = char_like_detail::char_like_impl; template< typename T > - concept decayed=std::same_as< T, tc::decay_t >; + concept decayed = std::same_as< T, tc::decay_t >; template - concept actual_integer=std::integral && (!std::same_as, bool>) && (!tc::char_type); + concept actual_integer = std::integral && (!std::same_as, bool>) && (!tc::char_type); + template + concept actual_unsigned_integer = tc::actual_integer && std::unsigned_integral; + template + concept actual_signed_integer = tc::actual_integer && std::signed_integral; template - concept actual_arithmetic=tc::actual_integer || std::floating_point; + concept actual_arithmetic = tc::actual_integer || std::floating_point; template concept empty_type = std::is_empty::value && std::is_trivial>::value; @@ -222,6 +224,15 @@ namespace tc { template concept enum_type = std::is_enum::value; + ////////////////////////////////////////////////////////////////////////// + // constant + + template + using constant = std::integral_constant; + + template requires (N >= 0) + using least_uint_constant = tc::constant::least>(N)>; + ////////////////////////// // derived_from @@ -304,7 +315,7 @@ template typename Temp using BOOST_PP_CAT(is_instance_or_derived, suffix) = \ decltype( \ no_adl::BOOST_PP_CAT(is_instance_or_derived_detector, suffix)