How to reliably "clamp" an iterator? - c++

I'm iterating through a vector as follows:
for(auto it = myVector.begin(); it != myVector.end(); ++it){
// Do some stuff
}
Inside the loop, based on some condition, I add a variable amount to the iterator so as to "jump forward" (but, importantly, never "move back"). What is the "right" way (or, at least, what is an effective/reliable way) to "clamp" the iterator such that I don't end up going beyond the end of the vector (ending up with a program crash or, worse yet, undefined behaviour)?

if (myVector.end() - it < variable_amount)
it += variable_amount;
else
break;
Using std::distance and std::advance would allow this to work with non-random access iterators, but that would be an inefficient algorithm for them, so I recommend not using them.
Algorithm for non-random-access iterators:
for (; variable_amount-- && it != myList.end(); ++it);

You can make your own version of advance that will never go past the end iterator. Adapted from https://en.cppreference.com/w/cpp/iterator/advance
// Assumes c++11 at least
template<class It, class Distance>
constexpr void clamped_advance(It& it, It limit, Distance n) {
using category = typename std::iterator_traits<It>::iterator_category;
static_assert(std::is_base_of<std::input_iterator_tag, category>::value, "Cannot advance non-input iterator");
auto dist = typename std::iterator_traits<It>::difference_type(n);
assert(("Can only clamped_advance forward", dist >= 0));
if (std::is_base_of<std::random_access_iterator_tag, category>::value)
std::advance(it, std::min(dist, std::distance(it, limit)));
else
while (dist-- > 0 && it != limit) ++it;
}
And you can use it like:
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
// Do some stuff
// If this was going to advance past the end, `it` would equal `myVector.end()`
// and the loop will stop
clamped_advance(it, myVector.end(), variableAmount);
}

I would begin by getting the distance between the current iterator and the begin iterator.
If that distance plus the offset is larger than the vector size, you're going out of bounds.

Related

Iterate up to and including position

Probably very simple but can't get my head around it atm
I have this
// standard std::map and std::map::iterator
auto pos = map.find(val);
for(auto it = map.begin; it != pos; ++it)
I want to search for an element and then process all elements before (container is ordered, so just in iteration order) and including the find location, however this doesn't appear to examine the final element at position 'pos'. How can I achieve this?
Just move things around.
auto pos = map.find(val);
for(auto it = map.begin(); it != map.end(); ++it)
{
// Do something
if (it == pos)
break;
}
If it's possible that the value does not exist in the map, this will simply end up iterating over the entire map and that's why it != map.end(); is explicitly needed, to catch this particular runaway train...
Caution: if you have explicit continues inside the for loop, some additional TLC will be needed.
You need to differentiate between the cases where val is found in map and otherwise:
void f(/*map::[const_]iterator*/ it);
auto pos = map.find(val);
for (auto it = map.begin(); it != pos; ++it)
f(it);
if (pos != map.end())
f(pos);
You could use a do...while() loop but you would have to check for pos!=end() each time.

Increment an iterator c++

My problem is as follows: I use an iterator, and I want to compare each element to the next element. Prototype looks like below, how can I increase the iterator to be able to compare?
Also, how can I set a proper condition for this to happen? I mean how to point on the last element, not on the next after the last like with end() function:
std::vector<T>::const_iterator it;
std::vector<T>::const_iterator it2;
for (it = set.begin(), it != set.end(); it++)
{
// some things happen
if ( final == it )
{
if ( it != set.end()-1 ) // how to write properly condition?
{
it2 = it + 1; //how to assign the next here?
if (...)//some condition
{
if ( it->func1() - it2->func1()) < 20 ) //actual comparison of two consecutive element values
// do something
}
}
}
}
In C++11 use the functions std::next() and std::prev().
Your code could become:
// before
it != std::set.end()-1
// after
it != std::prev(set.end())
and
// before
it2 = it + 1;
// after
it2 = std::next(it);
That is true also for non-vector containers, such as map,set or others.
NOTE: after std::next(it), "it" iterator remains unmodified!
NOTE 2: Use it2 = std::next(it,n); to increment as much as you need.
You can use adjacent_find to solve that. You should use the second form of that function (with predicate) and pass to the predicate your some things happen and some condition in c-tor
auto found = std::adjacent_find( set.begin(), set.end(),
[some_comdition]( const T & left, const T & right ) {
if ( some_comdition ) {
if ( left.func1() - right.func1() < 20 ) {
do_smth();
// return true; if there's no need to continue
}
}
return false;
}
);
Based on the fact that it++ is acceptable, we should define a new iterator called itplusone, which is initialized as itplusone = ++it. In this way, you can safely use the meaning of an iterator pointing to the next item of it. Also clearly, the range of iterator of itplusone bounded by terms itplusone != set.end(). I use this method to compute the total weight of a path, which is defined as a list object.
In the for loop, you use it++ which means it = it + 1, which is perfectly ok. So this one will be fine also it2 = it + 1. it2 will be pointing to the next value.
In the for loop again, you use it != set.end(), which is again perfectly ok. So you can also it + 1 < set.end(), just like you did in your code.
I don't see anything wrong in your code, just wanted to explain.
somewhat late, just discovered it, but like mentioned above, ++ iterator works fine.
vector<string> P
auto itA = begin(P);
while(itA != end(P))
{
if(itA != end(P))
{
++itA; //
}
}

