Skip to content

Commit

Permalink
ENH: Support C-style arrays as MetaDataObjectType for MetaDataDictionary
Browse files Browse the repository at this point in the history
Allows passing a N-dimensional C-style array as value, when calling
`itk::EncapsulateMetaData(dictionary, key, value)`.
  • Loading branch information
N-Dekker authored and dzenanz committed Mar 23, 2022
1 parent 7dd9e49 commit 7299e5c
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
48 changes: 47 additions & 1 deletion Modules/Core/Common/include/itkMetaDataObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class ITK_TEMPLATE_EXPORT MetaDataObject : public MetaDataObjectBase
friend bool
operator==(const Self & lhs, const Self & rhs)
{
return lhs.m_MetaDataObjectValue == rhs.m_MetaDataObjectValue;
return Self::EqualValues(lhs.m_MetaDataObjectValue, rhs.m_MetaDataObjectValue);
}

/** Returns (metaDataObject1 != metaDataObject2). */
Expand All @@ -149,6 +149,52 @@ class ITK_TEMPLATE_EXPORT MetaDataObject : public MetaDataObjectBase
~MetaDataObject() override = default;

private:
/** Assigns the value of `source` to `target`.
* \note The trailing return type is there, just to enable SFINAE.*/
template <typename TValue>
static auto
Assign(TValue & target, const TValue & source) -> decltype(target = source)
{
return target = source;
}

/** `Assign` overload for C-style arrays (as well as arrays of arrays). */
template <typename TValue, size_t VNumberOfElements>
static void
Assign(TValue (&target)[VNumberOfElements], const TValue (&source)[VNumberOfElements])
{
for (size_t i = 0; i < VNumberOfElements; ++i)
{
Self::Assign(target[i], source[i]);
}
}


/** Tells whether the specified arguments compare equal.
* \note The trailing return type is there, just to enable SFINAE.*/
template <typename TValue>
static auto
EqualValues(const TValue & lhs, const TValue & rhs) -> decltype(lhs == rhs)
{
return lhs == rhs;
}

/** `EqualValues` overload for C-style arrays (as well as arrays of arrays). */
template <typename TValue, size_t VNumberOfElements>
static bool
EqualValues(const TValue (&lhs)[VNumberOfElements], const TValue (&rhs)[VNumberOfElements])
{
for (size_t i = 0; i < VNumberOfElements; ++i)
{
if (!Self::EqualValues(lhs[i], rhs[i]))
{
return false;
}
}
return true;
}


/** Internal helper function used to implement operator== for MetaDataObjectBase. */
bool
Equal(const MetaDataObjectBase & metaDataObjectBase) const override
Expand Down
2 changes: 1 addition & 1 deletion Modules/Core/Common/include/itkMetaDataObject.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ template <typename MetaDataObjectType>
void
MetaDataObject<MetaDataObjectType>::SetMetaDataObjectValue(const MetaDataObjectType & newValue)
{
m_MetaDataObjectValue = newValue;
Self::Assign(m_MetaDataObjectValue, newValue);
}

template <typename MetaDataObjectType>
Expand Down
51 changes: 51 additions & 0 deletions Modules/Core/Common/test/itkMetaDataDictionaryGTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "itkMetaDataObject.h"

#include <iterator>
#include <numeric> // For iota.
#include <type_traits> // For remove_const_t, remove_reference_t, etc.

namespace
{
Expand Down Expand Up @@ -302,3 +304,52 @@ TEST(MetaDataDictionary, Equal)
expectUnequal(metaDataDictionary1, defaultMetaDataDictionary);
expectUnequal(metaDataDictionary2, defaultMetaDataDictionary);
}


TEST(MetaDataDictionary, SupportsCStyleArrays)
{
itk::MetaDataDictionary dictionary;

const auto check = [&dictionary](const auto & value) {
using ValueType = std::remove_const_t<std::remove_reference_t<decltype(value)>>;

const std::string key = "key";
itk::EncapsulateMetaData(dictionary, key, value);
const itk::MetaDataObjectBase * const base = dictionary.Get(key);
ASSERT_NE(base, nullptr);

const auto * const actualDataObject = dynamic_cast<const itk::MetaDataObject<ValueType> *>(base);
ASSERT_NE(actualDataObject, nullptr);

const ValueType & actualValue = actualDataObject->GetMetaDataObjectValue();

// itk::EncapsulateMetaData is expected to make a _copy_ of the input value. So it should not just store a reference
// to that value.
EXPECT_NE(&actualValue, &value);

const auto expectedDataObject = itk::MetaDataObject<ValueType>::New();
ASSERT_NE(expectedDataObject, nullptr);
expectedDataObject->SetMetaDataObjectValue(value);

// The value from the dictionary is expected to compare equal to the original input value.
EXPECT_EQ(*actualDataObject, *expectedDataObject);
};

for (const int i : { 0, 1 })
{
// Check a simple C-style array.
const int simpleArray[] = { i, 1 - i };
check(simpleArray);
}

// Check a two-dimensional array.
constexpr size_t numberOfRows{ 3 };
constexpr size_t numberOfColumns{ 4 };
int twoDimensionalArray[numberOfRows][numberOfColumns];

// Just ensure that each element of the two-dimentional array has a different value.
int * const beginOfData = &(twoDimensionalArray[0][0]);
std::iota(beginOfData, beginOfData + numberOfRows * numberOfColumns, 1);

check(twoDimensionalArray);
}

0 comments on commit 7299e5c

Please sign in to comment.