std::list<std::shared_ptr>::erase got an SIGSEGV - c++

Consider following code:
#include <iostream>
#include <list>
#include <memory>
class Foo;
class Foo {
public:
Foo(int i): id(i) {}
typename std::list<std::shared_ptr<Foo>>::iterator i2;
int id;
};
int main() {
std::list<std::shared_ptr<Foo>> l;
auto f1 = std::make_shared<Foo>(1);
f1->i2 = l.end();
l.insert(f1->i2, f1);
std::cout << f1->id << std::endl;
std::cout << l.size() << std::endl;
for (auto i: l) {
std::cout << i->id << std::endl;
}
auto t = f1->i2;
l.erase(t);
std::cout << l.size() << std::endl;
}
Executing these code will get an SIGSEGV at l.erase(t),seems ListNode were destroyed before shared_ptr decrease its ref_count. Why?How to fix it?

After insert your f1->i2 left l.end(). You try to erase l.end(), that is not allowed.
Fix is simple. Change the line where insert called:
f1->i2 = l.insert(f1->i2, f1);

Iterator,initialized before insert/remove operation, may be in an indeterminate state after the operation and should be updated again. Also you are trying to delete a node in the list through an iterator which itself is pointing to list.end(). For deleting the last element of a list, you can rather use std::list.pop_back();
auto t = f1->i2;
l.erase(--t); // Change this in your code - decrease the iterator

Related

Integer pointer only has correct value if I print it

I am implementing my own smart_pointer, which counts the references to the thing it points to. Here is my implementation so far:
#pragma once
#include <iostream>
template <typename T>
class smart_pointer{
T* pointer;
int* cnt;
public:
smart_pointer<T>(T *el): pointer(el) { int i = 1; cnt = &i; }; //
smart_pointer<T>(const smart_pointer<T>& other): pointer(other.pointer) {
// std::cout << ", *(other.cnt): " << *(other.cnt);
cnt = other.cnt;
(*cnt)++;
} // Copy-constructor
int counter(){
int c = *cnt;
return c;
}
};
In main.cpp, I did the following:
int main(){
// smart_pointer_examples();
std::string h("hello");
smart_pointer<std::string> p(&h);
std::cout << "p: " << p.counter();
smart_pointer<std::string> q(p);
std::cout << ", q: " << q.counter() << std::endl;
return 0;
}
The problem is that that outputs p: 1, q: 6487781. After a lot of time trying to find the issue by debugging and printing stuff, I found something that fixed my issue: By adding std::cout << ", *(other.cnt): " << *(other.cnt); somewhere in my copy-constructor, the output becomes p: 1, *(other.cnt): 1, q: 2, which is the desired behaviour. I can't for the life of me think of why printing the counter would change anything.
Edit: Also, if I only do *(other.cnt) without std::cout, the same problem that I started with happens.
You made a small mistake in implementing your idea.
I will not comment on the design of your smart pointer implementation.
The problem is that you implemented your counter as a pointer. That is wrong.
And, you are dereferencing a local variable. That is a semantic bug. The result is undefined. The value of the counter will be indeterminate. Additionally you should initialize your class members.
If we fix both, then your code will look like:
#pragma once
#include <iostream>
template <typename T>
class smart_pointer {
T* pointer{};
int cnt{};
public:
smart_pointer<T>(T* el) : pointer(el) { cnt = 1; }; //
smart_pointer<T>(const smart_pointer<T>& other) : pointer(other.pointer) {
// std::cout << ", *(other.cnt): " << *(other.cnt);
cnt = other.cnt;
cnt++;
} // Copy-constructor
int counter() const {
return cnt;
}
};
int main() {
// smart_pointer_examples();
std::string h("hello");
smart_pointer<std::string> p(&h);
std::cout << "p: " << p.counter();
smart_pointer<std::string> q(p);
std::cout << ", q: " << q.counter() << std::endl;
return 0;
}

Access map element via pointer in C++

