XLL+ contains several classes which support matrix operations, to make it easy to read and return 2-dimensional array arguments to and from Excel. You may already have code which uses your own matrix classes.
This walkthrough demonstrates how to use your own matrix classes in place of those contained in XLL+.
To create the project using Visual Studio 6
To create the project using Visual Studio .NET or Visual Studio 2005
For more details about creating projects, see Creating an add-in project in the XLL+ User Guide.
Adding the existing code
In this walkthrough, we assume that there is an existing matrix class and a body of code that uses it. The following steps create this "existing" code.
#ifndef __STDMATRIX_H__ #define __STDMATRIX_H__ namespace std_tools { template<class T, class A = std::allocator<T> > class matrix { public: typedef A::size_type size_type; matrix<T, A>(const A& al = A()) : n_(0), v_(al) {} matrix<T, A>(size_type m, size_type n, const T& v = T(), const A& al = A()) : n_(0), v_(al) { assign(m, n, v); } matrix<T, A>(const matrix<T, A>& x) : n_(0) { n_ = x.size1(); assign(x.size0() * x.size1(), x.begin(), x.end()); } matrix<T, A>(const T* v, size_type m, size_type n) {} void assign(size_type m, size_type n, const T& x = T()) { n_ = n; v_.assign(m * n, x); } size_type size0() const { return n_ ? (v_.size() / n_) : 0; } size_type size1() const { return n_; } T& at(size_type pos0, size_type pos1) { return v_.at(c_(pos0, pos1)); } const T& at(size_type pos0, size_type pos1) const { return v_.at(c_(pos0, pos1)); } std::vector<T, A>::iterator begin() { return v_.begin(); } std::vector<T, A>::const_iterator begin() const { return v_.begin(); } std::vector<T, A>::iterator end() { return v_.end(); } std::vector<T, A>::const_iterator end() const { return v_.end(); } protected: std::vector<T, A> v_; size_type n_; // Size of second dimension size_type c_(size_type i, size_type j) const { return (i * n_) + j; } }; // Example algorithms using matrix class template<class T> T sum(const matrix<T>& m) { T sum = 0; for (std::vector<T>::const_iterator it = m.begin(); it < m.end(); it++) sum += *it; return sum; } template<class T> void transpose(const matrix<T>& src, matrix<T>& dest) { matrix<T>::size_type i, j; dest.assign(src.size1(), src.size0()); for (i = 0; i < dest.size0(); i++) { for (j = 0; j < dest.size1(); j++) { dest.at(i, j) = src.at(j, i); } } } } // namespace std_tools #endif //__STDMATRIX_H__
Creating the adapter class
In order to install our matrix class into XLL+, we must first create an adapter class which will communicate between the matrix class and the XLL+ functions which use matrices.
The adapter class should implement the ple::imtx interface. The easiest way to achieve this is to inherit from the ple::mtx_impl class. The adapter must also provide a simple constructor (with no arguments) which creates an instance of the adapted matrix class within the adapter. (This constructor will be used by the code genereated by the XLL+ Function Wizard.)
#ifndef __STDMATRIX_ADAPT_H__ #define __STDMATRIX_ADAPT_H__ #include <plemtx.h> #include "stdmatrix.h" namespace std_tools { template<class T> class matrix_adapter : public ple::imtx_impl<T> { protected: matrix<T>* m_; bool owned_; public: matrix_adapter<T>() : m_(new matrix<T>), owned_(true) {} matrix_adapter<T>(matrix<T>& m) : m_(&m), owned_(false) {} ~matrix_adapter<T>() { if (owned_) delete m_; } virtual void resize(bool preserve, mtx_size dim0, mtx_size dim1) { if (preserve) throw "operation not supported"; m_->assign(dim0, dim1); } virtual T& at(mtx_size i0, mtx_size i1) { return m_->at(i0, i1); } virtual const T& at(mtx_size i0, mtx_size i1) const { return m_->at(i0, i1); } virtual mtx_size size(int dim) const { return (dim == 0) ? m_->size0() : m_->size1(); } virtual bool get_flat(T*& data) { data = &m_->at(0, 0); return true; } virtual bool get_flat(const T*& data) const { data = &m_->at(0, 0); return true; } matrix<T>& m() { return *m_; } const matrix<T>& m() const { return *m_; } }; template<class T> matrix_adapter<T> ple_adapter(matrix<T>& m) { return matrix_adapter<T>(m); } } // namespace std_tools #endif //__STDMATRIX_ADAPT_H__
matrix<T>* m_; bool owned_;m_ is a pointer to an instance of the adapted matrix class. The owned_ flag determines whether the matrix *m_ is owned by the adapter. If it is true, then *m_ will be deleted when the adapter is destroyed.
matrix_adapter<T>() : m_(new matrix<T>), owned_(true) {}The constructor creates a new, empty instance of matrix<T>, and sets _owned to true, to ensure that it is destroyed along with the adapter. This constructor (i.e. a constructor with no arguments) will be used in the code generated by the XLL+ Function Wizard.
matrix_adapter<T>(matrix<T>& m) : m_(&m), owned_(false) {}The constructor stores a reference to an existing instance of matrix<T>, and sets _owned to false, so that the referenced instance will not be destroyed along with the adapter. This constructor is useful for adapting matrix values so that they can be returned to Excel.
virtual void resize(bool preserve, mtx_size dim0, mtx_size dim1) { if (preserve) throw "operation not supported"; m_->assign(dim0, dim1); }Note that the method fails if the preserve flag is true, since the underlying matrix class does not support such an operation.
Installing the adapter
///////////////////////////////////////////////////////////////////////////// // Install matrix adapter #include "stdmatrix.h" #include "stdmatrix_adapt.h" using namespace std_tools; #undef MTX_PTRS #undef MTX_FLAT #define MTX_PTRS matrix_adapter #define MTX_FLAT matrix_adapter
Using the adapter class
// Function: MtxTest1 // Purpose: Matrix test routine //{{XLP_SRC(MtxTest1) // NOTE - the FunctionWizard will add and remove mapping code here. // DO NOT EDIT what you see in these blocks of generated code! IMPLEMENT_XLLFN2(MtxTest1, "RP", "MtxTest1", "X", "Math & Trig", "Matrix test routine", "No description provided\000", "B0()0()X No description provided\0", 1) extern "C" __declspec( dllexport ) LPXLOPER MtxTest1(const COper* X) { CXlOper xloResult; BOOL bOk = TRUE; MTX_PTRS<double> matX; bOk = bOk && X->ReadMatrix(matX, "X", xloResult, 0, 0, XLA_ARRAY_FLAGS_STD, 0, 0); if (!bOk) return xloResult.Ret(); //}}XLP_SRC // Pass nested matrix object to sum() algorithm, and return result xloResult = sum(matX.m()); return xloResult.Ret(); }
xloResult = sum(matX.m());Note that the m() method is used to pass the nested matrix object to the sum algorithm.
// Function: MtxTest2 // Purpose: Returns the transpose of a matrix //{{XLP_SRC(MtxTest2) // NOTE - the FunctionWizard will add and remove mapping code here. // DO NOT EDIT what you see in these blocks of generated code! IMPLEMENT_XLLFN2(MtxTest2, "RP", "MtxTest2", "X", "Math & Trig", "Returns the transpose of a matrix", "Input matrix\000", "B0()0()X Input matrix\0", 1) extern "C" __declspec( dllexport ) LPXLOPER MtxTest2(const COper* X) { CXlOper xloResult; BOOL bOk = TRUE; MTX_PTRS<double> matX; bOk = bOk && X->ReadMatrix(matX, "X", xloResult, 0, 0, XLA_ARRAY_FLAGS_STD, 0, 0); if (!bOk) return xloResult.Ret(); //}}XLP_SRC // Use the transpose algorithm to populate mY matrix<double> mY; transpose(matX.m(), mY); xloResult = ple_adapter(mY); return xloResult.Ret(); }
const CXlOper& operator=(const ple::imtx<T>& mat);
To test the add-in