Sign in to follow this  
leiavoia

Object Creation Through XML (Pluggable Factories?)

Recommended Posts

For my next trick, i want to create a "world" populated by objects which are created via XML. so lets say i have this very simple arrangement:
<WORLD>
<ENEMY X="100" Y="200" HEALTH="50" />
<WALL X="20" Y="20" WIDTH="50" HEIGHT="50" />
</WORLD>
Now when the world gets loaded, i want to create one Wall object and one Enemy object. Q: Would a Pluggable Factory be good for this situation? The XML would be put into a neat little condensed stringstream and sent to an object factory as a parameter. Then the object is created. What i imagine is that each class is pluggable and also would know how to decode it's own XML string. Am i on the right track or is there a better way to do this? Somehow i want to get from XML to a fully fleshed out environment. This seems like the best way from what i know. thanks for your input.

Share this post


Link to post
Share on other sites
I use something similar (although simpler) and it works quite well - and dead easy to maintain as well. :) Although instead of just an <ENEMY tag I include a 'type' attribute. All ENEMY tags then get forwarded to the factory which creates the object based on the type tag (and configures it with the remaining info). By explicitly catching different catergories (enemies, walls, etc.) and creating them via different factories the returned objects are all of a nicely defined interface.

Share this post


Link to post
Share on other sites
Thanks. If you could elaborate on your system it might help me a bit. I've never programmed anything quite like this so i'm trying to get a feel for any potential traps down the road.

The one thing that i'm wondering right now is what to pass to the factory as the parameter list:

- the straight unparsed XML (hard to do with the current system i have)

- the "pre-digested" version (elements returned by TinyXML parsing)

- the full condensed stringified stream version (requires parsing beforehand which possibly only the object itself knows how to do correctly).

Share this post


Link to post
Share on other sites
In a project I’m working on now, we do something similar to what your suggesting. It works well, so I think your idea is good. I wouldn’t allow the objects to decode their XML string, however. I would keep the XML out of the objects. I prefer to decouple the objects from the object builders. This makes it easier to update the XML reader code (even replacing it with something else - like a binary format) without affecting the objects themselves.

I've never used the TinyXML. That's probably because it doesn's support the DOM. I've used Xerces and MSXML which both support DOM. I find it hard to do any major XML work without DOM support.

In our case, we pass a DOM element to the factory method for the object. The factory uses normal DOM methods to read the XML and create a struct which has all the necessary initialization data for the object. This struct is passed to the object.

Share this post


Link to post
Share on other sites
Thanks for your input wmroll. That's basically what i'm leaning towards now. However, if i pass the object creator an "XML element" (that describes elements and attributes of the XML doc), i will certainly be coupling XML know-how with the classes themselves. That is not totally out of the question, and is certainly the easier way to go, but i'm just trying to think out other possibilities.

If i only pass in a string to the class, it adds two extra steps of serializing and deserializing the data into/out of a string. Furthermore, whoever gets that data (the factory) before the class itself does will have to know how to decode it all and "nice it up" for the class constructor. I would much rather just give the class the data it needs and let it construct itself through a regular constructor. But using just a string stream would cut down the unecessary dependencies.

Share this post


Link to post
Share on other sites
The idea about decoupling was to keep the xml decoding out of the the object classes, not necessarily out the factory classes.

Share this post


Link to post
Share on other sites
Lei,

I've posted this before around here somewhere, but here goes again: check out this here library:

XiMoL

It's basically a C++ stream implementation which wraps around XML decoding, and makes it nice and easy, so you don't have to fiddle with strings.

On a more high level note, I, personally, would rather put the XML parsing code into the specific class than a common factory. The reason is because if you have many different kinds of objects, then the factory will really get bloated with XML parsing code, and I like to keep files nice and simple and small, so I don't have to search a thousand lines of repetitive code (as it is bound to get) for something I need to change.

On the other hand, this approach is "bad" because now you've tighly coupled your class representation with it's physical representation on disk. Say you decide you don't wany XML any more, now you got every class that has a function you aren't using.

Technically, what you could do is implement what I believe is called the memento pattern. Basically, the idea is this:

1. Your factory method gets the XML - be it parsed form or raw or whatever you want.
2. The method then creates the memento from the XML - that is, you could have an associative array - or a hashmap - that has all the attribute-value pairs on your XML.
3. Based on the name of the XML tag, or some type attribute, the factory instantiates an appropriate class.
4. The instance is then given the memento to do with as it pleases.
5. The instance then reads the memento and matches keys to its own properties, and initializes them as appropriate.

That way, you still have to write the property-matching code in your objects, as you would otherwise, but:

1. The code is independent of how the information was loaded from disk, so if you choose to later create the memento using a database, or a binary file, or whatever else, you are free to do so.

2. Your XML parsing code becomes general, and you need only one copy of it, because now the factory doesn't know anything about what the object is going to do with the memento, and it doesn't really care either.

Basically now an example. Suppose you have the following XML file:


<objects>
<object type="CEnemy" x="1" y="2" strength="uberenemy" />
<object type="CWall" x="10" y="10" thickness="reallyreallyreallythick" />
</objects>


Then after the factory method processes the first object, for instance, it would create the following map:


"type" -> "CEnemy"
"x" -> "1"
"y" -> "2"
"strength" -> "uberenemy"


Then, it will look at the type attribute and say, hmmm, I need to instantiate a CEnemy, and so it will do. After that, the map will be passed to some initialize method, which will iterate over the map and each iteration will look something like this:

string key = //get current key
string value = //get current value
if (key == "x") //initialize x from the value, using that fancy new casting routine that uses streams.
//etc

In fact, you could even go a little further and abstract the whole iteration over the map thing out into some abstract class, and then at each iteration it would call some virtual method

void initializeProperty(string key, string value);

So you can get away with only writing the bare minimum of matching in each new class you write.

Vovan

Share this post


Link to post
Share on other sites
Just to throw into the pot,

we use a system like this for our sprites,

due the the co-felxability of subclassable objects and XML, we are able to allow our object subclasses take advantage of extra data that is in the xml file.

for instance, a sprite might just use one or two tags about the basic properties of a sprite, but then an enemy subclass of sprite might make use of new tags in the xml file, that describe other properties.

the xml file is loaded via an XMLDocument class, which parses and caches to an object model with common querying functions.

The sprite object then gets a pointer to the document, and can query the data it needs, any subclass also gets the chance to query it's data in turn, via a virtual function chain, it is _very_ important to cache your XML data, or some equivilent, to avoid re-loading and re-parsing your data each time you make a new character, out method was to simply store the loaded XMLDocument (in it's object model form, not raw text) in a key-map using the file path as the key, and having a system built into the document loader, that if you try and load a new XML file it checks the cache, and will not re-load a chached document, and the new XMLDoc class simply shares the original model pointer.

It has worked well for us =)

an example of the base sprite

<?xml version="1.0"?>
<sprite name="candle1">
<imagesets>
<imageset name="candle1" file="data/candle1.gmb" cols="4" rows="1" anchorx="16" anchory="85" areax="1" areay="1"></imageset>
</imgesets>
<light on="1" radius="200"></light>
<action>flicker</action>
<actions>
<action name="flicker" imageset="candle1" begcol="0" endcol="3" loops="-1" fps="12"></action>
</actions>
</sprite>




an example of a subclassed sprite

<?xml version="1.0"?>
<sprite name="morning" occlude="1">
<delegate>MorningDelegate</delegate>
<tooltip>Princess Morning</tooltip>
<portrait>data/portrait_morning.gmb</portrait>
<lifebarcolor r="255" g="222" b="0"></lifebarcolor>
<light on="0" radius="200"></light>

<imagesets>
<imageset name="morning_peace_idle" file="data/morning_peace_idle.gmb" cols="14" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
<imageset name="morning_peace_move" file="data/morning_peace_move.gmb" cols="14" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
<imageset name="morning_battle_idle" file="data/morning_battle_idle.gmb" cols="1" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
<imageset name="morning_battle_move" file="data/morning_battle_move.gmb" cols="14" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
<imageset name="morning_battle_attack" file="data/morning_battle_attack.gmb" cols="14" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
<imageset name="morning_dead" file="data/morning_dead.gmb" cols="14" rows="8" anchorx="50" anchory="85" areax="1" areay="1"></imageset>
</imagesets>

