Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

erasing nlohmann::json object during iteration causes segmentation fault

I have a simple database consisting of objects with strings containing unix time as keys and strings containing instructions as values

I want to iterate though the database and erase any object who’s key is smaller that current time ( so erase objects with dates before current date)

for (auto it = m_jsonData.begin(); it != m_jsonData.end(); it++) {
    if (std::stoi(it.key()) <= (std::time(NULL))) {
    std::cout << "command is behind schedule, removing\n";
    m_jsonData.erase(it);
    } else {
    /*
    */
    }
}

this code works fine as long as m_jsonData.erase(it); isn’t invoked. when it does, in the next iteration std::stoi(it.key()) causes a segfault, after a bit of playing with it I came to a conclusion that is somehow loses track of what it’s actually iterating. Is my conclusion true? If not then what is? And how do I fix it?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

It’s extremely normal for mutating container operations to invalidate iterators. It’s one of the first things you should check for.

Documentation for nlohnmann::json::erase():

Notes

  1. Invalidates iterators and references at or after the point of the erase, including the end() iterator.
  1. References and iterators to the erased elements are invalidated. Other references and iterators are not affected.

That means after this line:

m_jsonData.erase(it);

the iterator it can’t be used for anything including incrementing it to the next element. It is invalid.

Fortunately, the documentation also points out that the successor to the removed element is returned, so you can just write

for (auto it = m_jsonData.begin(); it != m_jsonData.end(); ) {
    if (std::stoi(it.key()) <= (std::time(NULL))) {
        it = m_jsonData.erase(it);
    } else {
        ++it;
    }
}

Note that when I say this is extremely normal, it’s because the standard containers often have similar behaviour. See the documentation for examples, but this is something everyone should be aware of:

This is exactly the reason std::remove_if was provided to suppport the erase(remove_if(...), end) idiom, instead of writing fragile mutating loops.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading