Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# fpgen
*Functional programming in C++ using C++20 coroutines*
![](https://img.shields.io/badge/test_coverage-98%25-brightgreen)
![](https://img.shields.io/badge/test_coverage-97%25-brightgreen)


## Aim
Expand All @@ -17,14 +17,9 @@ Currently supported features:
- Lazy `map`ping over generators.
- Lazy `zip`ping of generators.
- Lazy `filter`ing of generators.

Planned/In progress features:
- Commonly used aggregators:
- Lazy `fold`ing of generators.
- Lazy `sum`ming of generators.
- Ergonomic improvements:
- Enable streaming `<<` of generators.
- Simple structure which allows generator construction, manipulation and aggregation using member functions.

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

Expand Down
16 changes: 8 additions & 8 deletions inc/aggregators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace fpgen {
*/
template <typename T, typename... Args,
template <typename...> typename Container>
Container<T, Args...> &aggregate_to(generator<T> &gen,
Container<T, Args...> &aggregate_to(generator<T> gen,
Container<T, Args...> &out) {
while (gen) {
out.push_back(gen());
Expand All @@ -55,7 +55,7 @@ Container<T, Args...> &aggregate_to(generator<T> &gen,
template <typename TKey, typename TVal, typename... Args,
template <typename...> typename Container>
Container<TKey, TVal, Args...> &
tup_aggregate_to(generator<std::tuple<TKey, TVal>> &gen,
tup_aggregate_to(generator<std::tuple<TKey, TVal>> gen,
Container<TKey, TVal, Args...> &out) {
while (gen) {
std::tuple<TKey, TVal> tup = gen();
Expand All @@ -75,7 +75,7 @@ tup_aggregate_to(generator<std::tuple<TKey, TVal>> &gen,
* \param[in,out] gen The generator to iterate over.
* \returns The amount of elements in the generator.
*/
template <typename T> size_t count(generator<T> &gen) {
template <typename T> size_t count(generator<T> gen) {
size_t cnt = 0;
while (gen) {
gen();
Expand All @@ -102,7 +102,7 @@ template <typename T> size_t count(generator<T> &gen) {
* \returns The final accumulator value.
*/
template <typename TOut, typename TIn, typename Fun>
TOut fold(generator<TIn> &gen, Fun folder) {
TOut fold(generator<TIn> gen, Fun folder) {
TOut value = {};
while (gen) {
value = folder(value, gen());
Expand All @@ -129,7 +129,7 @@ TOut fold(generator<TIn> &gen, Fun folder) {
* \returns The final accumulator value.
*/
template <typename TOut, typename TIn, typename Fun>
TOut fold(generator<TIn> &gen, Fun folder, TOut initial) {
TOut fold(generator<TIn> gen, Fun folder, TOut initial) {
TOut value(initial);
while (gen) {
value = folder(value, gen());
Expand Down Expand Up @@ -157,7 +157,7 @@ TOut fold(generator<TIn> &gen, Fun folder, TOut initial) {
* now the output value.
*/
template <typename TOut, typename TIn, typename Fun>
TOut &fold_ref(generator<TIn> &gen, Fun folder, TOut &initial) {
TOut &fold_ref(generator<TIn> gen, Fun folder, TOut &initial) {
while (gen) {
initial = folder(initial, gen());
}
Expand All @@ -175,7 +175,7 @@ TOut &fold_ref(generator<TIn> &gen, Fun folder, TOut &initial) {
* \param[in,out] gen The generator to sum over.
* \returns The sum of all elements.
*/
template <typename T> T sum(generator<T> &gen) {
template <typename T> T sum(generator<T> gen) {
T accum = {};
while (gen) {
accum = accum + gen();
Expand All @@ -195,7 +195,7 @@ template <typename T> T sum(generator<T> &gen) {
* \param[in,out] gen The generator to iterate over.
* \param[in] func The function to use.
*/
template <typename T, typename Fun> void foreach (generator<T> &gen, Fun func) {
template <typename T, typename Fun> void foreach (generator<T> gen, Fun func) {
while (gen) {
func(gen());
}
Expand Down
2 changes: 2 additions & 0 deletions inc/fpgen.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#ifndef _FPGEN_MAIN
#define _FPGEN_MAIN

#include "aggregators.hpp"
#include "generator.hpp"
#include "manipulators.hpp"
#include "sources.hpp"
#include "stream.hpp"

#endif
66 changes: 39 additions & 27 deletions inc/generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ template <typename T, enabler<T> = true> class generator {
* coroutine calls `co_return`, the environment will call this function.
* \param[in] v The value to set.
*/
void return_value(value_type v) { value = v; }
// void return_value(value_type v) { value = v; }

void return_void() {}

/**
* \brief Sets an intermediate value from the coroutine.
*
Expand Down Expand Up @@ -155,12 +158,12 @@ template <typename T, enabler<T> = true> class generator {
* This value should only be true when the underlying generator is
* finished. Iterators are compared based on this value.
*/
bool is_finished;
// bool is_finished;
/**
* \brief The last value from the coroutine (and thus, the value currently
* held by the generator's promise).
*/
value_t value;
// value_t value;

/**
* \brief Constructs a new iterator from a source generator.
Expand All @@ -173,8 +176,7 @@ template <typename T, enabler<T> = true> class generator {
* modified).
* \param[in] is_finised Is this an `end` iterator?
*/
iterator_type(gen_t &source, bool is_finised)
: source{source}, is_finished{is_finised} {
iterator_type(gen_t &source, bool is_finised) : source{source} {
if (!is_finised)
++(*this);
}
Expand All @@ -190,20 +192,15 @@ template <typename T, enabler<T> = true> class generator {
* \param[in] is_finised Is this iterator finished?
* \param[in] value The value this iterator should hold.
*/
iterator_type(gen_t &source, bool is_finished, value_t value)
: source{source}, is_finished{is_finished}, value{value} {}
iterator_type(gen_t &source) : source{source} {}

/**
* \brief Steps the generator to the next value.
* \returns A new iterator for this generator, with the same state.
*/
iter_t operator++() {
if (!source)
is_finished = true;
value = source();
if (!source)
is_finished = true;
return {source, is_finished, value};
source.next();
return {source};
}

/**
Expand All @@ -219,19 +216,20 @@ template <typename T, enabler<T> = true> class generator {
* \param[in] other The iterator to compare against.
* \returns False if both are equal, otherwise true.
*/
bool operator!=(const iter_t &other) {
return is_finished != other.is_finished;
}
bool operator!=(const iter_t &) { return static_cast<bool>(source); }
/**
* \brief Gets the current value from the iterator.
* \returns The current value.
*/
value_t operator*() { return value; }
value_t operator*() {
source.contains = false;
return source._h.promise().value;
}
/**
* \brief Converts this iterator to the current value.
* \returns The current value.
*/
operator value_t() { return value; }
operator value_t() { return source._h.promise().value; }
};

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

/**
* \brief Copy-constructing generators results in undefined behaviour.
*/
generator(const generator &other) = delete;
generator(const generator &other) = default;
/**
* \brief Moves the data from the other generator into this one.
* \param[in,out] other The other generator.
Expand All @@ -265,7 +264,7 @@ template <typename T, enabler<T> = true> class generator {
/**
* \brief Copy-assigning generators results in undefined behaviour.
*/
generator &operator=(const generator &other) = delete;
generator &operator=(const generator &other) = default;
/**
* \brief Moves the data from the other generator into this one.
* \param[in,out] other The other generator.
Expand All @@ -287,7 +286,12 @@ template <typename T, enabler<T> = true> class generator {
* \brief Converts this generator to a bool.
* \returns True if more values remain, otherwise false.
*/
operator bool() const { return !_h.done(); }
operator bool() {
if (_h.done())
return false;
next();
return !_h.done();
}

/**
* \brief Gets an iterator to the current coroutine state.
Expand All @@ -312,15 +316,23 @@ template <typename T, enabler<T> = true> class generator {
* that can be thrown from the coroutine.
*/
value_type operator()() {
if (*this)
_h();
if (_h.promise().ex)
std::rethrow_exception(_h.promise().ex);
return _h.promise().value;
next();
contains = false;
return std::move(_h.promise().value);
}

private:
handle_type _h;
bool contains;

void next() {
if (!contains) {
_h();
if (_h.promise().ex)
std::rethrow_exception(_h.promise().ex);
contains = true;
}
}
};

} // namespace fpgen
Expand Down
Loading