Skip to content

Commit

Permalink
Add zip view and fix tuple vector traits (#286)
Browse files Browse the repository at this point in the history
Fixes `ranges::range_value_t<beluga::TupleVector>` returning a
`ranges::common_tuple` instead of `std::tuple`.

Signed-off-by: Nahuel Espinosa <[email protected]>
  • Loading branch information
nahueespinosa authored Jan 11, 2024
1 parent aeeabf9 commit 470557e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 35 deletions.
40 changes: 5 additions & 35 deletions beluga/include/beluga/tuple_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
#include <vector>

#include <beluga/type_traits.hpp>
#include <beluga/views/zip.hpp>
#include <range/v3/algorithm/copy.hpp>
#include <range/v3/iterator/insert_iterators.hpp>
#include <range/v3/view/const.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip.hpp>

/**
* \file
Expand All @@ -33,32 +32,6 @@

namespace beluga {

namespace detail {

/// Utility type to standardize the zip output to always dereference in tuples and not in pairs.
struct as_common_tuple_fn {
/// Overload that takes a common_tuple.
template <typename... Types>
constexpr auto operator()(ranges::common_tuple<Types...> tuple) const {
return tuple;
}

/// Overload that takes a common_pair and forwards the values as a common_tuple.
template <typename... Types>
constexpr auto operator()(ranges::common_pair<Types...> pair) const {
return std::apply(
[](auto&&... values) {
return ranges::common_tuple<Types...>{std::forward<Types>(values)...}; //
},
pair);
}
};

} // namespace detail

/// Utility to standardize the zip output to always dereference in tuples and not in pairs.
inline constexpr detail::as_common_tuple_fn as_common_tuple;

/// Primary template for a tuple of containers.
template <template <class> class InternalContainer, class T>
class TupleContainer;
Expand Down Expand Up @@ -231,19 +204,16 @@ class TupleContainer<InternalContainer, std::tuple<Types...>> {

[[nodiscard]] constexpr auto all() const {
return std::apply(
[](auto&&... containers) {
return ranges::views::zip(containers...) | //
ranges::views::transform(as_common_tuple) | //
ranges::views::const_;
[](auto&... containers) { //
return beluga::views::zip(containers...) | ranges::views::const_;
},
sequences_);
}

[[nodiscard]] constexpr auto all() {
return std::apply(
[](auto&&... containers) {
return ranges::views::zip(containers...) | //
ranges::views::transform(as_common_tuple);
[](auto&... containers) { //
return beluga::views::zip(containers...);
},
sequences_);
}
Expand Down
1 change: 1 addition & 0 deletions beluga/include/beluga/views.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <beluga/views/sample.hpp>
#include <beluga/views/take_evenly.hpp>
#include <beluga/views/take_while_kld.hpp>
#include <beluga/views/zip.hpp>

/**
* \file
Expand Down
77 changes: 77 additions & 0 deletions beluga/include/beluga/views/zip.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2024 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_VIEWS_ZIP_HPP
#define BELUGA_VIEWS_ZIP_HPP

#include <tuple>

#include <range/v3/view/zip.hpp>

/**
* \file
* \brief Implementation of a zip range adaptor object.
*/

namespace beluga::views {

namespace detail {

/// Utility type to adapt the zip output.
struct as_common_tuple_indirect_fn {
/// Reference overload.
template <class... Its>
constexpr auto operator()(const Its&... its) const //
noexcept((noexcept(ranges::iter_reference_t<Its>(*its)) && ...)) {
return ranges::common_tuple<ranges::iter_reference_t<Its>...>{*its...};
}

/// Move overload.
template <class... Its>
constexpr auto operator()(ranges::move_tag, const Its&... its) const
noexcept((noexcept(ranges::iter_rvalue_reference_t<Its>(ranges::iter_move(its))) && ...)) {
return ranges::common_tuple<ranges::iter_rvalue_reference_t<Its>...>{ranges::iter_move(its)...};
}

/// Copy overload.
/**
* This is needed for `ranges_value_t` to return `std::tuple` when this object is passed to a `zip_with_view`.
*/
template <class... Its>
constexpr auto operator()(ranges::copy_tag, Its...) const -> std::tuple<ranges::iter_value_t<Its>...> {
RANGES_EXPECT(false);
}
};

/// Implementation detail for a zip range adaptor object.
struct zip_fn {
/// Overload that implements the zip_view algorithm.
template <class... Ranges>
constexpr auto operator()(Ranges&&... ranges) const {
return ranges::views::iter_zip_with(as_common_tuple_indirect_fn{}, std::forward<Ranges>(ranges)...);
}
};

} // namespace detail

/// Given N ranges, return a new range where the Mth element is a tuple of the Mth elements of all N ranges.
/**
* Unlike `ranges::views::zip`, iterators always dereference into tuples, not pairs.
* Other than that, they are both equivalent views.
*/
inline constexpr detail::zip_fn zip;

} // namespace beluga::views

#endif
24 changes: 24 additions & 0 deletions beluga/test/beluga/test_tuple_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,30 @@ TEST(TupleVectorTest, ConceptChecks) {
static_assert(!ranges::contiguous_range<decltype(container)>);
}

TEST(TupleVectorTest, TraitConsistency) {
using Container = beluga::TupleVector<std::tuple<float, int>>;
using Iterator = decltype(ranges::begin(std::declval<Container&>()));

// Expected types
static_assert(std::is_same_v<
ranges::range_value_t<Container>, //
std::tuple<float, int>>);
static_assert(std::is_same_v<
ranges::range_reference_t<Container>, //
ranges::common_tuple<float&, int&>>);
static_assert(std::is_same_v<
ranges::range_rvalue_reference_t<Container>, //
ranges::common_tuple<float&&, int&&>>);

// Consistency
static_assert(std::is_same_v<
ranges::iter_value_t<Iterator>, //
typename Container::value_type>);
static_assert(std::is_same_v<
ranges::iter_reference_t<Iterator>, //
typename Container::reference_type>);
}

TEST(TupleVectorTest, ConstCorrectness) {
auto container = beluga::TupleVector<std::tuple<float>>{std::make_tuple(1)};
static_assert(std::is_same_v<decltype(*ranges::begin(container)), ranges::common_tuple<float&>>);
Expand Down

0 comments on commit 470557e

Please sign in to comment.