|
From: Slava M. <Sla...@ro...> - 2008-08-11 18:33:26
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>XLL Containers</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body bgcolor="#ffffff" text="#000000"> <h1>XLL Containers</h1> <p><a href="#Introduction">Introduction</a><br> <a href="#common_requirements">Common Requirements</a><br> <A href="#BestPractices">Best Practices</A><br> <a href="#Examples">Examples</a><br> </p> <h2><a name="Introduction">Introduction</a></h2> <p>Implementation of Excel add-in functional interface often requires dealing with <strong>XLOPER</strong> structure created by Excel and a pointer to which passed to a user defined function. In addition, sometimes a user defined function should return a pointer to <strong>XLOPER</strong> back to Excel as a return value. <strong>XLOPER</strong> structure is actually a <em>variant</em> type, which can contain data of several different types, but the vast majority of all applicable treatments of <strong>XLOPER</strong> structure falls in the following three categories: <ul> <li>a single <em>cell</em>,</li> <li>a one-dimensional range, or a <em>vector</em> of elements</li> <li>a two-dimensional range, or a <em>matrix</em> of elements</li> </ul> The scope of the solution under consideration is limited to those three cases.</p> <p>To extract useful information from <strong>XLOPER</strong> structure application programmers usually implement various <em>converters</em> to transform <strong>XLOPER</strong> structure to and from base types: <pre>int, double</pre> or STL types: <pre>std::string, std::vector</pre>Such an approach although straightforward and easily comprehensible sometimes entails an undesirable overhead in terms of memory management and computational efficiency.<br/> In addition, <em>converters</em> usually support only limited set of input/output types making such a solution non-generic.<br/> Moreover, it is sometimes not quite easy to decide when memory allocated for <strong>XLOPER</strong> structure can be safely released, which often leads to memory leaks.</p> <p>The solution proposed here takes a different approach. It is based on classes-adaptors that wraps around <strong>XLOPER</strong> and allow to treat it as a single value of a specific base type, or as a container complying with requirements of STL.</p> <p>Thus, each class in the proposed solution can be viewed as a <em>dual</em> entity: for Excel such a class is seen as an <strong>XLOPER</strong> structure, but for application functions it can be treated as a C++ basis or container type. Such a <em>dualism</em> contributes a great deal to efficient memory management, code brevity and exception safety.</p> <p>Check out the <a href="#Examples">examples</a> of use of XLL containers below.</p> <p>The XLL containers template library provides six container class templates:</p> <div align="left"> <table border="1" cellpadding="4" cellspacing="0"> <tr> <td><a href="./XllCell.htm"><b>xll::Cell</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A simple wrapper around <strong>XLOPER</strong>. Non-constructable and non-copyable.</td> </tr> <tr> <td><a href="./XllCellAlloc.htm"><b>xll::CellAlloc</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A subclass of <b>xll::Cell</b> that allow for construction by a user. Copy-constructable.</td> </tr> <tr> <td><a href="./XllMatrix.htm"><b>xll::Matrix</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A 2-d Excel range that can be treated as a matrix. Non-constructable and non-copyable.</td> </tr> <tr> <td><a href="./XllMatrixAlloc.htm"><b>xll::MatrixAlloc</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A subclass of <b>xll::Matrix</b> that allow for construction by a user. Copy-constructable.</td> </tr> <tr> <td><a href="./XllVector.htm"><b>xll::Vector</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A 1-d Excel range that can be treated as a standard vector. Non-constructable and non-copyable.</td> </tr> <tr> <td><a href="./XllVectorAlloc.html"><b>xll::VectorAlloc</b></a></td> <td><a href="../Include/XllArrays.hpp"><XllArrays.hpp></a></td> <td>A subclass of <b>xll::Vector</b> that allow for construction by a user. Copy-constructable.</td> </tr> </table> </div> <p>In addition, for each of <b>xll::Cell, xll::Matrix, xll::Vector</b> container classes the library provides implementation of five different pointer class concepts, which implement concepts of <a href="http://www.boost.org/libs/smart_ptr/scoped_ptr.htm">boost::scoped_pointer</a>, <a href="http://www.boost.org/libs/smart_ptr/shared_ptr.htm">boost::shared_pointer</a>, and <a href="http://www.boost.org/libs/smart_ptr/intrusive_ptr.html">boost::intrusive_pointer</a>:</p> <div align="left"> <table border="1" cellpadding="4" cellspacing="0"> <tr> <td><b>const-pointer</b></td> <td>Implements a const-pointer concept. Read-only, no memory management is assumed.</td> </tr> <tr> <td><b>weak-pointer</b></td> <td>A pointer, which is not responsible for releasing allocated memory within definition scope.<br/> Memory release happens later, either in a user provided <code>xlAutoFree</code> function or Excel itself takes care of the memory.</td> </tr> <tr> <td><b>scoped-pointer</b></td> <td>A pointer for an object, which is supposed to be created and used only within a current scope.<br/> Implements a <b>boost::scoped_pointer</b> concept.</td> </tr> <tr> <td><b>shared-pointer</b></td> <td>A pointer to a shared object to be used beyond the current scope, e.g. in <a href="http://www.objecthandler.org/">ObjectHandler</a> framework.<br/> Implements a <b>boost::shared_pointer</b> concept.</td> </tr> <tr> <td><b>temp-pointer</b></td> <td>A pointer to a temporal object allocated in a static memory of XLL framework.</td> </tr> </table> </div> <p>When implementing an Excel add-in functional interface the following five essentially different situation usually should be addressed:</p> <ol> <li>Excel passes a pointer to <strong>XLOPER</strong> to a user defined function that should be treated as an input, read-only data. A <b>const-pointer</b> type is suitable in this case.</li> <li>A user defined function must construct an <strong>XLOPER</strong> structure and pass it as a result to Excel. In this case a <b>weak-pointer</b> is a perfect choice.</li> <li>A local <strong>XLOPER</strong> object should be created to pass it to <code>Excel4v</code> function as a parameter-result (e.g. <code>xlfGetName</code>). A <b>scoped-pointer</b> is a convenient choice in this case.</li> <li>An Excel object should be constructed and stored in some external container (e.g. <a href="http://www.objecthandler.org/">ObjectHandler</a>) for future use. A <b>shared-pointer</b> is the only solution in this case.</li> <li>A temporal <strong>XLOPER</strong> should be created to pass it as an input parameter to <code>Excel4v</code> function. In this case a <b>temp-pointer</b> can be used. </ol> <p>The table below summarizes the references to all XLL Containers library classes and binds them to the use cases listed above (see also <a href="#Examples">examples</a> below):</p> <div align="left"> <table border="1" cellpadding="4" cellspacing="0"> <tr> <td><b>namespace xll::</b></td> <td><b>const-pointer</b><br/>use case 1</td> <td><b>weak-pointer</b><br/>use case 2</td> <td><b>scoped-pointer</b><br/>use case 3</td> <td><b>shared-pointer</b><br/>use case 4</td> <td><b>temp-pointer</b><br/>use case 5</td> </tr> <tr> <td><a href="./XllCell.htm"><b>Cell</b></a></td> <td><a href="./XllCellPtr.htm#const"><b>CellPtrConst</b></a></td> <td><a href="./CellPtrWeak.htm"><b>CellPtrWeak</b></a></td> <td><a href="./CellPtrScoped.htm"><b>CellPtrScoped</b></a></td> <td><a href="./CellPtrShared.htm"><b>CellPtrShared</b></a></td> <td><a href="./CellPtrTemp.htm"><b>CellPtrTemp</b></a></td> </tr> <tr> <td><a href="./XllMatrix.htm"><b>Matrix</b></a></td> <td><a href="./MatrixPtrConst.htm"><b>MatrixPtrConst</b></a></td> <td><a href="./MatrixPtrWeak.htm"><b>MatrixPtrWeak</b></a></td> <td><a href="./MatrixPtrScoped.htm"><b>MatrixPtrScoped</b></a></td> <td><a href="./MatrixPtrShared.htm"><b>MatrixPtrShared</b></a></td> <td><a href="./MatrixPtrTemp.htm"><b>MatrixPtrTemp</b></a></td> </tr> <tr> <td><a href="./XllVector.htm"><b>Vector</b></a></td> <td><a href="./VectorPtrConst.htm"><b>VectorPtrConst</b></a></td> <td><a href="./VectorPtrWeak.htm"><b>VectorPtrWeak</b></a></td> <td><a href="./VectorPtrScoped.htm"><b>VectorPtrScoped</b></a></td> <td><a href="./VectorPtrShared.htm"><b>VectorPtrShared</b></a></td> <td><a href="./VectorPtrTemp.htm"><b>VectorPtrTemp</b></a></td> </tr> </table> </div> <h2><a name="common_requirements">Common Requirements</a></h2> <p>All XLL Containers library class templates have a template parameter, <b>T</b>, which specifies the type of the object stored in a container. The following types can be specified: <ul> <li>integer types: <code>int, long, short, bool</code>; <li>floating point types: <code>float, double</code>; <li><code>std::string</code>; <li><a href="http://boost.org/doc/html/any.html"><b>boost::any</b></a> (which makes it an abstract container); <li>an error type cell: <code>xll::ErrorCode</code>; <li>a special case int type: <code>xll::Int</code> (to handle <code>xltypeInt</code> XLL type). </ul> </p> <h2><a name="BestPractices">Best Practices</a></h2> <p>Although it is possible to create an object of type <b>CellAlloc</b>, <b>MatrixAlloc</b>, or <b>VectorAlloc</b>, it is recommended to use pointer classes instead.<br/> The reason for this is that unlike object-containers, pointer classes explicitly specify the intention and reason for creating an object: <b>const-pointer</b> means read only data, <b>weak-pointer</b> indicates that the object created will be passed to Excel, <b>scoped-pointer</b> indicates local "in-scope" use of object, and <b>shared-pointer</b> means creation of a global, reusable object.<br/> Construction of object-containers is not safe in this respect since it is easy to make a mistake choosing an inappropriate allocation method.</p> <p>It is also not advisable using <b>static</b> objects since use of pointer classes discussed here makes it senseless.</p> <p>An access to the features of underlying class-containers can be done via usual pointer dereferencing technique as demonstrated in <a href="#Examples">examples</a> below.</p> <h2><a name="Examples">Examples</a></h2> <p>The following example demonstrates the use of a <b>const-pointer</b>.</p> <pre class="programlisting"> #include <XLLArrays.hpp> // In the function below it is assumed that parameter x represents a vector of doubles double Average (const XLOPER *x) { try { <a href="">xll::VectorPtrConst</a> <double> px (x); double res = 0; for (size_t i = 0; i < px->size(); ++i) res += px->at(i); // or res += (*px)[i]; return res / px->size(); } catch (const std::bad_cast &) { cerr << "Parameter x either not an array or its elements cannot be converted to double" << endl; } return 0; } </pre> <p><b><em>Notes.</em></b> <ol> <li><b>VectorPtrConst</b> class checks the type of input parameter x. If it cannot be converted to array of doubles the constructor throws a <b>bad_cast</b> exception.</li> <li>The code above works fine even if a user passes a single numeric cell to this function. <b>VectorPtrConst</b> will treat it as an array of size 1 automatically in this case. </ol></p> <p>The following example demonstrates the use of a <b>weak-pointer</b>.</p> <pre class="programlisting"> #include <XLLArrays.hpp> #include <algorithm> // This function generates an array of random numbers and returns it to Excel XLOPER * GenRandomVector (int how_many, int in_row) { xll::VectorPtrWeakExcel <int> px (how_many, in_row != 0); std::generate (px->begin(), px->end(), rand); return px; } </pre> <p><b><em>Note.</em></b> Memory allocated in the function above will be released by Excel. If instead of <b>VectorPtrWeakExcel</b> one used <b>VectorPtrWeak</b> class, the <code>xlAutoFree</code> function would have to be provided to release memory.</p> <p>The next example demonstrates the use of a <b>scoped-pointer</b> and a <b>temp-pointer</b>.<br/> <pre class="programlisting"> #include <XLLArrays.hpp> // A nifty implementation of xlAutoOpen interface function int xlAutoOpen() { using namespace xll; try { CellPtrScopedExcel <std::string> xDll; Excel(xlGetName, xDll, 0); Excel(xlfRegister, 0, 10, xDll, TempStr ("GenRandomVector"), //Function code name. TempStr ("PJJ"), //Parameter codes. TempStr ("GenRandomVector"), //Function display name. TempStr (""), TempStr ("1"), //Function type. TempStr ("My functions"), //Function category. TempStr (""), //shortcut text (command macros only). TempStr (""), //path to help file. TempStr ("Returns a vector of random numbers ") ); return 1; } catch (const std::exception &ex) { Excel(xlcAlert, 0, 1, TempStr(ex.what())); return 0; } catch (...) { Excel(xlcAlert, 0, 1, TempStr("Unknown exception")); return 0; } } </pre> <p>Note use of <b>CellPtrScopedExcel</b> class in the function above. We do not have to worry about the call of <code>xlFree</code> function to release memory allocated by Excel in <code>xlGetName</code> since implementation of <b>CellPtrScopedExcel</b> class takes care about it. If, however, we use <b>CellPtrScoped</b> class instead (and it is admittedly very easy to confuse) the code above most likely end up in a crash when <b>CellPtrScoped</b> destructor will try to release memory allocated by Excel in <code>xlGetName</code> call.</p> <p>The following example demonstrates the use of a <b>shared-pointer</b>.</p> <pre class="programlisting"> #include <XLLArrays.hpp> #include <boost/shared_ptr.hpp> #include <algorithm> // Calculates the sum of two arrays and returns the result to the outside world. boost::shared_ptr<XLOPER> SumArrays(const XLOPER *x, const XLOPER *y) { using namespace xll; VectorPtrConst <double> px(x); VectorPtrConst <double> py(y); VectorPtrShared <double> pz(px->size()); std::transform (px->begin(), px->end(), py->begin(), pz->begin(), std::plus); return boost::static_pointer_cast <XLOPER> (pz); } </pre> <p>Note that a shared pointer returned by the function above can be stored in <a href="http://www.objecthandler.org/">ObjectHandler</a>. Memory allocated by <b>VectorPtrShared</b> class is released in its destructor, which is called when reference counter for the instances of underlying object becomes zero.</p> <h2><a name="History">History</a> and Acknowledgements</h2> <p>April 2007. Original version.</p> <h2><a name="References">References</a></h2> <p>[<a name="XLOPER">XLOPER</a>] MSDN, <a href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/office97/html/SF7EF.asp"> The XLOPER Data Type</a></p> <p>Copyright 2007 ---</p> </body> </html> |