Skip to content

Commit

Permalink
ENH: ImageRegion support C++17 structured binding
Browse files Browse the repository at this point in the history
Added `get()` member functions and specializations of `std::tuple_size` and
`std::tuple_element` for ImageRegion, in order to support structured binding,
for example:

    auto [index, size] = image.GetRequestedRegion();

Suppressed the undeserved Clang-Tidy "cert-dcl58-cpp" warnings that appeared
when specializing those `std` templates.
  • Loading branch information
N-Dekker authored and hjmjohnson committed Dec 26, 2023
1 parent af1182a commit 72aa9a6
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/itk_dict.txt
Expand Up @@ -136,6 +136,8 @@ Minc
MincTransformIO
Minkowski
Musser
NOLINTBEGIN
NOLINTEND
NVidia
Negahdaripour
Newmark
Expand Down Expand Up @@ -279,6 +281,7 @@ callosum
centerline
cdf
cdfs
cert
cfl
cgi
cha
Expand All @@ -303,6 +306,7 @@ confocal
cooccurrence
costless
cp
cpp
cr
cranio
creativecommons
Expand All @@ -322,6 +326,7 @@ d'Attente
dDwWmMyY
da
dat
dcl
dcmimage
dcmtk
datablock
Expand Down
64 changes: 64 additions & 0 deletions Modules/Core/Common/include/itkImageRegion.h
Expand Up @@ -33,6 +33,8 @@
#include "itkSize.h"
#include "itkContinuousIndex.h"
#include "itkMath.h"
#include <type_traits> // For conditional and integral_constant.
#include <utility> // For tuple_element and tuple_size.

// Macro added to each `ImageRegion` member function that overrides a virtual member function of `Region`. In the
// future, `ImageRegion` will no longer inherit from `Region`, so then those `ImageRegion` member functions will no
Expand Down Expand Up @@ -357,6 +359,39 @@ class ITK_TEMPLATE_EXPORT ImageRegion final
SliceRegion
Slice(const unsigned int dim) const;

/** Supports tuple-like access: `get<0>()` returns a reference to the index and `get<1>()` returns a reference to the
* size of the region. */
template <size_t VTupleIndex>
[[nodiscard]] auto &
get()
{
if constexpr (VTupleIndex == 0)
{
return m_Index;
}
else
{
static_assert(VTupleIndex == 1);
return m_Size;
}
}

/** Supports tuple-like access. Const overload. */
template <size_t VTupleIndex>
[[nodiscard]] const auto &
get() const
{
if constexpr (VTupleIndex == 0)
{
return m_Index;
}
else
{
static_assert(VTupleIndex == 1);
return m_Size;
}
}

protected:
/** Methods invoked by Print() to print information about the object
* including superclasses. Typically not called by the user (use Print()
Expand All @@ -378,6 +413,35 @@ std::ostream &
operator<<(std::ostream & os, const ImageRegion<VImageDimension> & region);
} // end namespace itk


namespace std
{
// NOLINTBEGIN(cert-dcl58-cpp)
// Locally suppressed the following warning from Clang-Tidy (LLVM 17.0.1), as it appears undeserved.
// > warning: modification of 'std' namespace can result in undefined behavior [cert-dcl58-cpp]

/** `std::tuple_size` specialization, needed for ImageRegion to support C++ structured binding.
*
* Example, using structured binding to retrieve the index and size of a region:
\code
auto [index, size] = image.GetRequestedRegion();
\endcode
*/
template <unsigned int VImageDimension>
struct tuple_size<itk::ImageRegion<VImageDimension>> : integral_constant<size_t, 2>
{};

/** `std::tuple_element` specialization, needed for ImageRegion to support C++ structured binding. */
template <size_t VTupleIndex, unsigned int VImageDimension>
struct tuple_element<VTupleIndex, itk::ImageRegion<VImageDimension>>
: conditional<VTupleIndex == 0, itk::Index<VImageDimension>, itk::Size<VImageDimension>>
{
static_assert(VTupleIndex < tuple_size_v<itk::ImageRegion<VImageDimension>>);
};

// NOLINTEND(cert-dcl58-cpp)
} // namespace std

#undef itkRegionOverrideMacro

#ifndef ITK_MANUAL_INSTANTIATION
Expand Down
25 changes: 25 additions & 0 deletions Modules/Core/Common/test/itkImageRegionGTest.cxx
Expand Up @@ -194,3 +194,28 @@ TEST(ImageRegion, CropLargerToSmallerRegionAndViceVersa)
}
}
}


// Tests C++ structured binding of an ImageRegion.
TEST(ImageRegion, SupportsStructuredBinding)
{
using RegionType = itk::ImageRegion<2>;
using IndexType = RegionType::IndexType;
using SizeType = RegionType::SizeType;

RegionType region{};
auto && [index, size] = region;

static_assert(std::is_same_v<decltype(index), IndexType>);
static_assert(std::is_same_v<decltype(size), SizeType>);
EXPECT_EQ(&index, &(region.GetIndex()));
EXPECT_EQ(&size, &(region.GetSize()));

const RegionType constRegion{};
auto && [constIndex, constSize] = constRegion;

static_assert(std::is_same_v<decltype(constIndex), const IndexType>);
static_assert(std::is_same_v<decltype(constSize), const SizeType>);
EXPECT_EQ(&constIndex, &(constRegion.GetIndex()));
EXPECT_EQ(&constSize, &(constRegion.GetSize()));
}

0 comments on commit 72aa9a6

Please sign in to comment.