Tech/LxEngine/Tutorials/Tutorial 0

Overview
This is a very brief tutorial aimed at introducing the LxEngine Document Object Model (DOM). The tutorial loads an XML document and uses the "variant" data type to handle JSON-like data in a convenient manner.

The tutorial is quite simple but provides introduces the starting point of most LxEngine applications.

Headers
The core architecture of LxEngine is pulled in with a single header file,. All LxEngine files are located under the directory to avoid name collisions.

We include as well so that we can use  to print our message.

Loading a Document
LxEngine uses the following object model:


 * Every application has a single object, which coordinates any application-wide uses of LxEngine
 * The Engine can create or load 0 or more objects

The Engine acts as a sort of traffic controller for any application-wide events, data, synchronization, etc. The Document may be a scene in your graphics viewer, a level in your game, or whatever definition of "document" makes most sense for your application. There's obviously more to these classes, but that's sufficient for this simple tutorial.

The Engine Singleton
The first step is to create the Engine object. To ensure that only a single Engine object is every created (i.e. it remains a ), the static method is used:

Shared Pointers
Whoa - wait up, what's with the type?

LxEngine uses through its API via the std::shared_ptr<> class. Since reference counting is used so extensively throughout LxEngine, most LxEngine classes also provide a typedef ending in "Ptr" for a shared pointer to that class. Thus, EnginePtr and std::shared_ptr are one and the same. Likewise, there are DocumentPtr and ElementPtr typedefs for shared pointers to Document and Element objects.

Exceptions
Whoa - wait up, we skipped a line: what's with the {{C|try {}} block?

For errors, LxEngine logs them and then throws an exception. Return values are generally used for "expected" values, not error situations. This is a design choice. (For more details, see .) There's not too much to worry about at this point, but it's good to know to expect errors to be indicated via an exception, not a return value.

An Extra Code Block?
Whoa - wait up, we skipped a line: what's with the {{C|{}} starting a new code block after the Engine::acquire call?

We'll get to that in at the end of the tutorial. It's not a typo, but is actually important. In short, the Engine acquisition is put in it's own outer-most block to ensure LxEngine can clean-up in a well-defined manner: all the objects inside that next code block will go out of scope before the Engine object is cleaned up.

The Document
Loading the Document object is straightforward:

What is the Document? It's a XML document. The Document class provides a API very much like the HTML Document Object Model (DOM). There are differences, but in general, the LxEngine DOM tends to be a simplified version of the HTML DOM.

Extracing Data
Ok, the Document is loaded. Let's look at what's in that document first:

Get an Element
Let's get a shared pointer to a particular element in our document. Let's use the HTML DOM method to get the Phrase element:

Get an Attribute
Now, let's get that "repeat" attribute from the Element:

The method call to should be straightforward enough.

What's worth noting is the an implicit cast that's parsing that attribute string in the XML document yet returning an integer. This happens because the Element::getAttribute method returns an object of the class. The lxvar class is a variant data type that can store strings, integers, floats, arrays, and maps - and likewise - can be implicitly cast to any of those types. Sure, it circumvents the type safety of C++, but lxvars are designed to provide convenience. Strict C++ types can be used internally wherever needed, but lxvar can be used for data transfer purposes where the convenience of a variant data type is needed.

We'll talk more about lxvar in future tutorials: for now, think of it a variant data type designed to hold JSON-like data.

Get an Element value
Here's a big difference between HTML and the LxEngine XML DOM:


 * The value of an HTML Element is child nodes, some of which are TextNodes describing plain text between other child elements
 * The value of an LxEngine Element is a JSON object

Another important corollary: if an LxEngine Element has children, it should not have a value (i.e. it's value is an empty JSON object) and, vice-versa, if it has a value, it should not have children.

This restriction greatly reduces the complexity of parsing a LxEngine Document. Furthermore, since LxEngine Documents do not have the same goals as HTML in producing a human-readable text, this restriction rarely imposes any problematic constraints on the application.

So on to getting the value of the Element:

Here's lxvar again. The JSON data stored in the Element is returned as an lxvar. That JSON object in the XML document is now accessible via the lxvar object.

Using an lxvar
If you're unclear about what JSON is, at this point, it would likely be a good idea to read up on it as a full description is beyond the scope of this tutorial. In short, a JSON object can be a boolean value, an integer value, a floating point value, a string value, an array of JSON objects, or an associative array (i.e. map) of JSON objects. The lxvar object provides a set of methods to check the type, cast between types, access array elements, etc. These methods are designed for convenience so implicit casts and operator overloads are used whenever possible.

Let's get two C++ strings from the lxvar:

The has been overloaded to access map or array elements. The code here is simply assuming "value" is a map (you'd get an exception thrown if it wasn't). These return lxvars, which in turn are implicitly cast to std::strings.

Print!
Absolutely nothing interesting here! All the data has already been copied into standard C++ objects, so just print:

Clean-up
Three parts:
 * 1) End the enclosing } code block
 * 2) Engine::shutdown
 * 3) Catch any exceptions

As mentioned at the beginning of the tutorial, the Engine::acquire call was pushed outside the code block where the Document was loaded. Here we end that code block. So why did we add that extra code block in the first place? The reason is the shared_ptrs. But putting the DocumentPtr in the enclosing block, it will be released automatically when that block ends. In such a trivial example like this, that's not that much help - but as more LxEngine objects are created by the main application, this ensures they are all released at the end of the enclosing code block before the next call is made...

The next call is to. This call tells LxEngine that the application is done with LxEngine. It will check that all objects (e.g. Documents) have been freed and do all shutdown of any subsystems, plug-ins, etc. Why is this an explicit call rather than simply doing this in the Engine destructor? In this example, it doesn't really matter, but in more complex cases the subsystems may actually need to use the Engine object as they shut down. By providing an explicit shutdown, an awkward "chicken and egg" problem is avoided where subsystems may refer to the Engine during their shutdown but the Engine will only be shutdown when no one is referring to it. Or put differently, Engine::shutdown provides a clean "root" object to begin the shutdown process from so that things may clean up in an organized, well-defined order.

Lastly, the whole program is enclosed in a try { } catch block. Here we catch any exception that might have slipped through. We're not doing anything useful here - the code's just there as a reminder that LxEngine throws expections!

Conclusion
Summary:
 * Engine is a singleton
 * LxEngine throws exceptions on errors
 * LxEngine uses std::shared_ptr<> to do reference counting on all major objects
 * The Document Object Model is based on a simplification of the HTML DOM
 * The lxvar class provides a convenient C++ class to access JSON object data
 * LxEngine uses an explicit shutdown to ensure things clean-up correctly

This hopefully provides enough information to move on to something useful!

Appendix A: Feedback, Questions, & Issues
Please provide any feedback, questions, or issues on the forums.

LxEngine is in active development and in need of improvements. Suggestions for making it simpler to use are most welcome!