How to use Eigen::Ref with STL Vector? - c++

currently, I'm working on a specific problem where I have an STL vector with several mapped Eigen vectors of different sizes which I would like to keep.
When writing a function to accept my stl vector with mapped eigen vectors, I always encounter a compiler error, stating that conversion could not be performed.
When simply working without the surrounding STL vector (i.e. Eigen::Ref<Eigen::VectorXd>), everything works fine. However, once the STL vector comes into play, the given compiler error occurs:
My mwe looks as follows:
#include <iostream>
#include <vector>
#include <Eigen/Core>
void EigenRefFunction(const std::vector<Eigen::Ref<Eigen::VectorXd>>& vec)
{
for(int i=0;i<vec.size();++i)
{
for(int j=0;j<vec[i].rows();++j)
{
std::cout << i << "," << "j:\t" << vec[i](j) << std::endl;
}
}
}
int main()
{
//Examplary vector to map
std::vector<double> vec1(10, 1.0);
//Mapped vector
std::vector<Eigen::Map<Eigen::VectorXd>> vec_map;
vec_map.push_back(Eigen::Map<Eigen::VectorXd>(vec1.data(), vec1.size()));
//Call to function
EigenRefFunction(vec_map);
}
This results in compiler error C2665 on Visual Studio v142.
Edit: Sorry, just copied the generic translation. Here is the full error message from godbolt (thanks #Marek R):
: In function 'int main()': :26:22: error: invalid
initialization of reference of type 'const
std::vector > >&' from
expression of type 'std::vector, 0, Eigen::Stride > >' 26 | EigenRefFunction(vec_map);
| ^~~~~~~ :5:71: note: in passing argument 1 of 'void EigenRefFunction(const
std::vector > >&)'
5 | void EigenRefFunction(const std::vector>& vec)
If, for any reason, this is not resolvable: Is there another efficient way to store several differently sized mapped Eigen vectors in a data structure which is flexible to be extended to different sizes at runtime?
Any help would be much appreciated, thank you!
Edit:
Thanks for all the very quick answers. Marek R provided the code to resolve my issue by simply changing the provided parameter to the function call. The resulting copy is not an issue for me:
EigenRefFunction({ vec_map.begin(), vec_map.end() });

Related

Why output of boost::adaptors::filtered has no member named size?

