Skip to content

Commit

Permalink
Fix the compilation of the tests and the backports.
Browse files Browse the repository at this point in the history
Signed-off-by: Johannes Kalmbach <johannes.kalmbach@gmail.com>
  • Loading branch information
joka921 committed Dec 16, 2024
1 parent 6bd3411 commit c3feea5
Showing 3 changed files with 83 additions and 110 deletions.
27 changes: 14 additions & 13 deletions src/util/Iterators.h
Original file line number Diff line number Diff line change
@@ -5,9 +5,6 @@
#ifndef QLEVER_ITERATORS_H
#define QLEVER_ITERATORS_H

#include <c++/11/bits/ranges_base.h>

#include <boost/range/any_range.hpp>
#include <cstdint>
#include <iterator>
#include <type_traits>
@@ -53,7 +50,7 @@ class IteratorForAccessOperator {
using iterator_category = std::random_access_iterator_tag;
using difference_type = int64_t;
using index_type = uint64_t;
// It is possible to explicitly specify the `value_type` and `reference_type`
// It is possible to explicitly specify the `value_type` and `reference`
// if they differ from the defaults. For an example, see the `IdTable` class
// which uses a proxy type as its `reference`.
using value_type = std::conditional_t<
@@ -194,7 +191,7 @@ auto makeForwardingIterator(It iterator) {
// element, or `isFinished()` must return true ( for an empty range).
// * `bool isFinished()` -> has to return true if there are no more values, and
// calls to `get()` are thus impossible.
// * `reference_type get()` -> get the current value (typically as a reference).
// * `reference get()` -> get the current value (typically as a reference).
// * `void next()` advance to the next value. After calling `next()` either
// `isFinished()` must be true, or `get()` must return the next value.
template <typename Derived>
@@ -212,11 +209,13 @@ class InputRangeMixin {
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::int64_t;
using reference_type = decltype(std::declval<Derived&>().get());
using value_type = std::remove_reference_t<reference_type>;
InputRangeMixin* mixin_;
using reference = decltype(std::declval<Derived&>().get());
using value_type = std::remove_reference_t<reference>;
using pointer = value_type*;
InputRangeMixin* mixin_ = nullptr;

public:
Iterator() = default;
explicit Iterator(InputRangeMixin* mixin) : mixin_{mixin} {}
Iterator& operator++() {
mixin_->derived().next();
@@ -287,11 +286,13 @@ class InputRangeOptionalMixin {
using iterator_category = std::input_iterator_tag;
using difference_type = std::int64_t;
using value_type = typename InputRangeOptionalMixin::Storage::value_type;
using reference_type = std::add_lvalue_reference_t<value_type>;
using const_reference_type = std::add_const_t<reference_type>;
InputRangeOptionalMixin* mixin_;
using pointer = value_type*;
using reference = std::add_lvalue_reference_t<value_type>;
using const_reference = std::add_const_t<reference>;
InputRangeOptionalMixin* mixin_ = nullptr;

public:
Iterator() = default;
explicit Iterator(InputRangeOptionalMixin* mixin) : mixin_{mixin} {}
Iterator& operator++() {
mixin_->getNextAndStore();
@@ -301,8 +302,8 @@ class InputRangeOptionalMixin {
// Needed for the `range` concept.
void operator++(int) { (void)operator++(); }

reference_type operator*() { return mixin_->storage_.value(); }
const_reference_type operator*() const { return mixin_->storage_.value(); }
reference operator*() { return mixin_->storage_.value(); }
const_reference operator*() const { return mixin_->storage_.value(); }
decltype(auto) operator->() { return std::addressof(operator*()); }
decltype(auto) operator->() const { return std::addressof(operator*()); }

160 changes: 66 additions & 94 deletions src/util/Views.h
Original file line number Diff line number Diff line change
@@ -18,101 +18,10 @@

namespace ad_utility {

/// Takes a input-iterable and yields the elements of that view (no visible
/// effect). The iteration over the input view is done on a separate thread with
/// a buffer size of `blockSize`. This might speed up the computation when the
/// values of the input view are expensive to compute.

template <typename View>
struct BufferedAsyncView : InputRangeMixin<BufferedAsyncView<View>> {
View view_;
uint64_t blockSize_;
bool finished_ = false;

explicit BufferedAsyncView(View view, uint64_t blockSize)
: view_{std::move(view)}, blockSize_{blockSize} {
AD_CONTRACT_CHECK(blockSize_ > 0);
}

std::ranges::iterator_t<View> it_;
std::ranges::sentinel_t<View> end_ = std::ranges::end(view_);
using value_type = std::ranges::range_value_t<View>;
std::future<std::vector<value_type>> future_;

std::vector<value_type> buffer_;
std::vector<value_type> getNextBlock() {
std::vector<value_type> buffer;
buffer.reserve(blockSize_);
size_t i = 0;
while (i < blockSize_ && it_ != end_) {
buffer.push_back(*it_);
++it_;
++i;
}
return buffer;
};

void start() {
it_ = view_.begin();
buffer_ = getNextBlock();
finished_ = buffer_.empty();
future_ =
std::async(std::launch::async, [this]() { return getNextBlock(); });
}
bool isFinished() { return finished_; }
auto& get() { return buffer_; }
const auto& get() const { return buffer_; }

void next() {
buffer_ = future_.get();
finished_ = buffer_.empty();
future_ =
std::async(std::launch::async, [this]() { return getNextBlock(); });
}
};

template <typename View>
auto bufferedAsyncView(View view, uint64_t blockSize) {
return ql::views::join(BufferedAsyncView<View>{std::move(view), blockSize});
}
/*
template <typename View>
cppcoro::generator<typename View::value_type> bufferedAsyncView(
View view, uint64_t blockSize) {
using value_type = typename View::value_type;
auto it = view.begin();
auto end = view.end();
auto getNextBlock = [&it, &end, blockSize] {
std::vector<value_type> buffer;
buffer.reserve(blockSize);
size_t i = 0;
while (i < blockSize && it != end) {
buffer.push_back(*it);
++it;
++i;
}
return buffer;
};
auto block = getNextBlock();
auto future = std::async(std::launch::async, getNextBlock);
while (true) {
for (auto& element : block) {
co_yield element;
}
block = future.get();
if (block.empty()) {
co_return;
}
future = std::async(std::launch::async, getNextBlock);
}
}
*/

/// Takes a view and yields the elements of the same view, but skips over
/// consecutive duplicates.
template <typename SortedView,
typename ValueType = std::ranges::range_value_t<SortedView>>
typename ValueType = ql::ranges::range_value_t<SortedView>>
cppcoro::generator<ValueType> uniqueView(SortedView view) {
size_t numInputs = 0;
size_t numUnique = 0;
@@ -174,7 +83,7 @@ cppcoro::generator<typename SortedBlockView::value_type> uniqueBlockView(
}

// A view that owns its underlying storage. It is a replacement for
// `std::ranges::owning_view` which is not yet supported by `GCC 11` and
// `ranges::owning_view` which is not yet supported by `GCC 11` and
// `range-v3`. The implementation is taken from libstdc++-13. The additional
// optional `supportsConst` argument explicitly disables const iteration for
// this view when set to false, see `OwningViewNoConst` below for details.
@@ -284,7 +193,7 @@ CPP_concept can_ref_view = CPP_requires_ref(can_ref_view, Range);
// implementations.
template <typename Range>
constexpr auto allView(Range&& range) {
if constexpr (std::ranges::view<std::decay_t<Range>>) {
if constexpr (ql::ranges::view<std::decay_t<Range>>) {
return AD_FWD(range);
} else if constexpr (detail::can_ref_view<Range>) {
return ql::ranges::ref_view{AD_FWD(range)};
@@ -293,6 +202,69 @@ constexpr auto allView(Range&& range) {
}
}

namespace detail {
// The implementation of `bufferedAsyncView` (see below). It yields its result
// in blocks.
template <typename View>
struct BufferedAsyncView : InputRangeMixin<BufferedAsyncView<View>> {
View view_;
uint64_t blockSize_;
bool finished_ = false;

explicit BufferedAsyncView(View view, uint64_t blockSize)
: view_{std::move(view)}, blockSize_{blockSize} {
AD_CONTRACT_CHECK(blockSize_ > 0);
}

ql::ranges::iterator_t<View> it_;
ql::ranges::sentinel_t<View> end_ = ql::ranges::end(view_);
using value_type = ql::ranges::range_value_t<View>;
std::future<std::vector<value_type>> future_;

std::vector<value_type> buffer_;
std::vector<value_type> getNextBlock() {
std::vector<value_type> buffer;
buffer.reserve(blockSize_);
size_t i = 0;
while (i < blockSize_ && it_ != end_) {
buffer.push_back(*it_);
++it_;
++i;
}
return buffer;
};

void start() {
it_ = view_.begin();
buffer_ = getNextBlock();
finished_ = buffer_.empty();
future_ =
std::async(std::launch::async, [this]() { return getNextBlock(); });
}
bool isFinished() { return finished_; }
auto& get() { return buffer_; }
const auto& get() const { return buffer_; }

void next() {
buffer_ = future_.get();
finished_ = buffer_.empty();
future_ =
std::async(std::launch::async, [this]() { return getNextBlock(); });
}
};
} // namespace detail

/// Takes a input-iterable and yields the elements of that view (no visible
/// effect). The iteration over the input view is done on a separate thread with
/// a buffer size of `blockSize`. This might speed up the computation when the
/// values of the input view are expensive to compute.
///
template <typename View>
auto bufferedAsyncView(View view, uint64_t blockSize) {
return ql::views::join(
allView(detail::BufferedAsyncView<View>{std::move(view), blockSize}));
}

// Returns a view that contains all the values in `[0, upperBound)`, similar to
// Python's `range` function. Avoids the common pitfall in `ql::views::iota`
// that the count variable is only derived from the first argument. For example,
6 changes: 3 additions & 3 deletions test/IteratorTest.cpp
Original file line number Diff line number Diff line change
@@ -122,6 +122,7 @@ void testIota(MakeIotaRange makeIotaRange) {

// Test the interaction with the `ql::views` and `ql::ranges` machinery.
auto view = iota | ql::views::drop(3) | ql::views::take(7);
static_assert(ql::ranges::input_range<decltype(view)>);
sum = 0;
auto add = [&sum](auto val) { sum += val; };
ql::ranges::for_each(view, add);
@@ -151,7 +152,7 @@ TEST(Iterator, InputRangeMixin) {
testIota(makeIota);
}

// _____________________________________________________________________________
//_____________________________________________________________________________
TEST(Iterator, InputRangeOptionalMixin) {
using namespace ad_utility;
struct Iota : InputRangeOptionalMixin<size_t> {
@@ -171,8 +172,7 @@ TEST(Iterator, InputRangeOptionalMixin) {
};
testIota(makeIota);
}

// _____________________________________________________________________________
//_____________________________________________________________________________
TEST(Iterator, TypeErasedInputRangeOptionalMixin) {
using namespace ad_utility;
struct IotaImpl : InputRangeOptionalMixin<size_t> {

0 comments on commit c3feea5

Please sign in to comment.