Skip to content

[libc++] implement std::flat_set #125241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Implemented Papers
- P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
- P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github <https://github.com/llvm/llvm-project/issues/105180>`__)
- P2562R1: ``constexpr`` Stable Sorting (`Github <https://github.com/llvm/llvm-project/issues/105360>`__)
- P1222R4: A Standard ``flat_set`` is partially implemented and ``flat_set`` is provided (`Github <https://github.com/llvm/llvm-project/issues/105193>`__)

Improvements and New Features
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18",""
"`P0429R9 <https://wg21.link/P0429R9>`__","A Standard ``flat_map``","2022-07 (Virtual)","|Complete|","20",""
"`P1169R4 <https://wg21.link/P1169R4>`__","``static operator()``","2022-07 (Virtual)","|Complete|","16",""
"`P1222R4 <https://wg21.link/P1222R4>`__","A Standard ``flat_set``","2022-07 (Virtual)","","",""
"`P1222R4 <https://wg21.link/P1222R4>`__","A Standard ``flat_set``","2022-07 (Virtual)","|In progress|","",""
"`P1223R5 <https://wg21.link/P1223R5>`__","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","2022-07 (Virtual)","|Complete|","19",""
"`P1467R9 <https://wg21.link/P1467R9>`__","Extended ``floating-point`` types and standard names","2022-07 (Virtual)","","",""
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","",""
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ set(files
__flat_map/sorted_equivalent.h
__flat_map/sorted_unique.h
__flat_map/utils.h
__flat_set/flat_set.h
__flat_set/ra_iterator.h
__format/buffer.h
__format/concepts.h
__format/container_adaptor.h
Expand Down Expand Up @@ -995,6 +997,7 @@ set(files
fenv.h
filesystem
flat_map
flat_set
float.h
format
forward_list
Expand Down
846 changes: 846 additions & 0 deletions libcxx/include/__flat_set/flat_set.h

Large diffs are not rendered by default.

157 changes: 157 additions & 0 deletions libcxx/include/__flat_set/ra_iterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___FLAT_SET_RA_ITERATOR_H
#define _LIBCPP___FLAT_SET_RA_ITERATOR_H

#include "__type_traits/is_same.h"
#include <__compare/three_way_comparable.h>
#include <__config>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iterator_traits.h>
#include <__type_traits/is_constructible.h>
#include <__utility/move.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

#if _LIBCPP_STD_VER >= 23

_LIBCPP_BEGIN_NAMESPACE_STD

/**
* __ra_iterator is a random access iterator that wraps an underlying iterator.
* It also stores the underlying container type in its type so that algorithms
* can optimize based on the underlying container type, and to avoid inadvertently
* mixing iterators coming from different containers..
*/
template <class _Container, class _Iterator>
struct __ra_iterator {
private:
_Iterator __iter_;

friend _Container;

// note: checking the concept random_access_iterator does not work for incomplete types
static_assert(_IsSame<typename iterator_traits<_Iterator>::iterator_category, random_access_iterator_tag>::value,
"Underlying iterator must be a random access iterator");

public:
using iterator_concept = random_access_iterator_tag; // deliberately lower contiguous_iterator
using iterator_category = random_access_iterator_tag;
using value_type = iter_value_t<_Iterator>;
using difference_type = iter_difference_t<_Iterator>;

_LIBCPP_HIDE_FROM_ABI __ra_iterator()
requires is_default_constructible_v<_Iterator>
= default;

_LIBCPP_HIDE_FROM_ABI explicit constexpr __ra_iterator(_Iterator __iter) : __iter_(std::move(__iter)) {}

_LIBCPP_HIDE_FROM_ABI constexpr _Iterator __base() const noexcept(noexcept(_Iterator(__iter_))) { return __iter_; }

_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__iter_; }
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator->() const
requires requires { __iter_.operator->(); }
{
return __iter_.operator->();
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator& operator++() {
++__iter_;
return *this;
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator operator++(int) {
__ra_iterator __tmp(*this);
++*this;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator& operator--() {
--__iter_;
return *this;
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator operator--(int) {
__ra_iterator __tmp(*this);
--*this;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator& operator+=(difference_type __x) {
__iter_ += __x;
return *this;
}

_LIBCPP_HIDE_FROM_ABI constexpr __ra_iterator& operator-=(difference_type __x) {
__iter_ -= __x;
return *this;
}

_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const { return *(*this + __n); }

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __ra_iterator& __x, const __ra_iterator& __y) {
return __x.__iter_ == __y.__iter_;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __ra_iterator& __x, const __ra_iterator& __y) {
return __x.__iter_ < __y.__iter_;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __ra_iterator& __x, const __ra_iterator& __y) {
return __y < __x;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __ra_iterator& __x, const __ra_iterator& __y) {
return !(__y < __x);
}

_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __ra_iterator& __x, const __ra_iterator& __y) {
return !(__x < __y);
}

_LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __ra_iterator& __x, const __ra_iterator& __y)
requires three_way_comparable<_Iterator>
{
return __x.__iter_ <=> __y.__iter_;
}
Comment on lines +124 to +128
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the requires here is unnecessary. As specified in [container.reqmts]/39, /40, and /41, use of non-three-way-comparable iterator here is invalid, and the return type is required to be std::strong_ordering.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Hui was explaining to me just now, these requirements are added by containers. But if we want to reuse this iterator type in other contexts, these requirements might not apply and so we are probably better off with the more general implementation.


_LIBCPP_HIDE_FROM_ABI friend constexpr __ra_iterator operator+(const __ra_iterator& __i, difference_type __n) {
auto __tmp = __i;
__tmp += __n;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr __ra_iterator operator+(difference_type __n, const __ra_iterator& __i) {
return __i + __n;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr __ra_iterator operator-(const __ra_iterator& __i, difference_type __n) {
auto __tmp = __i;
__tmp -= __n;
return __tmp;
}

_LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __ra_iterator& __x, const __ra_iterator& __y) {
return __x.__iter_ - __y.__iter_;
}
};

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER >= 23

_LIBCPP_POP_MACROS

#endif // _LIBCPP___FLAT_SET_RA_ITERATOR_H
59 changes: 59 additions & 0 deletions libcxx/include/flat_set
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_FLAT_SET
#define _LIBCPP_FLAT_SET

/*
Header <flat_set> synopsis

#include <compare> // see [compare.syn]
#include <initializer_list> // see [initializer.list.syn]

namespace std {
// [flat.set], class template flat_set
template<class Key, class Compare = less<Key>, class KeyContainer = vector<Key>>
class flat_set;

struct sorted_unique_t { explicit sorted_unique_t() = default; };
inline constexpr sorted_unique_t sorted_unique{};

template<class Key, class Compare, class KeyContainer, class Allocator>
struct uses_allocator<flat_set<Key, Compare, KeyContainer>, Allocator>;

// [flat.set.erasure], erasure for flat_set
template<class Key, class Compare, class KeyContainer, class Predicate>
typename flat_set<Key, Compare, KeyContainer>::size_type
erase_if(flat_set<Key, Compare, KeyContainer>& c, Predicate pred);
}
*/

#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
# include <__cxx03/__config>
#else
# include <__config>

# if _LIBCPP_STD_VER >= 23
# include <__flat_map/sorted_unique.h>
# include <__flat_set/flat_set.h>
# endif

// for feature-test macros
# include <version>

// standard required includes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about implementing LWG3987 (#105309)? Is it already implemented by the complicated transitive inclusions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this touches flat_map as well. I'd like to do it in a separate patch and make this patch flat_set only

# include <compare>
# include <initializer_list>

# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

#endif // _LIBCPP_FLAT_SET
13 changes: 13 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,19 @@ module std [system] {
export *
}

module flat_set {
module flat_set {
header "__flat_set/flat_set.h"
export std.vector.vector
export std.vector.fwd
}
module ra_iterator { header "__flat_set/ra_iterator.h" }

header "flat_set"
export std.flat_map.sorted_unique
export *
}

module format {
module buffer {
header "__format/buffer.h"
Expand Down
3 changes: 0 additions & 3 deletions libcxx/modules/std.compat.cppm.in
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ module;
# if __has_include(<debugging>)
# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<debugging>)
# if __has_include(<flat_set>)
# error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<flat_set>)
# if __has_include(<generator>)
# error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<generator>)
Expand Down
4 changes: 1 addition & 3 deletions libcxx/modules/std.cppm.in
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ module;
#include <expected>
#include <filesystem>
#include <flat_map>
#include <flat_set>
#include <format>
#include <forward_list>
#if _LIBCPP_HAS_LOCALIZATION
Expand Down Expand Up @@ -162,9 +163,6 @@ module;
# if __has_include(<debugging>)
# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<debugging>)
# if __has_include(<flat_set>)
# error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<flat_set>)
# if __has_include(<generator>)
# error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<generator>)
Expand Down
4 changes: 3 additions & 1 deletion libcxx/modules/std/flat_set.inc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//

export namespace std {
#if 0
#if _LIBCPP_STD_VER >= 23
// [flat.set], class template flat_­set
using std::flat_set;

Expand All @@ -19,7 +19,9 @@ export namespace std {

// [flat.set.erasure], erasure for flat_­set
using std::erase_if;
#endif // _LIBCPP_STD_VER >= 23

#if 0
// [flat.multiset], class template flat_­multiset
using std::flat_multiset;

Expand Down
Loading