Because references in a vector point to locations of memory and not the abstract element, it can cause a few problems when altering the memory of the vector.
If a reference points to an element in a vector, and then that element is shuffled to another spot in the vector, the reference doesn't track the element, and will point to incorrect data after the shuffle.
If a element is invalidated, you can still access that elements contents without any safety checks, if you declared a references before invalidating the element.
If the vector resizes, all current references may be invalidated.
I wrote an example program that demonstrates all three problems.
#include <iostream>
#include <vector>
struct entity { //Simple struct of data.
bool alive;
float data;
};
class manager {
std::vector<entity> vec;
size_t count; // Amount of currently alive entities
public:
//Reserves initial_amount of entities, all set to dead, count set to 0.
manager(size_t initial_amount) : vec(initial_amount, { false, 0.0f }), count(0) {}
entity& create(float f) {
vec[count] = {true, f};
return vec[count++];
}
void refresh() { //Two iterators, one starts at the front of the vector, the other at
size_t front = 0; //count. The front iterator searches for dead entities and swaps them
size_t back = count; //with alive entities from the back iterator. For each swap we decrement
//count by 1, with the final result being all alive entities are between
while(true) { //0 and count.
for( ; true; ++front) {
if (front > back) return;
if (!vec[front].alive) break;
}
for( ; true; --back) {
if (vec[back].alive) break;
if (back <= front) return;
}
std::swap(vec[back], vec[front]);
--count;
++front;
--back;
}
}
void grow(size_t n) {
vec.resize(n);
}
void print() { //Prints all alive entities.
for (size_t index = 0; index < count; index++)
std::cout << vec[index].data << " ";
std::cout << std::endl;
}
};
int main() {
using namespace std;
manager c(10);
entity& d1 = c.create(5.5);
entity& d2 = c.create(10.5);
entity& d3 = c.create(7.5);
// Correct behavior
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 10.5
cout << d3.data << endl; // 7.5
cout << endl;
d2.alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities. (this will swap d2's and d3's data in the vector,
// but wont change the locations they point to)
// Oh no! d2 and d3 still point to the same locations in the vector and now their data
// is incorrect after the swap, also d2 is dead maybe that should just be an error.
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 7.5
cout << d3.data << endl; // 10.5
cout << endl;
c.print(); // Correct behavior, prints only alive entities.
cout << endl;
d3.data = 6.5; // Trying to change the value of d3, which should still be alive.
c.print(); // Error, because d3 still points to the 3rd slot the intended value hasn't been changed.
cout << endl;
c.grow(10000);
cout << d1.data << endl; // After resize all these references are invalidated,
cout << d2.data << endl; // and using them is undefined behavior.
cout << d3.data << endl;
return 0;
}
Is there a design pattern to create a smart reference or proxy type that solves these problems? An object that will track its elements position in the vector, does specific behavior if the element is alive or dead, and stay valid after a resize?
I'm fine with the implementation of the smart/proxy reference to not be an actual reference, could be a pointer, integer index, or whatever. But this is specifically for elements in a vector, not a linked-list, map, etc.
With std::vector<std::shared_ptr<entity>>, you may have the security you want:
class manager {
std::vector<std::shared_ptr<entity>> vec;
public:
//Reserves initial_amount of entities
explicit manager(size_t initial_amount) { vec.reserve(initial_amount); }
std::weak_ptr<entity> create(float f) {
vec.push_back(std::make_unique<entity>(entity{true, f}));
return vec.back();
}
void refresh() {
vec.erase(std::remove_if(vec.begin(), vec.end(),
[](const auto& ent) {return !ent->alive;}),
vec.end());
}
void grow(size_t n) { vec.reserve(n); }
void print() { //Prints all alive entities.
for (const auto& ent : vec)
std::cout << ent->data << " ";
std::cout << std::endl;
}
};
And then the test:
int main() {
manager c(10);
auto d1 = c.create(5.5);
auto d2 = c.create(10.5);
auto d3 = c.create(7.5);
// Correct behavior
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 10.5
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 7.5
std::cout << std::endl;
if (auto e = d2.lock()) e->alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities.
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 10.5
std::cout << std::endl;
c.print(); // Correct behavior, prints only alive entities.
std::cout << std::endl;
if (auto e = d3.lock()) e->data = 6.5; // Trying to change the value of d3,
// which should still be alive.
c.print();
std::cout << std::endl;
c.grow(10000);
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 6.5
}
Demo
Related
I can't seem to figure out why the program below is not running as intended.
After entering a negative integer value and breaking the first while loop in main, program control does not proceed.
I tried flushing the output stream at various key points but the issue persists.
#include <iostream>
#include <queue>
bool checkValidity(const std::queue<int>& q) {
for(auto i = q.front(); i != q.back(); ++i){
if (q.empty() || q.size() <= 1){
std::cout << "invalid entry, insufficient elements" << '\n';
return false;
break;
}
if (i > ++i) {
std::cout << "invalid entry, not properly sorted" << '\n';
return false;
break;
}
}
std::cout << "valid entry, properly sorted" << '\n';
return true;
}
const char* bool_cast(const bool b) {
return b ? "true" : "false";
}
int main () {
std::queue<int> numbers;
int temp;
std::cout << "Pushing..." << '\n';
while(temp >= 0){
std::cout << "Enter numbers: ";
std::cin >> temp;
if(temp >= 0){
numbers.push(temp);
}
}
bool ck = checkValidity(numbers);
std::cout << bool_cast(ck) << '\n';
std::cout << "{ ";
while(numbers.size() > 0){
std::cout << numbers.front();
numbers.pop();
std::cout << " ";
}
std::cout << "}" << '\n';
return 0;
}
You cannot iterate through the queue, therefore to iterate you need to copy the queue to another queue and then traverse by poping the elements from the queue. and comparing the elements.
Inline comments added for better understanding of the code.
bool checkValidity(const std::queue<int>& q) {
if (q.empty() || q.size() <= 1){ // This can be outside the loop and checked only onces
std::cout << "invalid entry, insufficient elements" << '\n';
return false;
}
std::queue<int> numbers=q; // a new queue to copy the queue
int first=numbers.front(); // to compare we need to variable first and second
numbers.pop();// pop the first value,as it is stored in first variable
int second;
while(!numbers.empty()){
second=numbers.front();
numbers.pop();
if (first> second) { // compare the first and second variable
std::cout << "invalid entry, not properly sorted" << '\n';
return false;
break;
}
first=second; // assign second to first and traverse the reamining queue
}
std::cout << "valid entry, properly sorted" << '\n';
return true;
}
Hope this help.
Inherited from std::queue and use its protected member Container c; to access begin() and end() of the underlying container as previously done here std::queue iteration.
#include <iostream>
#include <queue>
#include <deque>
#include <iterator>
template<typename T, typename Container=std::deque<T> >
class iterable_queue : public std::queue<T,Container>
{
public:
typedef typename Container::iterator iterator;
typedef typename Container::const_iterator const_iterator;
iterator begin() { return this->c.begin(); }
iterator end() { return this->c.end(); }
const_iterator begin() const { return this->c.begin(); }
const_iterator end() const { return this->c.end(); }
};
bool checkValidity(iterable_queue<int>& q) {
if (q.empty() || q.size() <= 1){
std::cout << "invalid entry, insufficient elements" << '\n';
return false;
}
while(q.size()){
auto i = q.begin();
auto j = ++q.begin();
for(; i < q.end() && j < ++q.end();){
std::cout << *i << " " << *j << '\n';
if (*(i) > *(j)) {
std::cout << "invalid entry, not properly sorted" << '\n';
return false;
}
i++, j++;
}
std::cout << "valid entry, properly sorted" << '\n';
return true;
}
std::cout << "invalid entry, insufficient elements" << '\n';
return false;
}
const char* bool_cast(const bool b) {
return b ? "true" : "false";
}
int main () {
iterable_queue<int> numbers;
int temp;
std::cout << "Pushing..." << '\n';
while(temp >= 0){
std::cout << "Enter numbers: ";
std::cin >> temp;
if(temp >= 0){
numbers.push(temp);
}
}
bool ck = checkValidity(numbers);
std::cout << bool_cast(ck) << '\n';
std::cout << "{ ";
while(numbers.size() > 0){
std::cout << numbers.front();
numbers.pop();
std::cout << " ";
}
std::cout << "}" << '\n';
return 0;
}
I'm having a sporadic problem right now where map::erase() says it worked, but the map sometimes still has the element in it.
auto iterator = device_map.begin(); // std::map<std::string,Device*>
size_t nDeleted = 0;
while (iterator != map.end()) {
Device* device = device_map[iterator->first];
if (device->done()) {
device->close();
cout << "Erasing " << iterator->first << endl;
nDeleted = device_map.erase(iterator->first);
delete device;
}
++iterator;
}
This is called in a std::thread every 500ms. I've tested it by printing the contents of the map before and after the loop, like this:
cout << "device_map = ";
for (std::map<string, Device*>::iterator it = device_map.begin(); it != device_map.end(); ++it) {
cout << it->first << " = " << it->second << "; ";
}
cout << endl;
Sometimes, even if nDeleted == 1, the contents of device_map is the same before and after unmapping the element (except that *device == nullptr because of the delete).
I've also put a breakpoint and device_map.size() is indeed unchanged after calling device_map.erase() does not work.
I have tried this other version of map::erase with the same results:
auto iterator = device_map.begin(); // std::map<std::string,Device*>
size_t nDeleted = 0;
while (iterator != map.end()) {
Device* device = device_map[iterator->first];
if (device->done()) {
device->close();
cout << "Erasing " << iterator->first << endl;
iterator = device_map.erase(iterator);
delete device;
} else {
++iterator;
}
}
Is there something that I'm doing wrong?
Thank you,
Fred
As #Jeffrey suggested, the issue was with another thread accessing the std::map object while map::erase was being called. Adding a mutex solved the problem.
For a school project I need to shuffle a vector with unique pointers.
However when I do this I get a read access violation error after some time.
I create the vector and then call the shuffle. This I do multiple times. I just create the vector with the values and shuffle it once and after a couple of times I get the read access violation error.
code
int RandomEngine::generateRandomInt(int minValue, int maxValue)
{
std::uniform_int_distribution<int> distribution(minValue, maxValue);
std::cout << "Random getal van " << minValue << " tot " << maxValue << std::endl;
return distribution(def_rand_engine);
}
void RandomEngine::shuffleCharacterCards(std::vector<std::unique_ptr<CharacterCard>>& cards)
{
// Succeeds once but with multiple swaps read access violation
//auto randomInt = generateRandomInt(0, cards.size() - 1);
//iter_swap(cards.begin() + randomInt, cards.begin());
// Read access violation
//for (int i = 0; i < cards.size(); i++)
//{
// std::swap(cards[i], cards[std::rand() % cards.size()]);
//}
// Read access violation
//std::shuffle(cards.begin(), cards.end(), std::mt19937{ def_rand_engine});
// Read access violation
//std::random_shuffle(cards.begin(), cards.end());
// Read access violation
//std::shuffle(cards.begin(), cards.end(), std::mt19937(std::random_device()()));
}
Other class where I call the shuffle from the static class
void CharacterCardStack::prepare()
{
std::cout << "Preparing CharacterCardStack..." << std::endl;
if(cards_.size() > 2)
{
// Shuffle the cards
RandomEngine::shuffleCharacterCards(cards_);
// Burn the first card
burnedCharacterCard_ = std::move(cards_[0]);
std::cout << "Burned charactercard: " << burnedCharacterCard_->getName() << std::endl;
cards_.erase(cards_.begin());
// Als de open kaart een koning is dan moet deze vervangen worden door een ander.
if(cards_[0]->getName() == "Koning")
{
turnedCharacterCard_ = std::move(cards_[1]);
}
else
{
turnedCharacterCard_ = std::move(cards_[0]);
}
std::cout << "Turned charactercard: " << turnedCharacterCard_->getName() << std::endl;
cards_.erase(cards_.begin());
}
else
{
std::cerr << "CharacterCardStack::prepare cards size is " << cards_.size() << std::endl;
throw std::exception("Error...");
}
for (const auto& c : cards_)
{
std::cout << "Other card: " << c->getName() << std::endl;
}
std::cout << "CharacterCardStack prepared" << std::endl;
}
You have flow in your logic, you move out either first or the second element based on condition:
if(cards_[0]->getName() == "Koning")
{
turnedCharacterCard_ = std::move(cards_[1]);
}
else
{
turnedCharacterCard_ = std::move(cards_[0]);
}
but then unconditionaly erase the first element:
cards_.erase(cards_.begin());
so you may end up with moved out pointers.
Simple fix could be:
if(cards_[0]->getName() == "Koning")
{
std::swap( cards_[0], cards_[1] );
}
turnedCharacterCard_ = std::move(cards_[0]);
std::cout << "Turned charactercard: " << turnedCharacterCard_->getName() << std::endl;
cards_.erase(cards_.begin());
this way it is easier to keep it correct and I would prefer use cards.front() instead of cards_[0], which is I think more readable in this case.
I've added some slight multi threading to a simple c++ program and have encountered a few issues along the way.
The latest of these issues is that historical::assignthreads for some reason is receiving an empty vector from the function historical::writeData.
Looking at the code below you will see that writeData iterates through a vector and puts the data in a placeholder before sending it forward to assignthreads (after 5 iterations) - meaning that the vector being sent from writeData to assignthreads shouldn't be empty.
However in assignthreads you will see that there are two cout:s, one before and one after the loop. Both writes to cout without the loop even starting.
Does anyone have any idea of how this could be the case?
void historical::writeData(std::vector<std::vector<std::wstring>> in, const string& symbol) {
std::cout << "Sending data to database connector" << std::endl;
std::vector<std::vector<std::wstring>> temp;
std::vector<std::vector<std::wstring>>::iterator it;
int count = 0;
for (it = in.begin(); it != in.end(); it++) {
if (count = 5) {
cout << "I'm in count 5" << endl;
assignthreads(temp, symbol);
temp.clear();
count = 0;
}
else {
cout << "I'm in count 0" << endl;
temp.push_back(*it);
count++;
}
}
if (!temp.empty()) {
cout << "I'm in empty" << endl;
assignthreads(temp, symbol);
}
else cout << "I'm empty!!" << endl;
}
void historical::assignthreads(std::vector<std::vector<std::wstring>>& partVec, const string& symbol) {
int i = 0;
cout << "I'm in assign" << endl;
vector<thread> threads(size(partVec));
std::vector<std::vector<std::wstring>>::iterator it;
for (it = partVec.begin();
it != partVec.end();
it++) {
cout << "I'm in the loop" << endl;
std::shared_ptr<database_con> sh_ptr(new database_con);
threads.at(i) = std::thread(&database_con::start, sh_ptr, *it, symbol);
i++;
}
cout << "I've finished" << endl;
for (auto& th : threads) th.join();
}
void historical::writer(string* pInput) {
ofstream mf("test.csv");
if (mf.is_open()) {
mf << *pInput;
mf.close();
}
else cout << "Unable to open file" << endl;
}
Your fundamental problem here is that count = 5 is an assignment and is therefore always true. You intended to use count == 5.
It's worth noting that particularly as your vector becomes large copying it is very wasteful, and you're doing this 2 ways:
The vector is passed into writeData by value, change to copying by reference: void writeData(std::vector<std::vector<std::wstring>>& in, const string& symbol)
temp will eventually copy every element of in, use iterators instead so your code would have to change to:
#define SIZE 5
void assignthreads(std::vector<std::vector<std::wstring>>::iterator start, std::vector<std::vector<std::wstring>>::iterator finish, const string& symbol) {
cout << "I'm in assign" << endl;
vector<thread> threads(distance(start, finish));
for(auto i = 0; start != finish; ++i, ++start) {
cout << "I'm in the loop" << endl;
std::shared_ptr<database_con> sh_ptr(new database_con);
threads.at(i) = std::thread(&database_con::start, sh_ptr, *start, symbol);
}
cout << "I've finished" << endl;
for (auto& th : threads) th.join();
}
void writeData(std::vector<std::vector<std::wstring>>& in, const string& symbol) {
std::cout << "Sending data to database connector" << std::endl;
auto count = 0;
while(count < in.size() - SIZE) {
auto start = next(in.begin(), count);
count += SIZE;
auto finish = next(in.begin(), count);
assignthreads(start, finish, symbol);
}
assignthreads(next(in.begin(), count), in.end(), symbol);
cout << "I'm empty!!" << endl;
}
I was trying to find the culprit behind a segfault. My debugger told be that there was no data for variable that the error was at. Every 10 seconds, there is a little script in my C++ code that runs. It does "garbage collection" and deletes some "sessions" that are probably dead.
To perform this efficiently, I use a timestamp -- when was the data last accessed. If the data is more than 10 seconds old, it is dead. There is a keepalive command that triggers every 4 seconds on the client.
To perform this GC, I loop through an std::unordered_map and substract the current time since epoch from the time stored as the value in that pair. If the time is too large, I add it to an std::vector that holds the keys to be deleted (yeah, I know it can be optimized to skip this step).
The problem that I was facing is that it loops right the first time. However, thereafter, I get a segfault, which points to the iterator value to be greater than size of the map.
Just switching back over to a standard std::map fixed the entire problem!
I shall attach the function that does all of this. All of the code is available at http://github.com/yash101/DrawingPad
Now, the code [{sourcedir}/source/Session.cxx]:
void SessionHost::cron()
{
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(10));
if(DEBUG)
{
std::cout << "Cron has started!" << std::endl;
}
while(!locky_thingy.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT)))
{}
int timethrough = 0;
std::vector<std::string> del;
for(std::map<std::string, long>::iterator ite = timestamp.begin(); ite != timestamp.end(); ++ite)
{
timethrough++;
std::cout << "Time through: " << timethrough << std::endl;
std::string curkey = ite->first;
long curval = ite->second;
std::cout << "Key: " << curkey << std::endl;
if(DEBUG)
{
std::cout << "Checking " << curkey << " with old ts of " << curval << std::endl;
}
u_int64_t curtm = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now().time_since_epoch()).count();
if(DEBUG)
{
std::cout << "Current time: " << curtm << std::endl;
}
if(curtm - curval > SESSION_TIMEOUT)
{
if(DEBUG)
{
std::cout << "Deleted session handle: [" << curkey << "]" << std::endl;
}
del.push_back(curkey);
}
else
{
if(DEBUG)
{
std::cout << "Kept back session handle: [" << curkey << "]" << std::endl;
}
}
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
}
locky_thingy.unlock();
}
}
You have:
for(std::map<std::string, long>::iterator ite = timestamp.begin();
ite != timestamp.end(); ++ite)
{
// ...
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]); // <--
// ...
}
}
In an unordered_map, erasing can invalidate iterators. So you can't erase while you're traversing - try to come up with a different algorithm. (I'm assuming some version of your question involves timestamp being an unordered_map - although there's no reference to this type in your code).
I think the error is here
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
} // <---------------- this is the end of the iterator loop
It should be moved up before the for loop so it doesn't invalidate.
} // <---------------- this is the end of the iterator loop
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
The data.erase might also have a fault if it is a vector.
If you have a vector you need to erase for you should mark the records and use
data.erase(std::remove_if(data.begin(), data.(end), CheckMark));
remove_if moves all valid data to the start of data, erase then erases from after the last valid.