Skip to content

Commit

Permalink
implement std::views::istream
Browse files Browse the repository at this point in the history
implement `std::ranges::basic_istream_view` and `std::views::istream`. Although the view itself is constexpr,
the constructor argument is a base class std::istream where its ctor/dtor are not constexpr. So no tests are performed in
constexpr

Differential Revision: https://reviews.llvm.org/D133317
  • Loading branch information
huixie90 committed Oct 6, 2022
1 parent 6e7df70 commit 96a509b
Show file tree
Hide file tree
Showing 22 changed files with 885 additions and 5 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Issues.csv
Expand Up @@ -296,6 +296,6 @@
"`3393 <https://wg21.link/LWG3393>`__","Missing/incorrect feature test macro for coroutines","Prague","|Complete|","14.0"
"`3395 <https://wg21.link/LWG3395>`__","Definition for three-way comparison needs to be updated (US 152)","Prague","","","|spaceship|"
"`3396 <https://wg21.link/LWG3396>`__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","",""
"`3397 <https://wg21.link/LWG3397>`__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","","","|ranges|"
"`3397 <https://wg21.link/LWG3397>`__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","|Complete|","16.0","|ranges|"
"`3398 <https://wg21.link/LWG3398>`__","``tuple_element_t``\ is also wrong for ``const subrange``\ ","Prague","|Complete|","14.0","|ranges|"
"`3446 <https://wg21.link/LWG3446>`__","``indirectly_readable_traits``\ ambiguity for types with both ``value_type``\ and ``element_type``\ ","November virtual meeting","|Complete|","13.0"
6 changes: 3 additions & 3 deletions libcxx/docs/Status/Cxx20Papers.csv
Expand Up @@ -108,7 +108,7 @@
"`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
"`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
"`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"
"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors","Cologne","",""
"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors","Cologne","|In Progress|",""
"`P1065R2 <https://wg21.link/P1065R2>`__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0"
"`P1135R6 <https://wg21.link/P1135R6>`__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0"
"`P1207R4 <https://wg21.link/P1207R4>`__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0"
Expand All @@ -124,7 +124,7 @@
"`P1523R1 <https://wg21.link/P1523R1>`__","LWG","Views and Size Types","Cologne","|Complete|","15.0"
"`P1612R1 <https://wg21.link/P1612R1>`__","LWG","Relocate Endian's Specification","Cologne","|Complete|","10.0"
"`P1614R2 <https://wg21.link/P1614R2>`__","LWG","The Mothership has Landed","Cologne","|In Progress|",""
"`P1638R1 <https://wg21.link/P1638R1>`__","LWG","basic_istream_view::iterator should not be copyable","Cologne","",""
"`P1638R1 <https://wg21.link/P1638R1>`__","LWG","basic_istream_view::iterator should not be copyable","Cologne","|Complete|","16.0"
"`P1643R1 <https://wg21.link/P1643R1>`__","LWG","Add wait/notify to atomic_ref","Cologne","",""
"`P1644R0 <https://wg21.link/P1644R0>`__","LWG","Add wait/notify to atomic<shared_ptr>","Cologne","",""
"`P1650R0 <https://wg21.link/P1650R0>`__","LWG","Output std::chrono::days with 'd' suffix","Cologne","",""
Expand Down Expand Up @@ -204,4 +204,4 @@
"`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","",""
"`P2415R2 <https://wg21.link/P2415R2>`__","LWG","What is a ``view``","October 2021","|Complete|","14.0"
"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Complete|","15.0"
"`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","",""
"`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","|Complete|","16.0"
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2bIssues.csv
Expand Up @@ -121,7 +121,7 @@
`3563 <https://wg21.link/LWG3563>`__,"``keys_view`` example is broken","October 2021","","","|ranges|"
`3566 <https://wg21.link/LWG3566>`__,"Constraint recursion for ``operator<=>(optional<T>, U)``","October 2021","","","|spaceship|"
`3567 <https://wg21.link/LWG3567>`__,"Formatting move-only iterators take two","October 2021","","","|format|"
`3568 <https://wg21.link/LWG3568>`__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","","","|ranges|"
`3568 <https://wg21.link/LWG3568>`__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|"
`3570 <https://wg21.link/LWG3570>`__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","",""
`3571 <https://wg21.link/LWG3571>`__,"``flush_emit`` should set ``badbit`` if the ``emit`` call fails","October 2021","",""
`3572 <https://wg21.link/LWG3572>`__,"``copyable-box`` should be fully ``constexpr``","October 2021","|Complete|","14.0","|ranges|"
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Expand Up @@ -481,6 +481,7 @@ set(files
__ranges/enable_view.h
__ranges/filter_view.h
__ranges/iota_view.h
__ranges/istream_view.h
__ranges/join_view.h
__ranges/lazy_split_view.h
__ranges/non_propagating_cache.h
Expand Down
139 changes: 139 additions & 0 deletions libcxx/include/__ranges/istream_view.h
@@ -0,0 +1,139 @@
// -*- 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___RANGES_ISTREAM_VIEW_H
#define _LIBCPP___RANGES_ISTREAM_VIEW_H

#include <__concepts/constructible.h>
#include <__concepts/derived_from.h>
#include <__concepts/movable.h>
#include <__config>
#include <__iterator/default_sentinel.h>
#include <__iterator/iterator_traits.h>
#include <__memory/addressof.h>
#include <__ranges/view_interface.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
#include <cstddef>
#include <iosfwd>

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

#if _LIBCPP_STD_VER >= 20

_LIBCPP_BEGIN_NAMESPACE_STD

namespace ranges {

template <class _Val, class _CharT, class _Traits>
concept __stream_extractable = requires(basic_istream<_CharT, _Traits>& __is, _Val& __t) { __is >> __t; };

template <movable _Val, class _CharT, class _Traits = char_traits<_CharT>>
requires default_initializable<_Val> && __stream_extractable<_Val, _CharT, _Traits>
class basic_istream_view : public view_interface<basic_istream_view<_Val, _CharT, _Traits>> {
public:
_LIBCPP_HIDE_FROM_ABI constexpr explicit basic_istream_view(basic_istream<_CharT, _Traits>& __stream)
: __stream_(std::addressof(__stream)) {}

_LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
*__stream_ >> __value_;
return __iterator{*this};
}

_LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }

private:
class __iterator;

basic_istream<_CharT, _Traits>* __stream_;
_LIBCPP_NO_UNIQUE_ADDRESS _Val __value_ = _Val();
};

