Skip to content

Commit

Permalink
Merge branch 'main' into pr/set-algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
jnytra authored May 24, 2023
2 parents d74ead1 + db6be7c commit e4b2c8b
Show file tree
Hide file tree
Showing 51 changed files with 1,462 additions and 307 deletions.
4 changes: 2 additions & 2 deletions benchmark/internal_iteration_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ int main(int argc, char** argv)
});

bench.run("transform_filter_flux", [&] {
auto r = flux::from(bunch_of_ints).map(triple).filter(is_even);
auto r = flux::ref(bunch_of_ints).map(triple).filter(is_even);
int res = r.sum();
an::doNotOptimizeAway(res);
});
Expand Down Expand Up @@ -118,7 +118,7 @@ int main(int argc, char** argv)

bench.run("concat_take_transform_filter_flux", [&] {
int res =
flux::chain(std::cref(bunch_of_ints), std::cref(moar_ints))
flux::chain(flux::ref(bunch_of_ints), flux::ref(moar_ints))
.take(1'500'000)
.map(triple)
.filter(is_even)
Expand Down
22 changes: 22 additions & 0 deletions docs/reference/adaptors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@ Adaptors
requires std::predicate<Pred, element_t<Seq>, element_t<Seq>> \
auto chunk_by(Seq seq, Pred pred) -> multipass_sequence auto;

``cursors``
^^^^^^^^^^^

.. function::
auto cursors(multipass_sequence auto seq) -> multipass_sequence auto;

Given a sequence :var:`seq`, :expr:`cursors(seq)` returns a new sequence whose elements are the cursors of the original sequence. The :func:`cursors` sequence retains all the capabilities of the source sequence (bidirectional, random access, sized etc), up to :concept:`contiguous_sequence`.

This is basically a passthrough adaptor, except that :expr:`read_at(seq, cur)` returns a copy of :var:`cur`.

:param seq: A multipass sequence

:returns: A sequence whose elements are the cursors of :var:`seq`

:example:

.. literalinclude:: ../../example/docs/cursors.cpp
:language: cpp
:dedent:
:lines: 16-29


``cycle``
^^^^^^^^^

Expand Down
42 changes: 42 additions & 0 deletions docs/reference/sources.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,48 @@ Sources
requires see_below \
auto iota(T from, T to) -> multipass_sequence auto;

``repeat``
----------

.. function::
template <typename T> \
requires std::movable<std::decay_t<T>> \
auto repeat(T&& obj) -> infinite_sequence auto;

.. function::
template <typename T> \
requires std::movable<std::decay_t<T>> \
auto repeat(T&& obj, std::integral auto count) -> random_access_sequence auto;

Returns a sequence which yields a const reference to :var:`obj` endlessly (for the first overload) or exactly :var:`count` times (for the second overload).

For both overloads, the returned sequence is always a :concept:`random_access_sequence`. For the second overload it additionally models :concept:`sized_sequence` and :concept:`bounded_sequence`.

.. caution::
In order to provide random-access functionality, cursors for repeat sequences keep a :type:`size_t` count of how many times they have been incremented. For very long-running programs using the infinite version of :func:`repeat` it may be possible to overflow this counter. (Assuming 1000 iterations per second, this would take approximately 49 days on a machine with a 32-bit :type:`size_t`, or around 500 million years on a 64-bit machine.)

While this won't cause undefined behaviour, calling :func:`distance` with cursors that have rolled over may give incorrect results, and may result in a runtime error in debug mode if the result cannot be represented as a :type:`distance_t`.

:param obj: A movable object which will be stored in the returned sequence object
:param count: If provided, a non-negative value to indicate the size of the returned sequence. If not provided, the returned sequence will be infinite.

:returns: A sequence which repeatedly yields a const reference to :var:`obj`

:example:

.. literalinclude:: ../../example/docs/repeat.cpp
:language: cpp
:dedent:
:lines: 16-32

:see also:

* `std::views::repeat <https://en.cppreference.com/w/cpp/ranges/repeat_view>`_ (C++23)
* :func:`flux::cycle`
* :func:`flux::single`
* :func:`flux::iota`


``single``
----------

Expand Down
2 changes: 2 additions & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_example(example-docs-any docs/any.cpp)
add_example(example-docs-compare docs/compare.cpp)
add_example(example-docs-contains docs/contains.cpp)
add_example(example-docs-count docs/count.cpp)
add_example(example-docs-cursors docs/cursors.cpp)
add_example(example-docs-cycle docs/cycle.cpp)
add_example(example-docs-drop docs/drop.cpp)
add_example(example-docs-ends-with docs/ends_with.cpp)
Expand All @@ -28,6 +29,7 @@ add_example(example-docs-set-symmetric-difference docs/set_symmetric_difference.
add_example(example-docs-set-intersection docs/set_intersection.cpp)
add_example(example-docs-set-union docs/set_union.cpp)
add_example(example-docs-read-only docs/read_only.cpp)
add_example(example-docs-repeat docs/repeat.cpp)
add_example(example-docs-scan docs/scan.cpp)
add_example(example-docs-scan-first docs/scan_first.cpp)
add_example(example-docs-starts-with docs/starts_with.cpp)
2 changes: 1 addition & 1 deletion example/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ app_args_t parse_args(int argc, char** argv) {
return std::pair{s.substr(0, pos), s.substr(std::min(flux::count(s), pos + 1))};
};

for (const auto& [key, val] : flux::from(args).map(to_pair)) {
for (const auto& [key, val] : flux::ref(args).map(to_pair)) {
if (auto it = args_map.find(key); it != args_map.end()) {
std::invoke(it->second, val);
} else {
Expand Down
2 changes: 1 addition & 1 deletion example/config_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ token_t parse_line(const std::string& line)
else if (line.starts_with("[") and line.ends_with("]"))
return section_t{line.substr(1, line.size() - 2)};

auto items = flux::from(line).split_string("=").to<std::vector>();
auto items = flux::ref(line).split_string("=").to<std::vector>();
if (items.size() != 2)
throw std::runtime_error("parse error");
return option_t{std::string{items[0]}, std::string{items[1]}};
Expand Down
30 changes: 30 additions & 0 deletions example/docs/cursors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com)
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <flux.hpp>

#include <array>
#include <iostream>
#include <string_view>

using namespace std::string_view_literals;

int main()
{
std::array const array{"alpha"sv, "bravo"sv, "charlie"sv, "delta"sv, "echo"sv};

auto long_words = flux::drop(array, 2);

// We can use the cursors() adaptor to iterate over the cursors of the
// sequence (in this case integer indices) and use those to read from the
// original sequence
for (auto idx : flux::cursors(long_words)) {
std::cout << idx << ": " << long_words[idx] << '\n';
}
// prints
// 2: charlie
// 3: delta
// 4: echo
}
33 changes: 33 additions & 0 deletions example/docs/repeat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com)
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <flux.hpp>

#include <array>
#include <cassert>
#include <string_view>

using namespace std::string_view_literals;

int main()
{
// flux::repeat(val) is a random-access sequence which endlessly repeats
// the given value
auto seq = flux::repeat(3);

auto cursor = flux::first(seq);
assert(flux::read_at(seq, cursor) == 3);
// fast-forward the cursor a lot...
cursor = flux::next(seq, cursor, 1'000'000);
assert(flux::read_at(seq, cursor) == 3); // still returning 3!

// We could use the take adaptor to make a repeat sequence finite...
auto taken = flux::take(seq, 5);
assert(flux::equal(taken, std::array{3, 3, 3, 3, 3}));

// ...but it's easier to use repeat(val, count) instead
auto police = flux::repeat("hello"sv, 3);
assert(flux::equal(police, std::array{"hello", "hello", "hello"}));
}
2 changes: 1 addition & 1 deletion example/merge_intervals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ int main()
// sort intervals according to begin
flux::sort(intervals, flux::proj(std::less{}, &interval_t::begin));

flux::from(intervals)
flux::ref(intervals)
.chunk_by(is_overlapped)
.map(merge)
.write_to(std::cout);
Expand Down
4 changes: 2 additions & 2 deletions example/moving_average.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int main() {
std::vector intervals = {1, 5, 6, 1, 2, 9, 7, -1, 0};

// compute moving average by scan adaptor (more effective for large windows)
auto ma = flux::from(intervals)
auto ma = flux::ref(intervals)
.scan(sliding_window, sliding_window_t(3))
.map(&sliding_window_t::average)
.to<std::vector>();
Expand All @@ -55,7 +55,7 @@ int main() {
assert(ma.back() == 2); // (7 + -1 + 0) / 3

// compute moving average by slide adaptor (less effective for large windows)
auto ma2 = flux::from(intervals)
auto ma2 = flux::ref(intervals)
.slide(3)
.map([](auto&& win) { return win.sum() / win.size(); })
.to<std::vector>();
Expand Down
2 changes: 2 additions & 0 deletions include/flux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <flux/op/compare.hpp>
#include <flux/op/contains.hpp>
#include <flux/op/count.hpp>
#include <flux/op/cursors.hpp>
#include <flux/op/cycle.hpp>
#include <flux/op/drop.hpp>
#include <flux/op/drop_while.hpp>
Expand Down Expand Up @@ -66,6 +67,7 @@
#include <flux/source/istream.hpp>
#include <flux/source/istreambuf.hpp>
#include <flux/source/range.hpp>
#include <flux/source/repeat.hpp>
#include <flux/source/single.hpp>

#endif
9 changes: 9 additions & 0 deletions include/flux/core/inline_sequence_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ struct inline_sequence_base {
return std::invoke(FLUX_FWD(func), std::move(derived()), FLUX_FWD(args)...);
}

constexpr auto ref() const& requires sequence<Derived const>;

auto ref() const&& -> void = delete;

constexpr auto mut_ref() &;

/*
* Iterator support
*/
Expand Down Expand Up @@ -229,6 +235,9 @@ struct inline_sequence_base {
[[nodiscard]]
constexpr auto chunk_by(Pred pred) &&;

[[nodiscard]]
constexpr auto cursors() && requires multipass_sequence<Derived>;

[[nodiscard]]
constexpr auto cycle() &&
requires infinite_sequence<Derived> || multipass_sequence<Derived>;
Expand Down
106 changes: 106 additions & 0 deletions include/flux/op/cursors.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@

// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com)
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef FLUX_OP_CURSORS_HPP_INCLUDED
#define FLUX_OP_CURSORS_HPP_INCLUDED

#include <flux/core.hpp>

namespace flux {

namespace detail {

template <typename Base>
struct cursors_adaptor : inline_sequence_base<cursors_adaptor<Base>> {
private:
Base base_;

public:
constexpr explicit cursors_adaptor(decays_to<Base> auto&& base)
: base_(FLUX_FWD(base))
{}

struct flux_sequence_traits {

static inline constexpr bool is_infinite = infinite_sequence<Base>;

static constexpr auto first(auto& self) -> decltype(flux::first(self.base_))
{
return flux::first(self.base_);
}

static constexpr auto is_last(auto& self, cursor_t<Base> const& cur) -> bool
{
return flux::is_last(self.base_, cur);
}

static constexpr auto inc(auto& self, cursor_t<Base>& cur) -> void
{
flux::inc(self.base_, cur);
}

static constexpr auto read_at(auto&, cursor_t<Base> const& cur)
-> cursor_t<Base>
{
return cur;
}

static constexpr auto dec(auto& self, cursor_t<Base>& cur) -> void
requires bidirectional_sequence<Base>
{
flux::dec(self.base_, cur);
}

static constexpr auto inc(auto& self, cursor_t<Base>& cur, distance_t offset) -> void
requires random_access_sequence<Base>
{
flux::inc(self.base_, cur, offset);
}

static constexpr auto distance(auto& self, cursor_t<Base> const& from,
cursor_t<Base> const& to) -> distance_t
requires random_access_sequence<Base>
{
return flux::distance(self.base_, from, to);
}

static constexpr auto last(auto& self) -> cursor_t<Base>
requires bounded_sequence<Base>
{
return flux::last(self.base_);
}

static constexpr auto size(auto& self) -> distance_t
requires sized_sequence<Base>
{
return flux::size(self.base_);
}
};
};

struct cursors_fn {
template <adaptable_sequence Seq>
requires multipass_sequence<Seq>
[[nodiscard]]
constexpr auto operator()(Seq&& seq) const -> sequence auto
{
return cursors_adaptor<std::decay_t<Seq>>(FLUX_FWD(seq));
}
};

} // namespace detail

inline constexpr auto cursors = detail::cursors_fn{};

template <typename D>
constexpr auto inline_sequence_base<D>::cursors() &&
requires multipass_sequence<D>
{
return flux::cursors(std::move(derived()));
}

} // namespace flux

#endif // FLUX_OP_CURSORS_HPP_INCLUDED
Loading

0 comments on commit e4b2c8b

Please sign in to comment.