1

This might be more of a c++ problem rather than a rapidxml problem, and I'm still learning c++ so sorry if this is a very stupid question.

I am trying to load an XML file within a try block:

rapidxml::xml_document<> doc;
rapidxml::xml_node<>* root_node;
try 
{
    rapidxml::file<> file((data_folder + "\\application.xml").c_str());
    doc.parse<0>(file.data());
    root_node = doc.first_node("Application");

    std::cout << root_node->first_node("AppMeta");
}
catch (const std::runtime_error& e)
{
    // ...
}

This code correctly prints the pointer of the 'AppMeta' node. The problem is when I move the cout line outside of the block:

rapidxml::xml_document<> doc;
rapidxml::xml_node<>* root_node;
try 
{
    rapidxml::file<> file((data_folder + "\\application.xml").c_str());
    doc.parse<0>(file.data());
    root_node = doc.first_node("Application");
}
catch (const std::runtime_error& e)
{
    // ...
}

std::cout << root_node->first_node("AppMeta");

I this case, the value that appears on the console is a nullptr (00000000). Why does this happen, and how can I access the XML dom after the try catch block?

devobvious
  • 11
  • 1

2 Answers2

1

Ending the try block also calls the destructor of rapidxml::file<> file, which is why you can no longer access the contents. Since moving the file up a scope, outside of the try block would fail to catch construction errors, copy the data out into a variable with larger scope. rapidxml::file<>::data() just returns a pointer into the data, but if that is no longer there, you can't use this function.

The documentation for the file class says 'Data will be automatically destroyed by the destructor.'

rapidxml::xml_document<> doc;
rapidxml::xml_node<>* root_node;
std::string copied_data;
try 
{
    rapidxml::file<> file((data_folder + "\\application.xml").c_str());
    copied_data = file.data();
    doc.parse<0>(copied_data.c_str());
    root_node = doc.first_node("Application");
}
catch (const std::runtime_error& e)
{
    // ...
}

std::cout << root_node->first_node("AppMeta");
TamaMcGlinn
  • 2,840
  • 23
  • 34
  • The problem is that the purpose of the try catch is to handle situations where thee file does not exist, isn't readable, etc... and these exceptions are thrown by the file constructor. – devobvious Oct 29 '19 at 14:26
  • Try copying the data out into a variable with larger scope. I.e. `std::string a;` `a = file.data()` or something. – TamaMcGlinn Oct 29 '19 at 19:55
0

Since the try block's sole purpose is to catch exceptions thrown by that constructor, moving file() up a scope wasn't an option. I ended up solving the problem by instantiating the file with the new keyword.

rapidxml::xml_document<> doc;
rapidxml::xml_node<>* root_node;
try 
{
    rapidxml::file<> *file = new rapidxml::file<>((data_folder + "\\application.xml").c_str());
    doc.parse<0>(file->data());
}
catch (const std::runtime_error& e)
{
    Utils::exitError(Utils::format("Unable to load the application data.\nDetails: %s", e.what()));
    return;
}

root_node = doc.first_node("Application");
std::cout << root_node->first_node("AppMeta")->name();
devobvious
  • 11
  • 1
  • This is a bad idea, as you're now never closing the file or freeing the memory. And if the file didn't exist, the reason your code no longer crashes is because you've added that `return;` line - nothing to do with "new". – Roddy Oct 29 '19 at 18:19
  • There are two rules of thumb in C++ that this violates; the first is 'every new should be paired with a corresponding delete' (read up on RAII - it comes down to writing new in the constructor of your class, and delete in the destructor). The second is 'never write new' - which doesn't absolutely work, but is a good rule of thumb when you are starting out. – TamaMcGlinn Oct 29 '19 at 19:57