I switched from c to c++ recently and just can't figure out what I'm doing wrong here.
I would like to access and set the member of a map via another function.
Here is my example which you can just copy to cpp.sh or so if you like
#include <iostream>
#include <map>
using namespace std;
struct test{
int i;
int j;
};
void addValues(test* val){
if (val == NULL){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
void printVal(test* val){
cout<<"finish " << val->i << " " << val->j;
}
int main()
{
map<string, test*> bla = {{"test1",NULL}};
addValues(bla.at("test1"));
printVal(bla.at("test1"));
return 0;
}
code from my project is a little bit more complex but it's basically this problem. I created a test in addValues() and have not deleted it. Why am I not able to print this value in printVal()? What am I missing?
Thanks in advance!
Parameters are passed by value. Pointers are no exception to that. Your addValues modifies a local copy of the pointer when a nullptr is passed. Modifying that local copy does not affect the pointer in the map. Pass the pointer by reference:
void addValues(test*& val){
if (val == nullptr){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
Or better yet, do not use raw pointers in the first place. Moreover, consider to write a constructor that initializes the members of test instead of relying on the caller to initialize them.
Example :
#include <iostream>
#include <map>
//using namespace std; NO teach yourself not to do this.
struct test
{
int i = 0; // <== in c++ you can initialize values of structs
int j = 0;
};
// this instead of printVal
std::ostream& operator<<(std::ostream& os, const test& t)
{
os << "i = " << t.i << ", j = " << t.j << "\n";
return os;
}
int main()
{
std::map<std::string, test> map =
{
{"test1",{1,1}},
{"test2",{2,2}},
};
// loop over all entries in the map
// range based for loop.
// each entry in the map is a key,value pair (not they key, not the value but a pair)
// https://en.cppreference.com/w/cpp/language/range-for
std::cout << "range based for over keyvalue pairs\n";
for (const auto& kv : map)
{
// note kv.second is where we use operator<< from earlier.
std::cout << "Key : " << kv.first << ", value : " << kv.second << "\n";
}
std::cout << "\n";
// structured bindings make code more readable
// https://en.cppreference.com/w/cpp/language/structured_binding
std::cout << "range based for using structured bindings \n";
for (const auto& [key, value] : map)
{
std::cout << "Key : " << key << ", value : " << value <<"\n";
}
std::cout << "\n";
return 0;
}

C++ I want to avoid pointers and instead use STLs and References for maintaining a small data cache

#include <iostream>
#include <vector>
#include <string>
struct A
{
std::string a;
std::vector<A> avector;
};
typedef std::vector<A> Avector;
A& func(A& x)
{
A& ret = x.avector[0];
return ret;
}
int main()
{
A genesis = { "Parent", std::vector<A>() };
A l1 = { "Child1", std::vector<A>() };
A l2 = { "Child2", std::vector<A>() };
genesis.avector.push_back(l1);
genesis.avector.push_back(l2);
std::cout << "l1: " << l1.a << std::endl; //shows "Child1"
std::cout << "l2: " << l2.a << std::endl; //shows "Child2"
A& lx = func(genesis);
lx.a = "Childx";
std::cout << "l1: " << l1.a << std::endl; //!!still shows "Child1"
return 1;
}
So, basically what i want is to have a single copy of data overall i.e., Genesis object and two more objects l1 and l2 as objecst of Genesis.avector
However I'm unable to modify this later as every time I end up modifying the copies but not the actual data under Genesis object.
Thanks for your help!
In your code:
A genesis = { "Parent", std::vector<A>() };
A l1 = { "Child1", std::vector<A>() };
A l2 = { "Child2", std::vector<A>() };
genesis .avector.push_back(l1);
genesis .avector.push_back(l2);
You have 5 A instances. The 3 that you declare and the 2 copies you have in your vector.
So the more correct (but possible buggy) way to do it would be:
A genesis = { "Parent", std::vector<A>() };
genesis .avector.emplace_back("Child1", std::vector<A>());
genesis .avector.emplace_back("Child2", std::vector<A>());
A& l1 = genesis.avector[0];
A& l2 = genesis.avector[1];
Now you only have 3 instances. l1 and l2 are references to the items in the vector, so changes to them will be refleced in the vector as well.
I said this is possibly buggy. When you alter the vector (adding something else), the vector might have to reallocate so any references you have will be invalid and the result is undefined behavior.
If you do need to modify the vector, I would do vector<unique_ptr<A>>. Then A& l1 = *genesis.avector[0] and this will remain valid until the item get's removed from the vector.
You can also try std::reference_wrapper instead of unique_ptr if you want the objects to live on the stack.
I would try to avoid this as it's more obvious that something get destroyed when you erase from a vector than when a local variable goes out of scope.
I was able to make this work with introducing a constructor and some help from #Sorin.
#include <iostream>
#include <vector>
#include <string>
struct A
{
std::string a;
std::vector<A> avector;
A(std::string aa)
{
a = aa;
}
};
typedef std::vector<A> Avector;
A& func(A& x)
{
return x.avector[0];
}
int main()
{
A genesis("Parent");
genesis.avector.push_back(static_cast<A>("Child1"));
genesis.avector.push_back(static_cast<A>("Child2"));
A& l1 = genesis.avector[0];
A& l2 = genesis.avector[1];
std::cout << "l1: " << l1.a << std::endl;
std::cout << "l2: " << l2.a << std::endl;
A& lx = func(genesis);
lx.a = "Childx";
std::cout << "l1: " << genesis.avector[0].a << std::endl;
std::cout << "l2: " << l2.a << std::endl;
return 1;
}
Output:
l1: Child1
l2: Child2
l1: Childx
l2: Child2

Adjacent adaptor using boost::range

I'm asking myself if it is possible to extend boost-range by an adaptor, which I call adjacentAdaptor. This adaptor should basically iterate over all pairs of adjacent elements in a vector, list and so on.
I think this function is very useful in my use cases, where I often have to iterate over lists representing time steps.
The output of the last for loop should be something like:
0 1
1 2
2 3
A vector having only one element or no elements should produce nothing.
I tried using boost::adaptors::sliced producing the necessary sublist, but then I don't know how boost::range can help me to zip both subranges to one.
I just found a probable solution using boost::iterators, but I really don't like the amount of code one has to write. Also I'm missing the first and second instead I have to write a clumsy get<>. Unfortunately, the program crashes if the vector is empty!
#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
int main()
{
std::vector<int> v = { 0,1,2,3 };
for (auto iter : v | boost::adaptors::sliced(0, v.size() - 1)) {
std::cout << "First: " << iter << std::endl;
}
for (auto iter : v | boost::adaptors::sliced(1, v.size())) {
std::cout << "Second: "<< iter << std::endl;
}
auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.begin(), v.begin() + 1));
auto e = boost::iterators::make_zip_iterator(boost::make_tuple(v.end()-1, v.end()));
for (auto iter : boost::make_iterator_range(s, e)) {
std::cout << iter.get<0>() << " " << iter.get<1>() << std::endl;
}
// for (auto iter : v | adjacentAdaptor) {
// std::cout << iter.first << " " << iter.second << std::endl;
// }
}
I'm very glad for any help I can receive in this question.
Own partial solution
After some template type deduction I came up with something relatively useable.
#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
template<typename T>
using retHelperType = decltype(boost::iterators::make_zip_iterator(boost::make_tuple(T().begin(), T().begin() + 1)));
template<typename T>
using retType = decltype(boost::make_iterator_range(retHelperType<T>(), retHelperType<T>()));
template<typename T>
retType<T> adjacentIterator(T& v) {
if (v.empty()) {
auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.end(), v.end()));
auto e = boost::iterators::make_zip_iterator(boost::make_tuple(v.end(), v.end()));
return boost::make_iterator_range(s, e);
}
else {
auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.begin(), std::next(v.begin())));
auto e = boost::iterators::make_zip_iterator(boost::make_tuple(std::prev(v.end()), v.end()));
return boost::make_iterator_range(s, e);
}
}
int main()
{
retType<std::vector<int>> x;
std::vector<int> v = { };
for (auto iter : adjacentIterator(v)) {
std::cout << iter.get<0>() << " " << iter.get<1>() << std::endl;
}
}
Still, it would be nicer to access the elements with first and second, but I have no idea to achieve this behavior.

