Click here to Skip to main content
Click here to Skip to main content

2D Wrapper for ATL's CComSafeArray

, 8 May 2003 Public Domain
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

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

Comments and Discussions

 
GeneralMy vote of 2 Pinmemberj1s2chen17-Dec-09 2:01 
GeneralVery handy!!! Pinmemberscreig13-Apr-06 1:49 
QuestionIs CComSafearray available on 64 bit? Pinmemberwzhao20002-Feb-04 11:38 
QuestionIs this work in VC6 ? PinsussRodrigo Pinho Pereira de Souza15-Jan-04 3:44 
AnswerRe: Is this work in VC6 ? Pinmembermdgray20-Jan-04 7:37 
GeneralRe: Is this work in VC6 ? PinmemberLess Wright6-Mar-04 9:36 
mdgray wrote:
It is probably possible to port ATL7's atlsafe.h to VC6 with a small amount of hacking though.
 
You can use it just fine if you replace AtlThrow calls in AtlSafe.h with something else. I haven't had a chance to look at AtlThrow since I don't have ATL7 here, but I'll replace it with something to throw an exception.
 
Anyway, this 2d wrapper saved me from a lot of grief - I just recently had to use SafeArrays for the first time ever, and I spent a lot of time failing to understand why my multi-dim code wasn't working, then after reading this article realized that SafeArrays are column-major, and not row-major as I expected. Using this wrapper makes everything make a lot more sense, and definitely helped boost perf.
 
Thanks Marty for sharing this wrapper!
Less
 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141030.1 | Last Updated 9 May 2003
Article Copyright 2003 by mdgray
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid