Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 356
Sparkles Avatar asked Oct 16 '12 10:10

Sparkles


1 Answers

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;
};
like image 185
PiotrNycz Avatar answered Nov 15 '22 09:11

PiotrNycz