Get the element index when iterating with an indexed_view
Monday, 25 March 2019
One crucial difference between using an index-based for
loop and a range-based for
loop is that
the former allows you to use the index for something other than just identifying the element,
whereas the latter does not provide you with access to the index at all.
The difference between index-based for
loops and range-based for
loops means that some people
are unable to use simple range-based for
loops in some cases, because they need the index.
For example, you might be initializing a set of worker threads in a thread pool, and each thread needs to know it's own index:
std::vector<std::thread> workers;
void setup_workers(unsigned num_threads){
workers.resize(num_threads);
for(unsigned i=0;i<num_threads;++i){
workers[i]=std::thread(&my_worker_thread_func,i);
}
}
Even though workers
has a fixed size in the loop, we need the loop index to pass to the thread
function, so we cannot use range-based for
. This requires that we duplicate num_threads
,
adding the potential for error as we must ensure that it is correctly updated in both places if we
ever change it.
jss::indexed_view
to the rescue
jss::indexed_view
provides a means of obtaining
that index with a range-based for
loop: it creates a new view range which wraps the original
range, where each element holds the loop index, as well as a reference to the element of the
original range.
With jss::indexed_view
, we can avoid the duplication from the previous example and use the
range-based for
:
std::vector<std::thread> workers;
void setup_workers(unsigned num_threads){
workers.resize(num_threads);
for(auto entry: jss::indexed_view(workers)){
entry.value=std::thread(&my_worker_thread_func,entry.index);
}
}
As you can see from this example, the value
field is writable: it is a reference to the underlying
value if the iterator on the source range is a reference. This allows you to use it to modify the
elements in the source range if they are non-const
.
jss::indexed_view
also works with iterator-based ranges, so if you have a pair of iterators, then
you can still use range-based for
loops. For example, the following code processes the elements up
to the first zero in the supplied vector, or the whole vector if there is no zero.
void foo(std::vector<int> const& v){
auto end=std::find(v.begin(),v.end(),0);
for(auto entry: jss::indexed_view(v.begin(),end)){
process(entry.index,entry.value);
}
}
Finally, jss::indexed_view
can also be used with algorithms that require iterator-based ranges,
so our first example could also be written as:
std::vector<std::thread> workers;
void setup_workers(unsigned num_threads){
workers.resize(num_threads);
auto view=jss::indexed_view(workers);
std::for_each(view.begin(),view.end(),[](auto entry){
entry.value=std::thread(&my_worker_thread_func,entry.index);
});
}
Final words
Having to use non-ranged for
loop to get the loop index introduces a potential source of error: it
is easy to mistype the loop index either in the for
-loop header, or when using it to get the
indexed element, especially in nested loops.
By using jss::indexed_view
to wrap the range, you can eliminate this particular source of error,
as well as making it clear that you are iterating across the entire range, and that you need the
index.
Get the source from github and use it in your project now.
Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: cplusplus, memory, safety, undefined, crash, security
Stumble It! | Submit to Reddit | Submit to DZone
If you liked this post, why not subscribe to the RSS feed or Follow me on Twitter? You can also subscribe to this blog by email using the form on the left.
Design and Content Copyright © 2005-2024 Just Software Solutions Ltd. All rights reserved. | Privacy Policy
No Comments