Jump to content

  • Log In with Google      Sign In   
  • Create Account

jdhardy

Member Since 28 Jun 2000
Offline Last Active Jun 16 2014 09:18 AM

Topics I've Started

Boost Installer

19 May 2006 - 03:51 PM

Seeing two threads today about difficulties installing Boost, I thought I'd pass this along: Boost Installer for MSVC 7.1 and 8.0. It includes prebuilt binaries of all of the libraries that need building, eliminating the biggest hurdle for using Boost. (I had nothing to do with this; I'm just pointing it out to those that didn't know.)

And now for something completely useless....

27 July 2004 - 05:10 PM

I saw this a while ago (I wish I could remember where), so I can't claim I invented it. It's pretty cool, although totally useless in production code. Let's say you don't like the current C++ function notation f(x, y) and want something new, such as x f y. Well, AFAIK that's not exactly possible, but you can get close. Let's look at the example of vector math - we can create a more natural syntax for the dot & cross products.
Vector3f up, forward, x, y;

Vector3f right = up <cross> forward;
float z = x <dot> y;

So what are <cross> an <dot>? They rely on a little operator overloading magic.
float z = x <dot> y;

will be parsed by the compiler as
float z = (x <dot)> y;

Let's look at the first part:
(x <dot)

We need to overload the less-than operator like so:
DotHalfOp operator<(Vector3f v, DotOp op)
{
    return(DotHalfOp(v));
}

Simple enough, but what are DotOp and DotHalfOp? DotOp is simply a special class that I'll present later. DotHalfOp is what makes the whole thing work - it holds the left-hand side of the operation to pass it to the right hand side.
struct DotHalfOp
{
    DotHalfOp(Vector3f arg) : left(arg) {}
    Vector3f left;
};

Why do we need DotHalfOp? If we didn't use it (let's say we returned a Vector3f instead) then the next part would become Vector3f operator>(Vector3f, Vector3f), which isn't what we want. Instead, the next part becomes
struct DotOp
{
    DotOp(Vector3f arg1, Vector3f arg2) : left(arg1), right(arg2) {}
    Vector3f left, right;
    
    float perform()
    {
        return(left.x*right.x + left.y*right.y+left.z*right.z);
    }
}

Vector3f operator>(DotHalfOp hop, Vector3f arg)
{
    DotOp op(lop.left, arg);
    return(op.perform());
}

op.perform() is the actual workhorse here, computing the dot product. But where does <dot> come from? So far, we only have a DotOp. Simple enough:
const static DotOp dot;

As you can imagine, <cross> is nearly identical, except for perform() and some types. I whipped up a little framework, op_creation.hpp, that allows you to create a new operator with a single macro call. There is also an example, optest.cpp. It basically works as follows:
CREATE_OPERATION(dot, float, Vector3f, Vector3f, return(left.x*right.x + left.y*right.y+left.z*right.z);)

Vector3f z = x <dot> y;

where dot is the name of the operation to create (and will add dot_op to the namespace), float is the return value of perform, the Vector3fs are the right and left arguments, and the final argument is the function data. This can be as long as you need; remember that C++ is delimited by semi-colons, and DON'T forget the return statement. You can also use the base classes & templates directly, if you need some more flexibility.

Presenting TinyXMLHelper

14 July 2004 - 12:04 PM

The following is reprinted at http://www.ualberta.ca/~jdhardy/tinyxmlhelper. It is included here to save the effort of an extra click. Any and all comments, suggestions, or constructive criticism is encouraged. TinyXML is a very handy library for parsing XML files. It provides a good portion of the functionality required to handle modest XML files. However, the interface is not very intuitive to certain functions. While everything described here can certainly be accomplished with raw TinyXML commands, these functions were designed to eliminate tedious typing and to provide a simpler, more C++-style approach to handling XML documents. TinyXML was designed to be portable, and thus does not make use of advanced C++ features like templates, RTTI, or namespaces. This helper library requires all of those to achieve the functionality, so it may not be as portable as TinyXML itself. It has been tested under gcc 3.2 (Dev-C++) and MSVC++ 8 (Whidbey) Express beta. The primary purpose of the library is to simplify situations with a significant amount of iteration over unknown elements. If the elements are known (such as in a configuration file), then TinyXML's handles provide a sufficient abstraction that they do not need to be wrapped. Thus, the library is designated a 'helper' rather than a 'wrapper'. It is possible, though unnecessary, to completely avoid TinyXML altogether; however, this is more of a side-effect than an explicit goal of the library. There are three options for downloading the library: For TinyXMLHelper to work properly, TIXML_USE_STL must be defined.

