head	1.2;
access;
symbols;
locks; strict;
comment	@# @;


1.2
date	2000.07.14.21.31.08;	author curt;	state Exp;
branches;
next	1.1;

1.1
date	2000.07.05.18.52.51;	author curt;	state Exp;
branches;
next	;


desc
@@


1.2
log
@Renamed props stuff from fg* to sg*
@
text
@<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SimGear Property Manager</title>
</head>

<body bgcolor="white">
<h1>SimGear Property Manager: C++ Programmer's Guide</h1>

<p><em>4 July, 2000</em></p>

<p>A <em>Public Domain</em> module that comes with <strong>NO
WARRANTY</strong> of any kind.</p>

<dl>
<dt>1. <a href="#synopsis">Synopsis</a></dt>
<dt>2. <a href="#description">Description</a></dt>
<dt>3. <a href="#classes">Classes</a></dt>
<dd><dl>
<dt>3.1. <a href="#classes.SGValue">The <var>SGValue</var> Class</a></dt>
<dt>3.2. <a href="#classes.SGValue">The <var>SGPropertyList</var>
Class</a></dt>
<dt>3.3. <a href="#classes.SGValue">The <var>SGPropertyNode</var>
Class</a></dt>
</dl></dd>
<dt>4. <a href="#examples">Examples</a></dt>
<dt>5. <a href="#todo">TODO</a></dt>
<dt>6. <a href="#author">Author and (lack of) Copyright</a></dt>
</dl>


<div><a name="synopsis"></a>
<h2>1. Synopsis</h2>

<blockquote><pre>
#include &lt;simgear/misc/props.hxx&gt;

SGPropertyList props;
</pre></blockquote>

<!-- end of "Synopsis" -->
</div>


<div><a name="description"></a>
<h2>2. Description</h2>

<p>Large games like simulators or adventures need to manage a lot of
state information.  As programs grow, a few problems can keep cropping
up and adding to the complexity of development, especially in an
open-source project:</p>

<ol>

<li>
<p>Contributors find it harder and harder to master the number of
interfaces necessary to make any useful additions to the program, so
contributions slow down.</p>
</li>

<li>
<p>Runtime configurability becomes increasingly difficult, with
different modules using different mechanisms (environment variables,
custom config files, command-line options, etc.).</p>
</li>

<li>
<p>The order of initialization of modules is complicated and brittle,
since one module's initialization routines might need to set or
retrieve state information from another that hasn't been initialized
yet.</p>
</li>

<li>
<p>Extensibility through add-on scripts, config files, etc. is limited
to the state information that the program provides, and
non-code-writing developers often have to wait too long for the C or
C++ programmers to get around to adding a new variable.</p>
</li>

</ol>

<p>The SimGear Property Manager is designed to work around all of
those problems by providing a single interface for all of a program's
state information, and by allowing new properties to be created
dynamically at run-time.</p>

<p>The property manager organizes information into a virtual directory
tree, much as it might appear in a file system.  For example, the
flaps in a flight simulator can have a name like
<var>/controls/flaps</var>, while the current altitude can have a name
like <var>/position/altitude</var>; there is no fixed limit to the
depth of a property, so a name like
<var>/environment/clouds/layers/layer1/type</var> is perfectly
acceptable.</p>

<p>To work around order-of-initialization problems (and to avoid
dangling pointers if a module is reinitialized), the property manager
uses an abstraction of a value in the <var><a href="#classes.SGValue"
>SGValue</a></var> class.  The abstract value object associated with a
property name will <em>never</em> change in the course of a program,
even if the actual numeric or string value changes many times and if
the value is tied to or untied from external functions: that means
that one module can set a default value for another before the other
is initialized, and the other can pick up that value automatically.
It also means that it's not necessary to incur the overhead of a
hashtable lookup every time you want to get or modify a value (just
look it up once and keep a reference to the SGValue object
returned).</p>

<p>The property manager can store six types of values:</p>

<ol>
<li>bool</li>
<li>int</li>
<li>float</li>
<li>double</li>
<li>string</li>
<li>unknown (to be determined later)</li>
</ol>

<p>Any of these can be managed internally or tied to external getter
and setter functions; the difference is invisible to anyone using the
abstract value object.</p>

<!-- end of "Description" -->
</div>


<div><a name="classes"></a>
<h2>3. Classes</h2>

<p>The SimGear property manager consists of three C++ classes, all
declared in <code>simgear/misc/props.hxx</code>:</p>

<ol>

<li>
<p>the <var><a href="#classes.SGValue">SGValue</a></var> class, which
represents the abstraction of a property value;</p>
</li>

<li>
<p>the <var><a href="#classes.SGPropertyList">SGPropertyList</a></var>
class, which maps property names to values; and</p>
</li>

<li>
<p>the <var><a href="#classes.SGPropertyNode">SGPropertyNode</a></var>
class, which allows the user to navigate the property list like a
directory tree.</p>
</li>

