Click here to Skip to main content
15,894,630 members
Articles / Programming Languages / C#

An extensible math expression parser with plug-ins

Rate me:
Please Sign up or sign in to vote.
4.92/5 (147 votes)
13 Mar 2008CPOL51 min read 1.5M   29K   364  
Design and code for an extensible, maintainable, robust, and easy to use math parser.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<title>MTParserLib: MTParserMacroFunc.cpp Source File</title>
<link href="doxygen.css" rel="stylesheet" type="text/css">
</head><body>
<!-- Generated by Doxygen 1.4.4 -->
<div class="qindex"><a class="qindex" href="main.html">Main&nbsp;Page</a> | <a class="qindex" href="namespaces.html">Namespace List</a> | <a class="qindex" href="hierarchy.html">Class&nbsp;Hierarchy</a> | <a class="qindex" href="annotated.html">Class&nbsp;List</a> | <a class="qindex" href="files.html">File&nbsp;List</a> | <a class="qindex" href="namespacemembers.html">Namespace&nbsp;Members</a> | <a class="qindex" href="functions.html">Class&nbsp;Members</a> | <a class="qindex" href="globals.html">File&nbsp;Members</a></div>
<h1>MTParserMacroFunc.cpp</h1><div class="fragment"><pre class="fragment"><a name="l00001"></a>00001 <span class="preprocessor">#include "<a class="code" href="MTParserMacroFunc_8h.html">MTParserMacroFunc.h</a>"</span>
<a name="l00002"></a>00002 <span class="preprocessor">#include "<a class="code" href="MTTools_8h.html">MTTools.h</a>"</span>
<a name="l00003"></a>00003 <span class="preprocessor">#include "<a class="code" href="MTParser_8h.html">MTParser.h</a>"</span>
<a name="l00004"></a>00004 <span class="preprocessor">#include "<a class="code" href="MTParserPrivate_8h.html">MTParserPrivate.h</a>"</span>
<a name="l00005"></a>00005 
<a name="l00006"></a>00006 
<a name="l00007"></a>00007 <span class="comment">//*************************************</span>
<a name="l00008"></a>00008 <span class="comment">// MTMacroFct</span>
<a name="l00009"></a>00009 
<a name="l00010"></a><a class="code" href="classMTMacroFct.html#a8">00010</a> <a class="code" href="classMTMacroFct.html#a8">MTMacroFct::MTMacroFct</a>()
<a name="l00011"></a>00011 {
<a name="l00012"></a>00012         m_pVars = NULL;
<a name="l00013"></a>00013         m_isCreated = <span class="keyword">false</span>;
<a name="l00014"></a>00014         m_pParser = NULL;
<a name="l00015"></a>00015         
<a name="l00016"></a>00016 
<a name="l00017"></a>00017 }
<a name="l00018"></a><a class="code" href="classMTMacroFct.html#a10">00018</a> <a class="code" href="classMTMacroFct.html#a10">MTMacroFct::~MTMacroFct</a>()
<a name="l00019"></a>00019 {
<a name="l00020"></a>00020         clean();
<a name="l00021"></a>00021 }
<a name="l00022"></a>00022 
<a name="l00023"></a>00023 
<a name="l00024"></a>00024 <span class="keywordtype">void</span> MTMacroFct::clean()
<a name="l00025"></a>00025 {
<a name="l00026"></a>00026         <span class="keywordflow">if</span>( m_pVars != NULL )
<a name="l00027"></a>00027         {
<a name="l00028"></a>00028                 <span class="keyword">delete</span> []m_pVars;                       
<a name="l00029"></a>00029                 m_pVars = NULL;
<a name="l00030"></a>00030         }
<a name="l00031"></a>00031 
<a name="l00032"></a>00032         <span class="keywordflow">if</span>( m_pParser != NULL )
<a name="l00033"></a>00033         {
<a name="l00034"></a>00034                 <span class="keyword">delete</span> m_pParser;
<a name="l00035"></a>00035                 m_pParser = NULL;
<a name="l00036"></a>00036         }
<a name="l00037"></a>00037 }
<a name="l00038"></a>00038 
<a name="l00039"></a>00039 <span class="keywordtype">void</span> MTMacroFct::parsePrototype(<span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> &amp;prototype, <span class="keyword">const</span> <a class="code" href="structMTSyntax.html">MTSyntax</a> &amp;syntax, <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> &amp;name, std::vector&lt;MTSTRING&gt; &amp;args) <span class="keywordflow">throw</span>(<a class="code" href="classMTChainedExceptions.html">MTParserException</a>)
<a name="l00040"></a>00040 {
<a name="l00041"></a>00041         <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> pos;       
<a name="l00042"></a>00042         <span class="keywordtype">bool</span> found;
<a name="l00043"></a>00043         std::vector&lt;unsigned int&gt; originalPos;
<a name="l00044"></a>00044 
<a name="l00045"></a>00045         <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> prototype2;
<a name="l00046"></a>00046         <a class="code" href="namespaceMTTools.html#a4">MTTools::removeSpaces</a>(prototype, originalPos, prototype2);
<a name="l00047"></a>00047         <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> length = prototype2.size();
<a name="l00048"></a>00048 
<a name="l00049"></a>00049         <span class="comment">// first extract the macro name...</span>
<a name="l00050"></a>00050         found = <a class="code" href="namespaceMTTools.html#a5">MTTools::findCharPos</a>(prototype2, 0, <a class="code" href="MTParserPublic_8h.html#a3">MTOPEN_BRACKET</a>, pos);
<a name="l00051"></a>00051                 
<a name="l00052"></a>00052         <span class="keywordflow">if</span>( length == 0 || !found || prototype2[length-1] != <a class="code" href="MTParserPublic_8h.html#a4">MTCLOSE_BRACKET</a>)
<a name="l00053"></a>00053         {
<a name="l00054"></a>00054                 <span class="comment">// missing open or close bracket ... bad prototype syntax               </span>
<a name="l00055"></a>00055                 <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> p = 0;                             
<a name="l00056"></a>00056                 <span class="keywordflow">if</span>( length &gt; 0 )
<a name="l00057"></a>00057                 {
<a name="l00058"></a>00058                         p = originalPos[length-1];
<a name="l00059"></a>00059                 }               
<a name="l00060"></a>00060                 
<a name="l00061"></a>00061                 <a class="code" href="MTParserPrivate_8h.html#a0">MTTHROW</a>(<a class="code" href="classMTExcepData.html">MTExcepData</a> (   <a class="code" href="MTParserException_8h.html#a30">MTDEFEXCEP_MacroProtoSyntax</a>,
<a name="l00062"></a>00062                                                                 <a class="code" href="MTParserException_8h.html#a52">MTEXCEPARG_POS</a>, <a class="code" href="namespaceMTTools.html#a14">MTTools::longToS</a>(p).c_str(),
<a name="l00063"></a>00063                                                                 <a class="code" href="MTParserException_8h.html#a53">MTEXCEPARG_ITEMNAME</a>, prototype.c_str() ));
<a name="l00064"></a>00064         }
<a name="l00065"></a>00065         
<a name="l00066"></a>00066         name = prototype2.substr(0, pos);
<a name="l00067"></a>00067 
<a name="l00068"></a>00068         <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> t = pos+1;
<a name="l00069"></a>00069         <a class="code" href="MTParserPublic_8h.html#a8">MTDOUBLE</a> varVal;                <span class="comment">// dummy var to allow validation</span>
<a name="l00070"></a>00070         <a class="code" href="classMTParser.html">MTParser</a> parser;
<a name="l00071"></a>00071 
<a name="l00072"></a>00072         <span class="keywordflow">do</span>
<a name="l00073"></a>00073         {
<a name="l00074"></a>00074                 found = <a class="code" href="namespaceMTTools.html#a5">MTTools::findCharPos</a>(prototype2, t, syntax.argumentSeparator, pos);
<a name="l00075"></a>00075 
<a name="l00076"></a>00076                 <span class="comment">// if this is the last argument, then we must look for the closing bracket</span>
<a name="l00077"></a>00077                 <span class="keywordflow">if</span>( !found )
<a name="l00078"></a>00078                 {
<a name="l00079"></a>00079                         found = <a class="code" href="namespaceMTTools.html#a5">MTTools::findCharPos</a>(prototype2, t, <a class="code" href="MTParserPublic_8h.html#a4">MTCLOSE_BRACKET</a>, pos);
<a name="l00080"></a>00080                 }
<a name="l00081"></a>00081 
<a name="l00082"></a>00082                 <span class="keywordflow">if</span>( found )
<a name="l00083"></a>00083                 {
<a name="l00084"></a>00084                         <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> var = prototype2.substr(t, pos-t);
<a name="l00085"></a>00085 
<a name="l00086"></a>00086                         <span class="keywordflow">if</span>( var.size() == 0 )
<a name="l00087"></a>00087                         {
<a name="l00088"></a>00088                                 <span class="comment">// void can only mean that the macro has no argument...</span>
<a name="l00089"></a>00089                                 <span class="keywordflow">if</span>( prototype2[length-2] != <a class="code" href="MTParserPublic_8h.html#a3">MTOPEN_BRACKET</a> ||
<a name="l00090"></a>00090                                         prototype2[length-1] != <a class="code" href="MTParserPublic_8h.html#a4">MTCLOSE_BRACKET</a> )
<a name="l00091"></a>00091                                 {                                       
<a name="l00092"></a>00092                                         
<a name="l00093"></a>00093                                         <a class="code" href="MTParserPrivate_8h.html#a0">MTTHROW</a>(<a class="code" href="classMTExcepData.html">MTExcepData</a> (   MTDEFEXCEP_MacroProtoSyntax,
<a name="l00094"></a>00094                                                                                         <a class="code" href="MTParserException_8h.html#a52">MTEXCEPARG_POS</a>, <a class="code" href="namespaceMTTools.html#a14">MTTools::longToS</a>(originalPos[t]).c_str(),
<a name="l00095"></a>00095                                                                                         <a class="code" href="MTParserException_8h.html#a53">MTEXCEPARG_ITEMNAME</a>, prototype.c_str()))
<a name="l00096"></a>00096                                 }
<a name="l00097"></a>00097                         }
<a name="l00098"></a>00098                         <span class="keywordflow">else</span>
<a name="l00099"></a>00099                         {                       
<a name="l00100"></a>00100                                 <span class="comment">// validate the variable name...</span>
<a name="l00101"></a>00101                                 <span class="keywordflow">try</span>
<a name="l00102"></a>00102                                 {
<a name="l00103"></a>00103                                         parser.<a class="code" href="classMTParser.html#z4_0">defineVar</a>(var.c_str(), &amp;varVal);
<a name="l00104"></a>00104                                 }
<a name="l00105"></a>00105                                 <span class="keywordflow">catch</span>( <a class="code" href="classMTChainedExceptions.html">MTParserException</a> &amp;e )
<a name="l00106"></a>00106                                 {                                       
<a name="l00107"></a>00107 
<a name="l00108"></a>00108                                         <a class="code" href="MTParserPrivate_8h.html#a1">MTRETHROW</a>(<a class="code" href="classMTExcepData.html">MTExcepData</a> ( MTDEFEXCEP_MacroProtoSyntax,
<a name="l00109"></a>00109                                                                                         <a class="code" href="MTParserException_8h.html#a52">MTEXCEPARG_POS</a>, <a class="code" href="namespaceMTTools.html#a14">MTTools::longToS</a>(originalPos[t]).c_str(),
<a name="l00110"></a>00110                                                                                         <a class="code" href="MTParserException_8h.html#a53">MTEXCEPARG_ITEMNAME</a>, prototype.c_str()), e)
<a name="l00111"></a>00111                                 }
<a name="l00112"></a>00112                                 args.push_back(var);
<a name="l00113"></a>00113                         }
<a name="l00114"></a>00114                 }
<a name="l00115"></a>00115 
<a name="l00116"></a>00116                 t = pos+1;
<a name="l00117"></a>00117         }
<a name="l00118"></a>00118         <span class="keywordflow">while</span>(found &amp;&amp; t &lt; length );
<a name="l00119"></a>00119         
<a name="l00120"></a>00120 }
<a name="l00121"></a>00121 
<a name="l00122"></a><a class="code" href="classMTMacroFct.html#a0">00122</a> <span class="keywordtype">void</span> <a class="code" href="classMTMacroFct.html#a0">MTMacroFct::create</a>(<span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> &amp;prototype, <span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> &amp;macro, <span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> &amp;description, <a class="code" href="classMTCompilerI.html">MTCompilerI</a> *pCompiler, <a class="code" href="classMTRegistrarI.html">MTRegistrarI</a> *pRegistrar ) <span class="keywordflow">throw</span>(<a class="code" href="classMTChainedExceptions.html">MTParserException</a>)
<a name="l00123"></a>00123 {
<a name="l00124"></a>00124         clean();
<a name="l00125"></a>00125 
<a name="l00126"></a>00126         m_pParser = <span class="keyword">new</span> <a class="code" href="classMTParser.html">MTParser</a>(pCompiler, pRegistrar);
<a name="l00127"></a>00127 
<a name="l00128"></a>00128         <span class="comment">// make sure not to use the auto var def feature...</span>
<a name="l00129"></a>00129         <span class="comment">// all variables must be declared in the prototype</span>
<a name="l00130"></a>00130         m_pParser-&gt;enableAutoVarDefinition(<span class="keyword">false</span>);
<a name="l00131"></a>00131 
<a name="l00132"></a>00132         <a class="code" href="MTUnicodeANSIDefs_8h.html#a0">MTSTRING</a> symbol;
<a name="l00133"></a>00133         std::vector&lt;MTSTRING&gt; args;
<a name="l00134"></a>00134         parsePrototype(prototype, pRegistrar-&gt;getSyntax(), symbol, args);
<a name="l00135"></a>00135         m_prototype = prototype;
<a name="l00136"></a>00136         
<a name="l00137"></a>00137 
<a name="l00138"></a>00138         m_pParser-&gt;undefineAllVars();           
<a name="l00139"></a>00139 
<a name="l00140"></a>00140         m_nbArgs = args.size();
<a name="l00141"></a>00141         <span class="keywordflow">if</span>( m_nbArgs &gt; 0 )
<a name="l00142"></a>00142         {
<a name="l00143"></a>00143                 m_pVars = <span class="keyword">new</span> <a class="code" href="MTParserPublic_8h.html#a8">MTDOUBLE</a>[m_nbArgs];
<a name="l00144"></a>00144         }
<a name="l00145"></a>00145         <span class="keywordflow">else</span>
<a name="l00146"></a>00146         {
<a name="l00147"></a>00147                 m_pVars = NULL;
<a name="l00148"></a>00148         }
<a name="l00149"></a>00149         m_helpString = symbol;
<a name="l00150"></a>00150         m_helpString += <a class="code" href="MTParserPublic_8h.html#a3">MTOPEN_BRACKET</a>;
<a name="l00151"></a>00151 
<a name="l00152"></a>00152         <span class="comment">// define the arguments in the parser object...</span>
<a name="l00153"></a>00153         <a class="code" href="structMTSyntax.html">MTSyntax</a> syntax = m_pParser-&gt;getSyntax();
<a name="l00154"></a>00154         <span class="keywordflow">for</span>( <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> t=0; t&lt;args.size(); t++ )
<a name="l00155"></a>00155         {
<a name="l00156"></a>00156                 m_pVars[t] = 0;         <span class="comment">// default value</span>
<a name="l00157"></a>00157                 m_pParser-&gt;defineVar(args[t].c_str(), &amp;m_pVars[t]);
<a name="l00158"></a>00158                 m_helpString += args[t];
<a name="l00159"></a>00159 
<a name="l00160"></a>00160                 <span class="keywordflow">if</span>( t != m_nbArgs-1 )
<a name="l00161"></a>00161                 {
<a name="l00162"></a>00162                         m_helpString += syntax.<a class="code" href="structMTSyntax.html#o1">argumentSeparator</a>;
<a name="l00163"></a>00163                         m_helpString += _T(<span class="stringliteral">" "</span>);                        
<a name="l00164"></a>00164                 }
<a name="l00165"></a>00165         }
<a name="l00166"></a>00166 
<a name="l00167"></a>00167         m_helpString += <a class="code" href="MTParserPublic_8h.html#a4">MTCLOSE_BRACKET</a>;
<a name="l00168"></a>00168         
<a name="l00169"></a>00169         m_pParser-&gt;compile(macro.c_str());      
<a name="l00170"></a>00170 
<a name="l00171"></a>00171         m_macro = macro;
<a name="l00172"></a>00172         m_description = description;
<a name="l00173"></a>00173         m_symbol = symbol;      
<a name="l00174"></a>00174 
<a name="l00175"></a>00175         m_isCreated = <span class="keyword">true</span>;
<a name="l00176"></a>00176 }
<a name="l00177"></a>00177 
<a name="l00178"></a><a class="code" href="classMTMacroFct.html#a1">00178</a> <span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a1">MTCHAR</a>* <a class="code" href="classMTMacroFct.html#a1">MTMacroFct::getSymbol</a>()
<a name="l00179"></a>00179 {
<a name="l00180"></a>00180         <span class="keywordflow">return</span> m_symbol.c_str();
<a name="l00181"></a>00181 }
<a name="l00182"></a><a class="code" href="classMTMacroFct.html#a2">00182</a> <span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a1">MTCHAR</a>* <a class="code" href="classMTMacroFct.html#a2">MTMacroFct::getHelpString</a>()
<a name="l00183"></a>00183 {
<a name="l00184"></a>00184         <span class="keywordflow">return</span> m_helpString.c_str();
<a name="l00185"></a>00185 
<a name="l00186"></a>00186 }
<a name="l00187"></a><a class="code" href="classMTMacroFct.html#a3">00187</a> <span class="keyword">const</span> <a class="code" href="MTUnicodeANSIDefs_8h.html#a1">MTCHAR</a>* <a class="code" href="classMTMacroFct.html#a3">MTMacroFct::getDescription</a>()
<a name="l00188"></a>00188 {
<a name="l00189"></a>00189         <span class="keywordflow">return</span> m_description.c_str();
<a name="l00190"></a>00190 
<a name="l00191"></a>00191 }
<a name="l00192"></a><a class="code" href="classMTMacroFct.html#a4">00192</a> <span class="keywordtype">int</span> <a class="code" href="classMTMacroFct.html#a4">MTMacroFct::getNbArgs</a>()
<a name="l00193"></a>00193 {
<a name="l00194"></a>00194         <span class="keywordflow">return</span> m_nbArgs;
<a name="l00195"></a>00195 
<a name="l00196"></a>00196 }
<a name="l00197"></a><a class="code" href="classMTMacroFct.html#a6">00197</a> <a class="code" href="MTParserPublic_8h.html#a8">MTDOUBLE</a> <a class="code" href="classMTMacroFct.html#a6">MTMacroFct::evaluate</a>(<span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> nbArgs, <span class="keyword">const</span> <a class="code" href="MTParserPublic_8h.html#a8">MTDOUBLE</a> *pArg)
<a name="l00198"></a>00198 {       
<a name="l00199"></a>00199         <span class="keywordflow">for</span>( <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> t=0; t&lt;m_nbArgs; t++ )
<a name="l00200"></a>00200         {
<a name="l00201"></a>00201                 m_pVars[t] = pArg[t];           
<a name="l00202"></a>00202         }
<a name="l00203"></a>00203 
<a name="l00204"></a>00204         <span class="keywordflow">return</span> m_pParser-&gt;<a class="code" href="classMTParser.html#z3_0">evaluate</a>();
<a name="l00205"></a>00205 }
<a name="l00206"></a>00206 
<a name="l00207"></a><a class="code" href="classMTMacroFct.html#a9">00207</a> <a class="code" href="classMTMacroFct.html#a8">MTMacroFct::MTMacroFct</a>(<span class="keyword">const</span> <a class="code" href="classMTMacroFct.html">MTMacroFct</a> &amp;obj)
<a name="l00208"></a>00208 {
<a name="l00209"></a>00209         <span class="comment">// Called when spawning.  If the macro is not used in the</span>
<a name="l00210"></a>00210         <span class="comment">// expression, then there is no need to compile the macro.</span>
<a name="l00211"></a>00211         <span class="comment">// So do as little initialization as possible in order to</span>
<a name="l00212"></a>00212         <span class="comment">// speed up parser duplication.         </span>
<a name="l00213"></a>00213         
<a name="l00214"></a>00214         m_prototype = obj.<a class="code" href="classMTMacroFct.html#r5">m_prototype</a>;
<a name="l00215"></a>00215         m_description = obj.<a class="code" href="classMTMacroFct.html#r1">m_description</a>;      
<a name="l00216"></a>00216         m_macro = obj.<a class="code" href="classMTMacroFct.html#r4">m_macro</a>;
<a name="l00217"></a>00217         m_symbol = obj.<a class="code" href="classMTMacroFct.html#r3">m_symbol</a>;        
<a name="l00218"></a>00218         m_nbArgs = obj.<a class="code" href="classMTMacroFct.html#r6">m_nbArgs</a>;                <span class="comment">// although the macro is not created, we must be able</span>
<a name="l00219"></a>00219                                                                         <span class="comment">// to tell how many arguments it has</span>
<a name="l00220"></a>00220 
<a name="l00221"></a>00221         m_pParser = NULL;
<a name="l00222"></a>00222         m_pVars = NULL;
<a name="l00223"></a>00223         m_isCreated = <span class="keyword">false</span>;    
<a name="l00224"></a>00224 }
<a name="l00225"></a><a class="code" href="classMTMacroFct.html#a5">00225</a> <span class="keywordtype">void</span> <a class="code" href="classMTMacroFct.html#a5">MTMacroFct::doLateInitialization</a>(<a class="code" href="classMTCompilerI.html">MTCompilerI</a> *pCompiler, <a class="code" href="classMTRegistrarI.html">MTRegistrarI</a> *pRegistrar) <span class="keywordflow">throw</span>(<a class="code" href="classMTChainedExceptions.html">MTParserException</a>)
<a name="l00226"></a>00226 {
<a name="l00227"></a>00227         <span class="comment">// If the macro hasn't been used yet, then compile the function...      </span>
<a name="l00228"></a>00228         <span class="keywordflow">if</span>( !m_isCreated )
<a name="l00229"></a>00229         {
<a name="l00230"></a>00230                 create(m_prototype, m_macro, m_description, pCompiler, pRegistrar);
<a name="l00231"></a>00231         }
<a name="l00232"></a>00232 }
<a name="l00233"></a>00233 
<a name="l00234"></a>00234 
<a name="l00235"></a><a class="code" href="classMTMacroFct.html#a7">00235</a> <a class="code" href="classMTFunctionI.html">MTFunctionI</a>* <a class="code" href="classMTMacroFct.html#a7">MTMacroFct::spawn</a>()
<a name="l00236"></a>00236 {
<a name="l00237"></a>00237         <a class="code" href="classMTMacroFct.html">MTMacroFct</a> *pFct = <span class="keyword">new</span> <a class="code" href="classMTMacroFct.html#a8">MTMacroFct</a>(*<span class="keyword">this</span>);       
<a name="l00238"></a>00238         <span class="keywordflow">return</span> pFct;
<a name="l00239"></a>00239 }
<a name="l00240"></a>00240         
<a name="l00241"></a>00241 
</pre></div><hr size="1"><address style="align: right;"><small>Generated on Sun Mar 9 17:39:36 2008 for MTParserLib by&nbsp;
<a href="http://www.doxygen.org/index.html">
<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.4.4 </small></address>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
Software Engineer working at a fun and smart startup company

Comments and Discussions