<action>idle</action>
<actions>
<action mode="0" name="idle" imageset="morning_peace_idle" begcol="0" endcol="13" loops="-1" fps="15"></action>
<action mode="0" name="move" imageset="morning_peace_move" begcol="0" endcol="13" loops="-1" fps="33"></action>
<action mode="0" name="die" imageset="morning_dead" begcol="0" endcol="0" loops="-1" fps="33"></action>
<action mode="1" name="idle" imageset="morning_battle_idle" begcol="0" endcol="0" loops="-1" fps="15"></action>
<action mode="1" name="move" imageset="morning_battle_move" begcol="0" endcol="13" loops="-1" fps="33"></action>
<action mode="1" name="attack" imageset="morning_battle_attack" begcol="0" endcol="13" loops="0" fps="33"></action>
<action mode="1" name="die" imageset="morning_dead" begcol="0" endcol="0" loops="0-1" fps="33"></action>
</actions>
<attributes>
<vitality>10</vitality>
<spirit>8</spirit>
<strength>8</strength>
<wisdom>4</wisdom>
<agility>10</agility>
<lifepoints>100</lifepoints>
<manapoints>80</manapoints>
</attributes>
</sprite>








You might also notice that our sprite files have optional tags for image loading, and describe animation sequences, and since this file is used for a subclass it also stores character stats information.

We dont use 'plugable factories' but we do have a system for runtime by-name object instantiation. our engine has an in code and script command,

CreateSprite("name","class","delegate","file");

wherein this creates a new sprite of type class, sets some properties and has it load 'file' which is an xml file, like above.


as for keeping things generic, you cant assume to much when it comes to using say, XML or a Database as your data provider, you will need a translation layer for sure, so i recomend either doing it in the factory class *centralized but lots of code* or in the sprite class itself *small, easy to locate code, but spread around*


Hope that gives you some ideas=D

[Edited by - EDI on August 19, 2004 11:13:12 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by leiavoia
Thanks for your input wmroll. That's basically what i'm leaning towards now. However, if i pass the object creator an "XML element" (that describes elements and attributes of the XML doc), i will certainly be coupling XML know-how with the classes themselves. That is not totally out of the question, and is certainly the easier way to go, but i'm just trying to think out other possibilities.


I have a similar system that I use - my solution to hiding the XML implementation is to use a Object Parameters class. This simply has methods to retrieve the attributes values by name e.g. position, fileName etc.. Of course the underlying implementation is simply a XML parser that contains the XML heirarchy from the object being created. This then allows the object to create child objects as well to give a heirarchial structure.

After using this a while, and trying to incorporate this into a scenegraph with multiple methods of accessing e.g. spatial, heirarchial, material. I'm leaning more towards using this as a sort of configuration file for a scene compiler instead of using this directly in the engine.

James

Share this post


Link to post
Share on other sites
Thanks for the input guys ("hi vovan!"). Those are both ideas i will probably use.

The only issues i have with passing a bla "momento" to the actual class is that it needs to be bla-typed. Everything will end up being a string. I'm not very familar with streams yet though (one of those things i've never stopped to get very involved with), but i think i might be able to make it easier if i just dumped the variable values into a stream (?)

Ultimately, i would like to be able to do this from the "momento"

Class::Class ( std::map< string, some_kind_of_stream > momento ) {
float_variable_x << momento["x_position"];
float_variable_y << momento["y_position"];
integer_variable << momento["health"];
string_variable << momento["name"];
}


But i don't think anything would work so elegantly as that (if i'm wrong please let me know!).

EDI gives me another idea but i'm not sure it will work. I'm trying to stuff the entire object creation interface through the factory, but some objects you know have extra parameters or whatever. The factory needs to know a lot of this, but i think just passing in a file name to parse additional data is a good idea. i'll keep that in mind.

Share this post


Link to post
Share on other sites
Well, but you do actually have a neat trick like that. It's called a boost lexical cast. You can learn about it more here, but it all boils down to the syntax:


char* value = "123";
int num = lexical_cast<int>(value); //num now contains 123.