</ol>


<div><a name="classes.SGValue"></a>
<h3>3.1. The <var>SGValue</var> Class</h3>

<p>The <var>SGValue</var> class represents the abstraction of a value:
once you have a reference to an SGValue object, the reference is
guaranteed valid for the rest of the life of the program, even if the
underlying type or storage method changes.  That means that a module
can look up a value at initialization time and keep a pointer to it,
or even set up a default value, before the module that actually
manages the value has been initialized.</p>

<p>Much of the functionality of the SimGear Property Manager resides
in the SGValue class; in fact, most of the methods in the <var><a
href="#classes.SGPropertyList">SGPropertyList</a></var> class are
simply wrappers that look up a value by name and then call the
<var>SGValue</var> methods to do the work.</p>

<p>A primitive value of an <var>SGValue</var> object can currently
have any of six types:</p>

<ol>
<li>SGValue::UNKNOWN</li>
<li>SGValue::BOOL</li>
<li>SGValue::INT</li>
<li>SGValue::FLOAT</li>
<li>SGValue::DOUBLE</li>
<li>SGValue::STRING</li>
</ol>

<p>The <var>UNKNOWN</var> type is identical to <var>STRING</var>,
except that it will be coerced automatically to a different type when
set or tied.</p>

<p>The class's methods fall into five major groups:</p>

<ol>
<li>status methods (the value type and whether it's tied to external
functions);</li>
<li>accessors for obtaining the primitive value</li>
<li>setters for modifying the primitive value</li>
<li>methods for tying the value to external functions</li>
<li>methods for untying the value from external functions</li>
</ol>

<div>
<h4>Status Methods</h4>

<dl>

<dt><code>SGValue::Type getType () const</code></dt>
<dd>Return the current primitive type of the value: it is always more
efficient to get and set the primitive type.  A type of
<var>SGValue::UNKNOWN</var> will change automatically when it is set;
any type will change automatically when it is tied.  As a result, it
is usually a good idea to check the type from time to time.</dd> 

<dt><code>bool isTied () const</code></dt>
<dd>Return <var>true</var> if the value is currently tied to external
functions.  If the value is tied, any addition attempt to tie it will
fail unless it is untied first.</dd>

</dl>

<!-- end of "Status Methods" -->
</div>


<div>
<h4>Accessor Methods</h4>

<p>Note: if you use an accessor that differs from the primitive value
type, the methods will attempt to coerce the return value, and will
return the default value if the primitive value cannot be coerced
(typically when converting from a string to a numeric type).</p>

<p>If the value is tied to external functions, but no getter is
available, these methods will also return the default values.</p>

<dl>

<dt><code>bool getBoolValue () const</code></dt>
<dd>Return a boolean value (default: <var>false</var>).</dd>

<dt><code>int getIntValue () const</code></dt>
<dd>Return an integer value (default: <var>0</var>).</dd>

<dt><code>float getFloatValue () const</code></dt>
<dd>Return a floating-point value (default: <var>0.0</var>).</dd>

<dt><code>double getDoubleValue () const</code></dt>
<dd>Return a double-precision floating-point value (default:
<var>0.0L</var>).</dd>

<dt><code>const string &amp;getStringValue () const</code></dt>
<dd>Return a string value (default: empty string).</dd>

</dl>

<!-- end of "Accessor Methods" -->
</div>


<div>
<h4>Setter Methods</h4>

<p>If the current type is <var>SGValue::UNKNOWN</var>, the setters
will automatically retype the value to their own type.</p>

<p>Setters return a boolean value indicating success or failure.  An
attempt to set a value may fail if the value is tied to an external
getter function without a corresponding setter.</p>

<dl>

<dt><code>bool setUnknownValue (const string &amp;value)</code></dt>
<dd>Assign a string value, but leave the type unknown (so that it can
be retyped to a numeric type if needed later).</dd>

<dt><code>bool setBoolValue (bool value)</code></dt>
<dd>Assign a boolean value, and retype to <var>SGValue::BOOLEAN</var>
if the current type is <var>SGValue::UNKNOWN</var>.</dd>

<dt><code>bool setIntValue (int value)</code></dt>
<dd>Assign an integer value, and retype to <var>SGValue::INT</var>
if the current type is <var>SGValue::UNKNOWN</var>.</dd>

<dt><code>bool setFloatValue (float value)</code></dt>
<dd>Assign a floating-point value, and retype to <var>SGValue::FLOAT</var>
if the current type is <var>SGValue::UNKNOWN</var>.</dd>

<dt><code>bool setDoubleValue (double value)</code></dt>
<dd>Assign a double-precision floating-point value, and retype to
<var>SGValue::DOUBLE</var> if the current type is
<var>SGValue::UNKNOWN</var>.</dd>

<dt><code>bool setStringValue (const string &amp;value)</code></dt>
<dd>Assign a boolean value, and retype to <var>SGValue::STRING</var>
if the current type is <var>SGValue::UNKNOWN</var>.</dd>