Can I iterate over the elements that are in one range of iterators but not in another?

Let's say I have a sequential container, and a range (pair of iterators) within that container of elements that are currently 'active'. At some point, I calculate a new range of elements that should be active, which may overlap the previous range. I want to then iterate over the elements that were in the old active range but that are not in the new active range to 'deactivate' them (and similarly iterate over the elements that are in the new range but not the old range to 'activate' them).
Is this possible?
Does it become easier if I know that the start of the new active range will always be later in the container than the start of the old active range?
For the purposes of the question, assume the container is a vector.
You can use two sets for the last active range and another for the current active range. Use the set_difference algorithm to get the objects to be activated/deactivated.
What you need is a range_difference function. I though Boost.Range would provide something like this, but I didn't find anything in their doc (well, I didn't search very thoroughly...), so I rolled up my own.
The following function returns a pair of range, containing the result of the difference between the range denoted by (first1,last1) and the one denoted by (first2,last2). A pre-condition is that first1 must be positioned before or at the same position as first2.
template <typename InputIterator>
std::pair<
std::pair<InputIterator, InputIterator>,
std::pair<InputIterator, InputIterator> >
range_difference(InputIterator first1, InputIterator last1,
InputIterator first2, InputIterator last2)
{
typedef std::pair<InputIterator, InputIterator> Range;
InputIterator it;
// first1 must be <= first2
for (it = first1 ; it != last1 && it != first2 ; ++it);
Range left_range = std::make_pair(first1, it); // Left range
if (it == last1)
return std::make_pair(left_range, std::make_pair(first2, first2));
// it == first2
while (it != last1 && it != last2) ++it;
return std::make_pair(left_range, std::make_pair(it, last1)); // Right range
}
The result of the difference can be composed of two parts, if range2 is completely included into range1. You end up with a left range and a right range:
|_____________________|__________________|________________________|
first1 first2 last2 last1
In this case, the function returns (first1, first2),(last2, last1).
In this other configuration,
|_____________________| |________________________|
first1 last1 first2 last2
the function returns (first1, last1),(first2, first2). There are many other possible configurations. However, one important thing to know is that in the case where the right range is empty, it will be positioned at max(first2, last1). You'll see how this is necessary in the example.
Finally, if first1 and first2 are at the same position, the returned left range will be empty, i.d. (first1,first1).
Now, how can we use this function to solve your problem? Well that's rather easy for the "deactivate" range, but a little trickier for the "activate" one:
typedef std::vector<Activable>::iterator Iterator;
Iterator old_beg, old_end, new_beg, new_end; // Old and new ranges
typedef std::pair<Iterator, Iterator> Range;
typedef std::pair<Range, Range> SplitRange;
SplitRange deactivate = range_difference(old_beg, old_end, new_beg, new_end);
// Left range
for (Iterator it = deactivate.first.first;
it != deactivate.first.second;
++it)
it->deactivate();
// Right range
for (Iterator it = deactivate.second.first;
it != deactivate.second.second;
++it)
it->deactivate();
SplitRange activate =
range_difference(new_beg, new_end, new_beg, deactivate.second.first);
// Note the use of the previously returned right range -------^
for (Iterator it = activate.second.first;
it != activate.second.second;
++it)
it->activate();
And there you go. Maybe this solution is a little overkill to your problem, but I think the range_difference function could be useful in many place.
Here's a simple solution:
typedef std::pair<std::vector<T>::iterator, std::vector<T>::iterator> Range;
void Update(std::vector<T>& v, Range oldActive, Range newActive)
{
int op = 0;
for (std::vector<T>::iterator i = v.begin(), end = v.end(); i != end; ++i)
{
if (i == oldActive.first) op += 1;
if (i == oldActive.second) op -= 1;
if (i == newActive.first) op += 2;
if (i == newActive.second) op -= 2;
if (op == 1) i->Deactivate();
if (op == 2) i->Activate();
}
}
This deliberately puts simplicity before efficiency as a starting point, since it scans the entire vector; on the other hand it's single pass and does no copying.
I think I'll keep it simple:
// Iterators denoting the old and new ranges (might be cleaner to use some kind
// of typedef like James Hopkin's did, but that's not the most important)
std::vector<Activable>::iterator old_beg,
old_end,
new_beg,
new_end;
std::vector<Activable>::iterator it;
// Deactivate
for (it = old_beg ; // go from the beginning of the old range
it != old_end && it != new_beg ; // to either the end of the old one or the
++it) // beginning of the new one
it->deactivate();
// "Jump" to the correct position
if (it == old_end) it = new_beg; // no overlap
else it = old_end; // overlap
// Activate
for (; it != new_end ; ++it)
it->activate();
You'll note that I assumed that the new range couldn't be totally contained into the old one (e.g. you can't have an old range going from index 4 to 10, and a new one going from 5 to 7). If this is a case, you'll need to change a little the algorithm.

What's the cleanest way to walk and unwalk a std::vector using iterators?

I have a situation where I'm marching through a vector, doing things:
std::vector<T>::iterator iter = my_list.begin();
for ( ; iter != my_list.end(); ++iter )
{
if ( iter->doStuff() ) // returns true if successful, false o/w
{
// Keep going...
}
else
{
for ( ; iter != m_list.begin(); --iter ) // ...This won't work...
{
iter->undoStuff();
}
}
}
Under normal conditions - assuming everything goes well - I march all the way to my_list.end() and end the loop successfully.
However, if something goes wrong while I'm doing stuff, I want to be able to undo everything - basically retrace my steps back to the very beginning of the vector, undoing everything one at a time in reverse order.
My problem is that when I get to my_list.begin() - as shown in the nested for loop - I'm really not done yet because I still need to call undoStuff() on my first element in the list. Now, I could just make the final call outside of the loop, but this seems a little unclean.
The way I see it, I'm only done when I get to my_list.rend(). However, I can't compare a std::vector::iterator to a std::vector::reverse_iterator.
Given what I'm trying to do, what's the best choice of iterator-type / loop combination?
I'm a little rusty when it comes to STL vectors, but would it be possible to create a std::vector::reverse_iterator from your initial iterator? Then you would only need to start at the last item you were at when going forward, and would be able to compare it to my_list.rend() to make sure that the first item is processed.
While using reverse iterators via rbegin() and rend() works nicely, unfortunately I find that converting between reverse and non-reverse iterarotrs tends to be quite confusing. I can never remember without having to go through a logic-puzzle exercise whether I need to increment or decrement before or after the conversion. As a result I generally avoid the conversion.
Here's the way I'd probably code your error handling loop. Note that I'd think that you wouldn't have to call undoStuff() for the iterator that failed - after all, doStuff() said it didn't succeed.
// handle the situation where `doStuff() failed...
// presumably you don't need to `undoStuff()` for the iterator that failed
// if you do, I'd just add it right here before the loop:
//
// iter->undoStuff();
while (iter != m_list.begin()) {
--iter;
iter->undoStuff();
}
There is of course no reason not to use the vectors operator[]() if that makes your code clearer, simpler and/or more efficient.
It depends on what your doStuff() function does, and how important performance is in your context. If possible, it would probably be clearer (ie - easier for the reader) to work on a copy of your vector, and only if everything is okay, swap the vectors.
std::vector<Foo> workingCopy;
workingCopy.assign(myVector.begin(), myVector.end());
bool success = true;
auto iter = workingCopy.begin();
for( ; iter != workingCopy.end() && success == true; ++iter )
success = iter->doStuff();
if( success )
myVector.swap(workingCopy);
Without using a reverse_iterator, you can walk backwards this way:
while(iter-- != m_list.begin())
{
iter->undoStuff();
}
Though this creates a copy of iter, the cost shouldn't be too great. You can refactor for better speed:
while(iter != m_list.begin())
{
--iter;
iter->undoStuff();
}
You need to use rbegin() to get a reversible iterator.
Personally I still prefer
for (int i=0;i<vecter.size();i++) { }
Ok, I'll go out on a limb here..
std::vector iterator iter = my_list.begin();
bool error = false;
while(iter != my_list.end())
{
error = !iter->doStuff();
if(error)
break
else
iter++;
}
if(error)
do
{
iter->undoStuff();
iter--;
}
while(iter != my_list.begin())
This is what I call over engineering, but it is so much fun
// This also can be done with adaptators I think
// Run DoStuff until it failed or the container is empty
template <typename Iterator>
Iterator DoMuchStuff(Iterator begin, Iterator end) {
Iterator it = begin;
for(; it != end; ++it) {
if(!*it->DoStuff()) {
return it;
}
}
return it;
}
// This can be replaced by adaptators
template <typename Iterator>
void UndoMuchStuff(Iterator begin, Iterator end) {
for(Iterator it = begin; it != end; ++it) {
it->UndoStuff();
}
}
// Now it is so much easier to read what we really want to do
typedef std::vector<MyObject*> MyList;
typedef MyList::iterator Iterator;
typedef MyList::reverse_iterator ReverseIterator;
Iterator it = DoMuchStuff(my_list.begin(), my_list.end());
if(it != my_list.end()) {
// we need to unprocess [begin,it], ie including it
UndoMuchStuff(ReverseIterator(1+it), ReverseIterator(my_list.begin()));
}
This can be done with a reverse_iterator:
bool shouldUndo(false);
std::vector::iterator iter(my_list.begin()), end(my_list.end());
for ( ; iter != end && !shouldUndo; ++iter )
{
shouldUndo = iter->doStuff(); // returns true if successful, false o/w
}
if (shouldUndo) {
reverse_iterator<std::vector::iterator> riter(iter), rend(my_list.rend());
//Does not call `undoStuff` on the object that failed to `doStuff`
for ( ; riter != rend; ++riter )
{
iter->undoStuff();
}
}

Is it good form to compare against changing values in a loop in C++?

No doubt some of you have seen my recent posting, all regarding the same program. I keep running into problems with it. To reiterate: still learning, not very advanced, don't understand pointers very well, not taking a class, don't understand OOP concepts at all, etc. This code just merges two sorted vectors, farray and sarray, into a single sorted vector. At least, I hope that's what it does. Tell me:
//int num is to find the size of the original vector and
//build up farray and sarray; not used in the merge process
int num = original.size()
std::vector<int> final;
std::vector<int>::iterator it = farray.begin();
std::vector<int>::iterator iter = sarray.begin();
//farray.size() == (0 thru (num / 2))
//sarray.size() == ((num / 2) thru num)
for (;it != farray.end() && iter != sarray.end();) {
if (*it > *iter) {
final.push_back(*it);
it++;
}
else
{
final.push_back(*iter);
iter++;
}
if (it == farray.end()) {
for (int i = 0; iter < sarray.end(); i++) {
final.push_back(*iter);
}
}
if (iter == sarray.end()) {
for (int i = 0; it < farray.end(); i++) {
final.push_back(*iter);
}
}
}
I rewrote the merge part of my merge sort function so as to...well, make it work. I actually have several questions about this code:
Is it good form to compare against std::vector::iterators it && iter for my last two if statements if the for loop might change them on its next pass?
Will the values of iter and it change on this loop's last pass and screw up my code? Will putting my last if statements before the *it and *iter comparison?
Does the end() member function refer to the last value of whatever is calling it? It seems like it might extend past it somehow.
EDIT: I will respond to all replies tomorrow, so check back then if you want to hear more. It's past midnight. G'night.
1 . It's fine to compare iterators which are from the same container as a for loop condition, but this only makes sense if you are moving one or other iterators in either the increment part if the for loop statement or in the body of the for loop itself. In this for loop you compare iter against sarray.end() but the for loop never changes iter. This means that either there will be no iterations or the for loop will never terminate. Also, you probably want to use != and not < for the comparison. == and != work for all iterators, < doesn't.
for (int i = 0; iter != sarray.end(); i++) {
final.push_back(*iter);
}
As iter starts where you want the loop to begin, you may want something like this:
for (; iter != sarray.end(); ++iter) {
final.push_back(*iter);
}
As you're still learning (although aren't we all!), it's probably instructive to work through an algorithm like this, but you should be aware of std::merge which probably does what you want.
std::merge( farray.begin(), farray.end(), sarray.begin(), sarray.end(), std::back_inserter( final ) );
(You need to #include <iterator> and <algorithm>.)
2 . I don't see incrementing iter or it in the outer for loop invalidating the logic in the later for loops, the point in 1. aside.
3 . end() points to one past the end of a container, so you can use it for loop termination checks, but you shouldn't try to dereference an iterator which is "==" to ".end()".
I didn't check your algorithm's implementation, I will just refer to your three questions:
Iterators are much like pointers to values of a container. It's exactly like using size_t i and then ++i in the for loop. would you feel it's problematic to compare farray[i] with sarray[i]? probably not, therefore it's OK.
What I see you doing in your code here, is that you just read the values of *it and *iter, you don't actually change them, therefore they won't change.
The end() points to an invalid place. It doesn't point to the last value, but to "after it". It's like "NULL" if you will, therefore if(iter == sarray.end()) is true, you will crash if you will write *iter, because you can't dereference an iterator which is equal to end().
Some general advice: You need to think about variable names. Calling your iterators 'it' and 'iter' is going to confuse you at some point. Actually, if you look closely, it already has. If 'farray' and 'sarray' are meaningful names, how about 'fiter' and 'siter'.
Also, think through what the merge sort is doing. Those last two blocks are there just to "drain" whichever iterator has some stuff left. So they don't need to be in the first loop.
I'd probably write it as (pseudocode):
while not (list1.empty and list2.empty):
if list1.empty:
result.push(list2.pop)
else if list2.empty:
result.push(list1.pop)
else if list1.top > list2.top:
result.push(list2.pop)
else:
result.push(list1.pop)
Or in somewhat rusty cargo-culted C++:
std::vector<int>::iterator fiter = farray.begin();
std::vector<int>::iterator siter = sarray.begin();
while (fiter != farray.end() || siter != sarray.end()) {
if (fiter == farray.end()) final.push_back(*siter++);
else if (siter == sarray.end()) final.push_back(*fiter++);
else if (*fiter > *siter) final.push_back(*siter++);
else final.push_back(*siter++);
}
You have a few things to think about here.
First, if you are merging two ranges you would be much better off using the std::merge function rather that rolling your own.
Your code is a little difficult to read because you use varying styles for indentation and where you out your curly braces. Pick a style & stick to it.
The first part of your for loop seems to be a correct implementation of a merge:
for (;it != farray.end() && iter != sarray.end();) {
if (*it > *iter) {
final.push_back(*it);
it++;
}
else
{
final.push_back(*iter);
iter++;
}
...and this should be all you need to get the job done.
The second part of your loop has a couple problems:
for (;it != farray.end() && iter != sarray.end();) {
: :
if (it == farray.end()) {
for (int i = 0; iter < sarray.end(); i++) {
final.push_back(*iter);
}
}
if (iter == sarray.end()) {
for (int i = 0; it < farray.end(); i++) {
final.push_back(*iter);
}
}
}
For one thing, the for() conditionals are written so that both it and iter must not point to the end() of their respective collection, or else the loop ends. So it can never point to sarray.end(), iter can never point to farray.end(), and neither if statement can ever fire. They are both dead (unreachable) code.
But even if they weren't dead code, they have bugs. The conditional in the for(...) breaks the loop when the iterator points to the end of the collection, but this iterator is never moved, so you have an infinite loop.
Again both of these for(...)s are uneeded dead code because the iterators can never point to the end of the vector.
One simple comment: why not use while (condition) instead of for(; !condition; ).
The latter construction is nonstandard and hard to understand!