8257466: Improve enum iteration
Improve support for iteration on enums that are just range of values, without named enumerators. Reviewed-by: iklam, lfoltan
This commit is contained in:
parent
02a0a027f4
commit
3932527288
@ -28,6 +28,7 @@
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "metaprogramming/enableIf.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
// Iteration support for enums.
|
||||
@ -73,21 +74,52 @@
|
||||
// }
|
||||
|
||||
// EnumeratorRange is a traits type supporting iteration over the enumerators of T.
|
||||
// Specializations must provide static const data members named
|
||||
// "_first" and "_last", whose values are the smallest / largest
|
||||
// (resp.) enumerator values for T. For iteration, the enumerators of
|
||||
// T must have sequential values in that range.
|
||||
// Specializations must provide static const data members named "_start" and "_end".
|
||||
// The type of _start and _end must be the underlying type of T.
|
||||
// _start is the inclusive lower bound of values in the range.
|
||||
// _end is the exclusive upper bound of values in the range.
|
||||
// The enumerators of T must have sequential values in that range.
|
||||
template<typename T> struct EnumeratorRange;
|
||||
|
||||
// Specialize EnumeratorRange<T>.
|
||||
#define ENUMERATOR_RANGE(T, First, Last) \
|
||||
template<> struct EnumeratorRange<T> { \
|
||||
static constexpr T _first = First; \
|
||||
static constexpr T _last = Last; \
|
||||
// Helper class for ENUMERATOR_RANGE and ENUMERATOR_VALUE_RANGE.
|
||||
struct EnumeratorRangeImpl : AllStatic {
|
||||
template<typename T> using Underlying = std::underlying_type_t<T>;
|
||||
|
||||
// T not deduced to verify argument is of expected type.
|
||||
template<typename T, typename U, ENABLE_IF(std::is_same<T, U>::value)>
|
||||
static constexpr Underlying<T> start_value(U first) {
|
||||
return static_cast<Underlying<T>>(first);
|
||||
}
|
||||
|
||||
// T not deduced to verify argument is of expected type.
|
||||
template<typename T, typename U, ENABLE_IF(std::is_same<T, U>::value)>
|
||||
static constexpr Underlying<T> end_value(U last) {
|
||||
Underlying<T> value = static_cast<Underlying<T>>(last);
|
||||
assert(value < std::numeric_limits<Underlying<T>>::max(), "end value overflow");
|
||||
return static_cast<Underlying<T>>(value + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialize EnumeratorRange<T>. Start and End must be constant expressions
|
||||
// whose value is convertible to the underlying type of T. They provide the
|
||||
// values of the required _start and _end members respectively.
|
||||
#define ENUMERATOR_VALUE_RANGE(T, Start, End) \
|
||||
template<> struct EnumeratorRange<T> { \
|
||||
static constexpr EnumeratorRangeImpl::Underlying<T> _start{Start}; \
|
||||
static constexpr EnumeratorRangeImpl::Underlying<T> _end{End}; \
|
||||
};
|
||||
|
||||
// A helper class for EnumIterator, computing some additional information the
|
||||
// iterator uses, based on T and EnumeratorRange.
|
||||
// Specialize EnumeratorRange<T>. First and Last must be constant expressions
|
||||
// of type T. They determine the values of the required _start and _end members
|
||||
// respectively. _start is the underlying value of First. _end is the underlying
|
||||
// value of Last, plus one.
|
||||
#define ENUMERATOR_RANGE(T, First, Last) \
|
||||
ENUMERATOR_VALUE_RANGE(T, \
|
||||
EnumeratorRangeImpl::start_value<T>(First), \
|
||||
EnumeratorRangeImpl::end_value<T>(Last));
|
||||
|
||||
// A helper class for EnumRange and EnumIterator, computing some
|
||||
// additional information based on T and EnumeratorRange<T>.
|
||||
template<typename T>
|
||||
class EnumIterationTraits : AllStatic {
|
||||
using RangeType = EnumeratorRange<T>;
|
||||
@ -96,21 +128,20 @@ public:
|
||||
// The underlying type for T.
|
||||
using Underlying = std::underlying_type_t<T>;
|
||||
|
||||
// The first enumerator of T.
|
||||
static constexpr T _first = RangeType::_first;
|
||||
|
||||
// The last enumerator of T.
|
||||
static constexpr T _last = RangeType::_last;
|
||||
|
||||
static_assert(static_cast<Underlying>(_last) <
|
||||
std::numeric_limits<Underlying>::max(),
|
||||
"No one-past-the-end value for enum");
|
||||
|
||||
// The value of the first enumerator of T.
|
||||
static constexpr Underlying _start = static_cast<Underlying>(_first);
|
||||
static constexpr Underlying _start = RangeType::_start;
|
||||
|
||||
// The one-past-the-end value for T.
|
||||
static constexpr Underlying _end = static_cast<Underlying>(_last) + 1;
|
||||
static constexpr Underlying _end = RangeType::_end;
|
||||
|
||||
// The first enumerator of T.
|
||||
static constexpr T _first = static_cast<T>(_start);
|
||||
|
||||
// The last enumerator of T.
|
||||
static constexpr T _last = static_cast<T>(_end - 1);
|
||||
|
||||
static_assert(_start != _end, "empty range");
|
||||
static_assert(_start <= _end, "invalid range"); // <= so only one failure when ==.
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -125,6 +156,8 @@ class EnumIterator {
|
||||
}
|
||||
|
||||
public:
|
||||
using EnumType = T;
|
||||
|
||||
// Return a beyond-the-end iterator.
|
||||
constexpr EnumIterator() : _value(Traits::_end) {}
|
||||
|
||||
@ -180,6 +213,7 @@ class EnumRange {
|
||||
Underlying _end;
|
||||
|
||||
public:
|
||||
using EnumType = T;
|
||||
using Iterator = EnumIterator<T>;
|
||||
|
||||
// Default constructor gives the full range.
|
||||
@ -214,6 +248,17 @@ public:
|
||||
constexpr size_t size() const {
|
||||
return static_cast<size_t>(_end - _start); // _end is exclusive
|
||||
}
|
||||
|
||||
constexpr T first() const { return static_cast<T>(_start); }
|
||||
constexpr T last() const { return static_cast<T>(_end - 1); }
|
||||
|
||||
// Convert value to a zero-based index into the range [first(), last()].
|
||||
// precondition: first() <= value && value <= last()
|
||||
constexpr size_t index(T value) const {
|
||||
assert(first() <= value, "out of bounds");
|
||||
assert(value <= last(), "out of bounds");
|
||||
return static_cast<size_t>(static_cast<Underlying>(value) - _start);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_UTILITIES_ENUMITERATOR_HPP
|
||||
|
112
test/hotspot/gtest/utilities/test_enumIterator.cpp
Normal file
112
test/hotspot/gtest/utilities/test_enumIterator.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "utilities/enumIterator.hpp"
|
||||
#include <type_traits>
|
||||
#include "unittest.hpp"
|
||||
|
||||
enum class ExplicitTest : int { value1, value2, value3 };
|
||||
ENUMERATOR_RANGE(ExplicitTest, ExplicitTest::value1, ExplicitTest::value3);
|
||||
constexpr int explicit_start = 0;
|
||||
constexpr int explicit_end = 3;
|
||||
|
||||
enum class ImplicitTest : int {};
|
||||
ENUMERATOR_VALUE_RANGE(ImplicitTest, 5, 10);
|
||||
constexpr int implicit_start = 5;
|
||||
constexpr int implicit_end = 10;
|
||||
|
||||
TEST(TestEnumIterator, explicit_full_range) {
|
||||
using Range = EnumRange<ExplicitTest>;
|
||||
constexpr Range range{};
|
||||
EXPECT_TRUE((std::is_same<ExplicitTest, Range::EnumType>::value));
|
||||
EXPECT_EQ(size_t(explicit_end - explicit_start), range.size());
|
||||
EXPECT_EQ(ExplicitTest::value1, range.first());
|
||||
EXPECT_EQ(ExplicitTest::value3, range.last());
|
||||
EXPECT_EQ(size_t(1), range.index(ExplicitTest::value2));
|
||||
}
|
||||
|
||||
TEST(TestEnumIterator, explicit_partial_range) {
|
||||
using Range = EnumRange<ExplicitTest>;
|
||||
constexpr Range range{ExplicitTest::value2};
|
||||
EXPECT_TRUE((std::is_same<ExplicitTest, Range::EnumType>::value));
|
||||
EXPECT_EQ(size_t(explicit_end - (explicit_start + 1)), range.size());
|
||||
EXPECT_EQ(ExplicitTest::value2, range.first());
|
||||
EXPECT_EQ(ExplicitTest::value3, range.last());
|
||||
EXPECT_EQ(size_t(0), range.index(ExplicitTest::value2));
|
||||
}
|
||||
|
||||
TEST(TestEnumIterator, implicit_full_range) {
|
||||
using Range = EnumRange<ImplicitTest>;
|
||||
constexpr Range range{};
|
||||
EXPECT_TRUE((std::is_same<ImplicitTest, Range::EnumType>::value));
|
||||
EXPECT_EQ(size_t(implicit_end - implicit_start), range.size());
|
||||
EXPECT_EQ(static_cast<ImplicitTest>(implicit_start), range.first());
|
||||
EXPECT_EQ(static_cast<ImplicitTest>(implicit_end - 1), range.last());
|
||||
EXPECT_EQ(size_t(2), range.index(static_cast<ImplicitTest>(implicit_start + 2)));
|
||||
}
|
||||
|
||||
TEST(TestEnumIterator, implicit_partial_range) {
|
||||
using Range = EnumRange<ImplicitTest>;
|
||||
constexpr Range range{static_cast<ImplicitTest>(implicit_start + 2)};
|
||||
EXPECT_TRUE((std::is_same<ImplicitTest, Range::EnumType>::value));
|
||||
EXPECT_EQ(size_t(implicit_end - (implicit_start + 2)), range.size());
|
||||
EXPECT_EQ(static_cast<ImplicitTest>(implicit_start + 2), range.first());
|
||||
EXPECT_EQ(static_cast<ImplicitTest>(implicit_end - 1), range.last());
|
||||
EXPECT_EQ(size_t(1), range.index(static_cast<ImplicitTest>(implicit_start + 3)));
|
||||
}
|
||||
|
||||
TEST(TestEnumIterator, explict_iterator) {
|
||||
using Range = EnumRange<ExplicitTest>;
|
||||
using Iterator = EnumIterator<ExplicitTest>;
|
||||
constexpr Range range{};
|
||||
EXPECT_EQ(range.first(), *range.begin());
|
||||
EXPECT_EQ(Iterator(range.first()), range.begin());
|
||||
EnumIterator<ExplicitTest> it = range.begin();
|
||||
++it;
|
||||
EXPECT_EQ(ExplicitTest::value2, *it);
|
||||
it = range.begin();
|
||||
for (int i = explicit_start; i < explicit_end; ++i, ++it) {
|
||||
ExplicitTest value = static_cast<ExplicitTest>(i);
|
||||
EXPECT_EQ(value, *it);
|
||||
EXPECT_EQ(Iterator(value), it);
|
||||
EXPECT_EQ(size_t(i - explicit_start), range.index(value));
|
||||
}
|
||||
EXPECT_EQ(it, range.end());
|
||||
}
|
||||
|
||||
TEST(TestEnumIterator, implicit_iterator) {
|
||||
using Range = EnumRange<ImplicitTest>;
|
||||
using Iterator = EnumIterator<ImplicitTest>;
|
||||
constexpr Range range{};
|
||||
EXPECT_EQ(range.first(), *range.begin());
|
||||
EXPECT_EQ(Iterator(range.first()), range.begin());
|
||||
EnumIterator<ImplicitTest> it = range.begin();
|
||||
for (int i = implicit_start; i < implicit_end; ++i, ++it) {
|
||||
ImplicitTest value = static_cast<ImplicitTest>(i);
|
||||
EXPECT_EQ(value, *it);
|
||||
EXPECT_EQ(Iterator(value), it);
|
||||
EXPECT_EQ(size_t(i - implicit_start), range.index(value));
|
||||
}
|
||||
EXPECT_EQ(it, range.end());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user