Angelscript/docs/manual/doc_use_script_class.html

225 lines
24 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.18"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Using script classes</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function() { init_search(); });
/* @license-end */
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="aslogo_small.png"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">AngelScript
</div>
</td>
<td> <div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.18 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search');
/* @license-end */
</script>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function(){initNavTree('doc_use_script_class.html',''); initResizable(); });
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="PageDoc"><div class="header">
<div class="headertitle">
<div class="title">Using script classes </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p>When there are multiple objects controlled by the same script implementation it may be favourable to use script classes, rather than global script functions. Using script classes each instance can have it's own set of variables within the class, contrary to the global functions that needs to rely on global variables to store persistent information.</p>
<p>Of course, it would be possible to duplicate the script modules, so that there is one module for each object instance, but that would be impose a rather big overhead for the application. Script classes don't have that overhead, as all instances share the same module, and thus the same bytecode and function ids, etc.</p>
<h1><a class="anchor" id="doc_use_script_class_1"></a>
Instantiating the script class</h1>
<p>Before instantiating the script class you need to know which class to instantiate. Exactly how this is done depends on the application, but here are some suggestions.</p>
<p>If the application knows the name of the class, either hardcoded or from some configuration, the class type can easily be obtained by calling the module's <a class="el" href="classas_i_script_module.html#a7fbc2bd888b248d2c2ee2d953b49eefc">GetTypeIdByDecl</a> with the name of the class. The application can also choose to identify the class through some properties of the class, e.g. if the class implements a predefined <a class="el" href="classas_i_script_engine.html#ae2d89b82561b7f9843f35693c664589f">interface</a>. Then the application can enumerate the class types implemented in the script with <a class="el" href="classas_i_script_module.html#a49ebfdd345f18d88e489edebd4888af7">GetObjectTypeByIndex</a> and then examine the type through the <a class="el" href="classas_i_type_info.html">asITypeInfo</a> interface.</p>
<p>A third option, if you're using the <a class="el" href="doc_addon_build.html">script builder add-on</a>, is to use the metadata to identify the class. If you choose this option, use the <a class="el" href="classas_i_script_module.html">asIScriptModule</a> to enumerate the declared types and then query the <a class="el" href="doc_addon_build.html">CScriptBuilder</a> for their metadata.</p>
<p>Once the object type is known you create the instance by calling the class' factory function, passing it the necessary arguments, e.g. a pointer to the application object which the script class should be bound to. The factory function id is found by querying the <a class="el" href="classas_i_type_info.html">asITypeInfo</a>.</p>
<div class="fragment"><div class="line"><span class="comment">// Get the object type</span></div>
<div class="line"><a class="code" href="classas_i_script_module.html">asIScriptModule</a> *module = engine-&gt;<a class="code" href="classas_i_script_engine.html#a9f7cdc52b59034e6e55eb8a56b427aa4">GetModule</a>(<span class="stringliteral">&quot;MyModule&quot;</span>);</div>
<div class="line"><a class="code" href="classas_i_type_info.html">asITypeInfo</a> *type = module-&gt;<a class="code" href="classas_i_script_module.html#a3c61bf81a0a88c0017a0e33aecc417ba">GetTypeInfoByDecl</a>(<span class="stringliteral">&quot;MyClass&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Get the factory function from the object type</span></div>
<div class="line"><a class="code" href="classas_i_script_function.html">asIScriptFunction</a> *factory = type-&gt;<a class="code" href="classas_i_type_info.html#aca1e08cd395231d30ad78a7ca3fea142">GetFactoryByDecl</a>(<span class="stringliteral">&quot;MyClass @MyClass()&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Prepare the context to call the factory function</span></div>
<div class="line">ctx-&gt;<a class="code" href="classas_i_script_context.html#a43976f42dfc6c1af23e132d36265173a">Prepare</a>(factory);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Execute the call</span></div>
<div class="line">ctx-&gt;<a class="code" href="classas_i_script_context.html#a8e52894432737acac2e1a422e496bf84">Execute</a>();</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Get the object that was created</span></div>
<div class="line"><a class="code" href="classas_i_script_object.html">asIScriptObject</a> *obj = *(<a class="code" href="classas_i_script_object.html">asIScriptObject</a>**)ctx-&gt;<a class="code" href="classas_i_script_context.html#a889bf11123beba669637849c8e9b2b86">GetAddressOfReturnValue</a>();</div>
<div class="line"> </div>
<div class="line"><span class="comment">// If you&#39;re going to store the object you must increase the reference,</span></div>
<div class="line"><span class="comment">// otherwise it will be destroyed when the context is reused or destroyed.</span></div>
<div class="line">obj-&gt;<a class="code" href="classas_i_script_object.html#a3e08890e31163e4d33c0f27dc9072662">AddRef</a>();</div>
</div><!-- fragment --><p>The factory function is <a class="el" href="doc_call_script_func.html">called as a regular global function</a> and returns a handle to the newly instanciated class.</p>
<h1><a class="anchor" id="doc_use_script_class_2"></a>
Calling a method on the script class</h1>
<p>Calling the methods of the script classes are similar to <a class="el" href="doc_call_script_func.html">calling global functions</a> except that you obtain the function id from the <a class="el" href="classas_i_type_info.html">asITypeInfo</a>, and you must set the object pointer along with the rest of the function arguments.</p>
<div class="fragment"><div class="line"><span class="comment">// Obtain the function object that represents the class method</span></div>
<div class="line"><a class="code" href="classas_i_script_function.html">asIScriptFunction</a> *func = type-&gt;<a class="code" href="classas_i_type_info.html#a80c61bb4d018647561ce3af24fedf65b">GetMethodByDecl</a>(<span class="stringliteral">&quot;void method()&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Prepare the context for calling the method</span></div>
<div class="line">ctx-&gt;<a class="code" href="classas_i_script_context.html#a43976f42dfc6c1af23e132d36265173a">Prepare</a>(func);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Set the object pointer</span></div>
<div class="line">ctx-&gt;<a class="code" href="classas_i_script_context.html#a10d3c152b25d07584999f4d9fe5ce8b1">SetObject</a>(obj);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Execute the call</span></div>
<div class="line">ctx-&gt;<a class="code" href="classas_i_script_context.html#a8e52894432737acac2e1a422e496bf84">Execute</a>();</div>
</div><!-- fragment --><h1><a class="anchor" id="doc_use_script_class_3"></a>
Receiving script classes</h1>
<p>In order for the application to register a function that receives a script class it must first know the type. Of course, since the class is declared in the script it isn't possible to know the type before the script is compiled. Instead the application can register an <a class="el" href="doc_global_interface.html">interface</a> with the engine. The function can then be registered to receive a handle to that interface.</p>
<div class="fragment"><div class="line"><span class="comment">// Register an interface</span></div>
<div class="line">engine-&gt;<a class="code" href="classas_i_script_engine.html#ae2d89b82561b7f9843f35693c664589f">RegisterInterface</a>(<span class="stringliteral">&quot;IMyObj&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// You can also register methods with the interface if you wish to force the script class to implement them</span></div>
<div class="line">engine-&gt;<a class="code" href="classas_i_script_engine.html#a43bd2c12c94a55c22be76d209de93f1a">RegisterInterfaceMethod</a>(<span class="stringliteral">&quot;IMyObj&quot;</span>, <span class="stringliteral">&quot;void RequiredMethod()&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Register a function that receives a handle to the interface</span></div>
<div class="line">engine-&gt;<a class="code" href="classas_i_script_engine.html#a2f84b9b51733f22c68b8448b02c2f1c7">RegisterGlobalFunction</a>(<span class="stringliteral">&quot;void ReceiveMyObj(IMyObj @obj)&quot;</span>, <a class="code" href="angelscript_8h.html#a78f8f2c7f1c88b12e74a5ac47b4184ae">asFUNCTION</a>(ReceiveMyObj), <a class="code" href="angelscript_8h.html#a3ec92ea3c4762e44c2df788ceccdd1e4a68ae43cc91cdfc3fa4590c9e6164e4f4">asCALL_CDECL</a>);</div>
</div><!-- fragment --><p>The function that receives the interface should be implemented to take a pointer to an <a class="el" href="classas_i_script_object.html">asIScriptObject</a>.</p>
<div class="fragment"><div class="line"><a class="code" href="classas_i_script_object.html">asIScriptObject</a> *gObj = 0;</div>
<div class="line"><span class="keywordtype">void</span> ReceiveMyObj(<a class="code" href="classas_i_script_object.html">asIScriptObject</a> *obj)</div>
<div class="line">{</div>
<div class="line"> <span class="comment">// Do something with the object</span></div>
<div class="line"> <span class="keywordflow">if</span>( obj )</div>
<div class="line"> {</div>
<div class="line"> <span class="keywordflow">if</span>( doStore )</div>
<div class="line"> {</div>
<div class="line"> <span class="comment">// If the object is stored, we shouldn&#39;t release the handle</span></div>
<div class="line"> gObj = obj;</div>
<div class="line"> }</div>
<div class="line"> <span class="keywordflow">else</span></div>
<div class="line"> {</div>
<div class="line"> <span class="comment">// If the object is not stored, we must release the handle before returning</span></div>
<div class="line"> obj-&gt;<a class="code" href="classas_i_script_object.html#a4bed3c3ac9f16294985835747aa122d3">Release</a>();</div>
<div class="line"> }</div>
<div class="line"> }</div>
<div class="line">}</div>
</div><!-- fragment --><p>If you don't want to use interfaces like this, then you may want to look into the <a class="el" href="doc_adv_var_type.html">variable argument type</a> or the generic <a class="el" href="doc_addon_handle.html">script handle add-on</a>, which are ways that can be used to receive values and objects of which the type is not known beforehand.</p>
<h1><a class="anchor" id="doc_use_script_class_4"></a>
Returning script classes</h1>
<p>Returning a script class from a registered function involves much of the same as <a class="el" href="doc_use_script_class.html#doc_use_script_class_3">receiving them</a>. In order to register the function either an interface needs to be used, or the generic <a class="el" href="doc_addon_handle.html">script handle add-on</a> can be used.</p>
<div class="fragment"><div class="line"><span class="comment">// The global variable is initialized elsewhere</span></div>
<div class="line"><a class="code" href="classas_i_script_object.html">asIScriptObject</a> *gObj;</div>
<div class="line"> </div>
<div class="line"><a class="code" href="classas_i_script_object.html">asIScriptObject</a> *ReturnMyObj()</div>
<div class="line">{</div>
<div class="line"> <span class="keywordflow">if</span>( gObj == 0 )</div>
<div class="line"> <span class="keywordflow">return</span> 0;</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Increase the refcount to account for the returned handle</span></div>
<div class="line"> gObj-&gt;<a class="code" href="classas_i_script_object.html#a3e08890e31163e4d33c0f27dc9072662">AddRef</a>();</div>
<div class="line"> <span class="keywordflow">return</span> gObj;</div>
<div class="line">}</div>
</div><!-- fragment --><p>This function can be registered as following:</p>
<div class="fragment"><div class="line"><span class="comment">// Register an interface</span></div>
<div class="line">engine-&gt;<a class="code" href="classas_i_script_engine.html#ae2d89b82561b7f9843f35693c664589f">RegisterInterface</a>(<span class="stringliteral">&quot;IMyObj&quot;</span>);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Register a function that returns a handle to the interface</span></div>
<div class="line">engine-&gt;<a class="code" href="classas_i_script_engine.html#a2f84b9b51733f22c68b8448b02c2f1c7">RegisterGlobalFunction</a>(<span class="stringliteral">&quot;IMyObj @ReturnMyObj()&quot;</span>, <a class="code" href="angelscript_8h.html#a78f8f2c7f1c88b12e74a5ac47b4184ae">asFUNCTION</a>(ReturnMyObj), <a class="code" href="angelscript_8h.html#a3ec92ea3c4762e44c2df788ceccdd1e4a68ae43cc91cdfc3fa4590c9e6164e4f4">asCALL_CDECL</a>);</div>
</div><!-- fragment --> </div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<div class="ttc" id="aclassas_i_script_engine_html_ae2d89b82561b7f9843f35693c664589f"><div class="ttname"><a href="classas_i_script_engine.html#ae2d89b82561b7f9843f35693c664589f">asIScriptEngine::RegisterInterface</a></div><div class="ttdeci">virtual int RegisterInterface(const char *name)=0</div><div class="ttdoc">Registers a script interface.</div></div>
<div class="ttc" id="aclassas_i_script_engine_html_a9f7cdc52b59034e6e55eb8a56b427aa4"><div class="ttname"><a href="classas_i_script_engine.html#a9f7cdc52b59034e6e55eb8a56b427aa4">asIScriptEngine::GetModule</a></div><div class="ttdeci">virtual asIScriptModule * GetModule(const char *module, asEGMFlags flag=asGM_ONLY_IF_EXISTS)=0</div><div class="ttdoc">Return an interface pointer to the module.</div></div>
<div class="ttc" id="aclassas_i_script_context_html_a889bf11123beba669637849c8e9b2b86"><div class="ttname"><a href="classas_i_script_context.html#a889bf11123beba669637849c8e9b2b86">asIScriptContext::GetAddressOfReturnValue</a></div><div class="ttdeci">virtual void * GetAddressOfReturnValue()=0</div><div class="ttdoc">Returns the address of the returned value.</div></div>
<div class="ttc" id="aclassas_i_script_context_html_a8e52894432737acac2e1a422e496bf84"><div class="ttname"><a href="classas_i_script_context.html#a8e52894432737acac2e1a422e496bf84">asIScriptContext::Execute</a></div><div class="ttdeci">virtual int Execute()=0</div><div class="ttdoc">Executes the prepared function.</div></div>
<div class="ttc" id="aclassas_i_script_engine_html_a43bd2c12c94a55c22be76d209de93f1a"><div class="ttname"><a href="classas_i_script_engine.html#a43bd2c12c94a55c22be76d209de93f1a">asIScriptEngine::RegisterInterfaceMethod</a></div><div class="ttdeci">virtual int RegisterInterfaceMethod(const char *intf, const char *declaration)=0</div><div class="ttdoc">Registers a script interface method.</div></div>
<div class="ttc" id="aclassas_i_type_info_html_a80c61bb4d018647561ce3af24fedf65b"><div class="ttname"><a href="classas_i_type_info.html#a80c61bb4d018647561ce3af24fedf65b">asITypeInfo::GetMethodByDecl</a></div><div class="ttdeci">virtual asIScriptFunction * GetMethodByDecl(const char *decl, bool getVirtual=true) const =0</div><div class="ttdoc">Returns the method by declaration.</div></div>
<div class="ttc" id="aclassas_i_script_context_html_a10d3c152b25d07584999f4d9fe5ce8b1"><div class="ttname"><a href="classas_i_script_context.html#a10d3c152b25d07584999f4d9fe5ce8b1">asIScriptContext::SetObject</a></div><div class="ttdeci">virtual int SetObject(void *obj)=0</div><div class="ttdoc">Sets the object for a class method call.</div></div>
<div class="ttc" id="aclassas_i_script_engine_html_a2f84b9b51733f22c68b8448b02c2f1c7"><div class="ttname"><a href="classas_i_script_engine.html#a2f84b9b51733f22c68b8448b02c2f1c7">asIScriptEngine::RegisterGlobalFunction</a></div><div class="ttdeci">virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &amp;funcPointer, asDWORD callConv, void *auxiliary=0)=0</div><div class="ttdoc">Registers a global function.</div></div>
<div class="ttc" id="aclassas_i_script_object_html_a3e08890e31163e4d33c0f27dc9072662"><div class="ttname"><a href="classas_i_script_object.html#a3e08890e31163e4d33c0f27dc9072662">asIScriptObject::AddRef</a></div><div class="ttdeci">virtual int AddRef() const =0</div><div class="ttdoc">Increase reference counter.</div></div>
<div class="ttc" id="aangelscript_8h_html_a78f8f2c7f1c88b12e74a5ac47b4184ae"><div class="ttname"><a href="angelscript_8h.html#a78f8f2c7f1c88b12e74a5ac47b4184ae">asFUNCTION</a></div><div class="ttdeci">#define asFUNCTION(f)</div><div class="ttdoc">Returns an asSFuncPtr representing the function specified by the name.</div><div class="ttdef"><b>Definition:</b> angelscript.h:675</div></div>
<div class="ttc" id="aclassas_i_type_info_html_aca1e08cd395231d30ad78a7ca3fea142"><div class="ttname"><a href="classas_i_type_info.html#aca1e08cd395231d30ad78a7ca3fea142">asITypeInfo::GetFactoryByDecl</a></div><div class="ttdeci">virtual asIScriptFunction * GetFactoryByDecl(const char *decl) const =0</div><div class="ttdoc">Returns the factory function by the declaration.</div></div>
<div class="ttc" id="aclassas_i_type_info_html"><div class="ttname"><a href="classas_i_type_info.html">asITypeInfo</a></div><div class="ttdoc">The interface for describing types This interface is used to describe the types in the script engine.</div><div class="ttdef"><b>Definition:</b> angelscript.h:3527</div></div>
<div class="ttc" id="aclassas_i_script_function_html"><div class="ttname"><a href="classas_i_script_function.html">asIScriptFunction</a></div><div class="ttdoc">The interface for a script function description.</div><div class="ttdef"><b>Definition:</b> angelscript.h:3823</div></div>
<div class="ttc" id="aclassas_i_script_module_html"><div class="ttname"><a href="classas_i_script_module.html">asIScriptModule</a></div><div class="ttdoc">The interface to the script modules.</div><div class="ttdef"><b>Definition:</b> angelscript.h:2218</div></div>
<div class="ttc" id="aclassas_i_script_object_html"><div class="ttname"><a href="classas_i_script_object.html">asIScriptObject</a></div><div class="ttdoc">The interface for an instance of a script object.</div><div class="ttdef"><b>Definition:</b> angelscript.h:3413</div></div>
<div class="ttc" id="aangelscript_8h_html_a3ec92ea3c4762e44c2df788ceccdd1e4a68ae43cc91cdfc3fa4590c9e6164e4f4"><div class="ttname"><a href="angelscript_8h.html#a3ec92ea3c4762e44c2df788ceccdd1e4a68ae43cc91cdfc3fa4590c9e6164e4f4">asCALL_CDECL</a></div><div class="ttdeci">@ asCALL_CDECL</div><div class="ttdoc">A cdecl function.</div><div class="ttdef"><b>Definition:</b> angelscript.h:226</div></div>
<div class="ttc" id="aclassas_i_script_context_html_a43976f42dfc6c1af23e132d36265173a"><div class="ttname"><a href="classas_i_script_context.html#a43976f42dfc6c1af23e132d36265173a">asIScriptContext::Prepare</a></div><div class="ttdeci">virtual int Prepare(asIScriptFunction *func)=0</div><div class="ttdoc">Prepares the context for execution of the function.</div></div>
<div class="ttc" id="aclassas_i_script_object_html_a4bed3c3ac9f16294985835747aa122d3"><div class="ttname"><a href="classas_i_script_object.html#a4bed3c3ac9f16294985835747aa122d3">asIScriptObject::Release</a></div><div class="ttdeci">virtual int Release() const =0</div><div class="ttdoc">Decrease reference counter.</div></div>
<div class="ttc" id="aclassas_i_script_module_html_a3c61bf81a0a88c0017a0e33aecc417ba"><div class="ttname"><a href="classas_i_script_module.html#a3c61bf81a0a88c0017a0e33aecc417ba">asIScriptModule::GetTypeInfoByDecl</a></div><div class="ttdeci">virtual asITypeInfo * GetTypeInfoByDecl(const char *decl) const =0</div><div class="ttdoc">Returns a type by declaration.</div></div>
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="footer">Generated on Sat Dec 5 2020 23:20:25 for AngelScript by
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.18 </li>
</ul>
</div>
</body>
</html>