</dl>

<!-- end of "Setter Methods" -->
</div>


<div>
<h4>Tying External Functions</h4>

<p>By default, the SimGear Property Manager stores a copy of the
primitive value internally; however, it is also possible to tie a
value to a pair of external static functions, a getter (which returns
the primitive type and has no arguments) and a setter (which has no
return value and takes an argument of the primitive type).  The
<var>SGValue</var> class provides convenient typedefs for this
functions:</p>

<blockquote><pre>
                              // External getters
typedef bool (*bool_getter)();
typedef int (*int_getter)();
typedef float (*float_getter)();
typedef double (*double_getter)();
typedef const string &(*string_getter)();

                              // External setters
typedef void (*bool_setter)(bool);
typedef void (*int_setter)(int);
typedef void (*float_setter)(float);
typedef void (*double_setter)(double);
typedef void (*string_setter)(const string &);
</pre></blockquote>

<p>All of the tying methods return <var>true</var> if the tie
succeeded, or <var>false</var> otherwise.  An attempt to tie external
functions will <em>always</em> fail if the value is already tied (you
can check with the <var>SGValue::isTied()</var> method), but you can
always untie it first and then retie it.</p>

<p>All of the tying methods take an optional <var>useDefault</var>: if
this argument is <var>true</var> (the default), then the method will
attempt to send the current value of the method to the setter
supplied: this behaviour is useful for preserving default values set
before a module has tied values to its functions.</p>

<p>If the setter argument is 0, then the value cannot be modified; if
the getter argument is 0, then the value cannot be read.</p>

<p>These methods always retype the value, no matter what its original
type was.</p>

<dl>

<dt><code>bool tieBool (bool_getter getter, bool_setter setter, bool
use_default = true)</code></dt>
<dd>Retype the value to <var>SGValue::BOOL</var> if necessary, and tie
it to the getter and setter provided.</dd>

<dt><code>bool tieInt (int_getter getter, int_setter setter, bool
use_default = true)</code></dt>
<dd>Retype the value to <var>SGValue::INT</var> if necessary, and tie
it to the getter and setter provided.</dd>

<dt><code>bool tieFloat (float_getter getter, float_setter setter, bool
use_default = true)</code></dt>
<dd>Retype the value to <var>SGValue::FLOAT</var> if necessary, and tie
it to the getter and setter provided.</dd>

<dt><code>bool tieDouble (double_getter getter, double_setter setter, bool
use_default = true)</code></dt>
<dd>Retype the value to <var>SGValue::DOUBLE</var> if necessary, and tie
it to the getter and setter provided.</dd>

<dt><code>bool tieString (string_getter getter, string_setter setter, bool
use_default = true)</code></dt>
<dd>Retype the value to <var>SGValue::STRING</var> if necessary, and tie
it to the getter and setter provided.</dd>

<dt><code>void untie ()</code></dt>
<dd>Untie this value from external functions; return <var>true</var>
if the value had been tied before, or <var>false</var> otherwise.</dd>

</dl>

<!-- end of "Tying External Functions" -->
</div>

<!-- end of "The SGValue Class" -->
</div>


<div><a name="classes.SGPropertyList"></a>
<h3>3.2. The <var>SGPropertyList</var> Class</h3>

<p>This class maps property names to values.  Property names must
use the following format:</p>

<ul>
<li>All property names must begin with '/'.</li>
<li>Property names must never end with '/'.</li>
<li>Property names must never contain two '/' character in a row.</li>
<li>Only the characters a-z, A-Z, or '_' may immediately follow a '/'
character; the characters 0-9, '.', or '-' may appear in other
positions.</li>
<li>Whitespace may never appear in a property name.</li>
</ul>

<p>The following are legal property names:</p>

<ul>
<li>/a/b</li>
<li>/controls/throttles/throttle1</li>
<li>/_ANY/foo123/this-that</li>
<li>/x-y-z</li>
</ul>

<p>The following are <em>not</em> legal property names (though the
module does not yet detect all of the errors):</p>

<ul>
<li>/</li>
<li>a/b</li>
<li>/data/current/</li>
<li>/a/b/..</li>
<li>/this that/and/the/other</li>
<li>/controls/throttles/1</li>
</ul>

<p>These restrictions exist so that property names can be mapped to a
directory structure for internal use or to an XML document for
persistent storage.</p>

<p>Most of the methods in the <var>SGPropertyList</var> class are
wrappers around <var>SGValue</var> methods: they look up the value by
name and then invoke the SGValue method directly.</p>


<div>
<h4>Status</h4>

<dl>

<dt><code>SGPropertyList::size_type size () const</code></dt>
<dd>Return the number of properties currently in the list.</dd>

</dl>

<!-- end of "Status Method" -->
</div>


<div>
<h4>Iteration</h4>

<dl>

