C++ `vector iterators incompatible` error only in Visual Studio - c++

I have a class representing a string of space-delimited words via a vector of those words and an iterator over the vector.
class WordCrawler{
public:
WordCrawler(std::string, bool reversed=false);
WordCrawler& operator--();
std::string operator* () const;
bool atBeginning() const;
private:
std::vector<std::string> words;
std::vector<std::string>::iterator it;
};
I am trying to print out the words in reverse order, using this function:
void print_in_reverse(std::string in) {
WordCrawler wc = WordCrawler(in, true);
while (!wc.atBeginning()) {
--wc;
std::cout << *wc << " ";
}
}
I construct my WordCrawler object with this constructor:
WordCrawler::WordCrawler(std::string in, bool reversed) {
std::istringstream iss(in);
std::string token;
while (std::getline(iss, token, ' '))
{
words.push_back(token);
}
if (reversed) {
it = words.end();
} else {
it = words.begin();
}
}
The rest of the member functions are pretty simple:
/**
True if pointer is at the beginning of vector
*/
bool WordCrawler::atBeginning() const {
return it == words.begin();
}
/**
Function that returns the string stored at the pointer's address
*/
std::string WordCrawler::operator*() const {
return *it;
}
/**
Function that increments the pointer back by one
*/
WordCrawler& WordCrawler::operator--() {
if (!atBeginning())
--it;
return *this;
}
I'm finding that everything works fine on Xcode and cpp.sh, but on Visual Studio it throws a runtime error saying vector iterators incompatible at atBeginning() function. My assumption would be that this is because the code is reliant on some sort of undefined behavior, but as I am relatively new to C++ I'm not sure what it is.
I know that it is always an iterator of the words vector, and I know that words does not change after it has been initialized, so I'm not sure what the issue is.
Full code at: http://codepad.org/mkN2cGaM

Your object has a rule of three violation - on copy/move construction the iterator will still point to the vector in the old object.
The line WordCrawler wc = WordCrawler(in, true); specifies a copy/move operation, triggering this problem. Most compilers perform copy elision here but I heard that older versions of MSVC don't, in debug mode anyway.
To fix this properly, I would recommend using an index instead of an iterator in the class. If you really want to use the iterator you will need to implement your own copy-constructor and move-constructor.
Changing that line to WordCrawler wc(in, true); would probably fix this particular program but the same problem would be lurking still, and might show up when you make further modifications later.

Related

iterating through all the directories and subdirectories in c++

