HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Elementwise iterator adaptor

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
adaptoriteratorelementwise

Problem

There are many C++ implementations of the Euclidean vector. I.e., a vector in what is typically a 3- or 4-dimensional space. Something along the lines of

struct vec3f { float x, y, z; };


It can of course also be a much more advanced implementation. Since we don't have a standard implementation, nearly every library provides their own. E.g., GLM, Assimp, FBX, etc.

It is common to work with ranges of vectors such as std::vector (in computer graphics for instance). Furthermore, it is common to elementwise (by x, y, z) iterate through such a range. E.g.,

std::vector uv_coordinates; // Given from 3rd party library
std::vector raw_uv_coordinates; // Used later for low-level transformations
for (const auto& position : positions) {
    raw_uv_coordinates.emplace_back(position.x);
    raw_uv_coordinates.emplace_back(position.y);
    // The z-coordinate is discarded since it is not used.
}


Note how I can't use std::copy since there isn't a one-to-one mapping between uv_coordinates and raw_uv_coordinates. Say you have to support another vector type like

struct Vector4 { double data[4] };


This will further complicate the code.

I solve this problem by introducing an iterator adaptor called elementwise. It wraps an iterator to a container of vector elements. E.g.,

auto first = elementwise(begin(uv_coordinates));
*first++; // Returns the x-coordinate of the 1st element.
*first++; // Returns the y-coordinate of the 1st element.
*first++; // Returns the z-coordinate of the 1st element.
*first++; // Returns the x-coordinate of the 2nd element.
...


This can be used to output all the coordinates

std::copy(
    elementwise(begin(uv_coordinates)),
    elementwise(end(uv_coordinates)),
    std::ostream_iterator{std::cout, ", "});


elementwise gets information about the underlying vector type (vec3f, Vector4, etc.) through the vector_traits class. Consequently, vector_traits must be specialized for each vector type you

Solution

I don't write C++, but one thing I've noticed is that your bracing style isn't consistent. You have the standard/expected C-style braces:

struct is_const_iterator
{
    typedef typename std::iterator_traits::pointer pointer;
    static const bool value = is_const_pointer::value;
};


Then you have the one-liner style:

template
auto elementwise( Iterator iterator )
{ return elementwise_iterator{iterator}; }


And then you have the Java-style braces:

void decrement() {
    if (!element) {
        element = N;
        --current;
    }
    --element;
}


Perhaps it's nitpicky, but you should strive to make your code look like it was written by one person - this looks like a Java and a C# programmer are fighting over which bracing style the C++ code base should be using:

void decrement() {
    if (!element) {
        element = N;
        --current;
    }
    --element;
}

void advance( difference_type n )
{ 
    auto div = std::div(n, N);
    current += div.quot; element += div.rem;
}


I'd say just pick one, and stick to it ;)

Code Snippets

struct is_const_iterator
{
    typedef typename std::iterator_traits<TIterator>::pointer pointer;
    static const bool value = is_const_pointer<pointer>::value;
};
template<int N, typename Iterator>
auto elementwise( Iterator iterator )
{ return elementwise_iterator<Iterator, N>{iterator}; }
void decrement() {
    if (!element) {
        element = N;
        --current;
    }
    --element;
}
void decrement() {
    if (!element) {
        element = N;
        --current;
    }
    --element;
}

void advance( difference_type n )
{ 
    auto div = std::div(n, N);
    current += div.quot; element += div.rem;
}

Context

StackExchange Code Review Q#68879, answer score: 5

Revisions (0)

No revisions yet.