Creating a map using struct members throws bad_alloc - c++

So I have a struct
struct float3
{
float x, y, z;
};
and I am trying to create a function to take the x,y,z values and map them to keys 0, 1, 2 for their respective dimension. I wrote the code below but it throws a bad alloc_exception. It appears like I'm running out of memory.
KdTree::float2map(std::vector<float3>& data)
{
std::vector<std::map<int, float> > m_pnts;
int cnt = 0;
for(int i = 0; i = data.size(); i++)
{
std::map<int, float> tmp;
tmp.insert(std::make_pair(0, data[i].x));
tmp.insert(std::make_pair(1, data[i].y));
tmp.insert(std::make_pair(2, data[i].z));
m_pnts.push_back(tmp);
std::cout << m_pnts.size() << std::endl;
}
}
return m_pnts;
}
I'm still fairly new to C++ so I'm sure there are many other ways to do this or to optimize this approach. The problem is I have to do this one 33,914,095 float3s and I can't think of another way to achieve this. Any help would be greatly appreciated.

Look at this line of code:
for(int i = 0; i = data.size(); i++)
You are setting i to data.size() in the condition of the for statement.
This will cause an out of bounds access because data.size() will become the index to data vector whose elements are indexed from 0 to data.size() - 1.
That was probably not your intention.
Use a proper condition in the for loop. It should be:
for(int i = 0; i < data.size(); i++)
Or better still, use a range-based for loop which help in avoiding such bugs:
for(const auto& ele: data)
{
std::map<int, float> tmp;
tmp.insert(std::make_pair(0, ele.x));
tmp.insert(std::make_pair(1, ele.y));
tmp.insert(std::make_pair(2, ele.z));
m_pnts.push_back(tmp);
std::cout << m_pnts.size() << std::endl;
}

Related

Accessing Values after Sorting Vector

I sort data elements according to the index of the sorted list. When I need to sort the data a second or third time, I need the original order of the data back each time to sort again. How can I access values stored in original order again?
decltype(m_data) new_m_datalast;
vector<int> m_newOrder;
for(int i=0; i< (int) m_data.size(); ++i)
new_m_datalast.push_back(std::move(m_data[m_newOrder[i]]));
m_data = new_m_datalast;
UPDATE
void Canvas::SetSortedOrder(std::vector<int> idxs)
{
m_newAxesOrder.clear();
m_newAxesOrder = idxs;
m_minmaxdata.clear();
QString filename(":/simdata/minmax.csv");
m_minmax = ReadCSV(filename);
decltype(m_data) new_m_data;
decltype(m_minmaxdata) new_m_minmaxdata;
for(int i=0; i< (int) m_data.size(); ++i)
new_m_data.push_back(std::move(m_data[m_newAxesOrder[i]]));
for(int i=0; i< (int) m_minmaxdata.size(); ++i)
new_m_minmax.push_back(std::move(m_minmax[m_newAxesOrder[i]]));
m_data = new_m_data;
m_minmax = new_m_minmax;
}
If you need to sort a lot of times and access original data later, I suggest you to use tag sort. Here is a small C++ example:
template<typename T>
std::vector<size_t> tag_sort(const std::vector<T>& arr) {
std::vector<size_t> tag(arr.size());
std::iota(tag.begin(), tag.end(), 1);
std::sort(tag.begin(), tag.end(), [&arr](auto a, auto b) { return arr[a] < arr[b]; });
return tag;
}
int main() {
std::vector<int> arr = {5, 7, 2, 5, 9, 6, 1};
auto tag = tag_sort(arr);
for (auto v : tag)
std::cout << v << " ";
std::cout << "\nsorted data:\n";
for (auto v : tag)
std::cout << arr[v];
std::cout << "\n";
}
As you see, you can access original data in arr, and use tag vector to use sorted data.
Tag sort is mainly used when switching elements by moving/copying is very expensive. But I think it is a good approach for your problem too.
I am sure this way isn't the best. But I do it anyway. I make a vector<pair<int,int>> v and I put the data I want to sort in first like: v[i].first = data and their indices in second like:v[i].second = i and I sort the vector or process it as I like. And I can access its original order any time I need.
Note: using sort(v.begin(),v.end()) will sort by first of the pair by default without the need to make a compare function.

Decimation in C++

