Index: libcxx/include/iterator =================================================================== --- libcxx/include/iterator +++ libcxx/include/iterator @@ -399,6 +399,11 @@ // 24.8, container access: template constexpr auto size(const C& c) -> decltype(c.size()); // C++17 template constexpr size_t size(const T (&array)[N]) noexcept; // C++17 + +template constexpr auto ssize(const C& c) + -> common_type_t>; // C++20 +template constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept; // C++20 + template constexpr auto empty(const C& c) -> decltype(c.empty()); // C++17 template constexpr bool empty(const T (&array)[N]) noexcept; // C++17 template constexpr bool empty(initializer_list il) noexcept; // C++17 @@ -1858,6 +1863,18 @@ inline _LIBCPP_INLINE_VISIBILITY constexpr size_t size(const _Tp (&)[_Sz]) noexcept { return _Sz; } +#if _LIBCPP_STD_VER > 17 +template +inline _LIBCPP_INLINE_VISIBILITY +constexpr auto ssize(const _Cont& __c) +-> common_type_t> +{ return static_cast>>(__c.size()); } + +template +inline _LIBCPP_INLINE_VISIBILITY +constexpr ptrdiff_t ssize(const _Tp (&)[_Sz]) noexcept { return _Sz; } +#endif + template _LIBCPP_NODISCARD_AFTER_CXX17 inline _LIBCPP_INLINE_VISIBILITY constexpr auto empty(const _Cont& __c) Index: libcxx/test/std/iterators/iterator.container/ssize.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/iterators/iterator.container/ssize.pass.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// +// template constexpr auto ssize(const C& c) +// -> common_type_t>; // C++20 +// template constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept; // C++20 + +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + + +struct short_container { + uint16_t size() const { return 60000; } + }; + +template +void test_const_container(const C& c) +{ +// Can't say noexcept here because the container might not be + static_assert( std::is_signed_v, ""); + assert ( std::ssize(c) == static_cast(c.size())); +} + +template +void test_const_container(const std::initializer_list& c) +{ +// ASSERT_NOEXCEPT(std::size(c)); + static_assert( std::is_signed_v, ""); +// For some reason, there isn't a std::size() for initializer lists, so it uses the generic one + assert ( std::ssize(c) == static_cast(c.size())); +} + +template +void test_container(C& c) +{ +// Can't say noexcept here because the container might not be + static_assert( std::is_signed_v, ""); + assert ( std::ssize(c) == static_cast(c.size())); +} + +template +void test_container(std::initializer_list& c) +{ +// ASSERT_NOEXCEPT(std::size(c)); +// For some reason, there isn't a std::size() for initializer lists, so it uses the generic one + static_assert( std::is_signed_v, ""); + assert ( std::ssize(c) == static_cast(c.size())); +} + +template +void test_const_array(const T (&array)[Sz]) +{ + ASSERT_NOEXCEPT(std::ssize(array)); + assert ( std::ssize(array) == Sz ); +} + +int main(int, char**) +{ + std::vector v; v.push_back(1); + std::list l; l.push_back(2); + std::array a; a[0] = 3; + std::initializer_list il = { 4 }; + test_container ( v ); + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(v))); + test_container ( l ); + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(l))); + test_container ( a ); + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(a))); + test_container ( il ); + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(il))); + + test_const_container ( v ); + test_const_container ( l ); + test_const_container ( a ); + test_const_container ( il ); + + std::string_view sv{"ABC"}; + test_container ( sv ); + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(sv))); + test_const_container ( sv ); + + static constexpr int arrA [] { 1, 2, 3 }; + ASSERT_SAME_TYPE(ptrdiff_t, decltype(std::ssize(arrA))); + static_assert( std::is_signed_v, ""); + test_const_array ( arrA ); + +// From P1227R2: +// Note that the code does not just return the std::make_signed variant of +// the container's size() method, because it's conceivable that a container +// might choose to represent its size as a uint16_t, supporting up to +// 65,535 elements, and it would be a disaster for std::ssize() to turn a +// size of 60,000 into a size of -5,536. + + short_container sc; +// is the return type signed? Is it bit enough to hold 60K? +// is the "signed version" of sc.size() too small? + static_assert( std::is_signed_v< decltype(std::ssize(sc))>, ""); + static_assert( std::numeric_limits< decltype(std::ssize(sc))>::max() > 60000, ""); + static_assert( std::numeric_limits>::max() < 60000, ""); + assert (std::ssize(sc) == 60000); + + return 0; +}