<dt><code>SGPropertyList::const_iterator begin () const</code></dt>
<dd>Return a read-only, forward STL iterator over the sorted
collection of properties.</dd>

<dt><code>SGPropertyList::const_iterator end () const</code></dt>
<dd>Return the read-only STL end iterator for the sorted collection of
properties.</dd>

</dl>

<!-- end of "Iteration" -->
</div>


<div>
<h4>Query</h4>

<p>For the sake of efficiency, it is best to look up an <var><a
href="#classes.SGValue">SGValue</a></var> once using the
<var>getValue</var> method and then query it directly.  In situations
where a few milliseconds don't matter (i.e. at startup or
reinitialization), the other methods in this section are more
convenient, since they allow you to look up the primitive value
directly.</p>

<dl>

<dt><code>SGValue * getValue (const string &amp;name, bool create =
false)</code></dt>
<dd>Look up a value by name, and return <var>0</var> if no value is
mapped to the name.  If the optional <var>create</var> parameter is
<var>true</var> (it is false by default), the method will create a new
value and return it if a value does not already exist.  If a non-zero
pointer is return, it is guaranteed valid for the lifetime of the
<var>SGPropertyList</var> object.</dd>

<dt><code>bool getBoolValue (const string &amp;name)</code></dt>
<dd>Return the primitive boolean value associated with the name, or
<var>false</var> if none is available.  Invokes
<var>SGValue::getBoolValue</var>.</dd>

<dt><code>int getIntValue (const string &amp;name)</code></dt>
<dd>Return the primitive integer value associated with the name, or
<var>false</var> if none is available.  Invokes
<var>SGValue::getIntValue</var>.</dd>

<dt><code>float getFloatValue (const string &amp;name)</code></dt>
<dd>Return the primitive float value associated with the name, or
<var>false</var> if none is available.  Invokes
<var>SGValue::getFloatValue</var>.</dd>

<dt><code>double getDoubleValue (const string &amp;name)</code></dt>
<dd>Return the primitive double value associated with the name, or
<var>false</var> if none is available.  Invokes
<var>SGValue::getDoubleValue</var>.</dd>

<dt><code>const string &amp;getStringValue (const string &amp;name)</code></dt>
<dd>Return the primitive string value associated with the name, or
<var>false</var> if none is available.  Invokes
<var>SGValue::getStringValue</var>.</dd>

</dl>

<!-- end of "Query" -->
</div>


<div>
<h4>Setter Methods</h4>

<p>These methods look up the <var>SGValue</var> object by name,
creating it if it does not already exist, then invoke the appropriate
<var>set*Value</var> method.  For example, the <var>setBool</var>
method is roughly equivalent to the following:</p>

<blockquote><pre>
return props.getValue("/foo/bar", true)->setBoolValue(value);
</pre></blockquote>

<p>These methods returns true if the primitive value was successfully
modified.  For more information, see the <cite>Setter Method</cite>
section of the <var><a href="#classes.SGValue">SGValue</a></var>
class.</p>

<dl>

<dt><code>bool setUnknownValue (const string &amp;name, const string
&amp;value)</code></dt>
<dd>Set a primitive unknown (string) value.</dd>

<dt><code>bool setBoolValue (const string &amp;name, bool
value)</code></dt>
<dd>Set a primitive bool value.</dd>

<dt><code>bool setIntValue (const string &amp;name, int
value)</code></dt>
<dd>Set a primitive int value.</dd>

<dt><code>bool setFloatValue (const string &amp;name, float
value)</code></dt>
<dd>Set a primitive float value.</dd>

<dt><code>bool setDoubleValue (const string &amp;name, double
value)</code></dt>
<dd>Set a primitive double value.</dd>

<dt><code>bool setStringValue (const string &amp;name, const string
&amp;value)</code></dt>
<dd>Set a primitive string value.</dd>

</dl>

<!-- end of "Setter Methods" -->
</div>

<div>
<h4>Tying Methods</h4>

<p>These methods are wrappers around the <var>tie*</var> methods of
the <var><a href="#classes.SGValue">SGValue</a></var> class.  They
return false if the value cannot be tied, and will create the value by
default if it does not already exist.  The <var>tieBool</var> method,
for example, is roughly equivalent to the following:</p>

<blockquote><pre>
return props.getValue("/foo/bar", true).tieBool(getter, setter, useDefault);
</pre></blockquote>

<p>These methods return <var>true</var> if the value was tied
successfully.  For more information, see the tying functions for the
<var>SGValue</var> class.</p>

<dl>

<dt><code>bool tieBool (const string &amp;name, bool_getter getter,
bool_setter setter = 0, bool useDefault = true)</code></dt>
<dd>Tie external functions to a bool value and retype it to
SGValue::BOOL if necessary.</dd>

<dt><code>bool tieInt (const string &amp;name, int_getter getter,
int_setter setter = 0, bool useDefault = true)</code></dt>
<dd>Tie external functions to a int value and retype it to
SGValue::INT if necessary.</dd>

