I am trying to return an iterator to the largest element in a filtered range. Here is what I have so far:
#include <boost/lambda/lambda.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>
#include <iostream>
using namespace boost::adaptors;
using namespace boost::lambda;
using namespace std;
int main ()
{
vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element(x | indexed(0) | filtered(_1>100)); /* problem here */
cout << it.index() << endl;
return 0;
}
I expected the code to print out the index in the vector x which has the largest element (ie 2), but unfortunately it does not compile (Linux 64bit, GCC 4.7.2), the problem being in the line indicated above. The first compilation error I get from the compiler (amongst others) is the following:
/boost/tuple/detail/tuple_basic.hpp:396:36: error: assignment of read-only member ‘boost::tuples::cons::head’
Any ideas what I am doing wrong? Or how else I can achieve what I am trying to do? Thanks in advance!
EDIT:
Changing the problematic line to :
auto it = boost::max_element<boost::return_found>(x | sliced(1,4) | filtered(boost::function<bool(double)>(_1>100)));
seems to return the iterator to the largest element. However, is there a way to check that the iterator is within the range? Comparing it with boost::end(x) gives me an error. The only thing I can think of is to return
auto another_range = boost::max_element<boost::return_found_end>(x | sliced(1,4) | filtered(boost::function<bool(double)>(_1>100)));
and check if boost::empty(another_range). Is this the only option? Thanks.
The specific error you've encountered appears because boost lambdas are not CopyAssignable. Here's a simpler way to achieve the same message:
auto f1 = _1 > 100;
auto f2 = f1;
f2 = f1; // same error
If you provide a CopyAssignable functor to filtered, boost.phoenix (which you should be using anyway, boost.lambda is on the road to deprecation in favor of phoenix), a hand-written struct, or the old faithful std::bind2nd(std::greater<double>(), 100), this line compiles with clang++:
bind2nd demo: http://liveworkspace.org/code/2xKZIf
phoenix demo: http://liveworkspace.org/code/18425g
It fails with gcc due to some boost.concept check, which is probably a bug, but it's a moot point because the result of filtered is boost::filtered_range, whose iterators don't have the .index() member function.
EDIT in response to comment:
comparing iterator into filtered_range with the iterator into the original vector wouldn't work. However, since you used vector, and since it's still accessible, you can compare addresses, since neither indexed nor filtered make copies
#include <vector>
#include <iostream>
#include <cassert>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/phoenix.hpp>
using namespace boost::adaptors;
using namespace boost::phoenix::placeholders;
int main ()
{
std::vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element( x | indexed(0) | filtered(arg1 < 110) );
assert(&x[0] <= &*it && &*it < &x[0] + x.size());
std::cout << "Element " << *it << " is at index " << &*it - &x[0] << '\n';
}
demo http://liveworkspace.org/code/1zBIJ9
Or, for a more general solution, you could transform your vector into a vector of pairs (when boost gets zip adaptor, it could be neatly zipped with counting_range), and carry the original sequence index along with the value through all the transformations.
Related
I'm new to C++ and I'm learning with C++20. I'm trying on a struct function, which is to wrap a function in a struct, while we can claim local attributes in this struct.
The thing is that when I pass this struct function to a for_each function, it does not work.
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
struct accumulateAmount{
int total_amount;
accumulateAmount() { total_amount = 100 ;} //constructor
void operator()(int num){
total_amount += num;
}
};
int main(){
vector<int> nums{1,2,3,4,5};
accumulateAmount acctor;
for_each(nums.begin(), nums.end(), acctor);
cout << acctor.total_amount << endl;
return 0;
}
The output is 100. It does not realize the accumulator functionality.
While if I change the loop from for_each to ordinary for loop as the following:
for (int i = 0; i < nums.size(); i++){
acctor(nums[i]);
}
It works.
So I wonder if it's because 'for_each' encompasses parallel computing hence for each int in the vector, we are using independent functions on them?
std::for_each takes the function by value. So your function gets copied, and std::for_each calls the copy. That’s why your acctor does not get modified.
You can force passing by reference though, by using std::ref:
for_each(nums.begin(), nums.end(), std::ref(acctor));
Alternatively, and perhaps more idiomatically, you can capture the return value of std::for_each:
auto const result = for_each(nums.begin(), nums.end(), accumulateAmount());
std::cout << result.total_amount << "\n";
The good thing about this code is that you don’t even need to introduce a name for acctor: you can pass a temporary and create the function object on the fly. This is nice because it means that you can make all your local objects const.
That said, std::for_each with a mutable function object is absolutely not idiomatic C++. Finding the suitable algorithm isn’t always obvious, but always worth it. In this case, you’d use std::reduce:
auto const result = std::reduce(nums.begin(), nums.end(), 100);
std::cout << result << "\n";
The quickest fix:
acctor = std::for_each(nums.begin(), nums.end(), accumulateAmount());
But rather than spinning your own functor from scratch, use C++'s lambdas (C++11+).
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main(){
std::vector<int> nums{1,2,3,4,5};
int val = 100;
std::for_each(nums.begin(), nums.end(), [&val](int n) { return val += n; });
std::cout << val << '\n';
return 0;
}
Some quick notes on the lambda:
[] is where you can 'capture' variables outside the scope of the lambda, that shouldn't be passed as arguments. In this case I capture val by reference. You are not required to capture anything, but the [] is required.
() parameter list, pretty straightforward.
{} function body, also straightforward.
As pointed out in a comment to another answer, this specific example is solved even simpler with std::accumulate or std::reduce (Shown in another answer (this one was new to me, and pretty cool)).
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
int main() {
std::vector<int> nums{1, 2, 3, 4, 5};
std::cout << std::accumulate(nums.begin(), nums.end(), 100) << '\n';
return 0;
}
I have following C++ object
std::vector<std::vector<SomeClass>> someClassVectors(sizeOFOuter);
where I know the size of "outer" vector, but sizes of "inner" vectors varies. I need to copy the elements of this structure into 1D array like this:
SomeClass * someClassArray;
I have a solution where I use std::copy like this
int count = 0;
for (int i = 0; i < sizeOfOuter; i++)
{
std::copy(someClassVectors[i].begin(), someClassVectors[i].end(), &someClassArray[count]);
count += someClassVectors[i].size();
}
but the class includes large matrices which means I cannot have the "vectors" structure and 1D array allocated twice at the same time.
Any ideas?
Do you previously preallocate someClassArray to a given size? I'd suggest using 1D vector for getting rid of known problems with the plain array if possible.
what about something like this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::vector<std::vector<int>> someClassVectors {
{1,2,3},
{4,5,6},
{7,8,9}
};
std::vector<int> flat;
while (!someClassVectors.empty())
{
auto& last = someClassVectors.back();
std::move(std::rbegin(last), std::rend(last), std::back_inserter(flat));
someClassVectors.pop_back();
}
std::reverse(std::begin(flat), std::end(flat));
int * someClassArray = flat.data();
std::copy(someClassArray, someClassArray + flat.size(), std::ostream_iterator<int>(std::cout, " "));
}
The extra reverse operation doesn't have an effect on memory metrics - such an approach helps to avoid unneeded memory reallocations resulting from removing vector elements from beginning to end.
EDIT
Inspired by comments I changed copy to move semantics
Embrace Range-v3 (or whatever will be introduced in C++20) and write a solution in (almost) a single line:
auto flattenedRange = ranges::views::join(someClassVectors);
this gives you a range in flattenedRange, which you can loop over or copy somewhere else easily.
This is a possible use case:
#include <iostream>
#include <vector>
#include <range/v3/view/join.hpp>
int main()
{
std::vector<std::vector<int>> Ints2D = {
{1,2,3},
{4},
{5,6}
};
auto Ints1D = ranges::views::join(Ints2D);
// here, going from Ints1D to a C-style array is easy, and shown in the other answer already
for (auto const& Int : Ints1D) {
std::cout << Int << ' ';
}
std::cout << '\n';
// output is: 1 2 3 4 5 6
}
In case you want to get a true std::vector instead of a range, before writing it into a C-style array, you can include this other header
#include <range/v3/range/conversion.hpp>
and pipe join's output into a conversion function:
auto Ints1D = ranges::views::join(Ints2D) | ranges::to_vector;
// auto deduces std::vector<int>
In terms of standard and versions, it doesn't really require much. In this demo you can see that it compiles and runs just fine with
compiler GCC 7.3
library Range-v3 0.9.1
C++14 standard (option -std=c++14 to g++)
As regards the copies
ranges::views::join(Ints2D) is only creating a view on Ints2D, so no copy happens; if view doesn't make sense to you, you might want to give a look at Chapter 7 from Functional Programming in C++, which has a very clear explanation of ranges, with pictures and everything;¹
even assigning that output to a variable, auto Ints1D = ranges::views::join(Ints2D);, does not trigger a copy; Ints1D in this case is not a std::vector<int>, even though it behaves as one when we loop on it (behaves as a vector because it's a view on it);
converting it to a vector, e.g. via | ranges::to_vector, obviously triggers a copy, because you are no more requesting a view on a vector, but a true one;
passing the range to an algorithm which loops on its elements doesn't trigger a copy.
Here's an example code that you can try out:
// STL
#include <iostream>
#include <vector>
// Boost and Range-v3
#include <boost/range/algorithm/for_each.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/range/conversion.hpp>
struct A {
A() = default;
A(A const&) { std::cout << "copy ctor\n"; };
};
int main()
{
std::vector<std::vector<A>> Ints2D = {
{A{},A{}},
{A{},A{}}
};
using boost::range::for_each;
using ranges::to_vector;
using ranges::views::join;
std::cout << "no copy, because you're happy with the range\n";
auto Ints1Dview = join(Ints2D);
std::cout << "copy, because you want a true vector\n";
auto Ints1D = join(Ints2D) | to_vector;
std::cout << "copy, despite the refernce, because you need a true vector\n";
auto const& Ints1Dref = join(Ints2D) | to_vector;
std::cout << "no copy, because we movedd\n";
auto const& Ints1Dref_ = join(std::move(Ints2D)) | to_vector;
std::cout << "no copy\n";
for_each(join(Ints2D), [](auto const&){ std::cout << "hello\n"; });
}
¹ In an attempt to try giving a clue of what a range is, I would say that you can imagine it as a thing wrapping two iterators, one poiting to the end of the range, the other one pointing to the begin of the range, the latter being incrementable via operator++; this opearator will take care of the jumps in the correct way, for instance, after viewing the element 3 in Ints2D (which is in Ints2D[0][2]), operator++ will make the iterator jump to view the elment Ints[1][0].
By design forward and reverse iterators and ranges are fundamentally different types. This is nice in the compile time optimization that it allows for. Sometimes it would be nice to hide that type difference behind an abstraction that allows them to be passed to the same run-time interface.
Are there any adapters in boost or the stl that make this easy? (ideally but not strictly C++11)
The following code shows both the known/expected failure and the desired hypothetical:
#include <boost/range.hpp>
#include <vector>
using Ints = std::vector<int>;
void real(boost::iterator_range<Ints::iterator> range){}
void fake(boost::agnostic_range<Ints::iterator> range){} // imaginary desired
int main()
{
auto ints = Ints{1,2,3,4,5};
real(boost::make_iterator_range(ints.begin(), ints.end()));
real(boost::make_iterator_range(ints.rbegin(), ints.rend())); // Error
fake(boost::make_agnsotic_range(ints.begin(), ints.end())); // imaginary
fake(boost::make_agnsotic_range(ints.rbegin(), ints.rend())); // imaginary
return 0;
}
Yes! Boost::any_range type erases the iterated object type and exposes only the output type and the iterator access type.
Note that the type erasure here requires a call through a virtual function to dereference the iterator so there's a performance cost there, but as long as non-trivial operations are performed inside the loop, this cost will be likely irrelevant.
BUG WARNING: boost::range had a big bug between ~1.55ish until release 1.74 (2020-08) which would cause access to destroyed items being passed through any_range that would cause UB (undefined behavior/probably crash) The work around to this exists in the code below where you explicitly pass the so-called reference type though the template parameters as const which causes some of the internal machinery to avoid tripping over the mistake.
#include <boost/range/adaptor/type_erased.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/any_range.hpp>
#include <vector>
#include <list>
#include <iostream>
// note const int bug workaround
using GenericBiDirIntRange =
boost::any_range<int, boost::bidirectional_traversal_tag, const int>;
void possible(GenericBiDirIntRange const &inputRange) {
for(auto item: inputRange)
std::cout << item << "\n";
}
// note const int bug workaround
using type_erased_bi =
boost::adaptors::type_erased<int, boost::bidirectional_traversal_tag, const int>;
using reversed = boost::adaptors::reversed;
auto main() -> int {
auto intVec = std::vector<int>{1, 2, 3, 4};
auto intList = std::list<int>{1, 2, 3, 4};
possible(intVec | type_erased_bi());
possible(intList | reversed | type_erased_bi());
return 0;
}
Is it legal to use thrust::sort_by_key like in following code?
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/sort.h>
#include <thrust/advance.h>
#include <thrust/copy.h>
#include <iterator>
#include <iostream>
int main()
{
int init[] = {2, 0, 1, 3, 4};
const thrust::device_vector< int > v{std::cbegin(init), std::cend(init)};
thrust::device_vector< std::intptr_t > index{v.size()};
thrust::sequence(index.begin(), index.end());
auto key =
thrust::make_permutation_iterator(
thrust::make_transform_iterator(
v.cbegin(),
thrust::identity< thrust::tuple< int > >{}),
index.cbegin());
thrust::sort_by_key(
key,
thrust::next(key, index.size()),
index.begin());
thrust::copy(
index.cbegin(), index.cend(),
std::ostream_iterator< std::intptr_t >(std::cout, ", "));
std::cout << std::endl;
}
Here index array points to v array of values. I want to have a "sorted view" of v.index after above sorting is the view, that is [v[i] for i in index] (pythonic pseudocode) is sorted.
The trick with identity transformation is crucial here: it transform values pointed to by index in v to one-element-tuple. thrust::tuple is a class and have operator =, which is not cv-ref-qualified for lvalues only and thus can be used on rvalues just returned as a result of dereferencing of the transform_iterator. thrust::tuple< int >(1) = 2; is a legal statement and effectively is a no-op, because left hand side value dropped right after the assignment. As a result key swaps in sort_by_key are all no-ops and real sorting occurs in "values" part of key-value sorting. Also not, that v is immutable here (result v.cbegin() is const iterator).
As I know, developers of Thrust generally assume, that all callables are idempotent. I believe the assumption is not violated here, because only the argument of the callable (thrust::identity) changed, not a state of the callable. But on the other hand any superposition of Thrust fancy iterators can be considered as a composition of a functions (say, permutation_iterator is a simple mapping).
In sort_by_key index is read and written. It can be prohibited by implicit rules of implementation. Is it correct code?
Is there a way to apply non-modifying standard library algorithms to discrete functions instead of containers?
For example, consider the following function
int sqr(int i)
{
return i*i;
}
How can I use std::find or std::lower_bound to search for the value 49, i.e. the algorithm should return 7? The easiest way would be to put the returns into a vector and apply the algorithm to the vector -- but this is obviously inefficient.
Hypothetically, you could use something like boost::iterator::counting_iterator. E.g., the following finds that 4 is the number whose square is 16:
#include <algorithm>
#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
using namespace std;
int main(int, char**)
{
auto f = std::find_if(
boost::make_counting_iterator<int>(0),
boost::make_counting_iterator<int>(20),
[](int i){return i * i == 16;});
cout << std::distance(
boost::make_counting_iterator<int>(0),
f) << endl;
return 0;
}
I think that this approach is problematic in many ways. In the above, in particular, note that it searches for such numbers up to 20.