Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / ATL
Article

2D Wrapper for ATL's CComSafeArray

Rate me:
Please Sign up or sign in to vote.
3.91/5 (8 votes)
8 May 2003Public Domain2 min read 70K   515   33   7
SafeArray Wrapper to simplify and speed up 2D CComSafeArray<>

Introduction

This is an attempt to make using 2D SAFEARRAYS palatable while providing C++ and VB like access. Starting in ATL7, Microsoft introduced a SAFEARRAY wrapper class, CComSafeArray<T> which provides some useful services like locking and destruction as well as providing vector access using the standard C++ "[]" notation. It is however, extremely slow and provides virtually no simplification for 2D (or more) arrays. Further, the documentation in MSDN creates a 3x3 array which it then populates with a 2x3 matrix totally obscuring any ability to see what goes on behind the scenes as the row count equals the column count.

This wrapper class attempted to address the following issues:

  • Provide Row Major (C++) and Column Major (VB) access mechanisms since VB seems to be the biggest user of 2D arrays but I am more comfortable with C++ ordering. Default is C++ like memory layout:SArray2<float,false,false>; a(3,4) has the same memory layout as float a[3][4].
  • Speed up the element access time. This is really important and I see a 40x improvement since CComSafeArray already locks the data.
  • Limit the wrapper to 2D arrays that are zero based. This is now the default with VB and required for native C++ arrays.
  • Provide VB like constructors as an option so that a declaration like SArray2<float,false,false> a(3,4) actually declares in the same way this dim a(3,4) as single does in VB. That has the same memory layout as a[5][4] in C++.
  • Inherits from CComSafeArray and avoids the use of any other state info which allows transparency with CComSafeArray. Methods that are not applicable to 2D SafeArrays have been masked by declaring them as private.
  • Is used with the ATL header file "atlsafe.h" which can be used anywhere, not just ATL projects.

The Wrapper

It's small enough to include:

// include ATL header that contains CComSafeArray<T>
// Writtten by M. Gray 5/03, donated to public domain, use at your own risk.
#include <atlsafe.h>

// Wrapper for ATL's CComSafeArray<T> providing 2D access.
// CComSafeArray<T> is great for 1D vectors but has awkward nD handling
template<typename T, bool rowMajor=true, bool extraElement=false>
class SArray2 : public CComSafeArray<T> {
    // These functions are overridden and made private since they are not 
    // useful for 2D arrays
    void Add(){}            // N/A
    void Create(){}            // N/A
    void GetAt(){}            // N/A
    void GetDimensions(){}    // N/A
    void GetLowerBound(){}    // N/A
    void SetAt(){}            // N/A
    void operator[](int){}        // N/A
public:
    SArray2(): CComSafeArray<T>(){}
    SArray2(UINT a_rows, UINT a_cols)
    {
        SAFEARRAYBOUND bounds[2];
        bounds[0].cElements = (UINT)extraElement + (rowMajor ? a_cols :
                                                               a_rows);
        bounds[0].lLbound = 0;
        bounds[1].cElements = (UINT)extraElement + (rowMajor ? a_rows
                                                             : a_cols);
        bounds[1].lLbound = 0;
        CComSafeArray<T>::Create(bounds ,2);
    }
    // Attach to safearray but check for 2D and zero base
    HRESULT Attach(const SAFEARRAY *psaSrc)
    {
        ATLASSERT(psaSrc->cDims==2 && psaSrc->rgsabound[0].lLbound==0 && 
                 psaSrc->rgsabound[1].lLbound==0);
        return CComSafeArray<T>::Attach(psaSrc);
    }
    T& operator()(UINT a_row, UINT a_col)
    {
        ATLASSERT(m_psa->rgsabound[rowMajor?0:1].cElements > a_row);
        ATLASSERT(m_psa->rgsabound[rowMajor?1:0].cElements > a_col);
        return static_cast<T *>(m_psa->pvData)
            [m_psa->rgsabound[1].cElements * (rowMajor ? a_row : a_col)
                                           + (rowMajor ? a_col : a_row)];
    }
    // this method will return the number of elements in a specific zero 
    // based dimension only
    ULONG GetCount(UINT uDim = 0) const 
              {return CComSafeArray<T>::GetCount(rowMajor ? 1-uDim : uDim);}
    // The zero based array dimension for which to get the upper bound. 
    // If omitted, the default is 0. 
    ULONG GetUpperBound(UINT uDim = 0) const 
           {return CComSafeArray<T>::GetUpperBound(rowMajor ? 1-uDim : uDim);}
    // A pointer to a SAFEARRAYBOUND structure that contains information on 
    // the number of elements and the lower bound of an array
    HRESULT Resize(const SAFEARRAYBOUND *pBound)
                                  {return CComSafeArray<T>::Resize(pBound);}
};

Using the code

This code is simple to use:

SArray2<float> sa(2,3);
sa(0,1)=1.3f;

The data portion of the SAFEARRAY has the same memory layout and meaning as float sa[2][3]

float sa[2][3]
sa[0][1]=1.3f;

To get an idea how ugly the code is using CComSafeArray directly, this accomplishes the same thing except slower:

SAFEARRAYBOUND bounds[2];
bounds[0].cElements = 3;
bounds[0].lLbound = 0;
bounds[1].cElements = 2;
bounds[1].lLbound = 0;

CComSafeArray<float> a;
a.Create(bounds ,2);
long index[2];
float fval=1.3f;
index[0]=1; // note: column major ordering of SafeArray Access
index[1]=0;
a.MultiDimSetAt(index, fval);

The included test harness provides a sample of various permuations and functions provided. It does nothing useful but shows various forms of row-major and column major declarations as well as the "k+1" DIM equivalents in VB.

History

Initial vers. 5/8/03

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Engineer
United States United States
Professional Engineer(EE), interests include DSP, Control Systems, Analog design, C++, C#, Color Science

Comments and Discussions

 
BugFor those that may still be interested... Pin
Jeremy Dickman29-Jan-16 1:46
Jeremy Dickman29-Jan-16 1:46 
GeneralMy vote of 2 Pin
j1s2chen17-Dec-09 2:01
j1s2chen17-Dec-09 2:01 
GeneralVery handy!!! Pin
screig13-Apr-06 1:49
screig13-Apr-06 1:49 
QuestionIs CComSafearray available on 64 bit? Pin
wzhao20002-Feb-04 11:38
wzhao20002-Feb-04 11:38 
QuestionIs this work in VC6 ? Pin
Rodrigo Pinho Pereira de Souza15-Jan-04 3:44
sussRodrigo Pinho Pereira de Souza15-Jan-04 3:44 
AnswerRe: Is this work in VC6 ? Pin
mdgray20-Jan-04 7:37
mdgray20-Jan-04 7:37 
GeneralRe: Is this work in VC6 ? Pin
Less Wright6-Mar-04 9:36
Less Wright6-Mar-04 9:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.