<dt><code>bool tieFloat (const string &amp;name, float_getter getter,
float_setter setter = 0, bool useDefault = true)</code></dt>
<dd>Tie external functions to a float value and retype it to
SGValue::FLOAT if necessary.</dd>

<dt><code>bool tieDouble (const string &amp;name, double_getter getter,
double_setter setter = 0, bool useDefault = true)</code></dt>
<dd>Tie external functions to a double value and retype it to
SGValue::DOUBLE if necessary.</dd>

<dt><code>bool tieString (const string &amp;name, string_getter getter,
string_setter setter = 0, bool useDefault = true)</code></dt>
<dd>Tie external functions to a string value and retype it to
SGValue::STRING if necessary.</dd>

<dt><code>bool untie (const string &amp;name)</code></dt>
<dd>Untie a value from external functions.</dd>

</dl>

<!-- end of "Tying Methods" -->
</div>

<!-- end of "The SGPropertyList Class" -->
</div>


<div><a name="classes.SGPropertyNode"></a>
<h3>3.3. The <var>SGPropertyNode</var> Class</h3>

<p>The <var>SGPropertyNode</var> class is a wrapper around an <var><a
href="#classes.SGPropertyList">SGPropertyList</a></var> that allows
tree- or directory-like navigation around a property list.  It is
possible to take any partial path, such as <var>/controls</var>, and
find out how many immediate child directories it has.</p>

<p>For example, imagine that the following three properties exist in a
property list:</p>

<ol>
<li><var>/controls/throttles/throttle1</var></li>
<li><var>/controls/throttles/throttle2</var></li>
</ol>

<p>The root node ("") will have one child, <var>controls</var>,
and no parent.  The "/controls" node will have one child,
<var>throttles</var>, and one parent, <var>controls</var>.  The
<var>/controls/throttles</var> node will have two children,
<var>throttle1</var> and <var>throttle2</var> and one parent,
<var>throttles</var>.  The <var>/controls/throttles/throttle1</var>
and <var>/controls/throttles/throttle2</var> properties will have no
children, but each will have a value.</p>

<p>Note that it is possible for a node to have both children and a
value.</p>

<p>This class is optimized for usability rather than efficiency: it is
suitable for use during initialization or reinitialization or for
occasional use in the main loop, but definitely should not be used
every frame; a future version of the SimGear Property Manager may
include a more efficient implementation of this class. </p>

<dl>

<dt><code>SGPropertyNode (const string &amp;path, SGPropertyList *
properties)</code></dt>
<dd>Construct a new, light-weight wrapper around a property list.
There is no fixed limit to the number of wrappers that may be
constructed, and the memory overhead is minimal (i.e. the whole
property list isn't copied each time).  The <var>path</var> argument
must be a complete path starting with '/' (or optionally, the empty
string for the root).  The <var>properties</var> parameter may be
<var>0</var>, in which case some (but not all) of the operations will
fail.  It may also be set and changed over an object's life span (as
may be the path, to aid in object reuse).</dd>

<dt><code>const string &amp;getPath () const</code></dt>
<dd>Return the current complete path for the node.</dd>

<dt><code>void setPath (const string &amp;path)</code></dt>
<dd>Set a new complete path for the node.</dd>

<dt><code>SGPropertyList * getPropertyList ()</code></dt>
<dd>Return the property list associated with the node, or 0 if there
is none.</dd>

<dt><code>void setPropertyList (SGPropertyList * properties)
const</code></dt>
<dd>Change the property list associated with the node.</dd>

<dt><code>int size () const</code></dt>
<dd>Return the number of child nodes of this node (or 0 if it is a
leaf).  Note that this method is fairly inefficient.</dd>

<dt><code>const string &getName () const</code></dt>
<dd>Return the local name of this node (with all the '/' stripped
off); for example, the local name of <var>/foo/bar/hack</var> is
<var>hack</var>.</dd>

<dt><code>SGValue * getValue ()</code></dt>
<dd>Return the value associated with this node, or 0 if it has no
value (i.e. the path doesn't actually correspond with a property
name, or there is no property list attached).</dd>

<dt><code>bool getParent (SGPropertyNode &parent) const</code></dt>
<dd>Instantiate the node provided to hold the parent of the current
node, if any.  Return true on success.</dd>

<dt><code>bool getChild (SGPropertyNode &parent, int n) const</code></dt>
<dd>Instantiate the node provided to hold the nth child of the current
node, if any.  Return true on success.</dd>

</dl>

<!-- end of "The SGPropertyNode Class" -->
</div>

<!-- end of "Classes" -->
</div>


<div><a name="examples"></a>
<h2>4. Examples</h2>

<p>First, create a new property list:</p>

<blockquote><pre>
SGPropertyList props;
</pre></blockquote>

