Skip to content

Commit 67f2c16

Browse files
committed
ENH: Add std::vector-like interface to VariableLengthVector
Added the following member functions: empty() size() at(size_type) front() back() data() begin() end() cbegin() cend() rbegin() rend() crbegin() crend() As well as STL-style nested type aliases. Aims to ease using `VariableLengthVector` in generic code.
1 parent 53158e3 commit 67f2c16

File tree

4 files changed

+365
-0
lines changed

4 files changed

+365
-0
lines changed

Modules/Core/Common/include/itkVariableLengthVector.h

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <cassert>
2222
#include <algorithm>
23+
#include <iterator>
2324
#include <type_traits>
2425
#include "itkNumericTraits.h"
2526
#include "itkMetaProgrammingLibrary.h"
@@ -314,6 +315,17 @@ class ITK_TEMPLATE_EXPORT VariableLengthVector
314315
/** Typedef used to indicate the number of elements in the vector */
315316
using ElementIdentifier = unsigned int;
316317

318+
// Nested types from C++ Standard section "Container requirements" [container.reqmts], and from `std::vector`:
319+
using value_type = TValue;
320+
using reference = TValue &;
321+
using const_reference = const TValue &;
322+
using iterator = TValue *;
323+
using const_iterator = const TValue *;
324+
using difference_type = ptrdiff_t;
325+
using size_type = size_t;
326+
using reverse_iterator = std::reverse_iterator<iterator>;
327+
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
328+
317329
/** Default constructor. It is created with an empty array
318330
* it has to be allocated later by assignment, \c SetSize() or \c Reserve().
319331
* \post \c m_Data is null
@@ -983,11 +995,156 @@ class ITK_TEMPLATE_EXPORT VariableLengthVector
983995
return !m_LetArrayManageMemory;
984996
}
985997

998+
[[nodiscard]] bool
999+
empty() const noexcept
1000+
{
1001+
return m_NumElements == 0;
1002+
}
1003+
1004+
[[nodiscard]] size_type
1005+
size() const noexcept
1006+
{
1007+
return m_NumElements;
1008+
}
1009+
1010+
[[nodiscard]] reference
1011+
at(size_type pos)
1012+
{
1013+
ExceptionThrowingBoundsCheck(pos);
1014+
return m_Data[pos];
1015+
}
1016+
1017+
[[nodiscard]] const_reference
1018+
at(size_type pos) const
1019+
{
1020+
ExceptionThrowingBoundsCheck(pos);
1021+
return m_Data[pos];
1022+
}
1023+
1024+
[[nodiscard]] reference
1025+
front()
1026+
{
1027+
return m_Data[0];
1028+
}
1029+
1030+
[[nodiscard]] const_reference
1031+
front() const
1032+
{
1033+
return m_Data[0];
1034+
}
1035+
1036+
[[nodiscard]] reference
1037+
back()
1038+
{
1039+
return m_Data[m_NumElements - 1];
1040+
}
1041+
1042+
[[nodiscard]] const_reference
1043+
back() const
1044+
{
1045+
return m_Data[m_NumElements - 1];
1046+
}
1047+
1048+
[[nodiscard]] TValue *
1049+
data() noexcept
1050+
{
1051+
return m_Data;
1052+
}
1053+
1054+
[[nodiscard]] const TValue *
1055+
data() const noexcept
1056+
{
1057+
return m_Data;
1058+
}
1059+
1060+
[[nodiscard]] const_iterator
1061+
cbegin() const noexcept
1062+
{
1063+
return m_Data;
1064+
}
1065+
1066+
[[nodiscard]] iterator
1067+
begin() noexcept
1068+
{
1069+
return m_Data;
1070+
}
1071+
1072+
[[nodiscard]] const_iterator
1073+
begin() const noexcept
1074+
{
1075+
return cbegin();
1076+
}
1077+
1078+
[[nodiscard]] const_iterator
1079+
cend() const noexcept
1080+
{
1081+
return m_Data + m_NumElements;
1082+
}
1083+
1084+
[[nodiscard]] iterator
1085+
end() noexcept
1086+
{
1087+
return m_Data + m_NumElements;
1088+
}
1089+
1090+
[[nodiscard]] const_iterator
1091+
end() const noexcept
1092+
{
1093+
return cend();
1094+
}
1095+
1096+
[[nodiscard]] reverse_iterator
1097+
rbegin()
1098+
{
1099+
return reverse_iterator{ end() };
1100+
}
1101+
1102+
[[nodiscard]] const_reverse_iterator
1103+
crbegin() const
1104+
{
1105+
return const_reverse_iterator{ cend() };
1106+
}
1107+
1108+
[[nodiscard]] const_reverse_iterator
1109+
rbegin() const
1110+
{
1111+
return crbegin();
1112+
}
1113+
1114+
[[nodiscard]] reverse_iterator
1115+
rend()
1116+
{
1117+
return reverse_iterator{ begin() };
1118+
}
1119+
1120+
[[nodiscard]] const_reverse_iterator
1121+
crend() const
1122+
{
1123+
return const_reverse_iterator{ cbegin() };
1124+
}
1125+
1126+
[[nodiscard]] const_reverse_iterator
1127+
rend() const
1128+
{
1129+
return crend();
1130+
}
1131+
9861132
private:
9871133
bool m_LetArrayManageMemory{ true }; // if true, the array is responsible
9881134
// for memory of data
9891135
TValue * m_Data{}; // Array to hold data
9901136
ElementIdentifier m_NumElements{ 0 };
1137+
1138+
void
1139+
ExceptionThrowingBoundsCheck(size_type pos)
1140+
{
1141+
if (pos >= m_NumElements)
1142+
{
1143+
throw std::out_of_range("Out of range: `itk::VariableLengthVector::at` argument `pos` (which is " +
1144+
std::to_string(pos) + ") should be less than `m_NumElements` (which is " +
1145+
std::to_string(m_NumElements) + ")!");
1146+
}
1147+
}
9911148
};
9921149

9931150
/// \cond HIDE_META_PROGRAMMING

Modules/Core/Common/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,7 @@ set(
19021902
itkSizeGTest.cxx
19031903
itkSmartPointerGTest.cxx
19041904
itkSymmetricSecondRankTensorGTest.cxx
1905+
itkVariableLengthVectorGTest.cxx
19051906
itkVectorContainerGTest.cxx
19061907
itkVectorGTest.cxx
19071908
itkWeakPointerGTest.cxx

Modules/Core/Common/test/itkRangeGTestUtilities.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ class RangeGTestUtilities
4141
}
4242

4343

44+
template <typename TRange>
45+
static void
46+
ExpectReverseBeginIsReverseEndWhenRangeIsDefaultConstructed()
47+
{
48+
TRange defaultConstructedRange;
49+
EXPECT_EQ(std::rbegin(defaultConstructedRange), std::rend(defaultConstructedRange));
50+
}
51+
52+
4453
template <typename TRange>
4554
static void
4655
ExpectZeroSizeWhenRangeIsDefaultConstructed()
@@ -156,6 +165,55 @@ class RangeGTestUtilities
156165
}
157166

158167

168+
// Checks the nested types that are required according to the C++ Standard section "Container requirements"
169+
// [container.reqmts].
170+
template <typename TContainer, typename TValue = typename TContainer::ValueType>
171+
static constexpr bool
172+
CheckContainerRequirementsOnNestedTypes()
173+
{
174+
AssertSameType<typename TContainer::value_type, TValue>();
175+
AssertSameType<typename TContainer::reference, TValue &>();
176+
AssertSameType<typename TContainer::const_reference, const TValue &>();
177+
178+
// The C++ Standard requires that "the type `iterator` is convertible to `const_iterator`".
179+
static_assert(std::is_convertible_v<typename TContainer::iterator, typename TContainer::const_iterator>);
180+
181+
using difference_type = typename TContainer::difference_type;
182+
using size_type = typename TContainer::size_type;
183+
184+
static_assert(std::is_signed_v<difference_type>);
185+
static_assert(std::is_integral_v<difference_type>);
186+
187+
// `size_type` must be "an unsigned integer type that can represent any non-negative value of `difference_type`."
188+
static_assert(std::is_unsigned_v<size_type>);
189+
static_assert(sizeof(size_type) >= sizeof(difference_type));
190+
return true;
191+
}
192+
193+
194+
// Expects that `distance(begin, end)` is equal to `size`, for the specified container.
195+
template <typename TContainer>
196+
static void
197+
ExpectDistanceFromBeginToEndEqualsSize(const TContainer & container)
198+
{
199+
const auto distance = std::distance(std::begin(container), std::end(container));
200+
ASSERT_GE(distance, 0);
201+
EXPECT_EQ(static_cast<size_t>(distance), std::size(container));
202+
}
203+
204+
205+
// Expects that `distance(&front, &back) + 1` is equal to `size`, for the specified (non-empty) container.
206+
template <typename TContainer>
207+
static void
208+
ExpectDistanceFromFrontToBackPlusOneEqualsSize(const TContainer & container)
209+
{
210+
ASSERT_FALSE(std::empty(container));
211+
const auto distance = std::distance(&container.front(), &container.back());
212+
ASSERT_GE(distance, 0);
213+
EXPECT_EQ(static_cast<size_t>(distance + 1), std::size(container));
214+
}
215+
216+
159217
// Tells if `distance(&front, &back) + 1` is equal to `size`, for the specified container.
160218
template <typename TContainer>
161219
static constexpr bool
@@ -177,6 +235,13 @@ class RangeGTestUtilities
177235
}
178236

179237
private:
238+
template <typename T1, typename T2>
239+
static constexpr void
240+
AssertSameType()
241+
{
242+
static_assert(std::is_same_v<T1, T2>);
243+
}
244+
180245
template <typename TRange>
181246
static void
182247
ExpectRangesHaveEqualBeginAndEnd(const TRange & range1, const TRange & range2)

0 commit comments

Comments
 (0)