The following dummy program compiles and runs
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <functional>
#include <utility>
#include <vector>
using boost::adaptors::filtered;
using boost::adaptors::transformed;
auto whatever = [](auto&& x){ return std::forward<decltype(x)>(x); };
auto whenever = [](auto&){ return true; };
int main() {
std::vector<int> v{1,2,3};
auto w1 = v | transformed(whatever);
auto w2 = v | transformed(whatever) | filtered(whenever);
w1.size();
//w2.size();
}
uncommenting the commented line and attempting a compilation with g++ -std=c++14 that_file.cpp causes this error:
uffa.cpp: In function ‘int main()’:
uffa.cpp:17:8: error: ‘struct
boost::range_detail::filtered_range<<lambda(auto:2&)>, const
boost::range_detail::transformed_range<<lambda(auto:1&&)>, std::vector<int> >
>’ has no member named ‘size’
17 | w2.size();
| ^~~~
Since filtered, just like transformed, takes a range and returns a range, I don't understand why size is not available on filtered's output.
I know that transformed and filtered are two different mathematical functions (e.g. the former assumes that its input is a functor, whereas the latter assumes, correct me if I'm wrong, that its input is a monad), but still... here the input is a std::vector so what's wrong with asking the size of the output of filtered?
When you transform a range, the size doesn't change. That means transform can known what the size will be, as it's the same as the input.
When you filter a range, you may or may not be removing elements from the range. This is done lazily, so you can't know until you go through the filtered range how large it will be. If you do a non-lazy filter you can know, but ranges are supposed to be lazy.
You can use boost::size:
boost::size(w2)
but it may be costly in terms of performance.

unordered_set creation for finding pairs of integers in a vector

For finding pairs in a vector, I am using unordered_set. I need help in why the creation is not working.
I am working on some code trying to find the pairs in a vector. I needed unordered_set for the logic.
Here is my code:
int sockMerchant(int n, vector<int> ar) {
set<int> colors = new unordered_set<int>();
int pairs;
for ( int i = 0 ; i < n ; i++ ) {
if(!colors.contains(ar[i])) {
colors.insert(ar[i]);
} else {
pairs++;
colors.erase(ar[i]);
}
}
return pairs;
}
Error message:
Solution.cpp: In function 'int sockMerchant(int, std::vector<int>)':
Solution.cpp:10:23: error: conversion from 'std::unordered_set<int>*' to non-scalar type 'std::set<int>' requested
set<int> colors = new unordered_set<int>();
^~~~~~~~~~~~~~~~~~~~~~~~
Solution.cpp:15:20: error: 'class std::set<int>' has no member named 'contains'
if(!colors.contains(ar[i])) {
^~~~~~~~
Exit Status
1
I except the unordered_set to be created and contains to be used.
There are a lot of issues here (and the compiler is nice enough to be really informative about it)
You're trying to assign std::unordered_set<int>* into a std::set<int> variable. The types don't match, one is a pointer (returned from new()) and the other is a local stack variable. The actual classes don't match either unordered_set != set
std::set nor std::unordered_set does not have a member function named contains (at least not in the current C++ standard, will be available in C++20). You can just use the find member function from within std::unordered_set to see if an element is already in the set.
You can have a look on the definition of the class in here:
https://en.cppreference.com/w/cpp/container/unordered_set
In another note, if there is no need to use new, don't use it. The function does not return the set that you created.
If you insist of using new, you should use delete after you finish using the set, if you won't do that you will have memory leaks.

Using set of function c++

I am trying to create a set of function pointers in c++ but getting error while inserting / deleting elements from it.
#include<bits/stdc++.h>
using namespace std;
void func(int x) {
cout<<x;
}
int main() {
set <function<void (int)>> mine;
mine.insert(func);
return 0;
}
i am getting error
/usr/include/c++/6/bits/stl_function.h:386:20: error: no match for ‘operator<’ (operand types are ‘const std::function’ and ‘const std::function’).
I think that this problem is because of operator that will be used to compare set values , can someone suggest how to make this work ?
How can i write a comparator for function pointers in this case ?
If you want to store just function pointers, you don't need std::function:
using MyFunctionPointer = void(*)(int);
void func(int x);
std::set<MyFunctionPointer> orderedSet;
int main()
{
orderedSet.emplace(func);
}
Demo
This works because you can compare (for std::set) or hash (for std::unordered set) function pointer values. But comparing or hashing std::function instances is not implemented in the standard library, and there is no portable way to add that after the fact.
Edit: As pointed out by #HolyBlackCat, while the builtin operator< is not required to induce the required total order on function pointers, std::less (as used by std::set) is required to do so for any pointer.

How to access members of thrust::device_vector<struct> [duplicate]

This question already has answers here:
Thrust inside user written kernels
(4 answers)
Closed 4 years ago.
CUDA has some documentation found here: https://docs.nvidia.com/cuda/thrust/index.html#vectors which allows the use of vector in device memory/code. I am trying to create a vector of a struct type to use for general processing. Here is the sample code:
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <iostream>
struct Data
{
double first, second, total;
};
__global__
void add(thrust::device_vector<Data> *d_matrix)
{
&d_matrix[1].total = &d_matrix[1].first + &d_matrix[1].second;
}
int main()
{
thrust::host_vector<Data> matrix;
thrust::device_vector<Data> *d_matrix;
int size = sizeof(thrust::host_vector<Data>);
matrix[1].first = 2100;
matrix[1].second = 100;
cudaMalloc(&d_matrix, size);
cudaMemcpy(d_matrix, &matrix, size, cudaMemcpyHostToDevice);
add<<<1,1>>>(d_matrix);
cudaMemcpy(&matrix, d_matrix, size, cudaMemcpyDeviceToHost);
cudaFree(d_matrix);
std::cout << "The sum is: " << matrix[1].total;
return 0;
}
I get the following error:
gpuAnalysis.cu(13): error: class "thrust::device_vector>" has no member "total"
gpuAnalysis.cu(13): error: class "thrust::device_vector>" has no member "first"
gpuAnalysis.cu(13): error: class "thrust::device_vector>" has no member "second"
3 errors detected in the compilation of
"/tmp/tmpxft_000013c9_00000000-8_gpuAnalysis.cpp1.ii".
According to the documentation provided on the nvidia site, these vectors are able to store all data types as std::vector. Is there a way to fix this error to access the members of the struct with each vector element?
void add(thrust::device_vector<Data> *d_matrix) {
&d_matrix[1].total = &d_matrix[1].first + &d_matrix[1].second;
}
In this code, d_matrix parameter is actually a pointer to an object of type thrust::device_vector<Data>. The expression &d_matrix[1].total is due to C++ operator precedence evaluated such that d_matrix[1] is considered to be the 2nd element of some non-existing array of elements of type thrust::device_vector<Data>, since a pointer can be treated as an array automatically. This (non-existing) 2nd element is then subject of .total member access, which does not exist.
Try (*d_matrix)[1].total = ... instead.
Also I am not sure that your code is correct. For example, you don't specify the size (number of elements, not size of an object) of neither your host_vector nor device_vector. You also cudaMemcpy vector object themselves; does it copy their content as well? Is it even allowed? I have no experience with Thrust, but according this page, there are much simpler way how to create device_vector.

Why are initializer lists not available when changing the allocator of std::vector?

In my project I changed the used point type from Eigen::Vector2f to Eigen::Vector2d and ran into the alignment problem.
Here is a simplified version of the code:
#include <vector>
#include <Eigen/Eigen>
int main()
{
std::vector<Eigen::Vector2d> points = { {0,0}, {0,1} };
}
I'm getting the following run time error:
eigen3/Eigen/src/Core/DenseStorage.h:78: Eigen::internal::plain_array<double, 2, 0, 16>::plain_array() [T = double, Size = 2, MatrixOrArrayOptions = 0, Alignment = 16]: Assertion `(reinterpret_cast<size_t>(array) & 0xf) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.
As the assert-message suggested, I read about the required alignment of Fixed-size vectorizable Eigen objects. And also the subsection about STL Containers. And it seems like I have two options:
use the Eigen::aligned_allocator
or use the EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION macro.
Both attempts do not compile (tested with GCC 4.8.3 and Clang 3.5) because the compiler is not able to properly convert the initializer list.
Here the altered code:
#include <vector>
#include <Eigen/Eigen>
#include <Eigen/StdVector>
// EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d)
int main()
{
std::vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> points = { {0,0}, {0,1} };
// std::vector<Eigen::Vector2d> points = { {0,0}, {0,1} };
}
GCC error output:
error: could not convert ‘{{0, 0}, {0, 1}}’ from ‘<brace-enclosed initializer list>’ to ‘std::vector<Eigen::Matrix<double, 2, 1>, Eigen::aligned_allocator<Eigen::Matrix<double, 2, 1> > >’
So I'm wondering:
Why are initializer lists not available when changing the allocator of the std::vector?
Is this because of alignment?
Can I somehow align the initializer list?
Why is the specialization version failing?
Are these lacking the initializer list feature?
After studying the Eigen/StdVector include file (to be precise it is in Eigen/src/StlSupport/StdVector.h line 68 of version 3.2.1), it seems that the problem originates from a partial template specialization of the std::vector within this header file. This partial template specialization replaces the STL vector as soon as you use Eigen::aligned_allocator as allocator. And this specialization seems to lack the C++11 features.
In detail why this specialization is necessary apart from replacing the allocator:
Before C++11 the resize function of the std::vector could take an additional parameter by value to initialize newly created elements. According to the Eigen3 Documentation passing a parameter by value discards any alignment modifiers and cannot be used with fixed-size vectorizable Eigen objects (see SIMD).
Edit:
After some more testing I realized that the C++11 implementation of the std::vector does not have the above stated issue. So to resolve the alignment problem you only need to fill in the Eigen::aligned_allocator. But do not include Eigen/StdVector. Including this file will prevent you from using the C++11 implementation of std::vector since this header defines a partial specialization with Eigen::aligned_allocator as allocator.