And that also works with standard C++ strings, I believe. The only thing to remember here is that that lexical cast actually has runtime overhead, because it's a function call, not a classic cast, and uses C++ streams to convert from one type to the other. But hey, otherwise, you'd have to do it manually any way, so that really doesn't detract that much from performance, specially if you are doing it on startup or during some level loading time, when the user expects to have to wait.

Vovan aka Scorpion

Share this post


Link to post
Share on other sites
That would work, but i would like to not introduce more libs or dependencies than i have to. Those make it harder to finally install on someone else's machine as i've learned.

*cough* ParaGUI *cough* :-)

So far, i'm only using SDL and OpenGL. I'm using TinyXML also, but that doesn't count as a lib since it compiles directly into your project.

Share this post


Link to post
Share on other sites
Quote:
Original post by leiavoia
That would work, but i would like to not introduce more libs or dependencies than i have to. Those make it harder to finally install on someone else's machine as i've learned.

*cough* ParaGUI *cough* :-)

So far, i'm only using SDL and OpenGL. I'm using TinyXML also, but that doesn't count as a lib since it compiles directly into your project.


Haha, yes, indeed. ParaGUI...

Well, I guess you could roll your own quick little version of the caster. It's really easy to do actually. I just made one copy that seems to work just fine. The caster itself is here:


// File: cast.h

#include <sstream>

namespace cast {
// Just a little exception to throw in case something goes wrong.
// Note here, that you may want to derive from std::bad_cast or
// something, just to make sense out of this class.
class BadCast {};

//Local static stream to use when casting.
static std::stringstream stream;

// This is where the magic happens. You can cast anything into
// anything else, elthough of course casting say float to double
// would be silly using this.
template< typename Target, typename Source >
Target lexical_cast(Source arg) {
//Create a placeholder for the result, of the type according to the template.
Target result;

//Clear stream, so if there's old junk in there, it goes away.
stream.clear();

//Stringinize the argument, and check that everything went along fine.
stream << arg;
if (stream.fail()) { throw BadCast(); }

//Convert the string to whatever it is we want here:
stream >> result;
if (stream.fail()) { throw BadCast(); }

//Return the result:
return result;
}
}



And so then you can use this as follows:


#include <iostream>
#include "cast.h"

int main() {
std::string intStr = "123";
std::string floatStr = "3.1415926";
int num = cast::lexical_cast<int>(intStr);
float fPi = cast::lexical_cast<float>(floatStr);
double dPi = cast::lexical_cast<double>(floatStr);

std::cout << "Converted " << intStr << " to (int)" << num << std::endl;
std::cout << "Converted " << floatStr << " to (float)" << fPi << std::endl;
std::cout << "Converted " << floatStr << " to (double)" << dPi << std::endl;

return 0;
}



The output of the above then is:


Converted 123 to (int)123
Converted 3.1415926 to (float)3.14159
Converted 3.1415926 to (double)3.14159
Press any key to continue


Of course, the fact that the pi has too few digits up there is only due to the way I printed the float and the double. If you look through the debugger at the real values, no precision was lost in the cast.

Vovan

Share this post


Link to post
Share on other sites
Hey, you're just full of cool tricks! I hope you work professionally. If you don't, some employer out there is really missing out :-)

So basically, if you shove something into and out of a stream, the standard stream will convert it for you, say INT<->STRING ?

Share this post


Link to post
Share on other sites
Quote:
Original post by leiavoia
Hey, you're just full of cool tricks! I hope you work professionally. If you don't, some employer out there is really missing out :-)

So basically, if you shove something into and out of a stream, the standard stream will convert it for you, say INT<->STRING ?


Well, like I said, it's not my trick. Got it from boost. [smile]

But yes, the streams will convert the strings to whatever format you want, and that's I believe simply because the << and >> operators are overloaded for all kinds of different primitive types (and even some objects, like string), so even if you have your own user-defined type, then you can overload these operators to operate on streams, like so:


void operator>>(stringstream str, myType& target) {
str >> target.data1;
str >> target.data2;
}


Though of course, you probably won't need that, since the whole idea of this thread was to come up with a more structured way of storing your own types - namely, XML. [smile]

Vovan (aka Scropion)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this