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

Iterator for void pointer ranges

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

Problem

In the old days when C was the predominant language under the hood and STL/templates were Alex Stepanov's dream, in order for programmers to achieve generality for functions and data-containers, the void* was used as input argument or underlying container type respectively. A typical example is qsort which is located in `.

Nowadays, when dealing with legacy code-bases or code written by dinosaurs, it's highly probable that you might stumble on such kind of data structures that most probably keep their elements in
void** buffers. The primary goal of-course would be to gradually move the code base towards use of modern STL containers and algorithms until these old data-structures become obsolete.

However, there are also the dinosaurs. Often, very large dinosaurs like a tyrannosaur that happens to be your manager. In order to convince them of the C++/STL superiority with out questioning the "usability" of the legacy data-structures that are being functioning all these years without a problem and most probable the author is one of them, I decided to engage the issue politically.

What I thought is to craft a template iterator that could deal with such
void** buffers and would act as a bridge with the STL algorithms (e.g., std::sort, std::copy etc.).

Down below lies in a very early stage such an iterator:

``
template
class Iterator : public std::iterator {
using T_ptr = std::remove_pointer_t*;
void **pos;
public:
Iterator(void **pos_) : pos(pos_) { }
bool operator==(Iterator const &other) const { return pos == other.pos; }
bool operator!=(Iterator const &other) const { return pos != other.pos; }
bool operator( Iterator const &other) const { return pos > other.pos; }
bool operator=(Iterator const &other) const { return pos >= other.pos; }
Iterator& operator++() {
++pos;
return *this;
}
Iterator operator++(int) {
Iterator out(*this);
++pos;
return out;
}
Iterator& operator--() {
--pos;
return *th

Solution

Foo f1(1), f2(2), f3(3), f4(4);
void* v[] = {&f4, &f2, &f1, &f3};




And then use for example std::sort to sort v with respect the Foo objects that indirectionally contains:

std::sort(Iterator(v), Iterator(v + sizeof(v) / sizeof(void*)));


This is already a disaster waiting to happen. v is an array of pointers, and you should only treat it as such. Try to imagine what happens one of the pointers in v is actually the nullptr?

That's perfectly legit with the original array, and also when using the old C functions such as qsort to sort the array itself (given that the predicate is nullptr aware).

As a matter of fact, using std::sort on the referenced values as you just did isn't even semantically the same as applying original qsort straight to the array. You are copying around values while the array remains unchanged, whereby qsort would have resorted the array while keeping the values constant.


I've been careful to inherit my iterator from the
std::iterator and not
std::iterator in order to avoid
treating the void** buffer as contiguous buffer of Ts with what
ever implications that might have.

Except that is is a contiguous buffer of Ts. Just mind again the difference between T and T.

Given the possibility of nullptr, it can't even be an iterator for T in any form. See the is dereferenceable requirement.

Yes, not being able to bridge to stl sucks. But to be honest, the problem started when someone decided to write this:

Foo f1(1), f2(2), f3(3), f4(4);
void* v[] = {&f4, &f2, &f1, &f3};


instead of this:

Foo v[] = {Foo(1), Foo(2), Foo(3), Foo(4)};


Which are both valid since C99.

Which could then have been a refactored to:

std::array v = {Foo(1), Foo(2), Foo(3), Foo(4)};


With v.data() giving you access to the raw array, if you actually still needed it.

Code Snippets

Foo f1(1), f2(2), f3(3), f4(4);
void* v[] = {&f4, &f2, &f1, &f3};
std::sort(Iterator<Foo>(v), Iterator<Foo>(v + sizeof(v) / sizeof(void*)));
Foo f1(1), f2(2), f3(3), f4(4);
void* v[] = {&f4, &f2, &f1, &f3};
Foo v[] = {Foo(1), Foo(2), Foo(3), Foo(4)};
std::array<Foo, 4> v = {Foo(1), Foo(2), Foo(3), Foo(4)};

Context

StackExchange Code Review Q#144626, answer score: 3

Revisions (0)

No revisions yet.