diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index b559f9c8336..1befa5febb8 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -156,6 +156,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/unordered_set ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/vector ${CMAKE_CURRENT_LIST_DIR}/inc/filesystem + ${CMAKE_CURRENT_LIST_DIR}/inc/flat_set ${CMAKE_CURRENT_LIST_DIR}/inc/format ${CMAKE_CURRENT_LIST_DIR}/inc/forward_list ${CMAKE_CURRENT_LIST_DIR}/inc/fstream diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index ccb3267943a..248d8819dc2 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/flat_set b/stl/inc/flat_set new file mode 100644 index 00000000000..67dc93c4da7 --- /dev/null +++ b/stl/inc/flat_set @@ -0,0 +1,721 @@ +// flat_set standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _FLAT_SET_ +#define _FLAT_SET_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX23 || !defined(__cpp_lib_concepts) // TRANSITION, GH-395 +_EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); +#else // ^^^ not supported / supported language mode vvv +#include +#include +#include +#include +#include + +_STD_BEGIN + +template +struct _NODISCARD _Clear_scope_guard { + _Ty* _Clearable; + ~_Clear_scope_guard() { + if (_Clearable) { + _Clearable->clear(); + } + } +}; + +template +concept _Allocator_for = uses_allocator_v<_Container, _Alloc>; + +template +class _Base_flat_set { +private: + static constexpr bool _Keylt_transparent = _Is_transparent_v<_Keylt>; + +public: + static_assert(same_as<_Kty, typename _Container::value_type>, + "The C++ Standard dictates that the Key type must be the " + "same as the container's value type [flatset.overview]"); + + using key_type = _Kty; + using value_type = _Kty; + using key_compare = _Keylt; + using value_compare = _Keylt; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = _Container::size_type; + using difference_type = _Container::difference_type; + using iterator = _Container::iterator; + using const_iterator = _Container::const_iterator; + using reverse_iterator = _STD reverse_iterator; + using const_reverse_iterator = _STD reverse_iterator; + using container_type = _Container; + + static_assert(random_access_iterator, "The C++ Standard forbids containers without random " + "access iterators from being adapted. See [flatset.overview]."); + + _Base_flat_set() : _My_pair(_Zero_then_variadic_args_t{}, _Keylt()) {} + + explicit _Base_flat_set(container_type _Cont, const key_compare& _Comp = key_compare()) + : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + _Make_invariants_fulfilled(); + } + template <_Allocator_for _Alloc> + _Base_flat_set(const container_type& _Cont, const _Alloc& _Al) : _Base_flat_set(container_type(_Cont, _Al)) {} + template <_Allocator_for _Alloc> + _Base_flat_set(const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_Cont, _Al), _Comp) {} + + _Base_flat_set(_Tsorted, container_type _Cont, const key_compare& _Comp = key_compare()) + : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + _Assert_after_sorted_input(); + } + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Cont, _Al)) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Cont, _Al), _Comp) {} + + explicit _Base_flat_set(const key_compare& _Comp) : _My_pair(_Zero_then_variadic_args_t{}, _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Comp, container_type(_Al)) {} + template <_Allocator_for _Alloc> + explicit _Base_flat_set(const _Alloc& _Al) : _Base_flat_set(container_type(_Al)) {} + + template + _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) + : _Base_flat_set(container_type(_First, _Last), _Comp) {} + template _Alloc> + _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_First, _Last, _Al), _Comp) {} + template _Alloc> + _Base_flat_set(_Iter _First, _Iter _Last, const _Alloc& _Al) : _Base_flat_set(container_type(_First, _Last, _Al)) {} + + template <_Container_compatible_range<_Kty> _Rng> + _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp = key_compare()) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range)), _Comp) {} + template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> + _Base_flat_set(from_range_t, _Rng&& _Range, const _Alloc& _Al) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al)) {} + template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> + _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al), _Comp) {} + + template + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Tsort, container_type(_First, _Last), _Comp) {} + template _Alloc> + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al), _Comp) {} + template _Alloc> + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al)) {} + + _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Ilist.begin(), _Ilist.end(), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(initializer_list<_Kty> _Ilist, const _Alloc& _Al) + : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Tsort, _Ilist.begin(), _Ilist.end(), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + + _Deriv& operator=(initializer_list<_Kty> _Ilist) { + _Get_cont() = container_type(_Ilist.begin(), _Ilist.end()); + _Make_invariants_fulfilled(); + return static_cast<_Deriv&>(*this); + } + + _NODISCARD iterator begin() noexcept { + return _Get_cont().begin(); + } + _NODISCARD const_iterator begin() const noexcept { + return _Get_cont().begin(); + } + _NODISCARD iterator end() noexcept { + return _Get_cont().end(); + } + _NODISCARD const_iterator end() const noexcept { + return _Get_cont().end(); + } + _NODISCARD reverse_iterator rbegin() noexcept { + return _Get_cont().rbegin(); + } + _NODISCARD const_reverse_iterator rbegin() const noexcept { + return _Get_cont().rbegin(); + } + _NODISCARD reverse_iterator rend() noexcept { + return _Get_cont().rend(); + } + _NODISCARD const_reverse_iterator rend() const noexcept { + return _Get_cont().rend(); + } + _NODISCARD const_iterator cbegin() const noexcept { + return _Get_cont().cbegin(); + } + _NODISCARD const_iterator cend() const noexcept { + return _Get_cont().cend(); + } + _NODISCARD const_reverse_iterator crbegin() const noexcept { + return _Get_cont().crbegin(); + } + _NODISCARD const_reverse_iterator crend() const noexcept { + return _Get_cont().crend(); + } + + _NODISCARD_EMPTY_MEMBER bool empty() const noexcept { + return _Get_cont().empty(); + } + _NODISCARD size_type size() const noexcept { + return _Get_cont().size(); + } + _NODISCARD size_type max_size() const noexcept { + return _Get_cont().max_size(); + } + + template + auto emplace(_Args&&... _Vals) { + insert<_Kty>(_Kty{_STD forward<_Args>(_Vals)...}); + } + + template + iterator emplace_hint(const_iterator _Hint, _Args&&... _Vals) { + return _Emplace_hint(_Hint, _Kty{_STD forward<_Args>(_Vals)...}); + } + + auto insert(const value_type& _Val) { + return _Insert<_Kty>(_Val); + } + auto insert(value_type&& _Val) { + return _Insert(_STD move(_Val)); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) + auto insert(_Other&& _Val) { + return _Insert(_STD forward<_Other>(_Val)); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) + iterator insert(const_iterator _Hint, _Other&& _Val) { + return _Emplace_hint(_Hint, _STD forward<_Other>(_Val)); + } + + iterator insert(const_iterator _Hint, const value_type& _Val) { + return _Emplace_hint(_Hint, _Val); + } + iterator insert(const_iterator _Hint, value_type&& _Val) { + return _Emplace_hint(_Hint, _STD move(_Val)); + } + + template + void insert(const _Iter& _First, const _Iter& _Last) { + _Insert_range(_First, _Last); + } + + template + void insert(_Tsorted, _Iter _First, _Iter _Last) { + _Insert_range(_First, _Last); + } + + template <_Container_compatible_range<_Kty> _Rng> + void insert_range(_Rng&& _Range) { + const size_type _Old_size = size(); + _Get_cont().append_range(_STD forward<_Rng>(_Range)); + _Restore_invariants_after_insert(_Old_size); + } + + _NODISCARD container_type extract() && { + // The container NEEDS to be cleared no matter what, + // which is not guaranteed by simply moving it away + // ("... valid but unspecified ...") + container_type& _Cont = _Get_cont(); + _Clear_scope_guard _Guard{this}; + container_type _Temp = _STD move(_Cont); + return _Temp; + } + + void replace(container_type&& _Cont) { + _Get_cont() = _STD move(_Cont); + _Assert_after_sorted_input(); + } + + iterator erase(iterator _Where) { + return _Get_cont().erase(_Where); + } + iterator erase(const_iterator _Where) { + return _Get_cont().erase(_Where); + } + size_type erase(const _Kty& _Val) { + return _Erase(_Val); + } + + template + requires _Keylt_transparent + size_type erase(_Other&& _Val) { + return _Erase(_STD forward<_Other>(_Val)); + } + + iterator erase(const_iterator _First, const_iterator _Last) { + return _Get_cont().erase(_First, _Last); + } + + void swap(_Deriv& _Other) noexcept { + _RANGES swap(_Get_comp(), _Other._Get_comp()); + _RANGES swap(_Get_cont(), _Other._Get_cont()); + } + + void clear() noexcept { + _Get_cont().clear(); + } + + _NODISCARD key_compare key_comp() const { + return _Get_comp(); + } + _NODISCARD value_compare value_comp() const { + return _Get_comp(); + } + + _NODISCARD iterator find(const _Kty& _Val) { + return _Find(_Val); + } + + _NODISCARD const_iterator find(const _Kty& _Val) const { + return _Find(_Val); + } + + template + requires _Keylt_transparent + _NODISCARD iterator find(const _Other& _Val) { + return _Find(_Val); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator find(const _Other& _Val) const { + return _Find(_Val); + } + + _NODISCARD size_type count(const _Kty& _Val) const { + const auto [_First, _Last] = equal_range(_Val); + return _STD distance(_First, _Last); + } + + template + requires _Keylt_transparent + _NODISCARD size_type count(const _Other& _Val) const { + const auto [_First, _Last] = equal_range(_Val); + return _STD distance(_First, _Last); + } + + _NODISCARD bool contains(const _Kty& _Val) const { + return find(_Val) != end(); + } + template + requires _Keylt_transparent + _NODISCARD bool contains(const _Other& _Val) const { + return find(_Val) != end(); + } + _NODISCARD iterator lower_bound(const _Kty& _Val) { + return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + } + _NODISCARD const_iterator lower_bound(const _Kty& _Val) const { + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD iterator lower_bound(const _Other& _Val) { + return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator lower_bound(const _Other& _Val) const { + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD iterator upper_bound(const _Kty& _Val) { + return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + } + + _NODISCARD const_iterator upper_bound(const _Kty& _Val) const { + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD iterator upper_bound(const _Other& _Val) { + return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator upper_bound(const _Other& _Val) const { + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD pair equal_range(const _Kty& _Val) { + return _STD equal_range(begin(), end(), _Val, _Get_comp()); + } + + _NODISCARD pair equal_range(const _Kty& _Val) const { + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD pair equal_range(const _Other& _Val) { + return _STD equal_range(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD pair equal_range(const _Other& _Val) const { + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD friend bool operator==(const _Deriv& _Lhs, const _Deriv& _Rhs) { + return _RANGES equal(_Lhs, _Rhs); + } + + _NODISCARD friend _Synth_three_way_result<_Kty> operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { + return _STD lexicographical_compare_three_way( + _Lhs.cbegin(), _Lhs.cend(), _Rhs.cbegin(), _Rhs.cend(), _Synth_three_way{}); + } + + friend void swap(_Deriv& _Lhs, _Deriv& _Rhs) noexcept { + _Lhs.swap(_Rhs); + } + +private: + void inline _Assert_after_sorted_input() const { + _STL_ASSERT(_STD is_sorted(begin(), end(), _Get_comp()), "Input was not sorted!"); + if constexpr (!_Multi) { + _STL_ASSERT(_Is_unique(), "Input was not unique!"); + } + } + + bool _Is_unique() const { + if (empty()) { + return true; + } + const_iterator _End = cend(); + const_iterator _It = begin(); + while (++_It != _End) { + if (_Keys_equal(*(_It - 1), *_It)) { + return false; + } + } + return true; + } + + template + requires (_Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> + void _Emplace_hint(const_iterator _Where, _Ty&& _Val) { + _Container& _Cont = _Get_cont(); + _Keylt& _Compare = _Get_comp(); + const iterator _Begin = begin(); + const iterator _End = end(); + + if (_Where == _End || !_Compare(*_Where, _Val)) { + // _Val <= *_Where + // Left of _Where + if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { + // _Val >= (*_Where - 1) + // Insert before _Where + } else { + // _Val < (*_Where - 1) + _Where = _STD upper_bound(_Begin, _Where, _Val, _Compare); + } + } else { + // _Val > *_Where + // Right of _Where + _Where = _STD lower_bound(_Where + 1, _End, _Val, _Compare); + } + + if constexpr (_Multi) { + return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + } else { + if (_Where == _End || !_Keys_equal(_Val, *_Where)) { + return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + } + return _Where; + } + } + + template + void _Insert_range(_Iter _First, _Iter _Last) { + const size_type _Old_size = size(); + _Container& _Cont = _Get_cont(); + _Cont.insert(_Cont.end(), _First, _Last); + _Restore_invariants_after_insert<_Presorted>(_Old_size); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> + auto _Insert(_Ty&& _Val) { + _Container& _Cont = _Get_cont(); + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if constexpr (_Multi) { + return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); + } else { + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return pair{_Where, false}; + } + return pair{_Cont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; + } + } + + template + requires _Keylt_transparent || is_same_v<_Ty, _Kty> + size_type _Erase(_Ty&& _Val) { + const auto [_First, _Last] = equal_range(_Val); + + const difference_type _Removed = _STD distance(_First, _Last); + _Get_cont().erase(_First, _Last); + return _Removed; + } + + template + requires _Keylt_transparent || is_same_v<_Other, _Kty> + _NODISCARD iterator _Find(const _Other& _Val) { + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return _Where; + } else { + return _End; + } + } + + template + requires _Keylt_transparent || is_same_v<_Other, _Kty> + _NODISCARD const_iterator _Find(const _Other& _Val) const { + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return _Where; + } else { + return _End; + } + } + + template + requires _Keylt_transparent || (is_same_v<_Kty, _Lhty> && is_same_v<_Lhty, _Rhty>) + _NODISCARD bool _Keys_equal(const _Lhty& _Lhs, const _Rhty& _Rhs) const { + const key_compare& _Compare = _Get_comp(); + return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); + } + + // O(N) if already sorted. + void _Sort_potentially_sorted(const iterator& _Begin, const iterator& _End) { + key_compare& _Compare = _Get_comp(); + const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Compare); + + _STD sort(_Begin_unsorted, _End, _Compare); + + _STD inplace_merge(begin(), _Begin_unsorted, _End, _Compare); + } + + void _Erase_dupes_if_needed() { + if constexpr (!_Multi) { + iterator _End = end(); + iterator _New_end = + _STD unique(begin(), _End, [&](const _Kty& _Lhs, const _Kty& _Rhs) { return _Keys_equal(_Lhs, _Rhs); }); + _Get_cont().erase(_New_end, _End); + } + if constexpr (!_Multi) { + _STL_INTERNAL_CHECK(_Is_unique()); + } + } + + template + void _Restore_invariants_after_insert(const size_type& _Old_size) { + key_compare& _Compare = _Get_comp(); + const iterator _Old_end = begin() + static_cast(_Old_size); + const iterator _New_end = end(); + + if constexpr (!_Presorted) { + _STD sort(_Old_end, _New_end, _Compare); + } else { + _STL_ASSERT(_STD is_sorted(_Old_end, _New_end, _Compare), "Input was not sorted!"); + } + + _STD inplace_merge(begin(), _Old_end, _New_end, _Compare); + + _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + + _Erase_dupes_if_needed(); + } + + void _Make_invariants_fulfilled() { + const iterator _Begin = begin(); + const iterator _End = end(); + + if (_Begin == _End) { + return; + } + + _Sort_potentially_sorted(_Begin, _End); + _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + + _Erase_dupes_if_needed(); + } + + _NODISCARD const _Container& _Get_cont() const noexcept { + return _My_pair._Get_first(); + } + + _NODISCARD _Container& _Get_cont() noexcept { + return _My_pair._Get_first(); + } + + _NODISCARD const key_compare& _Get_comp() const noexcept { + return _My_pair._Myval2; + } + + _NODISCARD key_compare& _Get_comp() noexcept { + return _My_pair._Myval2; + } + + _Compressed_pair _My_pair; +}; + +_EXPORT_STD struct sorted_unique_t { + explicit sorted_unique_t() = default; +}; +_EXPORT_STD inline constexpr sorted_unique_t sorted_unique{}; + +_EXPORT_STD struct sorted_equivalent_t { + explicit sorted_equivalent_t() = default; +}; +_EXPORT_STD inline constexpr sorted_equivalent_t sorted_equivalent{}; + +_EXPORT_STD template , class _Container = vector<_Kty>> +class flat_set + : public _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set<_Kty, _Keylt, _Container>, sorted_unique_t> { +public: + using _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set, sorted_unique_t>::_Base_flat_set; +}; + +_EXPORT_STD template , class _Container = vector<_Kty>> +class flat_multiset : public _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset<_Kty, _Keylt, _Container>, + sorted_equivalent_t> { +public: + using _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset, sorted_equivalent_t>::_Base_flat_set; +}; + +_EXPORT_STD template +size_t erase_if(flat_set<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + _Clear_scope_guard _Guard{&_Val}; + return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +} + +_EXPORT_STD template +size_t erase_if(flat_multiset<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + _Clear_scope_guard _Guard{&_Val}; + return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +} + +template +struct uses_allocator, _Alloc> + : bool_constant> {}; + +template +struct uses_allocator, _Alloc> + : bool_constant> {}; + +template > +flat_set(_Container, _Keylt = _Keylt()) -> flat_set; +template +flat_set(_Container, _Alloc) + -> flat_set, _Container>; +template +flat_set(_Container, _Keylt, _Alloc) -> flat_set; + +template > +flat_set(sorted_unique_t, _Container, _Keylt = _Keylt()) + -> flat_set; +template +flat_set(sorted_unique_t, _Container, _Alloc) + -> flat_set, _Container>; +template +flat_set(sorted_unique_t, _Container, _Keylt, _Alloc) -> flat_set; + +template >> +flat_set(_Iter, _Iter, _Keylt = _Keylt()) -> flat_set, _Keylt>; +template >> +flat_set(sorted_unique_t, _Iter, _Iter, _Keylt = _Keylt()) -> flat_set, _Keylt>; +template <_RANGES input_range _Range, class _Keylt = less<_RANGES range_value_t<_Range>>, + class _Alloc = allocator<_RANGES range_value_t<_Range>>> +flat_set(from_range_t, _Range&&, _Keylt = _Keylt(), _Alloc = _Alloc()) -> flat_set<_RANGES range_value_t<_Range>, + _Keylt, vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template <_RANGES input_range _Range, class _Alloc> +flat_set(from_range_t, _Range&&, _Alloc) -> flat_set<_RANGES range_value_t<_Range>, less<_RANGES range_value_t<_Range>>, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template > +flat_set(initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_set<_Kty, _Keylt>; +template > +flat_set(sorted_unique_t, initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_set<_Kty, _Keylt>; + + +template > +flat_multiset(_Container, _Keylt = _Keylt()) -> flat_multiset; +template +flat_multiset(_Container, _Alloc) + -> flat_multiset, _Container>; +template +flat_multiset(_Container, _Keylt, _Alloc) -> flat_multiset; + +template > +flat_multiset(sorted_equivalent_t, _Container, _Keylt = _Keylt()) + -> flat_multiset; +template +flat_multiset(sorted_equivalent_t, _Container, _Alloc) + -> flat_multiset, _Container>; +template +flat_multiset(sorted_equivalent_t, _Container, _Keylt, _Alloc) + -> flat_multiset; + +template >> +flat_multiset(_Iter, _Iter, _Keylt = _Keylt()) -> flat_multiset, iter_value_t<_Iter>, _Keylt>; +template >> +flat_multiset(sorted_equivalent_t, _Iter, _Iter, _Keylt = _Keylt()) + -> flat_multiset, iter_value_t<_Iter>, _Keylt>; +template <_RANGES input_range _Range, class _Keylt = less<_RANGES range_value_t<_Range>>, + class _Alloc = allocator<_RANGES range_value_t<_Range>>> +flat_multiset(from_range_t, _Range&&, _Keylt = _Keylt(), _Alloc = _Alloc()) + -> flat_multiset<_RANGES range_value_t<_Range>, _Keylt, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template <_RANGES input_range _Range, class _Alloc> +flat_multiset(from_range_t, _Range&&, _Alloc) + -> flat_multiset<_RANGES range_value_t<_Range>, less<_RANGES range_value_t<_Range>>, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template > +flat_multiset(initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_multiset<_Kty, _Keylt>; +template > +flat_multiset(sorted_equivalent_t, initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_multiset<_Kty, _Keylt>; + +_STD_END + +#endif // ^^^ supported language mode ^^^ +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _FLAT_SET_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 0966ec2accf..f4e4048b3b7 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -63,6 +63,7 @@ "execution", "expected", "filesystem", + "flat_set", "format", "forward_list", "fstream", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ceb2fce5ed8..761d5e2291d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -316,6 +316,7 @@ // P1132R7 out_ptr(), inout_ptr() // P1147R1 Printing volatile Pointers // P1206R7 Conversions From Ranges To Containers +// P1222R4 // P1223R5 ranges::find_last, ranges::find_last_if, ranges::find_last_if_not // P1272R4 byteswap() // P1328R1 constexpr type_info::operator==() @@ -1722,6 +1723,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_containers_ranges 202202L #define __cpp_lib_expected 202211L +#define __cpp_lib_flat_set 202207L #endif // __cpp_lib_concepts #define __cpp_lib_forward_like 202207L diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index 326d944d74f..cfae4e6fc95 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -59,6 +59,7 @@ export module std; #include #include #include +#include #include #include #include diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index ce362844dc7..6710a8e7099 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -234,6 +234,13 @@ void test_filesystem() { assert(info.capacity != static_cast(-1)); } +void test_flat_set() { + using namespace std; + puts("Testing ."); + + // FIXME! ADD TEST COVERAGE HERE! +} + void test_format() { using namespace std; puts("Testing ."); @@ -1082,6 +1089,7 @@ void all_cpp_header_tests() { test_execution(); test_expected(); test_filesystem(); + test_flat_set(); test_format(); test_forward_list(); test_fstream(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 822cf153b82..0c0c4e860f9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -542,6 +542,7 @@ tests\P1206R7_vector_assign_range tests\P1206R7_vector_from_range tests\P1206R7_vector_insert_range tests\P1208R6_source_location +tests\P1222R4_flat_set tests\P1223R5_ranges_alg_find_last tests\P1223R5_ranges_alg_find_last_if tests\P1223R5_ranges_alg_find_last_if_not diff --git a/tests/std/tests/P1222R4_flat_set/env.lst b/tests/std/tests/P1222R4_flat_set/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P1222R4_flat_set/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp new file mode 100644 index 00000000000..6a3ae2e1223 --- /dev/null +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace ranges; + +template +void assert_container_requirements(const T& s) { + T m = s; + assert(m == s); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_convertible_v); + static_assert(is_same_v m.end()), strong_ordering>); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + T my_moved = std::move(m); + assert(!(my_moved != s)); + + T empty{}; + assert(empty.empty()); + + T non_empty = s; + empty.swap(non_empty); + assert(non_empty.empty()); + assert(empty == s); + + std::swap(empty, non_empty); + assert(empty.empty()); + assert(non_empty == s); + + assert(s.cbegin() <= s.cend()); + assert(s.cbegin() < s.cend() || s.empty()); + + assert(m.begin() <= m.end()); + assert(m.begin() < m.end() || m.empty()); + + assert(static_cast(s.cend() - s.cbegin()) == s.size()); +} + +template +void assert_reversible_container_requirements(const T& s) { + static_assert(is_same_v, typename T::reverse_iterator>); + static_assert(is_same_v, typename T::const_reverse_iterator>); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_convertible_v); +} + +template +void assert_all_requirements_and_equals(const T& s, const initializer_list& il) { + assert_container_requirements(s); + assert_reversible_container_requirements(s); + + auto val_comp = s.value_comp(); + auto begin_it = s.cbegin(); + auto end_it = s.cend(); + assert(std::is_sorted(begin_it, end_it, val_comp)); + if constexpr (!_Is_specialization_v) { + if (!s.empty()) { + auto it = begin_it; + while (++it != end_it) { + assert(val_comp(*(it - 1), *it)); + } + } + } + + if (s.size() != il.size() || !std::equal(s.begin(), s.end(), il.begin())) { + cout << "Expected: {"; + for (auto&& e : il) { + cout << e << ", "; + } + cout << "}" << endl; + cout << "Got: {"; + for (auto&& e : s) { + cout << e << ", "; + } + cout << "}" << endl; + assert(false); + } +} + +template +void assert_basic() { + T s{3, 2, 2, 2, 1}; + assert_all_requirements_and_equals(s, {1, 2, 3}); + + s.insert(43); + assert_all_requirements_and_equals(s, {1, 2, 3, 43}); + + int my_ints[] = {1, 2, 3, 4, 55}; + s.insert_range(my_ints); + assert_all_requirements_and_equals(s, {1, 2, 3, 4, 43, 55}); +} + +template +void test_constructors() { + using lt = std::less; + using gt = std::greater; + + assert_all_requirements_and_equals(flat_set(), {}); + assert_all_requirements_and_equals(flat_multiset(), {}); + assert_all_requirements_and_equals(flat_set(C{3, 7, 1, 85, 222, 1}), {1, 3, 7, 85, 222}); + assert_all_requirements_and_equals(flat_multiset(C{3, 7, 1, 85, 7, 222, 1}), {1, 1, 3, 7, 7, 85, 222}); + assert_all_requirements_and_equals(flat_set(C{1, 2, 3, 3}, gt()), {3, 2, 1}); + assert_all_requirements_and_equals(flat_multiset(C{1, 1, 2, 3}, gt()), {3, 2, 1, 1}); + assert_all_requirements_and_equals(flat_set(sorted_unique, C{30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset(sorted_equivalent, C{3, 3, -1}, gt()), {3, 3, -1}); + assert_all_requirements_and_equals(flat_set({30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset({3, 3, -1}, gt()), {3, 3, -1}); + assert_all_requirements_and_equals(flat_set(sorted_unique, {30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset(sorted_equivalent, {3, 3, -1}, gt()), {3, 3, -1}); + + flat_set a{}; + a = {1, 7, 7, 7, 2, 100, -1}; + assert_all_requirements_and_equals(a, {-1, 1, 2, 7, 100}); + flat_multiset b{}; + b = {1, 7, 7, 7, 2, 100, -1}; + assert_all_requirements_and_equals(b, {-1, 1, 2, 7, 7, 7, 100}); +} + +template +void test_spaceship_operator() { + static constexpr bool multi = _Is_specialization_v; + static constexpr bool invert = is_same_v>; + + T a{3, 2, 2, 1}; + T b{1, 2, 3}; + assert((a <=> b) == (multi ? (invert ? strong_ordering::greater : strong_ordering::less) : strong_ordering::equal)); + + T c{3, 2}; + assert((c <=> b) == (invert ? strong_ordering::less : strong_ordering::greater)); + + T d{5, 6, 7, 7, 8, 9}; + T e{5, 6, 7, 8, 100}; + assert((d <=> e) == strong_ordering::less); + + T f{1, 2, 3, 4}; + assert((f <=> a) == strong_ordering::greater); +} + +template +struct proxy_comparer { + bool operator()(const T& lhs, const T& rhs) const { + return m_less ? (lhs < rhs) : (lhs > rhs); + } + + bool m_less = true; +}; + +void test_non_static_comparer() { + flat_set> a{3, 2, 2, 1}; + assert_all_requirements_and_equals(a, {1, 2, 3}); + auto b = flat_set>({-1, 5, 9, 9, 9, 9, 9}, proxy_comparer{.m_less = false}); + assert_all_requirements_and_equals(b, {9, 5, -1}); + + auto aBackup = a; + a = b; + assert_all_requirements_and_equals(a, {9, 5, -1}); + a.insert_range(vector{7, 7, 3, 3, 2}); + assert_all_requirements_and_equals(a, {9, 7, 5, 3, 2, -1}); + + a = std::move(aBackup); + assert_all_requirements_and_equals(a, {1, 2, 3}); + + a.insert(-100); + assert_all_requirements_and_equals(a, {-100, 1, 2, 3}); + + a = b; + assert_all_requirements_and_equals(a, {9, 5, -1}); + + a.insert(7); + assert_all_requirements_and_equals(a, {9, 7, 5, -1}); +} + +int main() { + test_spaceship_operator>(); + test_spaceship_operator>(); + test_spaceship_operator>>(); + test_spaceship_operator>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + + test_constructors>(); + test_constructors>(); + + test_non_static_comparer(); + + assert_basic>(); + assert_basic, deque>>(); + + flat_multiset, deque> d; +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index 70d0af803ec..51f0fd7365c 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -23,6 +23,7 @@ "execution", "expected", "filesystem", + "flat_set", "format", "forward_list", "fstream", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index f9fa3090a20..fa11f9e9a79 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -29,6 +29,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 276dd6fdd8d..8a626a8eeb0 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -876,6 +876,20 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_flat_set +#error __cpp_lib_flat_set is not defined +#elif __cpp_lib_flat_set != 202207L +#error __cpp_lib_flat_set is not 202207L +#else +STATIC_ASSERT(__cpp_lib_flat_set == 202207L); +#endif +#else +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#error __cpp_lib_flat_set is defined +#endif +#endif + #ifdef __cpp_lib_concepts #ifndef __cpp_lib_format #error __cpp_lib_format is not defined diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 9b041e051e5..0de7b470c6c 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -26,6 +26,7 @@ PM_CL="/DMEOW_HEADER=exception" PM_CL="/DMEOW_HEADER=execution" PM_CL="/DMEOW_HEADER=expected" PM_CL="/DMEOW_HEADER=filesystem" +PM_CL="/DMEOW_HEADER=flat_set" PM_CL="/DMEOW_HEADER=format" PM_CL="/DMEOW_HEADER=forward_list" PM_CL="/DMEOW_HEADER=fstream"