Dear Stack Community,
I'm doing a DSP exercise to complement my C++ FIR lowpass filter with filter coefficients designed in and exported from Matlab. The DSP exercise in question is the act of decimating the output array of the FIR lowpass filter to a lower sample rate by a factor of 'M'. In C++ I made a successful but extremely simple implementation within a .cpp file and I've been trying hard to convert it to a function to which I can give the output array of the FIR filter. Here is the very basic version of the code:
int n = 0;
int length = 50;
int M = 12;
float array[length];
float array2[n];
for (int i = 0 ; i<length; i++) {
array[i] = std::rand();
}
for (int i = 0; i<length; i=i+M) {
array2[n++] = array[i];
}
for (int i = 0; i<n; i++) {
std::cout << i << " " << array2[i] << std::endl;
}
As you can see very simple. My attempt to convert this to a function using is unfortunately not working. Here is the function as is:
std::vector<float> decimated_array(int M,std::vector<float> arr){
size_t n_idx = 0;
std::vector<float> decimated(n_idx);
for (int i = 0; i<(int)arr.size(); i = i + M) {
decimated[n_idx++] = arr[i];
}
return decimated;
}
This produces a very common Xcode error of EXC_BAD_ACCESS when using this section of code in the .cpp file. The error occurs in the line 'decimated[n_idx++] = arr[i];' specifically:
int length = 50;
int M = 3;
std::vector<float> fct_array(length);
for (int i = 0 ; i<length; i++) {
fct_array[i] = std::rand();
}
FIR_LPF test;
std::vector<float> output;
output = test.decimated_array(M,fct_array);
I'm trying to understand what is incorrect with my application of or perhaps just my translation of the algorithm into a more general setting. Any help with this matter would be greatly appreciated and hopefully this is clear enough for the community to understand.
Regards, Vhaanzeit
The issue:
size_t n_idx = 0;
std::vector<float> decimated(n_idx);
You did not size the vector before you used it, thus you were invoking undefined behavior when assigning to element 0, 1, etc. of the decimated vector.
What you could have done is in the loop, call push_back:
std::vector<float> decimated_array(int M,std::vector<float> arr)
{
std::vector<float> decimated;
for (size_t i = 0; i < arr.size(); i = i + M) {
decimated.push_back(arr[i]);
}
return decimated;
}
The decimated vector starts out empty, but a new item is added with the push_back call.
Also, you should pass the arr vector by const reference, not by value.
std::vector<float> decimated_array(int M, const std::vector<float>& arr);
Passing by (const) reference does not invoke a copy.
Edit: Changed loop counter to correct type, thus not needing the cast.

How to assign values to struct?

