Commit b29a17c5 authored by David Frank's avatar David Frank Committed by Tobias Lasser

add iterator support for DataContainer

parent a41728d7
Pipeline #171245 passed with stages
in 27 minutes and 13 seconds
......@@ -13,6 +13,7 @@ set(MODULE_HEADERS
DataDescriptor.h
BlockDescriptor.h
DataContainer.h
DataContainerIterator.h
DataHandler.h
DataHandlerCPU.h
LinearOperator.h)
......
......@@ -289,6 +289,82 @@ namespace elsa {
return;
}
template <typename data_t>
typename DataContainer<data_t>::iterator DataContainer<data_t>::begin()
{
detach();
return iterator(&(*this)[0]);
}
template <typename data_t>
typename DataContainer<data_t>::const_iterator DataContainer<data_t>::begin() const
{
return cbegin();
}
template <typename data_t>
typename DataContainer<data_t>::const_iterator DataContainer<data_t>::cbegin() const
{
return const_iterator(&(*this)[0]);
}
template <typename data_t>
typename DataContainer<data_t>::iterator DataContainer<data_t>::end()
{
detach();
return iterator(&(*this)[0] + getSize());
}
template <typename data_t>
typename DataContainer<data_t>::const_iterator DataContainer<data_t>::end() const
{
return cend();
}
template <typename data_t>
typename DataContainer<data_t>::const_iterator DataContainer<data_t>::cend() const
{
return const_iterator(&(*this)[0] + getSize());
}
template <typename data_t>
typename DataContainer<data_t>::reverse_iterator DataContainer<data_t>::rbegin()
{
detach();
return reverse_iterator(end());
}
template <typename data_t>
typename DataContainer<data_t>::const_reverse_iterator DataContainer<data_t>::rbegin() const
{
return crbegin();
}
template <typename data_t>
typename DataContainer<data_t>::const_reverse_iterator DataContainer<data_t>::crbegin() const
{
return const_reverse_iterator(cend());
}
template <typename data_t>
typename DataContainer<data_t>::reverse_iterator DataContainer<data_t>::rend()
{
detach();
return reverse_iterator(begin());
}
template <typename data_t>
typename DataContainer<data_t>::const_reverse_iterator DataContainer<data_t>::rend() const
{
return crend();
}
template <typename data_t>
typename DataContainer<data_t>::const_reverse_iterator DataContainer<data_t>::crend() const
{
return const_reverse_iterator(cbegin());
}
// ------------------------------------------
// explicit template instantiation
template class DataContainer<float>;
......
......@@ -4,6 +4,7 @@
#include "elsa.h"
#include "DataDescriptor.h"
#include "DataHandler.h"
#include "DataContainerIterator.h"
#include <memory>
#include <type_traits>
......@@ -20,7 +21,7 @@ namespace elsa
*
* \author Matthias Wieczorek - initial code
* \author Tobias Lasser - rewrite, modularization, modernization
* \author David Frank - added DataHandler concept
* \author David Frank - added DataHandler concept, iterators
*
* \tparam data_t - data type that is stored in the DataContainer, defaulting to real_t.
*
......@@ -208,6 +209,66 @@ namespace elsa
/// used for testing only and defined in test file
friend int useCount <> (const DataContainer<data_t>& dc);
/// iterator for DataContainer (random access and continuous)
using iterator = DataContainerIterator<DataContainer<data_t>>;
/// const iterator for DataContainer (random access and continuous)
using const_iterator = ConstDataContainerIterator<DataContainer<data_t>>;
/// alias for reverse iterator
using reverse_iterator = std::reverse_iterator<iterator>;
/// alias for const reverse iterator
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/// returns iterator to the first element of the container
iterator begin();
/// returns const iterator to the first element of the container (cannot mutate data)
const_iterator begin() const;
/// returns const iterator to the first element of the container (cannot mutate data)
const_iterator cbegin() const;
/// returns iterator to one past the last element of the container
iterator end();
/// returns const iterator to one past the last element of the container (cannot mutate data)
const_iterator end() const;
/// returns const iterator to one past the last element of the container (cannot mutate data)
const_iterator cend() const;
/// returns reversed iterator to the last element of the container
reverse_iterator rbegin();
/// returns const reversed iterator to the last element of the container (cannot mutate data)
const_reverse_iterator rbegin() const;
/// returns const reversed iterator to the last element of the container (cannot mutate data)
const_reverse_iterator crbegin() const;
/// returns reversed iterator to one past the first element of container
reverse_iterator rend();
/// returns const reversed iterator to one past the first element of container (cannot mutate data)
const_reverse_iterator rend() const;
/// returns const reversed iterator to one past the first element of container (cannot mutate data)
const_reverse_iterator crend() const;
/// value_type of the DataContainer elements for iterators
using value_type = data_t;
/// pointer type of DataContainer elements for iterators
using pointer = data_t*;
/// const pointer type of DataContainer elements for iterators
using const_pointer = const data_t*;
/// reference type of DataContainer elements for iterators
using reference = data_t&;
/// const reference type of DataContainer elements for iterators
using const_reference = const data_t&;
/// difference type for iterators
using difference_type = std::ptrdiff_t;
private:
/// the current DataDescriptor
std::unique_ptr<DataDescriptor> _dataDescriptor;
......
#pragma once
#include <iterator>
namespace elsa::detail
{
/**
* \brief iterator which uses a non-owning raw pointer to iterate over a container. The iterator is
* random access and assumes contiguous memory layout.
*
* \author David Frank - initial implementation
*
* \tparam T - the type of the container
*
* Note: comparing iterators from different containers is undefined behavior, so we do not check for it.
*/
template <typename T>
class ptr_iterator
{
public:
/// alias for iterator type
using self_type = ptr_iterator;
/// the iterator category
using iterator_category = std::random_access_iterator_tag;
/// the value type of container elements
using value_type = typename T::value_type;
/// pointer type of container elements
using pointer = typename T::pointer;
/// reference type of container elements
using reference = typename T::reference;
/// difference type of container
using difference_type = typename T::difference_type;
/// constructor taking a non-owning pointer to the data
explicit ptr_iterator(pointer ptr) : _ptr(ptr) { }
/// de-referencing operator
reference operator*() { return *_ptr; }
/// pointer access operator
pointer operator->() { return _ptr; }
/// subscript operator
reference operator[](int m) { return _ptr[m]; }
/// prefix increment operator
self_type operator++() { ++_ptr; return *this; }
/// postfix increment operator
self_type operator++(int) { auto i = *this; ++_ptr; return i; }
/// prefix decrement operator
self_type operator--() { --_ptr; return *this; }
/// postfix decrement operator
self_type operator--(int) { auto i = *this; --_ptr; return i; }
/// moving iterator forward by n
self_type& operator+=(int n) { _ptr += n; return *this;}
/// moving iterator backward by n
self_type& operator-=(int n) { _ptr -= n; return *this;}
/// return new iterator moved forward by n
self_type operator+(int n) const { self_type r(*this); return r += n;}
/// return new iterator moved backward by n
self_type operator-(int n) const { self_type r(*this); return r -= n;}
/// return the difference between iterators
difference_type operator-(ptr_iterator const& r) const { return _ptr - r._ptr; }
/// compare < with other iterator
bool operator< (const ptr_iterator& r) const { return _ptr < r._ptr; }
/// compare <= with other iterator
bool operator<=(const ptr_iterator& r) const { return _ptr <= r._ptr; }
/// compare > with other iterator
bool operator> (const ptr_iterator& r) const { return _ptr > r._ptr; }
/// compare >= with other iterator
bool operator>=(const ptr_iterator& r) const { return _ptr >= r._ptr; }
/// compare != with other iterator
bool operator!=(const ptr_iterator &r) const { return _ptr != r._ptr; }
/// compare == with other iterator
bool operator==(const ptr_iterator &r) const { return _ptr == r._ptr; }
private:
/// non-owning (!) pointer to data (do not clean up or anything)
pointer _ptr {};
};
/**
* \brief constant iterator which uses a non-owning raw pointer to iterate over a container. The iterator is
* random access and assumes contiguous memory layout. It is const in the sense that it cannot mutate the state
* of the object it iterates over.
*
* \author David Frank - initial implementation
*
* \tparam T - the type of the container
*
* Note: comparing iterators from different containers is undefined behavior, so we do not check for it.
*/
template<typename T>
class const_ptr_iterator
{
public:
/// alias for iterator type
using self_type = const_ptr_iterator;
/// the iterator category
using iterator_category = std::random_access_iterator_tag;
/// the value type of container elements
using value_type = typename T::value_type;
/// pointer type of container elements
using pointer = typename T::const_pointer;
/// reference type of container elements
using reference = typename T::const_reference;
/// difference type of container
using difference_type = typename T::difference_type;
/// constructor taking a non-owning pointer to the data
explicit const_ptr_iterator(pointer ptr) : _ptr(ptr) { }
/// de-referencing operator
reference operator*() { return *_ptr; }
/// pointer access operator
pointer operator->() { return _ptr; }
/// subscript operator
reference operator[](int m) { return _ptr[m]; }
/// prefix increment operator
self_type operator++() { ++_ptr; return *this; }
/// postfix increment operator
self_type operator++(int) { auto i = *this; ++_ptr; return i; }
/// prefix decrement operator
self_type operator--() { --_ptr; return *this; }
/// postfix decrement operator
self_type operator--(int) { auto i = *this; --_ptr; return i; }
/// moving iterator forward by n
self_type& operator+=(int n) { _ptr += n; return *this; }
/// moving iterator backward by n
self_type& operator-=(int n) { _ptr -= n; return *this; }
/// return new iterator moved forward by n
self_type operator+(int n) const { self_type r(*this); return r += n; }
/// return new iterator moved backward by n
self_type operator-(int n) const { self_type r(*this); return r -= n; }
/// return the difference between iterators
difference_type operator-(const_ptr_iterator const& r) const { return _ptr - r._ptr; }
/// compare < with other iterator
bool operator< (const self_type& r) const { return _ptr < r._ptr; }
/// compare <= with other iterator
bool operator<=(const self_type& r) const { return _ptr <= r._ptr; }
/// compare > with other iterator
bool operator> (const self_type& r) const { return _ptr > r._ptr; }
/// compare >= with other iterator
bool operator>=(const self_type& r) const { return _ptr >= r._ptr; }
/// compare != with other iterator
bool operator!=(const self_type &r) const { return _ptr != r._ptr; }
/// compare == with other iterator
bool operator==(const self_type &r) const { return _ptr == r._ptr; }
private:
/// non-owning (!) pointer to data (do not clean up or anything)
pointer _ptr {};
};
} // end namespace elsa::detail
namespace elsa
{
/// alias for the iterator for DataContainer
template <typename T>
using DataContainerIterator = detail::ptr_iterator<T>;
/// alias for the constant iterator for DataContainer
template <typename T>
using ConstDataContainerIterator = detail::const_ptr_iterator<T>;
} // end namespace elsa
......@@ -473,4 +473,160 @@ SCENARIO("Testing the copy-on-write mechanism") {
}
}
}
}
SCENARIO("Testing iterators for DataContainer") {
GIVEN("A 1D container") {
constexpr index_t size = 20;
IndexVector_t numCoeff(1);
numCoeff << size;
DataDescriptor desc(numCoeff);
DataContainer dc1(desc);
DataContainer dc2(desc);
Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size);
Eigen::VectorXf randVec2 = Eigen::VectorXf::Random(size);
for (index_t i = 0; i < size; ++i) {
dc1[i] = randVec1(i);
dc2[i] = randVec2(i);
}
THEN("We can iterate forward") {
int i = 0;
for (auto v = dc1.cbegin(); v != dc1.cend(); v++) {
REQUIRE(*v == randVec1[i++]);
}
REQUIRE(i == size);
}
THEN("We can iterate backward") {
int i = size;
for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
REQUIRE(*v == randVec1[--i]);
}
REQUIRE(i == 0);
}
THEN("We can iterate and mutate") {
int i = 0;
for (auto& v : dc1) {
v = v * 2;
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size);
i = 0;
for (auto v : dc1) {
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size);
}
THEN("We can use STL algorithms") {
REQUIRE(*std::min_element(dc1.cbegin(), dc1.cend()) == randVec1.minCoeff());
REQUIRE(*std::max_element(dc1.cbegin(), dc1.cend()) == randVec1.maxCoeff());
}
}
GIVEN("A 2D container") {
constexpr index_t size = 20;
IndexVector_t numCoeff(2);
numCoeff << size, size;
DataDescriptor desc(numCoeff);
DataContainer dc1(desc);
Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size);
for (index_t i = 0; i < dc1.getSize(); ++i) {
dc1[i] = randVec1[i];
}
THEN("We can iterate forward") {
int i = 0;
for(auto v : dc1) {
REQUIRE(v == randVec1[i++]);
}
REQUIRE(i == size * size);
}
THEN("We can iterate backward") {
int i = size * size;
for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
REQUIRE(*v == randVec1[--i]);
}
REQUIRE(i == 0);
}
THEN("We can iterate and mutate") {
int i = 0;
for (auto& v : dc1) {
v = v * 2;
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size * size);
i = 0;
for (auto v : dc1) {
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size * size);
}
THEN("We can use STL algorithms") {
REQUIRE(*std::min_element(dc1.cbegin(), dc1.cend()) == randVec1.minCoeff());
REQUIRE(*std::max_element(dc1.cbegin(), dc1.cend()) == randVec1.maxCoeff());
}
}
GIVEN("A 3D container") {
constexpr index_t size = 20;
IndexVector_t numCoeff(3);
numCoeff << size, size, size;
DataDescriptor desc(numCoeff);
DataContainer dc1(desc);
Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size * size);
for (index_t i = 0; i < dc1.getSize(); ++i) {
dc1[i] = randVec1[i];
}
THEN("We can iterate forward") {
int i = 0;
for (auto v : dc1) {
REQUIRE(v == randVec1[i++]);
}
REQUIRE(i == size * size * size);
}
THEN("We can iterate backward") {
int i = size * size * size;
for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
REQUIRE(*v == randVec1[--i]);
}
REQUIRE(i == 0);
}
THEN("We can iterate and mutate") {
int i = 0;
for (auto& v : dc1) {
v = v * 2;
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size * size * size);
i = 0;
for (auto v : dc1) {
REQUIRE(v == 2 * randVec1[i++]);
}
REQUIRE(i == size * size * size);
}
THEN("We can use STL algorithms") {
REQUIRE(*std::min_element(dc1.cbegin(), dc1.cend()) == randVec1.minCoeff());
REQUIRE(*std::max_element(dc1.cbegin(), dc1.cend()) == randVec1.maxCoeff());
}
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment