blob: 981969cb6142c4c9d3528ab4ecb8acdeddedcb8a [file] [log] [blame]
// { dg-do compile { target c++26 } }
#include <concepts>
#include <format>
#include <iterator>
#include <optional>
#include <ranges>
#include <string_view>
#include <vector>
#include <testsuite_hooks.h>
template<typename T>
constexpr
void
test_range_concepts()
{
using O = std::optional<T>;
static_assert(std::ranges::contiguous_range<O>);
static_assert(std::ranges::sized_range<O>);
static_assert(std::ranges::common_range<O>);
// an optional<T&> is borrowed range
constexpr bool is_ref_opt = std::is_reference_v<T>;
static_assert(std::ranges::borrowed_range<O> == is_ref_opt);
// an optional<const T> is not assignable, and therefore does not satisfy ranges::view
constexpr bool is_const_opt = std::is_const_v<T>;
static_assert(std::ranges::view<O> == !is_const_opt);
static_assert(std::ranges::viewable_range<O> == !is_const_opt);
}
template<typename O>
constexpr
void
test_iterator_concepts()
{
using T = typename O::value_type;
using iterator = typename O::iterator;
static_assert(std::contiguous_iterator<iterator>);
static_assert(std::is_same_v<typename std::iterator_traits<iterator>::value_type, std::remove_cv_t<T>>);
static_assert(std::is_same_v<std::iter_value_t<iterator>, std::remove_cv_t<T>>);
static_assert(std::is_same_v<typename std::iterator_traits<iterator>::reference, T&>);
static_assert(std::is_same_v<std::iter_reference_t<iterator>, T&>);
}
template<typename O>
constexpr
void
test_const_iterator_concepts()
{
using T = typename O::value_type;
using const_iterator = typename O::const_iterator;
static_assert(std::contiguous_iterator<const_iterator>);
static_assert(std::is_same_v<typename std::iterator_traits<const_iterator>::value_type, std::remove_cv_t<T>>);
static_assert(std::is_same_v<std::iter_value_t<const_iterator>, std::remove_cv_t<T>>);
static_assert(std::is_same_v<typename std::iterator_traits<const_iterator>::reference, const T&>);
static_assert(std::is_same_v<std::iter_reference_t<const_iterator>, const T&>);
}
template<typename T>
constexpr
void
test_empty()
{
using O = std::optional<T>;
O empty;
VERIFY(!empty);
VERIFY(empty.begin() == empty.end());
VERIFY(std::as_const(empty).begin() == std::as_const(empty).end());
VERIFY(std::ranges::empty(empty));
VERIFY(std::ranges::empty(std::as_const(empty)));
VERIFY(std::ranges::empty(empty | std::views::as_const));
VERIFY(std::ranges::size(empty) == 0);
VERIFY(std::ranges::size(std::as_const(empty)) == 0);
size_t count = 0;
for (const auto& x : empty)
++count;
VERIFY(count == 0);
}
template<typename T>
constexpr
void
test_non_empty(const T& value)
{
using O = std::optional<T>;
using V = typename O::value_type;
O non_empty(std::in_place, value);
VERIFY(non_empty);
if constexpr (!std::is_array_v<V>)
VERIFY(*non_empty == value);
VERIFY(non_empty.begin() != non_empty.end());
VERIFY(non_empty.begin() < non_empty.end());
VERIFY(std::as_const(non_empty).begin() != std::as_const(non_empty).end());
VERIFY(std::as_const(non_empty).begin() < std::as_const(non_empty).end());
VERIFY(!std::ranges::empty(non_empty));
VERIFY(!std::ranges::empty(std::as_const(non_empty)));
VERIFY(!std::ranges::empty(non_empty | std::views::as_const));
VERIFY(std::ranges::size(non_empty) == 1);
VERIFY(std::ranges::size(std::as_const(non_empty)) == 1);
size_t count = 0;
for (const auto& x : non_empty)
++count;
VERIFY(count == 1);
if constexpr (!std::is_const_v<V> && !std::is_array_v<V>) {
for (auto& x : non_empty)
x = V{};
VERIFY(non_empty);
VERIFY(*non_empty == V{});
}
}
template<typename T>
constexpr
void
test(const T& value)
{
using O = std::optional<T>;
test_range_concepts<T>();
test_iterator_concepts<O>();
if constexpr (!std::is_reference_v<T>)
test_const_iterator_concepts<O>();
test_empty<T>();
test_non_empty<T>(value);
static_assert(!std::formattable<O, char>);
static_assert(!std::formattable<O, wchar_t>);
static_assert(std::format_kind<O> == std::range_format::disabled);
}
constexpr
void
range_chain_example() // from P3168
{
std::vector<int> v{2, 3, 4, 5, 6, 7, 8, 9, 1};
auto test = [](int i) -> std::optional<int> {
switch(i) {
case 1:
case 3:
case 7:
case 9:
return i * 2;
default:
return {};
}
};
auto result = v
| std::views::transform(test)
| std::views::filter([](auto x) { return bool(x); })
| std::views::transform([](auto x){ return *x; })
| std::ranges::to<std::vector>();
bool ok = result == std::vector<int>{6, 14, 18, 2};
VERIFY(ok);
}
template<typename T>
constexpr void test_not_range()
{
static_assert(!requires { typename std::optional<T>::iterator; });
static_assert(!requires(std::optional<T> o) { o.begin(); });
static_assert(!requires(std::optional<T> o) { o.end(); });
};
constexpr
bool
all_tests()
{
test(42);
int i = 42;
int arr[10]{};
test(&i);
test(std::string_view("test"));
test(std::vector<int>{1, 2, 3, 4});
test(std::optional<int>(42));
test<const int>(42);
test<int&>(i);
test<const int&>(i);
test<int(&)[10]>(arr);
test<const int(&)[10]>(arr);
test_not_range<void(&)()>();
test_not_range<void(&)(int)>();
test_not_range<int(&)[]>();
test_not_range<const int(&)[]>();
range_chain_example();
return true;
}
static_assert(all_tests());