Skip to content

Commit 688e11c

Browse files
committed
[feature] radr::istream
1 parent 14909a6 commit 688e11c

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

docs/implementation_status.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ All range adaptors from this library are available in C++20, although `radr::as_
5151
| Range factories | Equivalent in `std::` | Remarks |
5252
|-------------------------------|-------------------------|------------------------------------------------------|
5353
| `radr::empty<T>` | `std::views::empty` | |
54+
| `radr::istream<Val>` | `std::views::istream` | |
5455
| `radr::repeat(val, bound)` | `std::views::repeat` | allows indirect storage and static bounds |
5556
| `radr::single(val)` | `std::views::single` | allows indirect storage |
5657

include/radr/factory/istream.hpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Copyright (c) 2023-2025 Hannes Hauswedell
5+
//
6+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
7+
// See the LICENSE file for details.
8+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#pragma once
13+
14+
#include <istream>
15+
16+
#include "radr/generator.hpp"
17+
18+
namespace radr::detail
19+
{
20+
21+
template <class Val, class CharT, class Traits>
22+
concept stream_extractable = requires(std::basic_istream<CharT, Traits> & is, Val & t) { is >> t; };
23+
24+
}
25+
26+
namespace radr
27+
{
28+
29+
/*!\brief A range factory that produces a range over the values from an istream.
30+
* \tparam Val The type to extract into.
31+
* \tparam CharT The character type of the stream.
32+
* \tparam Traits CharTraits of the stream.
33+
* \param[in,out] stream The stream to extract from.
34+
* \details
35+
*
36+
* Values are extracted from the stream as if by `stream >> val;`.
37+
*
38+
* The returned range is a specialisation of radr::generator, i.e. it is always a move-only, single-pass range.
39+
*
40+
*/
41+
template <typename Val, class CharT = char, class Traits = std::char_traits<CharT>>
42+
generator<Val &, Val> istream(std::basic_istream<CharT, Traits> & stream)
43+
{
44+
static_assert(std::movable<Val>, "The constraints for the Val type of radr::istream were not met.");
45+
static_assert(detail::stream_extractable<Val, CharT, Traits>,
46+
"The Val type cannot be extracted from the stream passed to radr::istream.");
47+
48+
Val value;
49+
stream >> value;
50+
51+
while (stream)
52+
{
53+
co_yield value;
54+
stream >> value;
55+
}
56+
}
57+
58+
} // namespace radr

tests/unit/factory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
radr_unit_test(empty)
2+
radr_unit_test(istream)
23
radr_unit_test(single)
34
radr_unit_test(repeat)

tests/unit/factory/istream.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include <sstream>
2+
#include <string>
3+
#include <vector>
4+
5+
#include <gtest/gtest.h>
6+
#include <radr/test/gtest_helpers.hpp>
7+
8+
#include <radr/factory/istream.hpp>
9+
10+
TEST(ViewsIstreamConceptsTest, ConceptValidation)
11+
{
12+
std::istringstream input("1 2 3");
13+
auto view = radr::istream<int>(input);
14+
15+
// Checking the relevant concepts
16+
EXPECT_TRUE(std::ranges::input_range<decltype(view)>);
17+
EXPECT_TRUE(std::ranges::view<decltype(view)>);
18+
EXPECT_TRUE(std::is_move_constructible_v<decltype(view)>);
19+
EXPECT_FALSE(std::ranges::common_range<decltype(view)>);
20+
EXPECT_FALSE(std::ranges::sized_range<decltype(view)>);
21+
EXPECT_FALSE(std::ranges::contiguous_range<decltype(view)>);
22+
EXPECT_FALSE(std::is_default_constructible_v<decltype(view)>);
23+
EXPECT_FALSE(std::ranges::random_access_range<decltype(view)>);
24+
}
25+
26+
TEST(istream, EmptyInput)
27+
{
28+
std::istringstream input("");
29+
auto view = radr::istream<int>(input);
30+
EXPECT_TRUE(view.begin() == view.end());
31+
}
32+
33+
TEST(istream, ReadIntegers)
34+
{
35+
std::istringstream input("10 20 30");
36+
auto view = radr::istream<int>(input);
37+
EXPECT_RANGE_EQ(view, (std::vector<int>{10, 20, 30}));
38+
}
39+
40+
TEST(istream, ReadStrings)
41+
{
42+
std::istringstream input("hello world test");
43+
auto view = radr::istream<std::string>(input);
44+
EXPECT_RANGE_EQ(view, (std::vector<std::string>{"hello", "world", "test"}));
45+
}
46+
47+
TEST(istream, StopOnMalformedData)
48+
{
49+
std::istringstream input("42 invalid 84");
50+
auto view = radr::istream<int>(input);
51+
EXPECT_RANGE_EQ(view, (std::vector<int>{42}));
52+
}
53+
54+
TEST(istream, ReadChars)
55+
{
56+
std::istringstream input("hello world test");
57+
auto view = radr::istream<char>(input);
58+
EXPECT_RANGE_EQ(view, std::string{"helloworldtest"});
59+
}

0 commit comments

Comments
 (0)