Sparkles
Sparkles

Reputation: 265

Generic function to convert variant SAFEARRAY to STL containers

I have some functions that I use to convert a 2D variant SAFEARRAY into various STL containers, kinda like so (illustrative only)

template<typename T>
std::set<T> SetFromSafeArray(VARIANT srcArray)
{
    CComSafeArray<T> srcComArray(srcArray.parray);
    std::set<T> destContainer;

    for (ULONG i=0;i<srcComArray.GetCount();++i)
        destContainer.insert(srcComArray.GetAt(i));

    return destContainer;
}

I feel it's not a very c++-ish way of going about it and it means there's a separate function for each STL container I convert to.

My idea was to write a wrapper and custom iterator for CComSafeArrays so I could just do...

std::copy(srcComArray.begin(), srcComArray.end(), destContainer.begin());

but having never written an iterator before and being a novice I really don't know if it will be easy.

Is a custom CComSafeArray iterator my best, standard c++ like, option (in which case I'm sure I can find a good tutorial on writing an iterator)? Or is there some other way of going about it?

Boost is not an option.

TIA

Upvotes: 5

Views: 738

Answers (1)

PiotrNycz
PiotrNycz

Reputation: 24422

My idea was to write a wrapper and custom iterator for CComSafeArrays

This is very good idea for creating iterator, but you don't need a wrapper around CComSafeArray<T>, only iterator is needed.

so I could just do...

std::copy(srcComArray.begin(), srcComArray.end(), destContainer.begin());

But instead of doing your way, you can do this:

SomeContainer<T> destContainer(begin(srcComArray), end(srcComArray));   

Because almost every STL container has constructor from range (pair of iterators).

Assuming you have written iterator over CComSafeArray<T> - functions begin/end will be like these:

template <typename T>
CComSafeArrayIterator<T> begin(CComSafeArray<T>& container)
{
    return CComSafeArrayIterator<T>(container, 0);
}
template <typename T>
CComSafeArrayIterator<T> end(CComSafeArray<T>& container)
{
    return CComSafeArrayIterator<T>(container, container.getSize());
}

Notice that begin() is zero position, end() is getSize() position.

And writing an iterator is not rocket science. Just a few functions. The most important is to know what you need to iterate. In your case: the container reference(pointer) and the current position. Iterating is just moving the position. Accessing is via container and position. Comparing is via comparing position.

template <typename T>
class CComSafeArrayIterator {
public:
   CComSafeArrayIterator(CComSafeArray<T>& container, ULONG position) 
   : container(&container), 
   position(position) 
   {}

   // prefix ++it
   CComSafeArrayIterator& operator ++() { ++position; return *this; }
   // postfix it++
   CComSafeArrayIterator operator ++(int) { 
       CComSafeArrayIterator prev = *this; 
       ++position; 
       return prev; 
   }
   // access by ->: it-> !! ony if getAt return reference not value 
   const T* operator -> () const {
      return &(container->getAt(position));
   }
   // access by *: *it
   const T& operator * () const {
      return container->getAt(position);
   }
   // comparing
   friend bool operator == (const CComSafeArrayIterator<T>& l, 
                            const CComSafeArrayIterator<T>& r)
   {
      return l.position == r.position;
   }
   friend bool operator != (const CComSafeArrayIterator<T>& l, 
                            const CComSafeArrayIterator<T>& r)
   {
      return l.position != r.position;
   }



private:
   // this is what you need
   CComSafeArray<T>* container;
   ULONG position;
};

Upvotes: 6

Related Questions