constexpr all the things in irange.h (#147633)

I got complaints while irangeifying some files in ExecuTorch
that irange could not be used in a constexpr function. This made the
complaints go away.

I added a constexpr function in irange_test that used to fail to build
with `error: variable of non-literal type 'iterator' (aka
'integer_iterator<int, true>') cannot be defined in a constexpr
function before C++23` and now builds fine.

Differential Revision: [D69959614](https://our.internmc.facebook.com/intern/diff/D69959614/)
Pull Request resolved: https://github.com/pytorch/pytorch/pull/147633
Approved by: https://github.com/albanD
This commit is contained in:
Scott Wolchok
2025-02-21 10:41:22 -08:00
committed by PyTorch MergeBot
parent 6e0b09728a
commit 84fcf1bb11
2 changed files with 42 additions and 12 deletions

View File

@@ -4,6 +4,8 @@
#include <gtest/gtest.h>
#include <array>
using namespace ::testing;
TEST(irangeTest, range_test) {
@@ -56,3 +58,31 @@ TEST(irange, empty_reverse_range_one_input) {
const std::vector<int> correct = {};
ASSERT_EQ(test_vec, correct);
}
constexpr std::array<int, 3> toy_iota() {
std::array<int, 3> result = {0};
for (const auto i : c10::irange(3)) {
result[i] = i;
}
return result;
}
constexpr std::array<int, 3> toy_iota_with_start(int start) {
std::array<int, 3> result = {0};
for (const auto i : c10::irange(start, start + 3)) {
result[i - start] = i;
}
return result;
}
TEST(irange, constexpr_ok) {
constexpr auto arr = toy_iota();
static_assert(arr[0] == 0);
static_assert(arr[1] == 1);
static_assert(arr[2] == 2);
constexpr auto arr2 = toy_iota_with_start(4);
static_assert(arr2[0] == 4);
static_assert(arr2[1] == 5);
static_assert(arr2[2] == 6);
}

View File

@@ -24,28 +24,28 @@ struct integer_iterator {
using pointer = I*;
using reference = I&;
explicit integer_iterator(I value) : value(value) {}
explicit constexpr integer_iterator(I value) : value(value) {}
I operator*() const {
constexpr I operator*() const {
return value;
}
I const* operator->() const {
constexpr I const* operator->() const {
return &value;
}
integer_iterator& operator++() {
constexpr integer_iterator& operator++() {
++value;
return *this;
}
integer_iterator operator++(int) {
constexpr integer_iterator operator++(int) {
const auto copy = *this;
++*this;
return copy;
}
bool operator==(const integer_iterator& other) const {
constexpr bool operator==(const integer_iterator& other) const {
if constexpr (one_sided) {
// Range-for loops' end test is `begin != end`, not `begin <
// end`. To handle `c10::irange(n)` where n < 0 (which should be
@@ -64,7 +64,7 @@ struct integer_iterator {
return false; // Horrible hack
}
bool operator!=(const integer_iterator& other) const {
constexpr bool operator!=(const integer_iterator& other) const {
return !(*this == other);
}
@@ -80,12 +80,12 @@ template <
std::enable_if_t<std::is_integral_v<I>, bool> = true>
struct integer_range {
public:
integer_range(I begin, I end) : begin_(begin), end_(end) {}
constexpr integer_range(I begin, I end) : begin_(begin), end_(end) {}
using iterator = detail::integer_iterator<I, one_sided>;
iterator begin() const {
constexpr iterator begin() const {
return begin_;
}
iterator end() const {
constexpr iterator end() const {
return end_;
}
@@ -103,7 +103,7 @@ template <
typename Integer2,
std::enable_if_t<std::is_integral_v<Integer1>, bool> = true,
std::enable_if_t<std::is_integral_v<Integer2>, bool> = true>
integer_range<Integer2> irange(Integer1 begin, Integer2 end) {
constexpr integer_range<Integer2> irange(Integer1 begin, Integer2 end) {
// If end<=begin then the range is empty; we can achieve this effect by
// choosing the larger of {begin, end} as the loop terminator
return {
@@ -116,7 +116,7 @@ integer_range<Integer2> irange(Integer1 begin, Integer2 end) {
template <
typename Integer,
std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
integer_range<Integer, true> irange(Integer end) {
constexpr integer_range<Integer, true> irange(Integer end) {
return {Integer(), end};
}