[libcxx-commits] [libcxx] [libc++] Implement P3168R2: Give optional range support (PR #149441)
Hristo Hristov via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jul 19 04:23:56 PDT 2025
================
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// template <class T> class optional::iterator;
+// template <class T> class optional::const_iterator;
+// constexpr iterator optional::begin() noexcept;
+// constexpr iterator optional::end() noexcept;
+// constexpr const_iterator optional::begin() noexcept;
+// constexpr const_iterator optional::end() noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+constexpr bool test_concepts() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert(std::contiguous_iterator<decltype(nonconst_opt.begin())>);
+ assert(std::contiguous_iterator<decltype(nonconst_opt.end())>);
+ assert(std::random_access_iterator<decltype(nonconst_opt.begin())>);
+ assert(std::random_access_iterator<decltype(nonconst_opt.end())>);
+
+ assert(std::contiguous_iterator<decltype(opt.begin())>);
+ assert(std::contiguous_iterator<decltype(opt.end())>);
+ assert(std::random_access_iterator<decltype(opt.begin())>);
+ assert(std::random_access_iterator<decltype(opt.end())>);
+
+ return true;
+}
+
+constexpr bool test_types() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert((std::is_same_v<typename decltype(opt.begin())::value_type, char>));
+ assert((std::is_same_v<typename decltype(opt.begin())::reference, const char&>));
+ assert((std::is_same_v<typename decltype(nonconst_opt.begin())::value_type, char>));
+ assert((std::is_same_v<typename decltype(nonconst_opt.begin())::reference, char&>));
+ return true;
+}
+
+constexpr bool test_size() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> unengaged_opt;
+
+ assert(std::ranges::size(opt) == 1);
+ assert(std::ranges::size(unengaged_opt) == 0);
+
+ return true;
+}
+
+constexpr bool test_begin_end() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> unengaged_opt;
+
+ assert(opt.begin() != opt.end());
+ assert(unengaged_opt.begin() == unengaged_opt.end());
+ return true;
+}
+
+constexpr bool test_noexcept() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ ASSERT_NOEXCEPT(opt.begin());
+ ASSERT_NOEXCEPT(opt.end());
+ ASSERT_NOEXCEPT(nonconst_opt.begin());
+ ASSERT_NOEXCEPT(nonconst_opt.end());
+
+ return true;
+}
+
+constexpr bool test_value() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert(*(opt.begin()) == 'a');
+ assert(*(nonconst_opt.begin()) == 'n');
+ return true;
+}
+
+constexpr bool test_syn() {
+ assert(std::ranges::enable_view<std::optional<char>> == true);
+ assert(std::format_kind<std::optional<char>> == std::range_format::disabled);
+ return true;
+}
+
+constexpr bool test_reset() {
+ std::optional<int> val{1};
+ assert(val.begin() != val.end());
+ val.reset();
+ assert(val.begin() == val.end());
+ val = 1;
+ assert(val.begin() != val.end());
+ assert(*(val.begin()) == 1);
+ return true;
+}
+
+int main(int, char**) {
+ // 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator
+ {
+ test_concepts();
+ static_assert(test_concepts());
+ }
+
+ // 2: The value types and reference types for optional::iterator and optional::const_iterator are {_Tp, _Tp&} and {_Tp, const _Tp&} respectively
+ {
+ test_types();
+ static_assert(test_types());
+ }
+
+ // 3: The optional::begin() and optional::end() are marked noexcept.
+ {
+ test_noexcept();
+ static_assert(test_noexcept());
+ }
+
+ // 4: optionals that have a value have begin() != end(), whereas one that doesn't has begin() == end();
+ {
+ test_begin_end();
+ static_assert(test_begin_end());
+ }
+
+ // 5: The corresponding size for the following optionals is respected: has_value() == 1, !has_value() == 0
+ {
+ test_size();
+ static_assert(test_size());
+ }
+
+ // 6: Dereferencing an engaged optional's iterator returns the correct value.
+ {
+ test_value();
+ static_assert(test_value());
+ }
+
+ // 7: std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
+ {
+ test_syn();
+ static_assert(test_syn());
+ }
+
+ // 8: An optional with value that is reset will have a begin() == end(), then when it is reassigned a value, begin() != end(), and *begin() will contain the new value.
+ {
+ test_reset();
+ static_assert(test_reset());
+ }
+
+ return 0;
----------------
Zingam wrote:
I may have confused you. Here is an example of what I meant:
https://github.com/llvm/llvm-project/blob/main/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/increment.pass.cpp
https://github.com/llvm/llvm-project/blob/main/libcxx/test/std/strings/basic.string/string.nonmembers/string_op%2B/string.string_view.pass.cpp
You didn't need to split all test cases in separate functions. Of course what you do depends on the context.
https://github.com/llvm/llvm-project/pull/149441
More information about the libcxx-commits
mailing list