1

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!

deery50
  • 65
  • 1
  • 5

1 Answers1

0

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>.

alexm
  • 6,854
  • 20
  • 24
  • Is variable _content_ declared once in the header? In the code above you declared it as local. – alexm Aug 15 '15 at 23:12
  • Sorry about that previous comment. I added exactly what you have to my header and changed the lines to have access to an instance of "SceneData" but I am still having the same problem. EDIT: The only difference I made is i made the parts vector and the cutscene document a pointer so I could instantiate them. – deery50 Aug 15 '15 at 23:12
  • content must be \0 terminated before you pass it to parser. _std::string_ type does not guarantee that. You could try std::vector as content – alexm Aug 15 '15 at 23:14
  • how do I change this line: "data->cutScene->parse<0>(data->content[0]);" to work with the vector? – deery50 Aug 15 '15 at 23:24
  • see Parse() method above. – alexm Aug 15 '15 at 23:36
  • cutScene->parse<0>(content.data()) doesn't return a bool – deery50 Aug 15 '15 at 23:41
  • Thanks. Also what exactly do you mean by "read part elements here" Can I not read and use the part elements after I run that method? – deery50 Aug 15 '15 at 23:46
  • Borrowing your terminology it is "Load parts", – alexm Aug 15 '15 at 23:49
  • It's working now! Thanks so much for your help. Feel free to answer this if you want but this is just for pure curiosity, as you can probably tell I am pretty new to c++, how exactly did using a struct solve our problem? – deery50 Aug 16 '15 at 00:00
  • The root cause of AV errors was improperly formatted input (not terminated by /0). Struct addresses other issues (e.g. validity of pointer references) explained above. – alexm Aug 16 '15 at 00:05