https://www.sirikata.com/wiki/index.php?title=Guides/Platform_Development/Tutorials/Adding_Options&feed=atom&action=historyGuides/Platform Development/Tutorials/Adding Options - Revision history2024-03-28T11:36:01ZRevision history for this page on the wikiMediaWiki 1.35.7https://www.sirikata.com/wiki/index.php?title=Guides/Platform_Development/Tutorials/Adding_Options&diff=1253&oldid=prevEwencp: Initial import from sirikata-docs2012-05-25T21:49:59Z<p>Initial import from sirikata-docs</p>
<p><b>New page</b></p><div>= Adding Options using Sirikata's Option Library =<br />
<br />
The primary method of customizing Sirikata's behavior is through plugins: different implementations of a particular service or interface are stored in and loaded from different plugins. To further customize behavior, Sirikata has an options system.<br />
<br />
== Option Parsing ==<br />
<br />
Options are generally specified in two places: the command line and configuration files. Command line options are specified in the standard format<br />
<br />
<pre>sirikata-binary --config.option.first=value1 --config.option.second=value2</pre><br />
Note that if you need to include special characters in an option (spaces, <tt>\</tt>, <tt>&quot;</tt>, etc), you'll need to quote and escape them:<br />
<br />
<pre>sirikata-binary &quot;--config.option.first=value with spaces&quot;</pre><br />
Configuration files are loaded by specifying them in the command line opion <tt>--cfg=file.cfg</tt>. They are formatted as a list of option-value pairs<br />
<br />
<pre>config.option.first=value1<br />
config.option.second=value2</pre><br />
== Recursive Options ==<br />
<br />
A key property of Sirikata's option system is that it will not allow unknown options. In order to accomplish this when there are options being added dynamically (e.g. by plugins) it uses recursive options.<br />
<br />
For instance, consider a the interface <tt>LocationService</tt> and two implementations, <tt>LocalLocationService</tt> and <tt>DistributedLocationService</tt>. The <tt>LocalLocationService</tt> may have no special options, but <tt>DistributedLocationService</tt> might need to allocate a network socket to communicate with neighboring servers. In this case, it needs a <tt>--port</tt> option. We can't add the option globally because the different plugins need different options and we don't want implementation details of the plugin to leak into the main binary using them. (Further the option name could easily conflict with the name used by many other plugins).<br />
<br />
Instead, we define only two options: <tt>loc</tt> and <tt>loc-options</tt>. The first specifies which plugin we use (e.g. <tt>local</tt> or <tt>distributed</tt>) and the second is just a string which gets passed along to the plugin as options. The plugin is then responsible for parsing them. Of course the plugin may parse them however it likes (it just receives them as a String), but using the Sirikata options system is highly encouraged to simplify the implementation, avoid redundancy, and for consistency.<br />
<br />
Note that these recursive options often require quoting:<br />
<br />
<pre>sirikata-binary --loc=distributed &quot;--loc-options=--port=1234 --max-dist=1000&quot;</pre><br />
== Options Basics ==<br />
<br />
Options are always provided initially as Strings, read from either the command line or a configuration file. The option system takes care of parsing these options, verifying their formatting, and converting them to the desired type.<br />
<br />
This entire process should be transparent to you: you just need to specify a set of '''options''', with their types, defaults, and descriptions, which you store in a '''module'''. You then request that the options are parsed and extract and convert the actual option values.<br />
<br />
In the following example, we'll create options for a simple <tt>comm</tt> plugin. First, we need the options library itself:<br />
<br />
<pre>#include &lt;sirikata/core/options/Options.hpp&gt;</pre><br />
Next, we need to specify our set of options. We'll create them under the module <tt>&quot;comm&quot;</tt> and add two options, `host` and `port`:<br />
<br />
<pre>Sirikata::InitializeClassOptions ico(&quot;comm&quot;,NULL,<br />
new Sirikata::OptionValue(&quot;host&quot;, &quot;127.0.0.1&quot;, Sirikata::OptionValueType&lt;String&gt;(), &quot;The host to connect to.&quot;),<br />
new Sirikata::OptionValue(&quot;port&quot;, &quot;7777&quot;, Sirikata::OptionValueType&lt;uint16&gt;(), &quot;The port to connect to.&quot;),<br />
NULL);</pre><br />
Make sure that your module is unique: while a conflict isn't a problem so long as types match up, you could allow options that shouldn't be permitted due to somebody else's options being permitted along with yours.<br />
<br />
<tt>InitializeClassOptions</tt> can accept an arbitrarily long list of <tt>OptionValue</tt>'s, terminated by a <tt>NULL</tt>. This registration process should be performed once. Usually this means registering them during plugin initialization. (If you need per-instance options, then you should generate a module for each of instance. Usually this is only necessary when you could be creating multiple instances simultaneously.)<br />
<br />
Next, when you actually have some options to parse (for instance, when a new instance of your implementation is requested), you can retrieve the <tt>OptionSet</tt>, parse, and extract options:<br />
<br />
<pre>OptionSet* optionsSet = OptionSet::getOptions(&quot;comm&quot;,NULL);<br />
optionsSet-&gt;parse(args);<br />
<br />
String server_host = optionsSet-&gt;referenceOption(&quot;host&quot;)-&gt;as&lt;String&gt;();<br />
uint16 server_port = optionsSet-&gt;referenceOption(&quot;port&quot;)-&gt;as&lt;uint16&gt;();</pre><br />
Note that the types requested during conversion match those specified in the registration. The system will throw an exception if you try to convert to the wrong type.<br />
<br />
== Adding Parsing Methods For a New Option Type ==<br />
<br />
Options are not limited to primitive types like integers and Strings. Any type can be used as an option so long as the option system can convert it to and from a String. For instance, Vector3 frequently appears as an option type for specifying properties like locations and bounding regions.<br />
<br />
Suppose you have a new type, <tt>MyStruct</tt>, which you need to use as an option value. While the Option system provides overloads for many types, you need to make sure the template instantiation <tt>OptionValueType&lt;MyStruct&gt;</tt> will be valid in order to use it as an option. In order for this to be valid, there must be an accessible implementation of the &quot;stream&quot; operators (<tt>&lt;&lt;</tt> and <tt>&gt;&gt;</tt>). If you're familiar with <tt>boost::lexical_cast</tt>, the requirements are essentially identical.<br />
<br />
If we have the following definition of <tt>MyStruct</tt>:<br />
<br />
<pre>struct MyStruct {<br />
int32 x;<br />
int32 y;<br />
};</pre><br />
then making the following globally accessible is a simple way to provide the necessary conversions:<br />
<br />
<pre>inline std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const MyStruct&amp; rhs) {<br />
os &lt;&lt; '&lt;' &lt;&lt; rhs.x &lt;&lt; ',' &lt;&lt; rhs.y &lt;&lt; '&gt;';<br />
return os;<br />
}<br />
<br />
inline std::istream&amp; operator&gt;&gt;(std::istream&amp; is, MyStruct&amp; rhs) {<br />
char dummy;<br />
is &gt;&gt; dummy &gt;&gt; rhs.x &gt;&gt; dummy &gt;&gt; rhs.y &gt;&gt; dummy;<br />
return is;<br />
}</pre><br />
Of course the parsing could be more robust, but this minimal example is sufficient to get conversions sufficient to use <tt>MyStruct</tt> as an option value type.</div>Ewencp