Structure

Nodes

Nodes are the fundamental unit of an XML file. There are various types of nodes (such as elements, comments, declarations, etc.), all of which may contain data. The library provides wrappers for the various TinyXML node classes that also double as the node policies described in the next section. These wrappers are provided to avoid some of the pointer mess-work associated with TinyXML.

Iterator

The most important part of the helper is the Xml::iterator class, which provides a STL-compliant iterator over parts of the DOM. The iterator uses template policies to determine how to iterate over the elements. The two necessary policies are an iteration policy and a node policy. (The iteration policy is specified in terms of the node policy). The iteration policy determines how to iterate over the elements. For example, the two provided iteration policies, Xml::child and Xml::sibling, iterate over the children of the given node and siblings of the given node, respectively. The node policy determines which nodes are iterated over. A given node in an XML document might have several children of varying types - such as elements, text, and comments. Often, only one of these is desired at a particular time - elements, for example. Node policies restrict iteration to the requested type. The provided polices are Xml::element, Xml::comment, Xml::text, Xml::declaration, Xml::unknown (for nodes the TinyXML doesn't recognize), and Xml::document. A special policy, Xml::node, is provided which will iterate over all children, in case the user desires the functionality.
Xml::iterator< child<element> > it;

The default constructor (above, for example) will create an iterator that can be used to represent the end of an iteration sequence. Note: the std namespace also include an iterator class. Therefore, it is a good idea to always qualify the namespace on Xml::iterator, and it is required if both the Xml and std namepsace are imported.

Data retrieval

XML files are only useful if data can retireved from them. To this end, the helper library provides a set of functions that retrieve data from the XML DOM: Xml::attribute(), Xml::tag(), Xml::childTag(), and Xml::getText(). Xml::attribute() is used to extract attributes from elements. Any type can be retreived from an attribute, as long as the type is InputStreamable (ie, it provides operator>>). Xml::tag() is used to extract data from an element. This is usually used when iterating over a set of elements. Xml::childTag() is used when the name of a child element is known, but there is no need to iterate over all child nodes. Xml::getText() is used to return the text in a Xml::text() node. Xml::tag() and Xml::childTag() both use loaders to retrieve data from elements. Loaders are simply functions that take a const Xml::element& paramater and return a value of the type requested. What occurs in these functions is up to the user; however, they must throw an exception to be handled by the user on error. The library does not handle loader failure.

Examples

Let's say that we need to parse the following XML file:
<test>
	<entity value1="1" value2="2"/>
	<entity value1="3" value2="4"/>
	<nonentity value1="54" value2="90"/>
	<text>Mystery <bold>bold</bold> text!</text>
	<entity value1="7" value2="8"/>
</test>
The following code could be used to do this:
struct entity
{
    string value1;
    string value2;
};

entity entity_loader(const element &elem)
{
    entity ent;
    ent.value1 = attribute<string>(elem, "value1");
    ent.value2 = attribute<string>(elem, "value2");
    return(ent);
}

struct xtext
{
    string value;
};

xtext xtext_loader(const element &elem)
{
    xtext t;
    t.value = "";

    Xml::iterator< child<text> > end;
    Xml::node n(elem);
    for(Xml::iterator< child<text> > it(elem); it != end; ++it)
    {
        t.value += getText(*it);
    }
    return(t);
}

int main(int argc, char *argv[])
{
    document doc("test.xml");

    element doc_root = doc->RootElement();
    entity ent;
    xtext t;
    
    Xml::iterator< child<element> > end;
    Xml::iterator< child<element> > it(doc_root);
    for(; it != end; ++it)
    {
        if(has_value(it, "entity"))
        {
            ent = tag<entity>(*it, entity_loader);
            cout<<"("<<ent.value1<<","<<ent.value2<<")"<<endl;
        }
        else if(has_value(it, "text"))
        {
            t = tag<xtext>(*it, xtext_loader);
            cout<<"("<<t.value<<")"<<endl;
        }
    }
    return 0;
}


Now let's examine this step by step, starting at the top.
struct entity
{
    string value1;
    string value2;
};

entity entity_loader(const element &elem)
{
    entity ent;
    ent.value1 = attribute<string>(elem, "value1");
    ent.value2 = attribute<string>(elem, "value2");
    return(ent);
}


One of the requirements of the library is that each element in an XML file must be represented as a class in code. So, we define a class entity to hold our entities, with the attributes value1 and value2 as members. Then, a loader is added that will retrieve the data contained in an entity. An entity contains a pair of attributes which contain strings, so we use Xml::attribute() to get them, specifying that they are std::strings. Xml::attribute() returns a value of the type requested.
struct xtext
{
	string value;
};

xtext xtext_loader(const element &elem)
{
	xtext t;
	t.value = "";

	Xml::iterator< child<text> > end;
	Xml::node n(elem);
	for(Xml::iterator< child<text> > it(elem); it != end; ++it)
	{
		t.value += getText(*it);
	}
	return(t);
}


We also want some text, so we declare a structure to hold it. This is used to demonstrate how the filtering works. If you look at the XML file, you'll notice that there is a <bold> tag present. This is an element that is a child of the <text> tag, and a sibling of the strings on either side.
Xml::element			text
	Xml::text		"Mystery "
	Xml::element		<bold>
		Xml::text	"bold"
	Xml::text		"text!"
In this case, only the text is iterator over. If you needed the <bold>g; tag, then you either have to iterate over the list, looking for Xml::elements, or iterate using Xml::nodes and do determination from there.
int main(int argc, char *argv[])
{
	document doc("test.xml");
	
	element doc_root = doc->RootElement();
	entity ent;
	xtext t;


The main part of the program loads an XML document and then gets the first (root) element of the document. Xml::document can also take an input stream and read from it.
	Xml::iterator< child<element> > end;


Then, an iterator to represent the end of the sequence is created (using the default constructor)...
	Xml::iterator< child<element> > it(doc_root);


...and an iterator for iterating over the children of the root element.
	for(; it != end; ++it)
	{
		if(has_value(it, "entity"))
		{
			ent = tag<entity>(*it, entity_loader);
			cout<<"("<<ent.value1<<","<<ent.value2<<")"<<endl;
		}
		else if(has_value(it, "text"))
		{
			t = tag<xtext>(*it, xtext_loader);
			cout<<"("<<t.value<<")"<<endl;
		}
	}
	return 0;
}


Iterating over the XML is the same as any other container. Xml::has_value() is used to match iterators to types to allow them to be loaded. Then Xml::tag() can be used to read the data into the necessary structure. Xml::tag(), like Xml::attribute(), returns a value of the type requested

Limitation

As the previous example hopefully showed, the helper library can make some tings easier. However, it does not support the same functionality as the already existing TiXmlHandles, nor does it provide support for modifying nodes in the DOM. It can only be used for reading from files. Thus, it merely 'helps' you with a subset of TinyXML's full capabilities, and does not attempt to provide a full interface.

Final Words

This library is licenced under a zlib-style licence, which basically states that you can do whatever you want with it, as long as you don't claim that you wrote it. I would like to know who is using it, however, so the least I expect is an email. I can be reached at jdhardy@gmail.com. EDIT: Can we please have post preview?

PARTNERS