Data placed into array correctly but is not there when array is printed

I'm implementing my own hash table and I am running into the following problem: when I insert my node(s) into the table, they are not printed out when I loop through the array. I am using an array of arrays as the underlying data structure and the logic is as follows:
I pass my node to an insert function. This function, based on the
type of data in my node, calls the appropriate hash function provided
by the C++ STL.
Then, I mod the hash value returned by the size of
my hash table and use that to determine which array to place the
node.
I also have an array of arrays of booleans (the same size as my
hash table) that I use to check whether a specific spot in my hash
table already has data in it.
If it does, I simply keep looping till
an empty spot is found.
Like I said before, the problem is that the data is inputed correctly into the array (I've checked that with print statements), but when I print the array, nothing is outputted. I have also checked if my object is being constructed correctly (again, with print statements), but everything is looking fine. I've included the full code below. Any help would be greatly appreciated!
///////START OF NODE.H///////////
#ifndef NODE_H
#define NODE_H
#include <iostream>
template <typename T>
class HashTable;
template <typename T>
class Node
{
friend class HashTable<T>;
private:
T data;
public:
Node(T Data): data(Data)
{
std::cout << "In the node constructor" << std::endl;
}
Node()
{
decltype(data) {};
}
T getData() const
{
return data;
}
};
#endif
//////////////////////END OF NODE.H////////////////////
/////START OF HASHTABLE.H///////
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include "Node.h"
#include <iostream>
#include <array>
#include <functional>
#include <typeinfo>
#include <string>
const int TABLE_SIZE=5;
template <typename T>
class HashTable
{
private:
std::array<std::array<Node<T>, TABLE_SIZE>, TABLE_SIZE> hashTable;
std::array<std::array<bool, TABLE_SIZE>, TABLE_SIZE> spots;
public:
HashTable()
{
for(int index=0;index<spots.size();++index)
{
for(int position=0;position<spots.at(index).size();++position)
{
spots.at(index).at(position)=false;
}
}
}
int hashFunction(Node<T> Node)
{
auto key=Node.getData();
std::hash<decltype(Node.getData())> hash_function {};
int hash=hash_function(key);
if(hash < 0)
{
hash*=-1;
}
//std::cout << "The hash value return by the STL hash function for the key " << key << " is " << hash << std::endl;
if(hash > TABLE_SIZE)
{
hash%=TABLE_SIZE;
}
std::cout << "The hash value for the key " << key << " is " << hash << std::endl;
return hash;
}
void insert(Node<T> Node)
{
int hashValue=hashFunction(Node);
auto location=hashTable.at(hashValue);
std::cout << "Going to insert " << Node.getData() << std::endl;
for(int index=0;index<location.size();++index)
{
if(spots.at(hashValue).at(index)==false)
{
std::cout << "Found a spot that is not taken!" << std::endl;
std::cout << "The size of the data at the spot in the array before we insert is: " << location.at(index).getData().size() << std::endl;
location.at(index)=Node;
std::cout << "The size of the data at the spot in the array after we insert is: " << location.at(index).getData().size() << std::endl;
std::cout << "The data that is in the spot in the array: " << location.at(index).getData() << std::endl;
std::cout << std::endl;
spots.at(hashValue).at(index)=true;
break;
}
}
}
bool contains(Node<T> Node)
{
int hashValue=hashFunction(Node);
auto location=hashTable.at(hashValue);
auto result=find_if(begin(location), end(location), [Node] (const auto & element) {return element.getData()==Node.getData();});
if(result!=end(location))
{
return true;
}
return false;
}
int getSize() const
{
int size {};
for(int index=0;index<hashTable.size();++index)
{
size+=hashTable.at(index).size();
}
return size;
}
void print()
{
std::cout << "In the print function" << std::endl;
for(int index=0;index<hashTable.size();++index)
{
//std::cout << hashTable.at(index).size() << std::endl;
for(int position=0;position<hashTable.at(index).size();++position)
{
std::cout << hashTable.at(index).at(position).getData().size() << std::endl;
}
}
/*
for(int index=0;index<spots.size();++index)
{
for(int position=0;position<spots.at(index).size();++position)
{
if(spots.at(index).at(position)==true)
{
std::cout << "There should be some data here" << std::endl;
}
}
}
*/
}
};
#endif
////////////END OF HASHTABLE.H//////////
////////////START OF MAIN.CPP///////////
#include "HashTable.h"
#include <cstdlib>
#include <random>
#include <algorithm>
using namespace std;
int main()
{
HashTable<string> hash_table;
hash_table.insert(Node<string>("Java"));
hash_table.insert(Node<string>("C++"));
hash_table.insert(Node<string>("C#"));
hash_table.insert(Node<string>("Latex"));
hash_table.insert(Node<string>("Python"));
}
/////////////END OF MAIN.CPP/////////////
One error is in your insert(Node<T> Node) function on these line:
auto location=hashTable.at(hashValue);
//...
location.at(index) = Node;
The location should be a reference not a copy. What is happening is that you're making changes to a local location, and not the actual location that the hash table uses. Thus none of your changes "stick".
The line above should be this:
auto& location=hashTable.at(hashValue); // <-- note that auto is a reference
//...
location.at(index) = Node;
Now you are assigning the returned reference to a reference.
Also, I highly recommend you use a debugger, as this error could have been easily diagnosed if you stepped through your code to see what was being done.
in HashTable::insert this line:
auto location = hashTable.at(hashValue);
makes a copy of a Node. You then operate on and store in the copy, not the node in hashTable. Taking a reference to the node
auto & location = hashTable.at(hashValue);
should fix it.