Loading XML data into a C++ project as globals

Started by
6 comments, last by chaospilot 17 years ago
Ok, I normally post in the General Programming section, but for some reason, I can't figure out how to do this and I feel it's a pretty newbie question. I have a node based XML parser, and I'm reading simulation data files and simulation parameter files. I know when and when not to use globals in C++, but in this case, several simulation parameters are read just inside the main(), but arent used until halfway through the simulation, and at a significantly lower level in the simulation than they were set. I have an implementation where they are passed through in constructors all the way down to use level, but this means that several classes have access to simulation data they have no business knowing (which is against the tenets of object oriented programming). What I would like is a simple way of setting a few global constants, and labelling them const so they can't be changed by anyone accessing them. However, they must be set inside a function, since I can't explore xmlnodes and extract the data from outside executable code. I imagine a header file "globals.h", that has something like:

extern const double SIMULATION_START_TIME_JD
extern const double SIMULATION_END_TIME_SECONDS







and then the main function defining:

// LOTS OF INCLUDES
#include "globals.h"
main(int argc, char *argv[ ], char *envp[ ])
{
     string inputFileName = argv[1];
     XMLNode mainXMLNode = XMLNode::openFileHelper(inputFileName.c_str() , "SCENARIO");
     XMLNode simParametersXMLNode = mainXMLNode.getChildNode("simulation");
     static double SIMULATION_START_TIME_JD = (atof(simParametersXMLNode.getAttribute("startJD")));
     static double SIMULATION_END_TIME_SECONDS = (atof(simParametersXMLNode.getAttribute("simEnd")));
// LOTS OF STUFF HERE
}






then any time I wanted to use those variables, I just include the header globals.h and rock. What's the best way to do this? [Edited by - chaospilot on March 23, 2007 5:33:38 PM]
Advertisement
That won't quite work. The variables declared in globals.h are different than the ones defined in main(). To make them the same you must move them out of main() and remove the static qualifier. But then you will have problems because the declaration doesn't match the definition (because of the const qualifier). The solution is to move the definitions out of main(), but keep them static. Then write global accessor functions that return their values.

I can see how passing private implementation parameters down as parameters is not ideal, but the system you want to use also has problems. The main problem is that the lower-level classes depend on the application. There are ways to correct this, but I wouldn't rate any of them as good. One possible solution is to have the lower-level objects initialize themselves by loading their data from their own XML file. Another possible solution is "dependency injection". I don't think you will find a perfect solution, so you should look for the best solution.

One more thing: For god's sake, don't create a file named "globals.h".
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
You could possibly wrap them in a class. Something like:

class Globals{public:    static double GetSimulationStartTime() { return m_simulationStartTime; }    static double GetSimulationEndTime() { return m_simulationEndTime; }    static void Init(XMLNode &node);private:    static double m_simulationStartTime;    static double m_simulationEndTime;};


Where the Init function parses out the variables from the XML.

That'd give you more or less what you want, where the variables can't be externally modified by other bits of code (well, other than by passing in a new XML node to the Init function, but you could make sure that can only be called once if you wanted).
For storing the values so they can't be change there's a pattern:
http://en.wikipedia.org/wiki/Immutable_object

template <typename T>class Value{  Value(T value) : m_value(value) {}  const T get()  {    return m_value;   }private:  const T m_value;};


Once created, value cannot be changed.

Then just provide a static manager (class, function) to retrieve them. You could store them as pointers so that you can check whether they've been initialized.
It sounds as though contructors may not be ideal for what you are doing, and initialize methods would serve you better.

You could, as suggested, pass in an XML node to a constructor or initializer and have each class responsible for parsing its own node. The question then becomes: Do I really want each object to have a dependancy on the XML parser? I'd say no.


A Better solution would be to create a sort of parameter database, which would be a singleton (it meets the definition.) This database object would parse the xml into a form which can be easily queried for various elements -- this could be as simple as a map of unique IDs to a value, or it could be a hierarchical system (as I've done in the past.) The creator of the objects can then query for these named values just prior to creating them, rather than passing them down the chain.

Of course, this introduces the problem that the creators of objects will be dependant on this database, but there should be fewer than if objects initialized themselves and at least the complexity of the xml parser will be hidden inside the database. The database itself only needs a handful of simple public query methods.


This is basically the method Antheus suggests.

throw table_exception("(? ???)? ? ???");

Alright, I tried to implement James' method, but I'm getting linker errors (which is common when not setting up globals correctly I think).

#ifndef SIMPARAMS_H#define SIMPARAMS_Hclass simParams{	static bool isInit;public:    static double SIMSTART_JD();    static double SIMEND_SECONDS();    static void LoadSimParameters(XMLNode& node);private:    static double dSIMSTART_JD;    static double dSIMEND_SECONDS;};#endif /*SIMPARAMS_H*/


and

#include "simParams.h"double simParams::SIMSTART_JD() { return dSIMSTART_JD; }double simParams::SIMEND_SECONDS() { return dSIMEND_SECONDS; }void simParams::LoadSimParameters(XMLNode& simParametersXMLNode) {	if(!isInit) {		isInit = TRUE;		dSIMSTART_JD = (atof(simParametersXMLNode.getAttribute("startJD")));		dSIMEND_SECONDS = (atof(simParametersXMLNode.getAttribute("simEnd")));	}}


Linker errors:

error LNK2001: unresolved external symbol "private: static double simParams::dSIMSTART_JD" (?dSIMSTART_JD@simParams@@0NA) simParams.obj
error LNK2001: unresolved external symbol "private: static double simParams::dSIMEND_SECONDS" (?dSIMEND_SECONDS@simParams@@0NA) simParams.obj
error LNK2001: unresolved external symbol "private: static bool simParams::isInit" (?isInit@simParams@@0_NA) simParams.obj

EDIT: Maybe I should include a little more information. Those are the cpp and header files. Then I call LoadSimParameters in the main() and then simParams::SIMEND_SECONDS() and simParams::SIMSTART_JD() in several low level class methods... I do include simParams.h for those low level classes...
Class static variables (unless inlined const integrals) need to be defined too:

bool simParams::isInit = false;double simParams::dSIMSTART_JD = 0.0; // whatever defaults.double simParams::dSIMEND_SECONDS = 0.0;


However, unless you plan on adding any non-static members, you should prefer to use a namespace for things like this:

// header filenamespace simParams {    double SIMSTART_JD();    double SIMEND_SECONDS();    void LoadSimParameters(XMLNode& node);};///////////////////////////////////////////// source file// make them private to this filenamespace {    double dSIMSTART_JD = 0.0;    double dSIMEND_SECONDS = 0.0;    bool isInit = false;}namespace simParams {  // define functions    } 
Worked Like A Charm! (tm)

Kudos points to all!

This topic is closed to new replies.

Advertisement