Demonstrates how to write a matrix adapter class, to integrate a matrix class into XLL+
This add-in demonstrates how to integrate matrix classes into XLL+.
The example uses the boost::multi_array
class as its matrix container.
A simple adapter class, mtx_multi_array_adapter<T>
,
is created, which provides a bridge between the ple::imtx<T>
interface, which is used by the XLL+ data validation functions,
and the boost::multi_array<T, 2>
template class.
See the boost web-site at http://www.boost.org/libs/multi_array/doc/index.html
for more information about the multi_array
class.
Visit http://www.boost.org to download a copy of the latest version of the boost libraries.
The sample displays the following features:
boost::multi_array<double, 2>
.
All the code for this is generated by the XLL+ Function Wizard.
In the example function, MyMatrixAdd, the two matrix arguments were amended in the XLL+ Function Wizard, as shown below:
The container class for the first argument was typed into the Container field. For the second argument, it appears in the drop-down, which saves on typing and reduces errors.
As a result of the change to the arguments' container class, slightly different code was generated by the XLL+ Function Wizard:
CXlOper* MyMatrixAdd_Impl(CXlOper& xloResult, const CXlOper* A_op, const CXlOper* B_op) { // Input buffers boost::multi_array<double, 2> A; boost::multi_array<double, 2> B; // Named bounds long heightOfA = -1; long widthOfA = -1; // Validate and translate inputs XlReadMatrix(*A_op, mtx_adapter(A), L"A", XLA_TRUNC_ONEMPTY| XLA_TRUNC_ONBLANK|XLA_FLAG_REJECT_NULL_ARRAY, &heightOfA, 0, &widthOfA); XlReadMatrix(*B_op, mtx_adapter(B), L"B", XLA_TRUNC_ONEMPTY| XLA_TRUNC_ONBLANK|XLA_FLAG_REJECT_NULL_ARRAY, &heightOfA, 0, &widthOfA); // End of generated code //}}XLP_SRC
The only difference between this code and the standard wizard-generated code
is in the declaration of the local variables that hold the matrix values.
Instead of the standard ple::mtx_ptrs
container,
boost::multi_array
is used.
The global function mtx_adapter
is called, which creates
an adapter for the multi_array
instance and passes that to
the validation function, XlReadMatrix
.
For more about setting an argument's container class, see Matrix containers in the User Guide.
The sample function uses the two input matrices, A and B, and adds them to
make a third matrix, C, also of type boost::multi_array
.
The function then returns the value to Excel by wrapping it in an adapter class,
as shown below:
CXlOper* MyMatrixAdd_Impl(CXlOper& xloResult, const CXlOper* A_op, const CXlOper * B_op) { // Input buffers boost::multi_array<double, 2> A; boost::multi_array<double, 2> B; ... // End of generated code //}}XLP_SRC boost::multi_array<double, 2> C(boost::extents[heightOfA][widthOfA]); for (long i = 0; i < heightOfA; i++) for (long j = 0; j < widthOfA; j++) C[i][j] = A[i][j] + B[i][j]; xloResult = mtx_adapter(C); return xloResult.Ret(); }
The matrix adapter class is defined in the file XlpMultiArray.h
in the sample directory.
The XLL+ validation methods expect matrix classes to support the ple::imtx interface. The following ple::imtx methods are used by the validation functions, and must be implemented by an adapter class.
void resize(bool preserve, mtx_size dim0, mtx_size dim1);
T& at(mtx_size i0, mtx_size i1);
In addition, if you wish to return matrix values to Excel, the adapter class must implement the following methods:
mtx_size size(int dim) const;
const T& at(mtx_size i0, mtx_size i1) const;
(Note: mtx_size
is typedef'd to size_t
in the XLL+
implementation of imtx
.)
The code for the adapter class is shown below:
template<class T> class mtx_multi_array_adapter : public ple::imtx_impl<T> { public: mtx_multi_array_adapter(boost::multi_array<T, 2>& impl) : _impl(&impl) {} virtual void resize(bool preserve, mtx_size dim0, mtx_size dim1) { boost::multi_array<T, 2>::extent_gen extents; (*_impl).resize(extents[dim0][dim1]); } virtual T& at(mtx_size i0, mtx_size i1) { return (*_impl)[i0][i1]; } virtual const T& at(mtx_size i0, mtx_size i1) const { return (*_impl)[i0][i1]; } virtual mtx_size size(int dim) const { return (mtx_size)(*_impl).shape()[dim]; } protected: boost::multi_array<T, 2>* _impl; }; template<class T> mtx_multi_array_adapter<T> mtx_adapter(boost::multi_array<T, 2>& src) { return mtx_multi_array_adapter<T>(src); }
The adapter is derived from the abstract base class ple::imtx_impl<T>
.
The constructor takes a reference to a multi_array
instance as its only argument, and saves it in _impl
.
resize()
calls multi_array::resize
with the arguments transformed into the notation used by multi_array
.
(Note that the preserve
argument is ignored.
The calls made to resize()
by the validation functions always assume that
the content is destroyed after a resize operation.)
The two at()
methods simply pass the coordinates through to the
adapted class.
size()
extracts the size of the matrix in the requested dimension
and returns it, cast to the appropriate integer type.
Finally, the generic template function mtx_adapter
creates a new
instance of the adapter class and returns it.
This global function is used by the validation functions to create adapters for
all matrix types.
As we saw above, it can also be used in your own code, to create an adapter for
a matrix, so that it can be used with the XLL+ run-time library.
In order to build this sample project, you need to do the following:
Download and install the boost libraries. (Visit http://www.boost.org.)
In the project's list of Additional Include Directories, change the
directory ..\..\..\3rdParty\Boost\boost_1_34_1
to the
directory where you installed the boost libraries.
(The directory you select should be the parent of the main boost
sub-directory.)
When you use the boost libraries under Visual Studio 2005, a host of
C4996
warnings appear.
These can be prevented by adding /D_SCL_SECURE_NO_WARNINGS
to the project's advanced compiler options.
This was done for the sample project.
Each sample project is located in a sub-directory of the Samples directory of the XLL+ installation. To use the sample project, open the solution file BoostMatrix.sln or the project file BoostMatrix.vcproj.
You can enable debugging under Excel by using the Setup Debugging command in the XLL+ ToolWindow.
When delivered, the help files are excluded from the build.
You can enable the help build by selecting the files
BoostMatrix.help.xml
and
BoostMatrix.chm
in the Solution Explorer,
and using the right-click menu to view Properties.
Select the page "Configuration Properties/General" and
set the "Excluded from build" property to "No".
See Generating help
in the User Guide for more information.