Skip to content

Commit c9dffa3

Browse files
committed
implement and test 'from_range'
1 parent 880743f commit c9dffa3

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ add_executable(unit_tests ${SOURCES}
4646
test/prototype/crtp_take.test.cpp
4747
test/duplicate.test.cpp
4848
test/from_vector.test.cpp
49+
test/from_range.test.cpp
4950
test/filter.test.cpp
5051
test/main.test.cpp
5152
test/map.test.cpp

src/source/from_range.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2025, Marco Nikander
2+
3+
#pragma once
4+
#include <iterator>
5+
#include <utility> // forward
6+
#include "../datatypes.hpp"
7+
8+
namespace seq {
9+
namespace {
10+
11+
// Is there a better way to make iteration end, than using the limit in the 'yield' function?
12+
// A later stage could communicate when to terminate the iteration, via a return value.
13+
// This would complicate the design though, and make it similar to my prior design.
14+
// Let's try it without back-communication, and see if we get a simpler design, which actually works.
15+
16+
template <typename I, typename S>
17+
struct FromRangeImpl {
18+
using Input = typename std::iterator_traits<I>::value_type;
19+
20+
FromRangeImpl(I first, I last, S successor) : _first{first}, _current{first}, _last{last}, _successor{successor} {}
21+
22+
// yield runs a fixed number of times, unless it gets a HALT signal beforehand
23+
// yield can be called again, and it will continue where it left off last time, even if it stopped due to a HALT
24+
Status yield(uint64_t const count = 1u) {
25+
Status status = OK;
26+
for(uint64_t i = 0; i < count && _current != _last && status == OK; ++i) {
27+
status = _successor.receive(Input{*_current}); // copy the element to get an rvalue :(
28+
++_current;
29+
}
30+
if (_current != _last) {
31+
status = HALT;
32+
}
33+
return status;
34+
}
35+
36+
Status run() {
37+
Status status = OK;
38+
while(_current != _last && status == OK) {
39+
status = _successor.receive(Input{*_current}); // copy the element to get an rvalue :(
40+
++_current;
41+
}
42+
status = HALT;
43+
return status;
44+
}
45+
46+
I const _first;
47+
I _current;
48+
I const _last;
49+
S _successor;
50+
};
51+
52+
template <typename I, typename S>
53+
auto from_range(I first, I last, S successor) {
54+
return FromRangeImpl<I, S>{first, last, successor};
55+
}
56+
57+
template <typename R, typename S>
58+
auto from_range(R const& range, S successor) {
59+
return FromRangeImpl<typename R::const_iterator, S>{range.cbegin(), range.cend(), successor};
60+
}
61+
62+
}
63+
}

test/from_range.test.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2025, Marco Nikander
2+
3+
#include <functional>
4+
#include <vector>
5+
#include <gtest/gtest.h>
6+
#include "../src/sink/to_range.hpp"
7+
#include "../src/sink/to_value.hpp"
8+
#include "../src/source/from_range.hpp"
9+
#include "../src/stage/filter.hpp"
10+
#include "../src/stage/map.hpp"
11+
#include "../src/stage/reduce.hpp"
12+
#include "../src/stage/take.hpp"
13+
#include "../src/datatypes.hpp"
14+
15+
TEST(from_range, sum)
16+
{
17+
using namespace seq;
18+
std::vector<i64> const input{0, 1, 2, 4, 8};
19+
i64 result = 0;
20+
21+
// pipeline stages, from last to first
22+
auto sink = to_value(result);
23+
auto stage = reduce<i64>(std::plus<i64>{}, 0L, sink);
24+
auto sequence = from_range(input.cbegin(), input.cend(), stage);
25+
26+
sequence.yield(5);
27+
EXPECT_EQ(result, 15);
28+
}
29+
30+
TEST(from_range, nested_pipeline)
31+
{
32+
using namespace seq;
33+
std::vector<i64> const input{0, 1, 2, 4, 8};
34+
i64 result = 0;
35+
36+
// pipeline, nested in order
37+
auto sequence =
38+
from_range(input,
39+
reduce<i64>(std::plus<i64>{}, 0L,
40+
to_value(result)));
41+
42+
sequence.yield(5);
43+
EXPECT_EQ(result, 15);
44+
}
45+
46+
TEST(from_range, map_filter_reduce) {
47+
using namespace seq;
48+
49+
auto plusTwo = [](i64 i){ return i + 2; };
50+
auto isEven = [](i64 i){ return i % 2 == 0; };
51+
std::vector<i64> const input{0, 1, 2, 4, 8};
52+
i64 result = 0;
53+
54+
// pipeline, nested in order
55+
auto sequence =
56+
from_range(input,
57+
map<i64>(plusTwo,
58+
filter<i64>(isEven,
59+
reduce<i64>(std::plus<i64>{}, 0L,
60+
take<i64>(4,
61+
to_value(result))))));
62+
sequence.run(); // sum of (2, 4, 6, 10)
63+
EXPECT_EQ(result, 22);
64+
}

0 commit comments

Comments
 (0)