Skip to content

Commit e1d177e

Browse files
authored
Merge pull request #9 from jay-tux/feat/stream
Some more manipulators
2 parents 79af0c4 + e8ff499 commit e1d177e

File tree

12 files changed

+356
-73
lines changed

12 files changed

+356
-73
lines changed

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# fpgen
22
*Functional programming in C++ using C++20 coroutines*
3-
![](https://img.shields.io/badge/test_coverage-98%25-brightgreen)
3+
![](https://img.shields.io/badge/test_coverage-97%25-brightgreen)
44

55

66
## Aim
@@ -17,14 +17,9 @@ Currently supported features:
1717
- Lazy `map`ping over generators.
1818
- Lazy `zip`ping of generators.
1919
- Lazy `filter`ing of generators.
20-
21-
Planned/In progress features:
2220
- Commonly used aggregators:
2321
- Lazy `fold`ing of generators.
2422
- Lazy `sum`ming of generators.
25-
- Ergonomic improvements:
26-
- Enable streaming `<<` of generators.
27-
- Simple structure which allows generator construction, manipulation and aggregation using member functions.
2823

2924
Got another idea? Drop a feature request on the repo.
3025

inc/aggregators.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace fpgen {
2828
*/
2929
template <typename T, typename... Args,
3030
template <typename...> typename Container>
31-
Container<T, Args...> &aggregate_to(generator<T> &gen,
31+
Container<T, Args...> &aggregate_to(generator<T> gen,
3232
Container<T, Args...> &out) {
3333
while (gen) {
3434
out.push_back(gen());
@@ -55,7 +55,7 @@ Container<T, Args...> &aggregate_to(generator<T> &gen,
5555
template <typename TKey, typename TVal, typename... Args,
5656
template <typename...> typename Container>
5757
Container<TKey, TVal, Args...> &
58-
tup_aggregate_to(generator<std::tuple<TKey, TVal>> &gen,
58+
tup_aggregate_to(generator<std::tuple<TKey, TVal>> gen,
5959
Container<TKey, TVal, Args...> &out) {
6060
while (gen) {
6161
std::tuple<TKey, TVal> tup = gen();
@@ -75,7 +75,7 @@ tup_aggregate_to(generator<std::tuple<TKey, TVal>> &gen,
7575
* \param[in,out] gen The generator to iterate over.
7676
* \returns The amount of elements in the generator.
7777
*/
78-
template <typename T> size_t count(generator<T> &gen) {
78+
template <typename T> size_t count(generator<T> gen) {
7979
size_t cnt = 0;
8080
while (gen) {
8181
gen();
@@ -102,7 +102,7 @@ template <typename T> size_t count(generator<T> &gen) {
102102
* \returns The final accumulator value.
103103
*/
104104
template <typename TOut, typename TIn, typename Fun>
105-
TOut fold(generator<TIn> &gen, Fun folder) {
105+
TOut fold(generator<TIn> gen, Fun folder) {
106106
TOut value = {};
107107
while (gen) {
108108
value = folder(value, gen());
@@ -129,7 +129,7 @@ TOut fold(generator<TIn> &gen, Fun folder) {
129129
* \returns The final accumulator value.
130130
*/
131131
template <typename TOut, typename TIn, typename Fun>
132-
TOut fold(generator<TIn> &gen, Fun folder, TOut initial) {
132+
TOut fold(generator<TIn> gen, Fun folder, TOut initial) {
133133
TOut value(initial);
134134
while (gen) {
135135
value = folder(value, gen());
@@ -157,7 +157,7 @@ TOut fold(generator<TIn> &gen, Fun folder, TOut initial) {
157157
* now the output value.
158158
*/
159159
template <typename TOut, typename TIn, typename Fun>
160-
TOut &fold_ref(generator<TIn> &gen, Fun folder, TOut &initial) {
160+
TOut &fold_ref(generator<TIn> gen, Fun folder, TOut &initial) {
161161
while (gen) {
162162
initial = folder(initial, gen());
163163
}
@@ -175,7 +175,7 @@ TOut &fold_ref(generator<TIn> &gen, Fun folder, TOut &initial) {
175175
* \param[in,out] gen The generator to sum over.
176176
* \returns The sum of all elements.
177177
*/
178-
template <typename T> T sum(generator<T> &gen) {
178+
template <typename T> T sum(generator<T> gen) {
179179
T accum = {};
180180
while (gen) {
181181
accum = accum + gen();
@@ -195,7 +195,7 @@ template <typename T> T sum(generator<T> &gen) {
195195
* \param[in,out] gen The generator to iterate over.
196196
* \param[in] func The function to use.
197197
*/
198-
template <typename T, typename Fun> void foreach (generator<T> &gen, Fun func) {
198+
template <typename T, typename Fun> void foreach (generator<T> gen, Fun func) {
199199
while (gen) {
200200
func(gen());
201201
}

inc/fpgen.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#ifndef _FPGEN_MAIN
22
#define _FPGEN_MAIN
33

4+
#include "aggregators.hpp"
45
#include "generator.hpp"
56
#include "manipulators.hpp"
67
#include "sources.hpp"
8+
#include "stream.hpp"
79

810
#endif

inc/generator.hpp

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ template <typename T, enabler<T> = true> class generator {
100100
* coroutine calls `co_return`, the environment will call this function.
101101
* \param[in] v The value to set.
102102
*/
103-
void return_value(value_type v) { value = v; }
103+
// void return_value(value_type v) { value = v; }
104+
105+
void return_void() {}
106+
104107
/**
105108
* \brief Sets an intermediate value from the coroutine.
106109
*
@@ -155,12 +158,12 @@ template <typename T, enabler<T> = true> class generator {
155158
* This value should only be true when the underlying generator is
156159
* finished. Iterators are compared based on this value.
157160
*/
158-
bool is_finished;
161+
// bool is_finished;
159162
/**
160163
* \brief The last value from the coroutine (and thus, the value currently
161164
* held by the generator's promise).
162165
*/
163-
value_t value;
166+
// value_t value;
164167

165168
/**
166169
* \brief Constructs a new iterator from a source generator.
@@ -173,8 +176,7 @@ template <typename T, enabler<T> = true> class generator {
173176
* modified).
174177
* \param[in] is_finised Is this an `end` iterator?
175178
*/
176-
iterator_type(gen_t &source, bool is_finised)
177-
: source{source}, is_finished{is_finised} {
179+
iterator_type(gen_t &source, bool is_finised) : source{source} {
178180
if (!is_finised)
179181
++(*this);
180182
}
@@ -190,20 +192,15 @@ template <typename T, enabler<T> = true> class generator {
190192
* \param[in] is_finised Is this iterator finished?
191193
* \param[in] value The value this iterator should hold.
192194
*/
193-
iterator_type(gen_t &source, bool is_finished, value_t value)
194-
: source{source}, is_finished{is_finished}, value{value} {}
195+
iterator_type(gen_t &source) : source{source} {}
195196

196197
/**
197198
* \brief Steps the generator to the next value.
198199
* \returns A new iterator for this generator, with the same state.
199200
*/
200201
iter_t operator++() {
201-
if (!source)
202-
is_finished = true;
203-
value = source();
204-
if (!source)
205-
is_finished = true;
206-
return {source, is_finished, value};
202+
source.next();
203+
return {source};
207204
}
208205

209206
/**
@@ -219,19 +216,20 @@ template <typename T, enabler<T> = true> class generator {
219216
* \param[in] other The iterator to compare against.
220217
* \returns False if both are equal, otherwise true.
221218
*/
222-
bool operator!=(const iter_t &other) {
223-
return is_finished != other.is_finished;
224-
}
219+
bool operator!=(const iter_t &) { return static_cast<bool>(source); }
225220
/**
226221
* \brief Gets the current value from the iterator.
227222
* \returns The current value.
228223
*/
229-
value_t operator*() { return value; }
224+
value_t operator*() {
225+
source.contains = false;
226+
return source._h.promise().value;
227+
}
230228
/**
231229
* \brief Converts this iterator to the current value.
232230
* \returns The current value.
233231
*/
234-
operator value_t() { return value; }
232+
operator value_t() { return source._h.promise().value; }
235233
};
236234

237235
/**
@@ -250,12 +248,13 @@ template <typename T, enabler<T> = true> class generator {
250248
* This method should only be called from the environment.
251249
* \param[in] p The promise to use.
252250
*/
253-
generator(promise_type &p) : _h{handle_type::from_promise(p)} {}
251+
generator(promise_type &p)
252+
: _h{handle_type::from_promise(p)}, contains{false} {}
254253

255254
/**
256255
* \brief Copy-constructing generators results in undefined behaviour.
257256
*/
258-
generator(const generator &other) = delete;
257+
generator(const generator &other) = default;
259258
/**
260259
* \brief Moves the data from the other generator into this one.
261260
* \param[in,out] other The other generator.
@@ -265,7 +264,7 @@ template <typename T, enabler<T> = true> class generator {
265264
/**
266265
* \brief Copy-assigning generators results in undefined behaviour.
267266
*/
268-
generator &operator=(const generator &other) = delete;
267+
generator &operator=(const generator &other) = default;
269268
/**
270269
* \brief Moves the data from the other generator into this one.
271270
* \param[in,out] other The other generator.
@@ -287,7 +286,12 @@ template <typename T, enabler<T> = true> class generator {
287286
* \brief Converts this generator to a bool.
288287
* \returns True if more values remain, otherwise false.
289288
*/
290-
operator bool() const { return !_h.done(); }
289+
operator bool() {
290+
if (_h.done())
291+
return false;
292+
next();
293+
return !_h.done();
294+
}
291295

292296
/**
293297
* \brief Gets an iterator to the current coroutine state.
@@ -312,15 +316,23 @@ template <typename T, enabler<T> = true> class generator {
312316
* that can be thrown from the coroutine.
313317
*/
314318
value_type operator()() {
315-
if (*this)
316-
_h();
317-
if (_h.promise().ex)
318-
std::rethrow_exception(_h.promise().ex);
319-
return _h.promise().value;
319+
next();
320+
contains = false;
321+
return std::move(_h.promise().value);
320322
}
321323

322324
private:
323325
handle_type _h;
326+
bool contains;
327+
328+
void next() {
329+
if (!contains) {
330+
_h();
331+
if (_h.promise().ex)
332+
std::rethrow_exception(_h.promise().ex);
333+
contains = true;
334+
}
335+
}
324336
};
325337

326338
} // namespace fpgen

0 commit comments

Comments
 (0)