XLL+ Class Library

Adapter classes for imtx<T>

Introduction

If your existing code makes of a matrix class, and you want to make use of the matrix conversion methods contained in the COper, CXlOper and CXlArray classes, in order to transfer data between Excel and your existing matrix class, you should write an adapter class.

The adapter class must implement all of the methods defined in the imtx<T> interface. You can then use any of the following methods to transfer data to and from Excel:

COper::ReadMatrix()
COper::WriteMatrix()
CXlOper::ReadMatrix()
CXlOper::operator=()
CXlArray::ReadMatrix()

Requirements

An adapter class must implement each of the following methods:

void resize(bool preserve, mtx_size dim0, mtx_size dim1);
T& at(mtx_size i0, mtx_size i1);
const T& at(mtx_size i0, mtx_size i1) const;
size_t size(int dim) const;
bool get_ptrs(T**& ptrs);
bool get_ptrs(const T**& ptrs) const;
bool get_flat(T*& data);
bool get_flat(const T*& data) const;

The required behavior of each method is described under the appropriate imtx<T> method description.

Example

Take as an example the matrix<T> class supported by earlier versions of XLL+. The class declaration is summarised below:

template<class T>
class matrix {
public:
    matrix();
    matrix(size_t rows, size_t cols, const T& def = T());
    ~matrix() {}

    matrix(const matrix<T>& that);
    const matrix<T>& operator=(const matrix& that);

    size_t rows() const { return m_rows; }
    size_t cols() const { return m_cols; }

    void clear();

    void insert_rows(size_t before, size_t n, const T& def = T());
    void insert_cols(size_t before, size_t n, const T& def = T());
    void insert_lbounds(size_t rows, size_t cols, const T& def = T());
    void resize(size_t rows, size_t cols, bool preserve = false,
                const T& def = T());
                
    const T& at(size_t r, size_t c) const;
    T& at(size_t r, size_t c);
    std::vector<T> row(size_t r) const;
    std::vector<T> col(size_t c) const;

    void add_row(const T* p);
    void add_col(const T* p);

    size_t find_in_row(const T& t, size_t r) const;
    size_t find_in_col(const T& t, size_t c) const;
    void transpose();
    
    T* col_ptr(size_t col) {
        return &at(0, col);
    }
    const T* col_ptr(size_t col) const {
        return &at(0, col);
    }

    void get_col_ptrs(std::vector<T*>& v);
    std::vector<T*> col_ptrs();
    void get_const_col_ptrs(std::vector<const T*>& v) const;
    std::vector<const T*> const_col_ptrs() const;

    void get_region(size_t top, size_t left, size_t height, size_t width, 
                    matrix<T>& matOut) const;

    bool concat_horz(const matrix<T> *amat, int cmat);

    T* begin() { return m_vec.begin(); }
    const T* begin() const { return m_vec.begin(); }
    T* end() { return m_vec.end(); }
    const T* end() const { return m_vec.end(); }

    enum { npos = (size_t)-1 };
protected:
    typedef std::vector<T> _vec;
    _vec m_vec;
};

A straight-forward adapter can be coded as follows:

template<class T>
class mtx_matrix_adapter : public ple::imtx_impl<T> {
public:
    mtx_matrix_adapter(xlp::matrix<T>& impl) : _impl(impl) {}
    virtual void resize(bool preserve, mtx_size dim0, mtx_size dim1) {
        _impl.resize(dim0, dim1, preserve);
    }
    virtual T& at(mtx_size i0, mtx_size i1) {
        return _impl.at(i0, i1);
    }
    virtual const T& at(mtx_size i0, mtx_size i1) const {
        return _impl.at(i0, i1);
    }
    virtual mtx_size size(int dim) const {
        return (dim == 0) ? _impl.rows() : _impl.cols();
    }
    virtual bool get_flat(T*& data) {
        data = _impl.begin();
        return true;
    }
    virtual bool get_flat(const T*& data) const {
        data = _impl.begin();
        return true;
    }
protected:
    xlp::matrix<T>& _impl;
};

template<class T>
mtx_adapter<T> mtx_adapter(xlp::matrix<T>& src) {
    return mtx_matrix_adapter<T>(src);
}

Let us examine each section in turn, beginning with the declaration of the adapter class:

template<class T>
class mtx_matrix_adapter : public ple::imtx_impl<T> {
    ...
};
The class is descended from the imtx_impl<T> template class. This provides default implementations of the optional functionality of an adapter, and ensures that the compiler will check for the existence of all required functionality.

The constructor associates the adapter class with an existing instance of the class to be adapted, and retains a reference to the adapted class:

public:
    mtx_matrix_adapter(xlp::matrix<T>& impl) : _impl(impl) {}
    ...
protected:
    xlp::matrix<T>& _impl;

The resize(), size() and at() methods are very simple:

public:
    ...
    virtual void resize(bool preserve, mtx_size dim0, mtx_size dim1) {
        _impl.resize(dim0, dim1, preserve);
    }
    virtual T& at(mtx_size i0, mtx_size i1) {
        return _impl.at(i0, i1);
    }
    virtual const T& at(mtx_size i0, mtx_size i1) const {
        return _impl.at(i0, i1);
    }
    virtual mtx_size size(int dim) const {
        return (dim == 0) ? _impl.rows() : _impl.cols();
    }
In each case the methods are simple rephrasings of existing matrix methods, with the understanding that dimension 0 is a row and dimension 1 is a column.

The get_ptrs() method is not implemented, because the storage method of the matrix<T> class does not suit it: the items in matrix<T> are kept in a "flat" arrangement, rather than in separate arrays for each row/column. The adapter class therefore inherits the default method of imtx_impl<T> which returns false, to indicate that the operation is not supported.

The get_flat() method is implemented, because the storage method of the matrix<T> class does suit it. The calls make use of the begin() methods of the matrix<T> class:

    virtual bool get_flat(T*& data) {
        data = _impl.begin();
        return true;
    }
    virtual bool get_flat(const T*& data) const {
        data = _impl.begin();
        return true;
    }

As a final touch, add a template method to simplify construction of the adapter:

template<class T>
mtx_adapter<T> mtx_adapter(xlp::matrix<T>& src) {
    return mtx_matrix_adapter<T>(src);
}

You can now make use of the adapter class by wrapping an instance of your matrix class with a call to mtx_adapter(), as in the following examples:

extern "C" __declspec( dllexport )
LPXLOPER ReadXlOper(const COper* Arg1)
{
    CXlOper xloResult;
    ...
    xlp::matrix<double> matrix1, matrix2;
    bOk = bOk && valArg2.ReadMatrix(mtx_adapter(matrix1), "Arg1", strError);
    ...
    xloResult.FromMatrix(mtx_adapter(matrix2));
    ...
}