template <movable _Val, class _CharT, class _Traits>
requires default_initializable<_Val> && __stream_extractable<_Val, _CharT, _Traits>
class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
public:
using iterator_concept = input_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = _Val;

_LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view& __parent) noexcept
: __parent_(std::addressof(__parent)) {}

__iterator(const __iterator&) = delete;
_LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;

__iterator& operator=(const __iterator&) = delete;
_LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&&) = default;

_LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
*__parent_->__stream_ >> __parent_->__value_;
return *this;
}

_LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }

_LIBCPP_HIDE_FROM_ABI _Val& operator*() const { return __parent_->__value_; }

_LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __x, default_sentinel_t) {
return !*__x.__get_parent_stream();
}

private:
basic_istream_view* __parent_;

_LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
return __parent_->__stream_;
}
};

template <class _Val>
using istream_view = basic_istream_view<_Val, char>;

#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class _Val>
using wistream_view = basic_istream_view<_Val, wchar_t>;
#endif

namespace views {
namespace __istream {

// clang-format off
template <class _Tp>
struct __fn {
template <class _Up, class _UnCVRef = remove_cvref_t<_Up>>
requires derived_from<_UnCVRef, basic_istream<typename _UnCVRef::char_type,
typename _UnCVRef::traits_type>>
_LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Up&& __u) const
noexcept(noexcept(basic_istream_view<_Tp, typename _UnCVRef::char_type,
typename _UnCVRef::traits_type>(std::forward<_Up>(__u))))
-> decltype( basic_istream_view<_Tp, typename _UnCVRef::char_type,
typename _UnCVRef::traits_type>(std::forward<_Up>(__u)))
{ return basic_istream_view<_Tp, typename _UnCVRef::char_type,
typename _UnCVRef::traits_type>(std::forward<_Up>(__u));
}
};
// clang-format on

} // namespace __istream

inline namespace __cpo {
template <class _Tp>
inline constexpr auto istream = __istream::__fn<_Tp>{};
} // namespace __cpo
} // namespace views

} // namespace ranges

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER >= 20