<p>Most programs (games or otherwise) will probably already have a
global property list somewhere, and they might also have specialised
lists for individual simulation models, etc.</p>

<p>Now, let's set a few default values:</p>

<blockquote><pre>
props.setIntValue("/environment/temperature", 32);
props.setIntValue("/environment/wind/speed", 15);
props.setIntValue("/environment/wind/direction", 180);
</pre></blockquote>

<p>It doesn't matter whether the environment module of the program is
actually initialized or not -- if it's not ready yet, the property
manager will store the values for it until it's ready to take them
over.</p>

<p>Now, let's assume that the environment module has its own functions
for getting and setting information:</p>

<blockquote><pre>
int envGetTemperature ();
void envSetTemperature (int temp);

int envGetWindSpeed ();
void envSetWindSpeed (int speed);

int envGetWindDirection ();
void envSetWindDirection (int direction);
</pre></blockquote>

<p>When the module initializes itself, it can simply tie these
functions to the appropriate properties:</p>

<blockquote><pre>
props.tieInt("/environment/temperature", envGetTemperature,
             envSetTemperature);
props.tieInt("/environment/wind/speed", envGetWindSpeed,
             envSetWindSpeed);
props.tieInt("/environment/wind/direction", envGetWindDirection,
             envSetWindDirection);
</pre></blockquote>

<p>As a side effect, the original default values of 32, 15, and 180
will be passed to the <var>envGetTemperature</var>,
<var>envGetWindSpeed</var>, and <var>envGetWindDirection</var>
functions (you can prevent that by setting the optional
<var>useDefault</var> parameter to <var>false</var>).</p>

<p>Now, imagine that in a different module we have a GUI that can
display or adjust the current temperatue.  When that module is
initialized, it should grab a copy of the <var>SGValue</var>
object:</p>

<blockquote><pre>
SGValue * temp = props.getValue("/environment/temperature", true);
</pre></blockquote>

<p>Note the second <var>true</var> parameter.  That asks the property
manager to create the value if it doesn't already exist: it's a good
idea to do so to help avoid problems with initialization order (in
case the GUI is set up before the environment module).  If the value
is created here, it can still be tied to the functions in the
environment module later.</p>

<p>Whenever the GUI needs to display or change the temperature, it
uses the value object that it has already saved (there's no need to do
a new map lookup every time):</p>

<blockquote><pre>
field.setText(temp.getStringValue());
</pre></blockquote>

<p>Note that the property manager can automatically convert the value
to a string when requested.  To update the temperature value, the GUI
simply uses a different method on the same value:</p>

<blockquote><pre>
if (!temp.setIntValue(atoi(field.getText())))
  reportError("Failed to modify /environment/temperature as requested");
</pre></blockquote>

<p>It would also have been possible to call <var>setStringValue</var>
and allow the property manager to perform the conversion from a string
to an integer.</p>

<p>A different module of the program might need to deal with cloud
layers.  Image that the program allows an unlimited number of layers,
initialized as follow:</p>

<blockquote><pre>
props.setIntValue("/environment/clouds/layer1/altitude", 2500);
props.setStringValue("/environment/clouds/layer1/type", "overcast");
props.setFloatValue("/environment/clouds/layer1/coverage", 0.5);

props.setIntValue("/environment/clouds/layer2/altitude", 15000);
props.setStringValue("/environment/clouds/layer2/type", "cirrus");
props.setFloatValue("/environment/clouds/layer2/coverage", 0.1);

// etc.
</pre></blockquote>

<p>The cloud module cannot know the exact property names in advance,
since there may be any number of layers; instead, it uses the
<var>SGPropertyNode</var> interface to iterate through the directory
structure.  First, it creates a new property node wrapped around the
current list:</p>

<blockquote><pre>
SGPropertyNode clouds("/environment/clouds", &amp;props);
</pre></blockquote>

<p>Next, the module iterates through each of the children, adding
cloud layers as required:</p>

<blockquote><pre>
SGPropertyNode child;
int size = clouds.size();

for (int i = 0; i < size; i++) {
  CloudLayer * layer = new CloudLayer;

  if (!clouds.getChild(child, i))
    reportError("Cannot read child");

  if (child.getName() == "altitude" && child.getValue != 0)
    layer.setAltitude(child.getValue()->getIntValue());
  else if (child.getName() == "type" && child.getValue != 0) 
    layer.setType(child.getValue()->getStringValue());
  else if (child.getName() == "coverage" && child.getValue != 0)
    layer.setCoverage(child.getValue()->getFloatValue());

  clouds.addLayer(layer);
}
</pre></blockquote>

<p>These examples cover most of the typical uses that programs will
make of the property manager.</p>

<!-- end of "Examples" -->
</div>


<div><a name="todo"></a>
<h2>5. TODO</h2>

<p>This is an early release of the SimGear Property Manager, and
there's a lot that can be done: user feedback will determine the
priorities, but here are some possible enhancements:</p>

<ul>

