diff --git a/libvmake/examples/empty_sequence.cpp b/libvmake/examples/empty_sequence.cpp new file mode 100644 index 0000000..a2e9c1a --- /dev/null +++ b/libvmake/examples/empty_sequence.cpp @@ -0,0 +1,8 @@ +#include "../vmake.hpp" +#include +using namespace std; + +int main() { + vmake::output(cout, vmake::nothing()); + return 0; +} diff --git a/libvmake/examples/filtering.cpp b/libvmake/examples/filtering.cpp new file mode 100644 index 0000000..3aca603 --- /dev/null +++ b/libvmake/examples/filtering.cpp @@ -0,0 +1,14 @@ +#include "../vmake.hpp" +#include +using namespace std; + +int main() { + vmake::output(cout, " ", vmake::filter(vmake::range(1, 10), [](int x) { + return x % 2 == 0; + })); + cout << endl; + vmake::output(cout, " ", vmake::take(vmake::filter(vmake::iota(1), [](int x) { + return x <= 5; + }), 5)); + return 0; +} diff --git a/libvmake/vmake.hpp b/libvmake/vmake.hpp index 7952ffe..dbb2734 100644 --- a/libvmake/vmake.hpp +++ b/libvmake/vmake.hpp @@ -7,21 +7,106 @@ namespace vmake { struct sequence_terminated_error : std::exception { - // TODO + virtual const char *what() const noexcept override final { + return "iterating on a terminated sequence."; + } }; namespace details { +template +struct iota_sequence { + using result = T; + + T start; + iota_sequence(const T& start) : start(start) {} + + bool is_terminated() const noexcept { + return false; + } + + auto operator()() { + return start++; + } +}; + +template +struct ranged_sequence { + using result = T; + + T start, end; + ranged_sequence(const T& start, const T& end) : start(start), end(end) {} + + bool is_terminated() const noexcept { + return start == end; + } + + auto operator()() { + if (start == end) throw sequence_terminated_error(); + return start++; + } +}; + +template +struct ranged_step_sequence { + using result = T; + + T start, end, step; + ranged_step_sequence(const T& start, const T& end, const T& step) : start(start), end(end), step(step) {} + + bool is_terminated() const noexcept { + return start == end; + } + + auto operator()() { + if (start == end) throw sequence_terminated_error(); + T res = start; + start += step; + return res; + } +}; + +} // namespace details + +template +inline auto iota(const Val &start) { + return details::iota_sequence(start); +} + +template +inline auto range(const Val &start, const Val &end) { + return details::ranged_sequence(start, end); +} + +template +inline auto range(const Val &start, const Val &end, const Val &step) { + return details::ranged_step_sequence(start, end, step); +} + +namespace details { + +template +struct empty_sequence { + using result = T; + + bool is_terminated() const noexcept { + return true; + } + + T operator()() { + throw sequence_terminated_error(); + } +}; + template struct extractor { - using core = ContIt; using result = typename std::remove_reference())>::type; ContIt cur, end; extractor(const ContIt &begin, const ContIt &end) : cur(begin), end(end) {} - bool is_terminated() const { + bool is_terminated() const noexcept { return cur == end; } @@ -33,7 +118,6 @@ struct extractor { template struct generator { - using core = Func; using result = typename std::result_of::type; Func g; @@ -46,7 +130,7 @@ struct generator { generator(generator &&y) = default; generator(const generator &y) = default; - bool is_terminated() const { + bool is_terminated() const noexcept { return false; } @@ -66,7 +150,7 @@ struct limitor { limitor(limitor &&g) = default; limitor(const limitor &g) = default; - bool is_terminated() const { + bool is_terminated() const noexcept { return lim <= 0; } @@ -78,6 +162,11 @@ struct limitor { } +template +inline auto nothing() { + return details::empty_sequence(); +} + template inline auto take(Gen &&g, size_t lim) { return details::limitor::type>(std::move(g), lim); @@ -90,7 +179,7 @@ inline auto make_generator(Args ...args) { namespace rng { -template +template inline auto common() { // NOTE: MinGW GCC older than 9.2 have a fixed random_device return make_generator(std::random_device{}()); @@ -101,10 +190,12 @@ inline auto common(Seed seed) { return make_generator(seed); } -struct cstyle_rng { - using result = int; +namespace details { - bool is_terminated() const { +struct cstyle_rng { + using result = decltype(std::rand()); + + bool is_terminated() const noexcept { return false; } @@ -113,16 +204,18 @@ struct cstyle_rng { } }; +} + inline auto cstyle() { std::srand(time(0)); std::rand(); - return cstyle_rng(); + return details::cstyle_rng(); } inline auto cstyle(int seed) { std::srand(seed); std::rand(); - return cstyle_rng(); + return details::cstyle_rng(); } } @@ -162,7 +255,7 @@ struct concator { concator(const concator &c) = default; // concator& operator=(concator &&c) = default; - bool is_terminated() const { + bool is_terminated() const noexcept { return g1.is_terminated() && g2.is_terminated(); } @@ -173,7 +266,7 @@ struct concator { }; template -inline auto concat(Gen1 &&x, Gen2 &&y) { +inline constexpr auto concat(Gen1 &&x, Gen2 &&y) noexcept { return concator(std::move(x), std::move(y)); } @@ -190,7 +283,7 @@ struct transformer { transformer(const transformer &c) = default; // transformer& operator=(transformer &&c) = default; - bool is_terminated() const { + bool is_terminated() const noexcept { return g.is_terminated(); } @@ -211,7 +304,7 @@ inline auto output(OutputStream& out, const char *delim, Gen &&g) { template inline auto output(OutputStream& out, Gen &&g) { - return copy(std::ostream_iterator::type::result>(out), g); + return copy(std::ostream_iterator::type::result>(out), std::move(g)); } template @@ -225,8 +318,8 @@ inline auto output_n(OutputStream& out, size_t n, Gen &&g) { } template -inline auto generate(Func &&f) { - return details::generator(std::move(f)); +inline constexpr auto generate(Func &&f) { + return details::generator(std::forward(f)); } namespace polyfill { @@ -237,7 +330,7 @@ struct as_const_reference_t { }; template -inline auto as_const_reference(typename as_const_reference_t::type x) { +inline constexpr auto as_const_reference(typename as_const_reference_t::type x) noexcept { return x; } @@ -254,30 +347,35 @@ struct filteror { filteror(filteror &&) = default; filteror(const filteror &) = default; - Gen g; + mutable Gen g; Pred p; - polyfill::optional preview; + mutable polyfill::optional preview; filteror(Gen &&g, Pred &&p) : g(std::forward(g)), p(std::forward(p)), preview() { _find_next(); }; - bool is_terminated() const { - return g.is_terminated() && !preview.has_value(); + bool is_terminated() const noexcept { + if (g.is_terminated() && !preview.has_value()) return true; + if (!preview.has_value()) _find_next(); + return !preview.has_value(); } auto operator()() { if (is_terminated()) throw sequence_terminated_error(); result res = std::move(*preview); - _find_next(); + preview.reset(); return res; } private: - void _find_next() { + void _find_next() const { preview.reset(); do { - if (g.is_terminated()) return; + if (g.is_terminated()) { + preview.reset(); + return; + } preview.emplace(std::move(g())); } while (!p(polyfill::as_const_reference(*preview))); }