I have a struct to assign values to it. But my programm crashs it. Hopefully you can help me.
struct HashEntry{
std::string key; //the key of the entry
bool used; //the value of the entry
int value; //marks if the entry was used before
};
HashEntry *initHashList(int N){
HashEntry* hashList = new HashEntry[N];
for (int i = 0; i <= N; i++){
hashList[i].key = " ";
hashList[i].value = -1;
hashList[i].used = false;
}
for(int i = 0; i <N; i++){
cout<<hashList[i].value<<endl;
}
return hashList;
}
You iterate through one element too many on creation:
for (int i = 0; i <= N; i++){
Shoule be
for (int i = 0; i < N; i++){
It's because with arrays being 0-based, you can't access the element N of an array of the size N, only N-1, but in return also element 0.
Also, to make the code clearer and less error prone, you could use std::array instead of a pure C style array, or even an std::vector to be able to loop through them range based. You might also rething your use of new which should be avoided in most cases. If you don't really need that, I'd change the function to
std::vector<HashEntry> initHashList(int N) {
std::vector<HashEntry> hashList(N, { "", false, -1, }); //Creating vector of N elements
for (const HashEntry& entry : hashList) { //Iterating through the elements
std::cout << entry.value << std::endl;
}
return hashList;
}
I hope this makes it clearer how you can approach such a problem.
This way of creating the vector and looping through it avoids the potential access errors and is easier to read, imo. For more information, search for std::vector, its constructors, and range-based loops.

Sort a data structure and keep record of the original indices [duplicate]

This question already has answers here:
C++ sorting and keeping track of indexes
(16 answers)
Closed 7 years ago.
Suppose I have code such as the following, to sort elements of a data structure in another data structure, but keep a record of their original indices:
std::vector<int> numbers = {..};
std::vector<std::pair<int, std::vector<int>::size_type>> temp;
for (std::vector<int>::size_type i = 0; i < numbers.size(); i++)
{
temp.push_back({ numbers[i], i });
}
std::sort(temp.begin(), temp.end(), [](const auto& x, const auto& y) { return x.first < y.first; });
So far so good. But what I really want is to store the data and indices in different data structures:
std::vector<int> sorted;
std::vector<std::vector<int>::size_type> indices;
Such that the element at sorted[i] was at index indices[i] in the original data structure.
Other than rolling out my own sorting algorithm, or splitting the data structure after the fact, is there any easy trick using the standard library to do this?
The idea is to store your data in one container and have other containers with indices (based on different sorting criteria).
The idea here is to write function objects that compare two indices based on the sorting criteria and the value in the data container.
The next step is to pass your function object to the appropriate sorting function.
Clarifying Thomas Matthew answer ... Create a vectors of indices 0 to n-1. Use a lambda function to sort the vector of indices according to the data vector. Example code including an optional reorder in place function.
void reorder(std::vector<int>& vA, std::vector<size_t> vI);
int main()
{
std::vector <int> vdata = { 7,2,4,5,3,1,6,0 }; // original data
std::vector <size_t> vindex = { 0,1,2,3,4,5,6,7 }; // original indices
std::sort(vindex.begin(), vindex.end(), [&vdata](size_t i, size_t j)
{return vdata[i] < vdata[j];});
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << vindex[i] << "]=" << vdata[vindex[i]] << " ";
std::cout << std::endl;
reorder(vdata, vindex);
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << i << "]=" << vdata[i] << " ";
return 0;
}
// every move places an element in it's final location
// vI passed by value (copy)
void reorder(std::vector<int>& vA, std::vector<size_t> vI)
{
size_t i, j, k;
int tA;
for (i = 0; i < vA.size(); i++) {
if (i != vI[i]) {
tA = vA[i];
k = i;
while (i != (j = vI[k])) {
vA[k] = vA[j];
vI[k] = k;
k = j;
}
vA[k] = tA;
vI[k] = k;
}
}
}

C++ Persistent Vector, fill vector with data from a text file

i am currently trying to learn some C++ and now i got stuck in an exercise with vectors. So the task is to read ints from a text file and store them in the vector which should be dynamic.
I guess there is something wrong with the while-loop?
If I start this, the program fails and if I set the vector size to 6, I get
6 0 0 0 0 0 as output.
Thanks for any hints.
int main()
{
const string filename = "test.txt";
int s = 0;
fstream f;
f.open(filename, ios::in);
vector<int> v;
if (f){
while(f >> s){
int i = 0;
v[i] = s;
i = i+1;
}
f.close();
}
for(int i = 0; i < 6; i++){
cout << v[i] << "\n";
}
}
You don't grow the vector. It is empty and cannot hold any ints. You'll need to either resize it every time you want to add another int or you use push_back which automatically enlarges the vector.
You set i = 0 for every iteration so you would change the first value of the vector every iteration instead of the next one.
Go for:
v.push_back(s);
in your loop and
for(int i = 0; i < v.size(); i++) { // ...
Remark:
You normally don't hardcode vector sizes/bounds. One major point about using std::vector is its ability to behave dynamically with respect to its size. Thus, the code dealing with vectors should not impose any restrictions about the size of the vector onto the respective object.
Example:
for(int i = 0; i < 6; i++){ cout << v[i] << "\n"; }
requires the vector to have at least 6 elements, otherwise (less than 6 ints) you access values out of bounds (and you potentially miss elements if v contains more than 6 values).
Use either
for(int i = 0; i < v.size(); i++){ cout << v[i] << "\n"; }
or
for(std::vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(auto i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(int x : v){ cout << x << "\n"; }
or
for(auto && x : v){ cout << x << "\n"; }
or
std::for_each(v.begin(), v.end(), [](int x){ std::cout << x << "\n"; });
or variants of the above which possibly pre-store v.size() or v.end()
or whatever you like as long as you don't impose any restriction on the dynamic size of your vector.
The issue is in the line i= 0. Fixing that will give an issue in the line v[i] = s.
You always initialise i to 0 in the while loop, and that is responsible for the current output. You should shift it out of the while loop.
After fixing that, you have not allocated memory to that vector, and so v[i] doesn't make sense as it would access memory beyond bounds. This will give a segmentation fault. Instead, it should be v.push_back(i), as that adds elements to the end of a vector, and also allocates memory if needed.
If you are using std::vector you can use v.push_back(i) to fill this vector
Error is this line int i = 0;
because you declare i=0 every time in while-loop.
To correct this move this line outside from loop.
Note: this will work, if you declare v like normal array for example int v[101]
When you use std vectors you can just push element at the end of vector with v.push_back(element);
v[i] = s; //error,you dont malloc room for vector
change into : v.push_back(s);