<li>
<p>Optimize, optimize, optimize: as soon as the code has been given a
good workout, I want to move as much of it as possible inline, and
maybe make most of the methods non-virtual as well.</p>
</li>

<li>
<p>Allow variable pointers as well as functions to be tied, so that
you can do something like props.tieInt("/foo/bar", &amp;myBar):this
will be problematic, since the host module has no easy way to do
bounds checking.</p>
</li>

<li>
<p>Allow method pointers as well as functions to be tied: this is
problematic, since a pointer to the object will also have to be
passed, but the object's type will not be known.  Most likely, the
methods will have to take a pointer to a static function that receives
the object pointer as a void pointer and then casts up and invokes the
correct method.  Ouch.</p>
</li>

<li>
<p>Add more primitive types for values, such as Cartesian points,
date/time, area, and volume.</p>
</li>

<li>
<p>Allow a value to hold additional meta information, such as flags
(archive, network, read-only, etc.) and units (feet, meters,
etc.).</p>
</li>

<li>
<p>Design an easy-to-use XML reader and writer (I plan to do this very
soon).</p>
</li>

<li>
<p>Write bindings for scripting languages such as Perl, Python, and
the various Schemes (I won't be doing this myself -- volunteers?).</p>
</li>

<li>
<p>Write C++-based GUIs for viewing and editing a property list
(volunteers?).</p>
</li>

</ul>

<!-- end of "TODO" -->
</div>


<div><a name="author"></a>
<h2>Author and (lack of) Copyright</h2>

<p>This module was written by David Megginson (<a
href="mailto:david@@megginson.com" >david@@megginson.com</a>), and is
hereby released into the Public Domain with no restrictions at all on
its use, commercial or non-commercial.  This module comes with no
warranty at all, and the author assumes no responsibility for its
correct performance, etc. etc. blah blah blah...</p>

<p>The module is in the public domain because copyright of any kind
(even GPL) implies a threat to take legal action against anyone who
violates it.  I have no intention to take any kind of legal action no
matter what people do with this code, and I don't believe in making
empty threats.</p>

<p>Because the module is public domain, you are free to switch it to
whatever license you want to use in your program, free or otherwise.</p>

<!-- end of "Author" -->
</div>

<hr />

<address>$Id: props.html,v 1.1 2000/07/05 18:52:51 curt Exp $</address>

</body>
</html>
@


1.1
log
@Initial revision.
@
text
@d22 2
a23 2
<dt>3.1. <a href="#classes.FGValue">The <var>FGValue</var> Class</a></dt>
<dt>3.2. <a href="#classes.FGValue">The <var>FGPropertyList</var>
d25 1
a25 1
<dt>3.3. <a href="#classes.FGValue">The <var>FGPropertyNode</var>
d40 1
a40 1
FGPropertyList props;
d101 2
a102 2
uses an abstraction of a value in the <var><a href="#classes.FGValue"
>FGValue</a></var> class.  The abstract value object associated with a
d110 1
a110 1
look it up once and keep a reference to the FGValue object
d141 1
a141 1
<p>the <var><a href="#classes.FGValue">FGValue</a></var> class, which
d146 1
a146 1
<p>the <var><a href="#classes.FGPropertyList">FGPropertyList</a></var>
d151 1
a151 1
<p>the <var><a href="#classes.FGPropertyNode">FGPropertyNode</a></var>
d159 2
a160 2
<div><a name="classes.FGValue"></a>
<h3>3.1. The <var>FGValue</var> Class</h3>
d162 2
a163 2
<p>The <var>FGValue</var> class represents the abstraction of a value:
once you have a reference to an FGValue object, the reference is
d171 2
a172 2
in the FGValue class; in fact, most of the methods in the <var><a
href="#classes.FGPropertyList">FGPropertyList</a></var> class are
d174 1
a174 1
<var>FGValue</var> methods to do the work.</p>
d176 1
a176 1
<p>A primitive value of an <var>FGValue</var> object can currently
d180 6
a185 6
<li>FGValue::UNKNOWN</li>
<li>FGValue::BOOL</li>
<li>FGValue::INT</li>
<li>FGValue::FLOAT</li>
<li>FGValue::DOUBLE</li>
<li>FGValue::STRING</li>
d208 1
a208 1
<dt><code>FGValue::Type getType () const</code></dt>
d211 1
a211 1
<var>FGValue::UNKNOWN</var> will change automatically when it is set;
d264 1
a264 1
<p>If the current type is <var>FGValue::UNKNOWN</var>, the setters
d278 2
a279 2
<dd>Assign a boolean value, and retype to <var>FGValue::BOOLEAN</var>
if the current type is <var>FGValue::UNKNOWN</var>.</dd>
d282 2
a283 2
<dd>Assign an integer value, and retype to <var>FGValue::INT</var>
if the current type is <var>FGValue::UNKNOWN</var>.</dd>
d286 2
a287 2
<dd>Assign a floating-point value, and retype to <var>FGValue::FLOAT</var>
if the current type is <var>FGValue::UNKNOWN</var>.</dd>
d291 2
a292 2
<var>FGValue::DOUBLE</var> if the current type is
<var>FGValue::UNKNOWN</var>.</dd>
d295 2
a296 2
<dd>Assign a boolean value, and retype to <var>FGValue::STRING</var>
if the current type is <var>FGValue::UNKNOWN</var>.</dd>
d312 1
a312 1
<var>FGValue</var> class provides convenient typedefs for this
d334 1
a334 1
can check with the <var>FGValue::isTied()</var> method), but you can
d353 1
a353 1
<dd>Retype the value to <var>FGValue::BOOL</var> if necessary, and tie
d358 1
a358 1
<dd>Retype the value to <var>FGValue::INT</var> if necessary, and tie
d363 1
a363 1
<dd>Retype the value to <var>FGValue::FLOAT</var> if necessary, and tie
d368 1
a368 1
<dd>Retype the value to <var>FGValue::DOUBLE</var> if necessary, and tie
d373 1
a373 1
<dd>Retype the value to <var>FGValue::STRING</var> if necessary, and tie
d385 1
a385 1
<!-- end of "The FGValue Class" -->
d389 2
a390 2
<div><a name="classes.FGPropertyList"></a>
<h3>3.2. The <var>FGPropertyList</var> Class</h3>
d430 3
a432 3
<p>Most of the methods in the <var>FGPropertyList</var> class are
wrappers around <var>FGValue</var> methods: they look up the value by
name and then invoke the FGValue method directly.</p>
d440 1
a440 1
<dt><code>FGPropertyList::size_type size () const</code></dt>
d454 1
a454 1
<dt><code>FGPropertyList::const_iterator begin () const</code></dt>
d458 1
a458 1
<dt><code>FGPropertyList::const_iterator end () const</code></dt>
d472 1
a472 1
href="#classes.FGValue">FGValue</a></var> once using the
d481 1
a481 1
<dt><code>FGValue * getValue (const string &amp;name, bool create =
d488 1
a488 1
<var>FGPropertyList</var> object.</dd>
d493 1
a493 1
<var>FGValue::getBoolValue</var>.</dd>
d498 1
a498 1
<var>FGValue::getIntValue</var>.</dd>
d503 1
a503 1
<var>FGValue::getFloatValue</var>.</dd>
d508 1
a508 1
<var>FGValue::getDoubleValue</var>.</dd>
d513 1
a513 1
<var>FGValue::getStringValue</var>.</dd>
d524 1
a524 1
<p>These methods look up the <var>FGValue</var> object by name,
d535 1
a535 1
section of the <var><a href="#classes.FGValue">FGValue</a></var>
d573 1
a573 1
the <var><a href="#classes.FGValue">FGValue</a></var> class.  They
d584 1
a584 1
<var>FGValue</var> class.</p>
d591 1
a591 1
FGValue::BOOL if necessary.</dd>
d596 1
a596 1
FGValue::INT if necessary.</dd>
d601 1
a601 1
FGValue::FLOAT if necessary.</dd>
d606 1
a606 1
FGValue::DOUBLE if necessary.</dd>
d611 1
a611 1
FGValue::STRING if necessary.</dd>
d621 1
a621 1
<!-- end of "The FGPropertyList Class" -->
d625 2
a626 2
<div><a name="classes.FGPropertyNode"></a>
<h3>3.3. The <var>FGPropertyNode</var> Class</h3>
d628 2
a629 2
<p>The <var>FGPropertyNode</var> class is a wrapper around an <var><a
href="#classes.FGPropertyList">FGPropertyList</a></var> that allows
d662 1
a662 1
<dt><code>FGPropertyNode (const string &amp;path, FGPropertyList *
d680 1
a680 1
<dt><code>FGPropertyList * getPropertyList ()</code></dt>
d684 1
a684 1
<dt><code>void setPropertyList (FGPropertyList * properties)
d697 1
a697 1
<dt><code>FGValue * getValue ()</code></dt>
d702 1
a702 1
<dt><code>bool getParent (FGPropertyNode &parent) const</code></dt>
d706 1
a706 1
<dt><code>bool getChild (FGPropertyNode &parent, int n) const</code></dt>
d712 1
a712 1
<!-- end of "The FGPropertyNode Class" -->
d725 1
a725 1
FGPropertyList props;
d779 1
a779 1
initialized, it should grab a copy of the <var>FGValue</var>
d783 1
a783 1
FGValue * temp = props.getValue("/environment/temperature", true);
d832 1
a832 1
<var>FGPropertyNode</var> interface to iterate through the directory
d837 1
a837 1
FGPropertyNode clouds("/environment/clouds", &amp;props);
d844 1
a844 1
FGPropertyNode child;
d958 1
a958 1
<address>$Id$</address>
@

