Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-28 08:27:09

0001 // Licensed to the Apache Software Foundation (ASF) under one
0002 // or more contributor license agreements.  See the NOTICE file
0003 // distributed with this work for additional information
0004 // regarding copyright ownership.  The ASF licenses this file
0005 // to you under the Apache License, Version 2.0 (the
0006 // "License"); you may not use this file except in compliance
0007 // with the License.  You may obtain a copy of the License at
0008 //
0009 //   http://www.apache.org/licenses/LICENSE-2.0
0010 //
0011 // Unless required by applicable law or agreed to in writing,
0012 // software distributed under the License is distributed on an
0013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0014 // KIND, either express or implied.  See the License for the
0015 // specific language governing permissions and limitations
0016 // under the License.
0017 
0018 #pragma once
0019 
0020 #include <cstddef>
0021 #include <cstdint>
0022 #include <iterator>
0023 #include <numeric>
0024 #include <tuple>
0025 #include <utility>
0026 #include <vector>
0027 
0028 namespace arrow::internal {
0029 
0030 /// Create a vector containing the values from start up to stop
0031 template <typename T>
0032 std::vector<T> Iota(T start, T stop) {
0033   if (start > stop) {
0034     return {};
0035   }
0036   std::vector<T> result(static_cast<size_t>(stop - start));
0037   std::iota(result.begin(), result.end(), start);
0038   return result;
0039 }
0040 
0041 /// Create a vector containing the values from 0 up to length
0042 template <typename T>
0043 std::vector<T> Iota(T length) {
0044   return Iota(static_cast<T>(0), length);
0045 }
0046 
0047 /// Create a range from a callable which takes a single index parameter
0048 /// and returns the value of iterator on each call and a length.
0049 /// Only iterators obtained from the same range should be compared, the
0050 /// behaviour generally similar to other STL containers.
0051 template <typename Generator>
0052 class LazyRange {
0053  private:
0054   // callable which generates the values
0055   // has to be defined at the beginning of the class for type deduction
0056   const Generator gen_;
0057   // the length of the range
0058   int64_t length_;
0059 #ifdef _MSC_VER
0060   // workaround to VS2010 not supporting decltype properly
0061   // see https://stackoverflow.com/questions/21782846/decltype-for-class-member-function
0062   static Generator gen_static_;
0063 #endif
0064 
0065  public:
0066 #ifdef _MSC_VER
0067   using return_type = decltype(gen_static_(0));
0068 #else
0069   using return_type = decltype(gen_(0));
0070 #endif
0071 
0072   /// Construct a new range from a callable and length
0073   LazyRange(Generator gen, int64_t length) : gen_(gen), length_(length) {}
0074 
0075   // Class of the dependent iterator, created implicitly by begin and end
0076   class RangeIter {
0077    public:
0078     using difference_type = int64_t;
0079     using value_type = return_type;
0080     using reference = const value_type&;
0081     using pointer = const value_type*;
0082     using iterator_category = std::forward_iterator_tag;
0083 
0084 #ifdef _MSC_VER
0085     // msvc complains about unchecked iterators,
0086     // see https://stackoverflow.com/questions/21655496/error-c4996-checked-iterators
0087     using _Unchecked_type = typename LazyRange<Generator>::RangeIter;
0088 #endif
0089 
0090     RangeIter() = delete;
0091     RangeIter(const RangeIter& other) = default;
0092     RangeIter& operator=(const RangeIter& other) = default;
0093 
0094     RangeIter(const LazyRange<Generator>& range, int64_t index)
0095         : range_(&range), index_(index) {}
0096 
0097     const return_type operator*() const { return range_->gen_(index_); }
0098 
0099     RangeIter operator+(difference_type length) const {
0100       return RangeIter(*range_, index_ + length);
0101     }
0102 
0103     // pre-increment
0104     RangeIter& operator++() {
0105       ++index_;
0106       return *this;
0107     }
0108 
0109     // post-increment
0110     RangeIter operator++(int) {
0111       auto copy = RangeIter(*this);
0112       ++index_;
0113       return copy;
0114     }
0115 
0116     bool operator==(const typename LazyRange<Generator>::RangeIter& other) const {
0117       return this->index_ == other.index_ && this->range_ == other.range_;
0118     }
0119 
0120     bool operator!=(const typename LazyRange<Generator>::RangeIter& other) const {
0121       return this->index_ != other.index_ || this->range_ != other.range_;
0122     }
0123 
0124     int64_t operator-(const typename LazyRange<Generator>::RangeIter& other) const {
0125       return this->index_ - other.index_;
0126     }
0127 
0128     bool operator<(const typename LazyRange<Generator>::RangeIter& other) const {
0129       return this->index_ < other.index_;
0130     }
0131 
0132    private:
0133     // parent range reference
0134     const LazyRange* range_;
0135     // current index
0136     int64_t index_;
0137   };
0138 
0139   friend class RangeIter;
0140 
0141   // Create a new begin const iterator
0142   RangeIter begin() { return RangeIter(*this, 0); }
0143 
0144   // Create a new end const iterator
0145   RangeIter end() { return RangeIter(*this, length_); }
0146 };
0147 
0148 /// Helper function to create a lazy range from a callable (e.g. lambda) and length
0149 template <typename Generator>
0150 LazyRange<Generator> MakeLazyRange(Generator&& gen, int64_t length) {
0151   return LazyRange<Generator>(std::forward<Generator>(gen), length);
0152 }
0153 
0154 /// \brief A helper for iterating multiple ranges simultaneously, similar to C++23's
0155 /// zip() view adapter modelled after python's built-in zip() function.
0156 ///
0157 /// \code {.cpp}
0158 /// const std::vector<SomeTable>& tables = ...
0159 /// std::function<std::vector<std::string>()> GetNames = ...
0160 /// for (auto [table, name] : Zip(tables, GetNames())) {
0161 ///   static_assert(std::is_same_v<decltype(table), const SomeTable&>);
0162 ///   static_assert(std::is_same_v<decltype(name), std::string&>);
0163 ///   // temporaries (like this vector of strings) are kept alive for the
0164 ///   // duration of a loop and are safely movable).
0165 ///   RegisterTableWithName(std::move(name), &table);
0166 /// }
0167 /// \endcode
0168 ///
0169 /// The zipped sequence ends as soon as any of its member ranges ends.
0170 ///
0171 /// Always use `auto` for the loop's declaration; it will always be a tuple
0172 /// of references so for example using `const auto&` will compile but will
0173 /// *look* like forcing const-ness even though the members of the tuple are
0174 /// still mutable references.
0175 ///
0176 /// NOTE: we *could* make Zip a more full fledged range and enable things like
0177 /// - gtest recognizing it as a container; it currently doesn't since Zip is
0178 ///   always mutable so this breaks:
0179 ///       EXPECT_THAT(Zip(std::vector{0}, std::vector{1}),
0180 ///                   ElementsAre(std::tuple{0, 1}));
0181 /// - letting it be random access when possible so we can do things like *sort*
0182 ///   parallel ranges
0183 /// - ...
0184 ///
0185 /// However doing this will increase the compile time overhead of using Zip as
0186 /// long as we're still using headers. Therefore until we can use c++20 modules:
0187 /// *don't* extend Zip.
0188 template <typename Ranges, typename Indices>
0189 struct Zip;
0190 
0191 template <typename... Ranges>
0192 Zip(Ranges&&...) -> Zip<std::tuple<Ranges...>, std::index_sequence_for<Ranges...>>;
0193 
0194 template <typename... Ranges, size_t... I>
0195 struct Zip<std::tuple<Ranges...>, std::index_sequence<I...>> {
0196   explicit Zip(Ranges... ranges) : ranges_(std::forward<Ranges>(ranges)...) {}
0197 
0198   std::tuple<Ranges...> ranges_;
0199 
0200   using sentinel = std::tuple<decltype(std::end(std::get<I>(ranges_)))...>;
0201   constexpr sentinel end() { return {std::end(std::get<I>(ranges_))...}; }
0202 
0203   struct iterator : std::tuple<decltype(std::begin(std::get<I>(ranges_)))...> {
0204     using std::tuple<decltype(std::begin(std::get<I>(ranges_)))...>::tuple;
0205 
0206     constexpr auto operator*() {
0207       return std::tuple<decltype(*std::get<I>(*this))...>{*std::get<I>(*this)...};
0208     }
0209 
0210     constexpr iterator& operator++() {
0211       (++std::get<I>(*this), ...);
0212       return *this;
0213     }
0214 
0215     constexpr bool operator!=(const sentinel& s) const {
0216       bool all_iterators_valid = (... && (std::get<I>(*this) != std::get<I>(s)));
0217       return all_iterators_valid;
0218     }
0219   };
0220   constexpr iterator begin() { return {std::begin(std::get<I>(ranges_))...}; }
0221 };
0222 
0223 /// \brief A lazy sequence of integers which starts from 0 and never stops.
0224 ///
0225 /// This can be used in conjunction with Zip() to emulate python's built-in
0226 /// enumerate() function:
0227 ///
0228 /// \code {.cpp}
0229 /// const std::vector<SomeTable>& tables = ...
0230 /// for (auto [i, table] : Zip(Enumerate<>, tables)) {
0231 ///   std::cout << "#" << i << ": " << table.name() << std::endl;
0232 /// }
0233 /// \endcode
0234 template <typename I = size_t>
0235 constexpr auto Enumerate = [] {
0236   struct {
0237     struct sentinel {};
0238     constexpr sentinel end() const { return {}; }
0239 
0240     struct iterator {
0241       I value{0};
0242 
0243       constexpr I operator*() { return value; }
0244 
0245       constexpr iterator& operator++() {
0246         ++value;
0247         return *this;
0248       }
0249 
0250       constexpr std::true_type operator!=(sentinel) const { return {}; }
0251     };
0252     constexpr iterator begin() const { return {}; }
0253   } out;
0254 
0255   return out;
0256 }();
0257 
0258 }  // namespace arrow::internal