Add Enumerate() a C++ version of Python's builtin.
It allows code like the following:
for (auto [i, value] : container) { ... }
Bug: dawn:2222
Change-Id: I0fc95db690f738e742da1c8e661b8f8a98d03f3b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162502
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/common/BUILD.gn b/src/dawn/common/BUILD.gn
index 6d9d917..48c12fe 100644
--- a/src/dawn/common/BUILD.gn
+++ b/src/dawn/common/BUILD.gn
@@ -255,6 +255,7 @@
"CoreFoundationRef.h",
"DynamicLib.cpp",
"DynamicLib.h",
+ "Enumerator.h",
"FutureUtils.h",
"GPUInfo.cpp",
"GPUInfo.h",
diff --git a/src/dawn/common/CMakeLists.txt b/src/dawn/common/CMakeLists.txt
index f676382..cfbcce8 100644
--- a/src/dawn/common/CMakeLists.txt
+++ b/src/dawn/common/CMakeLists.txt
@@ -58,6 +58,7 @@
"CoreFoundationRef.h"
"DynamicLib.cpp"
"DynamicLib.h"
+ "Enumerator.h"
"FutureUtils.h"
"GPUInfo.cpp"
"GPUInfo.h"
diff --git a/src/dawn/common/Enumerator.h b/src/dawn/common/Enumerator.h
new file mode 100644
index 0000000..7227ca1
--- /dev/null
+++ b/src/dawn/common/Enumerator.h
@@ -0,0 +1,83 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_COMMON_ENUMERATOR_H_
+#define SRC_DAWN_COMMON_ENUMERATOR_H_
+
+#include <type_traits>
+#include <utility>
+
+namespace dawn {
+
+// An iterator over a range that gives both the index and the value. It can be used like so:
+//
+// ityp::span<Index, Value> span = ...;
+// for (auto [i, value] : Enumerate(span)) {
+// // Use i that's of type Index.
+// // Use value that's of type Value&.
+// }
+template <typename Index, typename Value>
+class EnumerateRange final {
+ public:
+ EnumerateRange(Index size, Value* begin) : mSize(size), mBegin(begin) {}
+
+ class Iterator final {
+ public:
+ Iterator(Index index, Value* value) : mIndex(index), mValue(value) {}
+ bool operator==(const Iterator& other) const { return other.mIndex == mIndex; }
+ bool operator!=(const Iterator& other) const { return !(*this == other); }
+ Iterator& operator++() {
+ mIndex++;
+ mValue++;
+ return *this;
+ }
+ std::pair<Index, Value&> operator*() const { return {mIndex, *mValue}; }
+
+ private:
+ Index mIndex;
+ Value* mValue;
+ };
+
+ Iterator begin() const { return Iterator(Index{}, mBegin); }
+ // Note that iterator comparison only uses mIndex, so we can save the computation of mValue for
+ // the end() iterator.
+ Iterator end() const { return Iterator(mSize, nullptr); }
+
+ private:
+ Index mSize;
+ Value* mBegin;
+};
+
+template <typename T,
+ typename Index = decltype(std::declval<T>().size()),
+ typename Value = std::remove_pointer_t<decltype(std::declval<T>().data())>>
+EnumerateRange<Index, Value> Enumerate(T& v) {
+ return {v.size(), v.data()};
+}
+} // namespace dawn
+
+#endif // SRC_DAWN_COMMON_ENUMERATOR_H_
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 15b5c6d..8080bb4 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -308,6 +308,7 @@
"unittests/ContentLessObjectCacheTests.cpp",
"unittests/EnumClassBitmasksTests.cpp",
"unittests/EnumMaskIteratorTests.cpp",
+ "unittests/EnumeratorTests.cpp",
"unittests/ErrorTests.cpp",
"unittests/FeatureTests.cpp",
"unittests/GPUInfoTests.cpp",
diff --git a/src/dawn/tests/unittests/EnumeratorTests.cpp b/src/dawn/tests/unittests/EnumeratorTests.cpp
new file mode 100644
index 0000000..c20db30
--- /dev/null
+++ b/src/dawn/tests/unittests/EnumeratorTests.cpp
@@ -0,0 +1,136 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <array>
+#include <vector>
+
+#include "dawn/common/Compiler.h"
+#include "dawn/common/Enumerator.h"
+#include "dawn/common/ityp_array.h"
+#include "dawn/common/ityp_span.h"
+#include "dawn/common/ityp_vector.h"
+#include "gtest/gtest.h"
+
+namespace dawn {
+namespace {
+
+class EnumeratorTest : public testing::Test {
+ protected:
+ template <typename Index, typename Thing, typename Value>
+ void Check(Thing thingToEnumerate, const Value* values) {
+ size_t i = 0;
+ for (auto [index, value] : Enumerate(thingToEnumerate)) {
+ ASSERT_EQ(index, Index(i));
+ ASSERT_EQ(value, values[i]);
+ i++;
+ }
+ }
+
+ template <typename Thing>
+ void CheckEmpty(Thing thingToEnumerate) {
+ for (auto [index, value] : Enumerate(thingToEnumerate)) {
+ DAWN_UNUSED(index);
+ DAWN_UNUSED(value);
+ FAIL();
+ }
+ }
+
+ using Int = TypedInteger<struct IntT, size_t>;
+};
+
+// Test that Enumerate works with std::array
+TEST_F(EnumeratorTest, StdArray) {
+ // Empty array
+ std::array<uint32_t, 0> emptyThing{};
+ CheckEmpty(emptyThing);
+
+ // Non-empty array
+ std::array<uint32_t, 3> thing = {37, 45, 67};
+ Check<size_t>(thing, thing.data());
+}
+
+// Test that Enumerate works with std::vector
+TEST_F(EnumeratorTest, StdVector) {
+ // Empty vector
+ std::vector<uint32_t> emptyThing;
+ CheckEmpty(emptyThing);
+
+ // Non-empty vector
+ std::vector<uint32_t> thing = {{37, 45, 67}};
+ Check<size_t>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::array
+TEST_F(EnumeratorTest, ITypArray) {
+ // Empty array
+ ityp::array<Int, uint32_t, 0> emptyThing;
+ CheckEmpty(emptyThing);
+
+ // Non-empty array
+ ityp::array<Int, uint32_t, 3> thing = {37u, 45u, 67u};
+ Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::span
+TEST_F(EnumeratorTest, ITypSpan) {
+ // Empty span
+ ityp::span<Int, uint32_t> emptyThing;
+ CheckEmpty(emptyThing);
+
+ // Empty span for a non-null pointer.
+ uint32_t whatever = 923847;
+ auto emptyThingWithGarbagePointer = ityp::SpanFromUntyped<Int>(&whatever, 0);
+ CheckEmpty(emptyThingWithGarbagePointer);
+
+ // Non-empty span
+ std::array<uint32_t, 3> backingArray = {{37, 45, 67}};
+ auto thing = ityp::SpanFromUntyped<Int>(backingArray.data(), 3);
+ Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate works with ityp::vector
+TEST_F(EnumeratorTest, ITypVector) {
+ // Empty vector
+ ityp::vector<Int, uint32_t> emptyThing;
+ CheckEmpty(emptyThing);
+
+ // Non-empty vector
+ ityp::vector<Int, uint32_t> thing = {{37, 45, 67}};
+ Check<Int>(thing, thing.data());
+}
+
+// Test that Enumerate(const container) will give const reference to the values.
+TEST_F(EnumeratorTest, ConstContainer) {
+ // Non-empty array
+ const ityp::array<Int, uint32_t, 3> values = {37u, 45u, 67u};
+ for (auto [i, value] : Enumerate(values)) {
+ static_assert(std::is_same_v<decltype(value), const uint32_t&>);
+ }
+}
+
+} // anonymous namespace
+} // namespace dawn