i have a strange problem with QList and the boost:shared_ptr. I'm afraid i am not able to strip the problem apart, that's why i will post the wholoe function later on.
What i want to do: I have a list (_fileList) which stores boost::shared ptrs to QFile Objects. The files are XML files. Than i want to parse this file and resolve all includes which means adding the files specified by the include tag also to the _fileList and scanning them for further include tags. The code works fine when resolving 3 includes (in my small test there is only one include per file). The third time the line
boost::shared_ptr file(*iter); leads to a segmentation fault.
I would be glad if anyone of you can help me or give me hints how i can find this bug.
void XmlParser::expandIncludes()
{
//search all files already in the file list for include tags, if any new are found append this file to the file list
QList<boost::shared_ptr<QFile> >::iterator iter = this->_fileList.begin();
while(iter!= this->_fileList.end())
{
boost::shared_ptr<QFile> file(*iter);
QDomDocument doc("activeFile");
if (!file->open(QIODevice::ReadOnly)){
return;
}
if (!doc.setContent(&(*file))) {
file->close();
return;
}
file->close();
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChildElement("include");
while(!n.isNull()) {
QDomElement e = n.toElement(); // try to convert the node to an element.
if(!e.isNull()) {
QString nextFile = e.text();
QString nextFileAbsolutePath = this->_workingDir.absolutePath() +"/"+nextFile;
boost::shared_ptr<QFile> newFileObject(new QFile(nextFileAbsolutePath));
this->_fileList.append(newFileObject);
}
n = n.nextSiblingElement("include");
}
doc.clear();
iter++;
}
}
Iterators pointing to elements in a QList becomes invalid after you insert new elements into the list. You could use a QLinkList instead.
From the Qt Container docs:
Iterators pointing to an item in a QLinkedList remain valid as long as the item exists, whereas iterators to a QList can become invalid after any insertion or removal.
Related
So I recently got a hold of RapidXML to use as a way to parse XML in my program, I have mainly been using it as a way to mess around but I have been getting some very weird issues that I'm really struggling to track down. Try and stick with me through this, because I was pretty thorough with trying to fix this issue, but I must be missing something.
First off here's the XML:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<image key="tilemap_roguelikesheet" path="res/media/tilemaps/roguelikesheet.png" />
<image key="tilemap_tiles" path="res/media/tilemaps/tiles.png" />
</resources>
The function the segfault occurs:
void TextureManager::LoadResource(const char* pathToFile)
{
rapidxml::xml_document<>* resource = Resources::LoadResource(pathToFile);
std::string imgName;
std::string imgPath;
if (resource != NULL)
{
rapidxml::xml_node<>* resourcesNode = resource->first_node("resources");
if (resourcesNode != NULL)
{
for (rapidxml::xml_node<>* child = resourcesNode->first_node("image"); child; child = child->next_sibling())
{
//Crash here on the second loop through.
imgName = child->first_attribute("key")->value();
imgPath = child->first_attribute("path")->value();
Astraeus::Log(moduleName, "Image Name: " + imgName);
Astraeus::Log(moduleName, "Image Path: " + imgPath);
TextureManager::AddTexture(imgName, imgPath);
}
}
else
{
Astraeus::Error(moduleName, "Resources node failed to load!");
}
resource->clear();
}
else
{
std::string fileName(pathToFile);
Astraeus::Error(moduleName, fileName + " could not be loaded.");
}
}
So segfault happens on the second loop of the for loop to go through all the nodes, and triggers when it tries to do the imgName assignment. Here's where things get a bit odd. When doing a debug of the program, the initial child nodes breakdown shows it has memory pointers to the next nodes and it's elements/attributes etc. When investigating those nodes, you can see that the values exist and rapidxml has seemingly successfully parsed the file.
However, when the second loop occurs, child is shown to still have the exact same memory pointers, but this time the breakdown in values show they are essentially NULL values, so the program fails and we get the code 139. If you try and look at the previous node, that we have just come from the values are also NULL.
Now say, I comment out the line that calls on the AddTexture function, the node is able to print out all the nodes values no problems at all. (The Log method is essentially just printing to console until I do some more funky stuff with it.) so the problem must lie in the function? Here it is:
void TextureManager::AddTexture(const std::string name, const std::string path)
{
Astraeus::Log(moduleName, "Loading texture: " + path);
if (texturesLookup.find(name) != texturesLookup.end())
{
Astraeus::Error(moduleName, "Texture Key: " + name + " already exists in map!");
}
else
{
texturesLookup.insert(std::make_pair(name, path));
//Texture* texture = new Texture();
/*if (texture->LoadFromFile(path))
{
//textures.insert(std::make_pair(name, texture));
}
else
{
Astraeus::Error(moduleName, "Failed to add texture " + name + " to TextureManager!");
}*/
}
}
Ignoring the fact that strings are passed through and so should not affect the nodes in any way, this function is still a bit iffy. If I comment out everything it can work, but sometimes just crashes out again. Some of the code got commented out because instead of directly adding the key name, plus a memory pointer to a texture, I switched to storing the key and path strings, then I could just load the texture in memory later on as a workaround. This solution worked for a little bit, but sure enough began to segfault all over again.
I can't really reliably replicate or narrow down what causes the issue everytime, so would appreciate any help. Is RapidXML doc somehow going out of scope or something and being deleted?
For the record the class is practically just static along with the map that stores the texture pointers.
Thanks!
So for anybody coming back again in the future here's what was happening.
Yes, it was a scope issue but not for the xml_document as I kept initially thinking. The xml_file variable that was in the resources load function was going out of scope, which meant due to the way RapidXML stores things in memory, as soon as that goes out of scope then it frees up the memory, which led to the next time dynamic allocation happened by a specific function it would screw up the xml document and fill it with garbage data.
So I guess the best idea is to make sure xml_file and xml_document do not go out of scope. I have added some of the suggestions from previous answers, but I will point out those items WERE in the code, before being removed to help with the debug process.
Thanks everybody for the help/advice.
I'm not sure, but I think that Martin Honnen made the point.
If next_sibling() return the pointer to the text node between the two "image" elements, when you write
imgName = child->first_attribute("key")->value();
you obtain that child->first_attribute("key") is a null pointer, so the ->value() is dereferencing a null pointer. Crash!
I suppose you should get the next_sibling("image") element; something like
for (rapidxml::xml_node<>* child = resourcesNode->first_node("image");
child;
child = child->next_sibling("image"))
And to be sure not to use a null pointer, I strongly suggest you to check the attribute pointers (are you really sure that "image" elements ever carry the "key" and the "path" elements?); something like this
if ( child->first_attribute("key") )
imgName = child->first_attribute("key")->value();
else
; // do something
if ( child->first_attribute("path") )
imgPath = child->first_attribute("path")->value();
else
; // do something
p.s.: sorry for my bad English.
This line is setting my teeth on edge...
rapidxml::xml_document<>* resource = Resources::LoadResource(pathToFile);
LoadResource returns a pointer, but you never free it anywhere...?
Are you 100% sure that function isn't returning a pointer to an object that's now gone out of scope. Like this classic bug...
int * buggy()
{
int i= 42;
return &i; // UB
}
As #max66 says. You should use next_sibling("image"). If that's failing, you need to find out why.
Problem:
I am writing a simple file manager application. In this program I have a "Directory" class:
class Directory
{
public:
Directory(string address, string directoryname)
{
this->path = address;
this->name = directoryname;
}
string GetFullPath(){ return path == "/" ? path + name : path + "/" + name; }
string path;
string name;
string user;
};
and a linked-list of directory objects:
list<Directory*> DirectoryList;
I want to implement the "rm -r directorypath" shell command in linux, so I need to browse through the list and remove the "directorypath" directory and all of its sub-directories. The problem is that I don't know how to browse through the link list and remove all directories whose parent directory is "directorypath". I have tried these two methods:
method 1:
This method encounters a runtime error, because it cannot access the list anymore after the first deletion.
for (auto address : DirectoryList)
if (address->GetFullPath() == directorypath)
{
for (auto subdirectory : DirectoryList)
if (subdirectory ->path == address->GetFullPath())
DirectoryList.remove(subdirectory );
}
method 2:
for (auto address : DirectoryList)
if (address->GetFullPath() == directorypath)
{
for (auto it = DirectoryList.begin(); it != DirectoryList.end();)
it = DirectoryList.erase(it);
return true;
}
this method can access all the elements perfectly even after deletion but I don't know how to check this if condition using the iterator it:
if (subdirectory ->path == address->GetFullPath())
Your method 1 is failing because std::list.remove(val) removes all elements in your list that compare equal to val. You call it once and you're done. The for() loop shouldn't be there, it's not the way it's intended to be used. Good example is here.
Note that this method will modify your container and the size of it. You need to be careful here and make sure that the your iterators are still valid after calling erase. My gut feeling is that indeed the iterators get invalided and that's why you're getting errors.
Your method 2 looks almost fine. First of all, fallow niceguy's advice to check the condition:
if ((*it).path == address->GetFullPath())
Now, bear in mind that erasing it will update the iterator to point to the location after the iterator you removed. This counts as one update to your iterator, it. It will be further updated in the for loop, but that's not something that you want (i.e. two updates per iteration mean that you're skipping some elements). You could try something like this instead:
auto it = DirectoryList.begin()
while (it != DirectoryList.end())
{
if ((*it).path == address->GetFullPath())
DirectoryList.erase(it);
}
This is a snippet of an open source code. Full source code is available https://github.com/gec/dnp3/blob/master/src/opendnp3/DNP3/ResponseContext.h
ObjectWriteIterator owi = arAPDU.WriteContiguous(apObject, start,stop);
for(size_t i = start; i <= stop; ++i) {
if(owi.IsEnd()) { // out of space in the fragment
this->mStaticWriteMap[arKey] =
boost::bind(&ResponseContext::WriteStaticObjects<T>, this, apObject,
arStart, arStop, arKey, _1); return false;
}
apObject->Write(*owi, arStart->mValue);
++arStart; //increment the iterators
++owi;
}
ObjectWriteIterator::ObjectWriteIterator() :
mpPos(NULL),
mIndex(1),
mStart(0),
mStop(0),
mObjectSize(0)
{}
My question is: I don't understand is where *owi is referring in this context.
owi is an iterator, which is a 'standard' C++ interface for iterating over some collection.
The interface has them use pointer-symantics, so the * operator 'dereferences' the iterator and returns a reference to the value it currently 'points' to, and incrementing it via ++ moves it to the next item in the collection being iterated over.
In this case, it looks like a collection of ObjectWrite objects inside the collection specified by apObject between start and stop (start and stop are also typically defined by iterators set to some location in the collection).
sorry, I was earlier not sure about one can build a self contained 'Mock up' iterator class which use hidden is the header file
inline boost::uint8_t* ObjectWriteIterator::operator*() const
{
if(this->IsEnd()) throw InvalidStateException(LOCATION, "End of
iteration");
return mpPos;
}
in the header file. Sorry for wild goose run. Thanks for the prompt reply and I learned something new about the the core implementation of the iterator as well.
I was not allowed to create the new tag 'tinyxml2', that's why I am using the tag 'tinyxml', however I am using 'tinyxml2' !
I am trying to insert a subtree element to an existing XML file. My problem is, that after running the program and checking the XML file the subtree simply does not exist within the document. In the original code I am also checking for errors while loading and saving the file so there is no problem with these functions, they are working correctly. I tried a few different approaches and also adding a single element by using the UserList.NewElement(*name*)-function does also work fine.
Now I want to insert a whole subtree from a text variable...
My latest approach looks like this (simplified without checking LoadFile and SaveFile):
tinyxml2::XMLDocument UserList;
UserList.LoadFile(*Path*);
const char* XMLText = "<user name=\"test-user\" gender=\"male\"><ability description=\"I_can_do_magic\" /></user>";
tinyxml2::XMLDocument TestParse;
TestParse.Parse(XMLText);
tinyxml2::XMLElement* myNewUser = TestParse.RootElement();
UserList.FirstChildElement( "magicians" )->InsertEndChild(myNewUser);
UserList.SaveFile(*Path*);
By the way...
When I tried to parse my XMLText by using the tinyxml2::XMLDocument UserList the saved XML file will be empty after running the program. This means neither the original XML Document content, nor the newly parsed subtree will be saved when trying to do this. This fact made me use the second tinyxml2::XMLDocument TestParse. Now the XML file is saved containing it's original content, however the parsed subtree is still missing... thank you very much for any solution / help / advice.
TinyXML-2 allocates memory for its nodes (XMLNode) in memory pools stored in the XMLDocument. This fixes the memory fragmentation problems present in TinyXML-1.
The side effect is that elements can not be moved from one XMLDocument to another. They can only be copied. Regrettably, TinyXML-2 doesn't currently support deep copies (tree copies), so can't do what you want. (Although a deep copy is a requested on the github site.)
I would expect the code you wrote to assert (in debug mode) or crash, by the way, since myNewUser is in a different memory pool from UserList.
I wrote a deep copy function using XMLVisitor of TinyXML-2. Hopefully this is useful for you:
#include <stack>
#include "tinyxml2.h"
using namespace tinyxml2;
class MyXMLVisitor: public XMLVisitor
{
public:
MyXMLVisitor(XMLDocument *doc)
: m_doc(doc)
{
}
virtual bool VisitEnter (const XMLElement &el, const XMLAttribute *attr)
{
XMLElement *new_el = m_doc->NewElement(el.Name());
m_elementStack.push(new_el);
return true;
}
virtual bool Visit(const XMLText &txt)
{
m_elementStack.top()->SetText(txt.Value());
return true;
}
virtual bool VisitExit (const XMLElement &el)
{
XMLElement *top_el = m_elementStack.top();
m_elementStack.pop();
if (m_elementStack.empty()) {
m_element = top_el;
return false;
}
else {
m_elementStack.top()->InsertEndChild(top_el);
return true;
}
}
std::stack<XMLElement*> m_elementStack;
XMLDocument *m_doc;
XMLElement *m_element;
};
XMLElement* DeepCopyElement(XMLDocument &doc, const XMLElement *el)
{
MyXMLVisitor my_visitor(&doc);
el->Accept(&my_visitor);
return my_visitor.m_element;
}
int main(int argc, char* argv[])
{
XMLDocument doc;
doc.LoadFile( "test.xml" );
XMLElement *modulesElement = doc.FirstChildElement("modules");
XMLElement *moduleElement = modulesElement->FirstChildElement("module");
modulesElement->InsertEndChild(DeepCopyElement(doc, moduleElement));
doc.SaveFile("test_out.xml");
return 0;
}
I'm using Qt 4.7.4. In my program, each QDomNode in a QDomDocument will have a unique identifier attribute. Is there a simple way to locate all nodes (in this case, only a single node) with a given attribute?
Nothing that I have found suggests that this is possible, but I thought that I might as well ask.
I suppose that I could place the identifier in a child node of the original node, search for the identifier node, and then take its parent, but I would prefer to keep it in an attribute.
You will need to recursively scan the document tree to find the elements yourself. For example, to find all elements with a given attribute name:
void findElementsWithAttribute(const QDomElement& elem, const QString& attr, QList<QDomElement> foundElements)
{
if( elem.attributes().contains(attr) )
foundElements.append(elem);
QDomElement child = elem.firstChildElement();
while( !child.isNull() ) {
findElementsWithAttribute(child, attr, foundElements);
child = child.nextSiblingElement();
}
}
QList<QDomElement> foundElements;
QDomDocument doc;
// Load document
findElementsWithAttribute(doc.documentElement(), "myattribute", foundElements);