#endif // _LIBCPP___RANGES_ISTREAM_VIEW_H
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap.in
Expand Up @@ -1014,6 +1014,10 @@ module std [system] {
module enable_view { private header "__ranges/enable_view.h" }
module filter_view { private header "__ranges/filter_view.h" }
module iota_view { private header "__ranges/iota_view.h" }
module istream_view {
@requires_LIBCXX_ENABLE_LOCALIZATION@
private header "__ranges/istream_view.h"
}
module join_view { private header "__ranges/join_view.h" }
module lazy_split_view { private header "__ranges/lazy_split_view.h" }
module non_propagating_cache { private header "__ranges/non_propagating_cache.h" }
Expand Down
17 changes: 17 additions & 0 deletions libcxx/include/ranges
Expand Up @@ -228,6 +228,19 @@ namespace std::ranges {
inline constexpr unspecified lazy_split = unspecified;
}
// [range.istream], istream view
template<movable Val, class CharT, class Traits = char_traits<CharT>>
requires see below
class basic_istream_view;
template<class Val>
using istream_view = basic_istream_view<Val, char>;
template<class Val>
using wistream_view = basic_istream_view<Val, wchar_t>;
namespace views { template<class T> inline constexpr unspecified istream = unspecified; }
// [range.zip], zip view
template<input_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
Expand Down Expand Up @@ -305,6 +318,10 @@ namespace std {
#include <type_traits>
#include <version>

#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#include <__ranges/istream_view.h>
#endif

// standard-mandated includes

// [ranges.syn]
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/libcxx/private_headers.verify.cpp
Expand Up @@ -512,6 +512,7 @@ END-SCRIPT
#include <__ranges/enable_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_view.h'}}
#include <__ranges/filter_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/filter_view.h'}}
#include <__ranges/iota_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/iota_view.h'}}
#include <__ranges/istream_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/istream_view.h'}}
#include <__ranges/join_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/join_view.h'}}
#include <__ranges/lazy_split_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/lazy_split_view.h'}}
#include <__ranges/non_propagating_cache.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/non_propagating_cache.h'}}
Expand Down
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// 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: no-localization
// UNSUPPORTED: c++03, c++11, c++14, c++17

// clang-cl and cl currently don't support [[no_unique_address]]
// XFAIL: msvc

// Test the libc++ extension that the value stored in `std::ranges::istream_view` has been marked
// as _LIBCPP_NO_UNIQUE_ADDRESS

#include <istream>
#include <ranges>

struct Empty {
friend std::istream& operator>>(std::istream& i, Empty const&) { return i; }
};

static_assert(sizeof(std::ranges::istream_view<Empty>) == sizeof(void*));

@@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
//
// 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: no-localization
// UNSUPPORTED: c++03, c++11, c++14, c++17

// constexpr auto begin();

#include <cassert>
#include <ranges>
#include <sstream>

#include "test_macros.h"
#include "utils.h"

template <class T>
concept HasBegin = requires(T t) { t.begin(); };

static_assert(HasBegin<std::ranges::istream_view<int>>);
static_assert(!HasBegin<const std::ranges::istream_view<int>>);

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(HasBegin<std::ranges::wistream_view<int>>);
static_assert(!HasBegin<const std::ranges::wistream_view<int>>);
#endif

template <class CharT>
void test() {
// begin should read the first element
{
auto iss = make_string_stream<CharT>("12 3");
std::ranges::basic_istream_view<int, CharT> isv{iss};
auto it = isv.begin();
assert(*it == 12);
}

// empty stream
{
auto iss = make_string_stream<CharT>("");
std::ranges::basic_istream_view<int, CharT> isv{iss};
auto it = isv.begin();
assert(it == isv.end());
}
}

int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}
@@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// 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: no-localization
// UNSUPPORTED: c++03, c++11, c++14, c++17

// std::views::istream<T>

#include <cassert>
#include <ranges>
#include <sstream>

#include "test_macros.h"
#include "utils.h"

static_assert(!std::is_invocable_v<decltype((std::views::istream<int>))>);
static_assert(std::is_invocable_v<decltype((std::views::istream<int>)), std::istream&>);
static_assert(!std::is_invocable_v<decltype((std::views::istream<int>)), const std::istream&>);
static_assert(!std::is_invocable_v<decltype((std::views::istream<int>)), int>);

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(std::is_invocable_v<decltype((std::views::istream<int>)), std::wistream&>);
static_assert(!std::is_invocable_v<decltype((std::views::istream<int>)), const std::wistream&>);
#endif

template <class CharT>
void test() {
auto iss = make_string_stream<CharT>("12 3");
auto isv = std::views::istream<int>(iss);
auto it = isv.begin();
assert(*it == 12);
}

int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}

0 comments on commit 96a509b

Please sign in to comment.