I wanted to use the std::filesystem::recursive_directory_iterator class to create a class method iterating through all subdirectories and processing found xml files.
The only way I have found on the internet to do this was using a for loop like this:
for (fs::directory_entry p : fs::recursive_directory_iterator("my_file"))
do_something(p);
The problem is that i need to store my iterator (or atleast where it's pointing) inbetween function calls as i can only process one file at a time. I tried implementing it like this:
class C {
private:
std::filesystem::recursive_directory_iterator it;
std::filesystem::directory_entry p;
public:
C(std::filesystem::path);
std::string find_file();
};
C::C(std::filesystem::path path)
{
it = fs::recursive_directory_iterator(path);
p = fs::directory_entry(it.begin());
}
std::string C::find_file()
{
do { //using do while so my function won't load the same file twice
++p;
} while (!is_xml(p.path()) && p != it.end());
}
But it seems that std::filesystem::recursive_directory_iterator doesn't have begin() and end() methods and can't be compared.
I have no idea how my code is different from the working for range loop except for storing the iterator and having an extra condition.
If you look a std::filesystem::recursive_directory_iterator Non-member functions you can see that there is:
// range-based for loop support
begin(std::filesystem::recursive_directory_iterator)
end(std::filesystem::recursive_directory_iterator)
And then std::filesystem::begin(recursive_directory_iterator), std::filesystem::end(recursive_directory_iterator) with more details:
end(recursive_directory_iterator) Returns a default-constructed recursive_directory_iterator, which serves as the end iterator. The argument is ignored.
So you will check if it is not equal to std::end(it), so see if there are any more elements. And you have to increment it and not p.
You also need to check if it != std::end(it) before you do !is_xml(*it.path())
std::string C::find_file()
{
do { //using do while so my function won't load the same file twice
++it;
} while (it != std::end(it) && !is_xml(*it.path()));
}
recursive_directory_iterator is already an iterator by itself (it says so right in its name), so you don't need to use begin() and end() at all. It implements operator==, operator!=, operator->, and operator++, which are all you need in this case.
Also, there is no reason for p to be a class member at all. It should be a local variable of find_file() instead (actually, in this case, it can be eliminated completely). And the loop should be a while loop instead of a do..while loop, in case the iterator is already at its "end" when find_file() is entered.
Try this instead:
class C {
private:
std::filesystem::recursive_directory_iterator it;
public:
C(std::filesystem::path);
std::string find_file();
};
C::C(std::filesystem::path path)
: it(path)
{
}
std::string C::find_file()
{
static std::filesystem::directory_iterator end;
while (it != end) {
auto p = it->path();
if (is_xml(p))
return p.string();
++it;
}
return "";
}

Custom iterator out of bounds

I have an iterator class. Let's call it PIterator here. A MessageBuffer is iterated and is being outputted correctly, unless the nSizeOfMessage plus where the iterator currently points to is equal to the size of the whole message (position correct, index one too large).
If I check for the last element and decrement by one, it should work. Though it seems to be a "wrong way" to me. Yeah, I am not quite sure on this, so my problem is shown in this code snippet, maybe someone knows a good solution, tried to figure it out for quite a while.
Yes, I do know how to use a debugger, I know where the problem lies and it is explained just fine. I do not know how to fix this, unless used the way I mentioned.
This compiles fine under Visual Studio 2015.
Please also see the comments in the main function.
#include <iostream>
#include <vector>
class MessageBuffer
{
public:
MessageBuffer(const std::string &s)
{
_msgBuffer.assign(s.begin(), s.end());
}
char &operator[](std::size_t nIndex)
{
return _msgBuffer[nIndex];
}
//more functions...
private:
std::vector<char> _msgBuffer;
};
class PIterator
{
public:
PIterator(MessageBuffer &b)
: m_Ref(b)
, m_Where(0)
{ }
PIterator &operator=(PIterator &other)
{
if (this == &other)
return *this;
this->m_Ref = other.m_Ref;
this->m_Where = other.m_Where;
return *this;
}
//more functions...
PIterator operator+(unsigned int nValue) const
{
PIterator copy(*this);
copy.m_Where += nValue;
return copy;
}
PIterator &operator+=(unsigned int nValue)
{
m_Where += nValue;
return *this;
}
char &operator*()
{
return m_Ref[m_Where];
}
private:
MessageBuffer &m_Ref;
std::size_t m_Where;
};
int wmain(int argv, wchar_t **args)
{
std::string msg = "123MyMessage"; //Length 12
// ^ Index 3, Position 4
MessageBuffer mb(msg);
PIterator itr(mb);
//Calculations - here the results hardcoded
std::size_t nSizeOfMessage = 9; //The size of the message without the numbers
//itr.m_Where is 3 - That's where the non-numeric part of the message starts
itr += 3;
std::string needThis;
PIterator cpy = itr + nSizeOfMessage; //itr points to the first element of the message
//cpy is now out of bounds - position is correct, but index is 1 too large
needThis.assign(&*itr, &*cpy); //boom
return 0;
}
Instead of
needThis.assign(&*itr, &*cpy);
you need to use
needThis.assign(itr, cpy);
This will work if your PIterator satisfies iterator requirements.
The way you call assign, you pass pointers instead of iterators, which is valid by itself. But, to get the pointers, you dereference the iterators first. Dereferencing past-the-end iterator is undefined behavior, which is caught in Debug configuration of the compiler.
The solution I came up with was quite simple.
Instead of having a temporary iterator, I'll be using the char pointer and increment it's address by the size of the message, thus receiving always the correct last element. Should've seen that earlier.
needThis.assign(&*itr, (&*itr) + nSizeOfMessage);

How to iterate through all elements of set C++

[UPDATE: My problem is solved! Lots of thanks to Mike Seymour and Niall and all you guys!]
My code has errors in the for loop and I do not know how to fix it :(
MyClass::ITECH7603Class(set<Student>* students) {
/* Initialize dynamically the group field */
group = new map<string, Student>();
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
addStudent(it);
}
}
void MyClass::addStudent(Student* studentPtr) {
string fullName = studentPtr->getName() + " " + studentPtr->getSurname();
group->insert(pair<string, Student>(fullName, *studentPtr));
}
So the main idea is to loop through all students in the set, and add each student into a map group. Any help? Thank you very much!
for (set<Student>::iterator it = students->begin; it != students->end; it++) {
addStudent(it);
}
should be:
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
//^^ //^^
addStudent(it);
}
addStudent takes a pointer, while it is an iterator, so can't be passed directly.
You should change addStudent to take either a value or a pointer/reference to const:
// option 1
void addStudent(Student);
addStudent(*it);
// option 2
void addStudent(Student const &);
addStudent(*it);
// option 3
void addStudent(Student const *);
addStudent(&*it);
If, as you say in a comment, you must leave it taking a mutable pointer, then you'll need some grotesquery to deal with the fact that elements of the set are immutable:
// nasty option
addStudent(const_cast<Student*>(&*it));
// slightly less nasty option
Student copy = *it;
addStudent(&copy);
Beware that the first option will give undefined behaviour if the function uses the dodgy pointer to make any modification to the Student object stored in the set. The second makes a temporary copy, which can be modified without breaking the set. This is fine as long as addStudent only stores a copy of the object passed to it, not the pointer itself, which will become invalid when copy is destroyed.
In c++11 you can use range for sytax:
for (const auto &student : *students)
{
addStudent(it);
}
Then change addStudent function signature to accept reference:
void MyClass::addStudent(const Student &student) {
While you've gotten answers that "fix" your code to the extent of compiling and producing results that you apparently find acceptable, I don't find them very satisfying in terms of code style. I would do this job rather differently. In particular, my code to do this wouldn't have a single (explicit) loop. If I needed to do approximately what you're asking for, I'd probably use code something like this:
std::pair<std::string, Student> make_mappable(Student &stud) {
return std::make_pair(stud.getName() + " " + stud.getSurName(), stud);
}
std::map<std::string, Student> gen_map(std::set<Student> const &input) {
std::map<std::string, Student> ret;
std::transform(input.begin(), input.end(),
std::inserter(ret, ret.end()),
make_mappable);
return ret;
}
There definitely would not be any new in sight, nor would there be any passing a pointer to a Student.
OTOH, since the data you're using as the key for your map is data that's already in the items in the set, it may more convenient all around to continue to use a set, and just specify a comparison function based on the student's name:
struct by_given_name {
bool operator()(Student const &a, Student const &b) const {
if (a.getName() < b.getName())
return true;
if (b.getName() < a.getName())
return false;
return a.getSurName() < b.getSurName();
}
};
std::set<Student, by_given_name> xform(std::set<Student> const &in) {
return std::set<Student, by_given_name>{in.begin(), in.end()};
}
For what its worth, a Live Demo of the latter.
Whether the latter is practical will typically depend on one other factor though: your ability to create a Student from only a name/surname. If you can't do that, searching by name will be inconvenient (at best), so you'd want to use a map.
I realize this probably isn't much (if any) help in completely what's apparently home-work for a class--but even if your class prevents you from actually turning in decent code, it seems worthwhile to me to at least try to learn to write decent code in addition to what it requires. If you do pass the class and get a job writing code, you'd probably rather your coworkers didn't want to hurt you.

Overloading [] operator in C++

Im trying to overload the [] operator in c++ so that I can assign / get values from my data structure like a dictionary is used in c#:
Array["myString"] = etc.
Is this possible in c++?
I attempted to overload the operator but it doesnt seem to work,
Record& MyDictionary::operator[] (string& _Key)
{
for (int i = 0; i < used; ++i)
{
if (Records[i].Key == _Key)
{
return Records[i];
}
}
}
Thanks.
Your code is on the right track - you've got the right function signature - but your logic is a bit flawed. In particular, suppose that you go through this loop without finding the key you're looking for:
for (int i = 0; i < used; ++i)
{
if (Records[i].Key == _Key)
{
return Records[i];
}
}
If this happens, your function doesn't return a value, which leads to undefined behavior. Since it's returning a reference, this is probably going to cause a nasty crash the second that you try using the reference.
To fix this, you'll need to add some behavior to ensure that you don't fall off of the end of the function. One option would be to add the key to the table, then to return a reference to that new table entry. This is the behavior of the STL std::map class's operator[] function. Another would be to throw an exception saying that the key wasn't there, which does have the drawback of being a bit counterintuitive.
On a totally unrelated note, I should point out that technically speaking, you should not name the parameter to this function _Key. The C++ standard says that any identifier name that starts with two underscores (i.e. __myFunction), or a single underscore followed by a capital letter (as in your _Key example) is reserved by the implementation for whatever purposes they might deem necessary. They could #define the identifier to something nonsensical, or have it map to some compiler intrinsic. This could potentially cause your program to stop compiling if you move from one platform to another. To fix this, either make the K lower-case (_key), or remove the underscore entirely (Key).
Hope this helps!
On a related note, one of the problems with operator[](const Key& key) is that, as templatetypedef states, in order to return a reference it needs to be non-const.
To have a const accessor, you need a method that can return a fail case value. In STL this is done through using find() and the use of iterators and having end() indicate a fail.
An alternative is to return a pointer, with a null indicating a fail. This is probably justified where the default constructed Record is meaningless. This can be also be done with the array operator:
Record* MyDictionary::operator[] (const string& keyToFind) const
{
for (int i = 0; i < used; ++i)
{
if (Records[i].Key == keyToFind)
{
return &Records[i];
}
}
return 0;
}
There is certainly a view that operator[] should return a reference. In that case, you'd most likely implement find() as well and implement operator[] in terms of it.
To implement find() you need to define an iterator type. The convenient type will depend in implementation. For example, if Records[] is a plain old array:
typedef Record* iterator;
typedef const Record* const_iterator;
const_iterator MyDictionary::end()const
{
return Records + used;
}
const_iterator MyDictionary::begin() const
{
return Records;
}
const_iterator MyDictionary::find(const string& keyToFind) const
{
for (iterator it = begin(); it != end(); ++it)
{
if (it->Key == keyToFind)
{
return it;
}
}
return end();
}

How to iterate over a std::map full of strings in C++

I have the following issue related to iterating over an associative array of strings defined using std::map.
-- snip --
class something
{
//...
private:
std::map<std::string, std::string> table;
//...
}
In the constructor I populate table with pairs of string keys associated to string data. Somewhere else I have a method toString that returns a string object that contains all the keys and associated data contained in the table object(as key=data format).
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string* strToReturn = new std::string("");
for (iter = table.begin(); iter != table.end(); iter++) {
strToReturn->append(iter->first());
strToReturn->append('=');
strToRetunr->append(iter->second());
//....
}
//...
}
When I'm trying to compile I get the following error:
error: "error: no match for call to ‘(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >) ()’".
Could somebody explain to me what is missing, what I'm doing wrong?
I only found some discussion about a similar issue in the case of hash_map where the user has to define a hashing function to be able to use hash_map with std::string objects. Could be something similar also in my case?
Your main problem is that you are calling a method called first() in the iterator. What you are meant to do is use the property called first:
...append(iter->first) rather than ...append(iter->first())
As a matter of style, you shouldn't be using new to create that string.
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string strToReturn; //This is no longer on the heap
for (iter = table.begin(); iter != table.end(); ++iter) {
strToReturn.append(iter->first); //Not a method call
strToReturn.append("=");
strToReturn.append(iter->second);
//....
// Make sure you don't modify table here or the iterators will not work as you expect
}
//...
return strToReturn;
}
edit: facildelembrar pointed out (in the comments) that in modern C++ you can now rewrite the loop
for (auto& item: table) {
...
}
Don't write a toString() method. This is not Java. Implement the stream operator for your class.
Prefer using the standard algorithms over writing your own loop. In this situation, std::for_each() provides a nice interface to what you want to do.
If you must use a loop, but don't intend to change the data, prefer const_iterator over iterator. That way, if you accidently try and change the values, the compiler will warn you.
Then:
std::ostream& operator<<(std::ostream& str,something const& data)
{
data.print(str)
return str;
}
void something::print(std::ostream& str) const
{
std::for_each(table.begin(),table.end(),PrintData(str));
}
Then when you want to print it, just stream the object:
int main()
{
something bob;
std::cout << bob;
}
If you actually need a string representation of the object, you can then use lexical_cast.
int main()
{
something bob;
std::string rope = boost::lexical_cast<std::string>(bob);
}
The details that need to be filled in.
class somthing
{
typedef std::map<std::string,std::string> DataMap;
struct PrintData
{
PrintData(std::ostream& str): m_str(str) {}
void operator()(DataMap::value_type const& data) const
{
m_str << data.first << "=" << data.second << "\n";
}
private: std::ostream& m_str;
};
DataMap table;
public:
void something::print(std::ostream& str);
};
Change your append calls to say
...append(iter->first)
and
... append(iter->second)
Additionally, the line
std::string* strToReturn = new std::string("");
allocates a string on the heap. If you intend to actually return a pointer to this dynamically allocated string, the return should be changed to std::string*.
Alternatively, if you don't want to worry about managing that object on the heap, change the local declaration to
std::string strToReturn("");
and change the 'append' calls to use reference syntax...
strToReturn.append(...)
instead of
strToReturn->append(...)
Be aware that this will construct the string on the stack, then copy it into the return variable. This has performance implications.
Note that the result of dereferencing an std::map::iterator is an std::pair. The values of first and second are not functions, they are variables.
Change:
iter->first()
to
iter->first
Ditto with iter->second.
iter->first and iter->second are variables, you are attempting to call them as methods.
Use:
std::map<std::string, std::string>::const_iterator
instead:
std::map<std::string, std::string>::iterator
Another worthy optimization is the c_str ( ) member of the STL string classes, which returns an immutable null terminated string that can be passed around as a LPCTSTR, e. g., to a custom function that expects a LPCTSTR. Although I haven't traced through the destructor to confirm it, I suspect that the string class looks after the memory in which it creates the copy.
In c++11 you can use:
for ( auto iter : table ) {
key=iter->first;
value=iter->second;
}