Search code examples
c++xmlvisual-studio-2013rapidxml

C++ rapidxml access violation after a certain amount of time (visual studio 2013)


I have been using the excellent rapidxml library to read and use information from XML files to hold cutscene information for a game I am programming in C++. I have run into an odd problem,

I start by loading the XML file into a rapidxml::xmldocument<>* from std::ifstream* XMLFile

std::stringstream buffer; //Create a string buffer to hold the loaded information
buffer << XMLFile->rdbuf(); //Pass the ifstream buffer to the string buffer
XMLFile->close(); //close the ifstream
std::string content(buffer.str()); //get the buffer as a string
buffer.clear();
cutScene = new rapidxml::xml_document<>;
cutScene->parse<0>(&content[0]);
root = cutScene->first_node();

my cutscene xml file is made up of "parts" and at the beginning I want to load all of those parts (which are all xml_nodes) into a vector

//Load parts
if (parts->size() == 0) {
    rapidxml::xml_node<>* partNode = root->first_node("part");
    parts->push_back(partNode);
    for (int i = 1; i < numParts; i++) {
        parts->push_back(partNode->next_sibling());
        printf("name of part added at %i: %s.\n", i, parts->at(i)->name());
    }
}

That last line prints "name of part added at 1: part" to the console. The problem is for some reason, whenever I try to access the vector and print the same name of that same specific part not as a part of this method, the name can be accessed but is just a random string of letters and numbers. It seems that for some reason rapidxml is deleting everything after my load method is complete. I am still new to posting on stackoverflow so if you need more information just ask, thanks!


Solution

  • Rapidxml is in-situ xml parser. It alters the original string buffer (contentin your case) to format null-terminated tokens such as element and attribute names. Secondly, the lifespan of tree nodes referenced byparts items is defined by xml_document (currscene) instance.

    Keep currscene and content instances together with the vector, this will keep the vector items alive as well. e.g:

     struct SceneData
     {
        std::vector<char> content;
        rapidxml::xml_document<> cutScene;
        std::vector<rapidxml::xml_node<>*> parts;
    
        bool Parse(const std::string& text);
     };
    
     bool SendData::Parse(const std::string& text)
     {
           content.reserve(text.length() + 1);
           content.assign(text.begin(), text.end());
           content.push_back('\0');
    
           parts.clear();
    
           try
           {
             cutScene.parse<0>(content.data());
           }
           catch(rapidxml::parse_error & err)
           {
               return false;
           }
    
    
           // Load parts.   
           rapidxml::xml_node<>* root = cutScene.first_node();      
           rapidxml::xml_node<>* partNode = root->first_node("part");
           parts->push_back(partNode);
           for (int i = 1; i < numParts; i++) {
                 parts->push_back(partNode->next_sibling());
                 //printf("name of part added at %i: %s.\n", i, parts->at(i)->name());
           }
    
           return true ;
      }
    

    EDITED

    The parser expects a sequence of characters terminated by '\0 as input. Since a buffer referenced by &string[0] is not guaranteed to be null-terminated, it is recommended to copy the string content into std::vector<char>.