Search code examples
c++sortinglambdaimport-from-excelsafearray

Sort Array of Indexes Into Primary Array


I'm coding a C++ dll to sort a SAFEARRAY passed from VBA.

I'm not using any OLE libraries, but rather accessing the array descriptor and data directly.

I have no problem sorting an array of any native VBA types. For example, the following fragment sorts an array of BSTRs:

long * p = (long*)pData; 

std::sort(p, p + elems, comparestring);

...which uses this comparison function:

bool comparestring(const long& lhs, const long& rhs) {

   wchar_t * lhs_ = (wchar_t*)lhs;
   wchar_t * rhs_ = (wchar_t*)rhs;

   return _wcsicmp(lhs_, rhs_) < 0;
}

I realize I'm cheating here since wchar_t is very different from BSTR, but it's not common to have a zero char within the payload of an Excel string and so I'm OK with that. The above works well.

THE PROBLEM

I want the dll to optionally be able to sort a companion array of indexes into the primary data array. In this mode only the index array would be sorted, leaving the source data untouched.

My research suggests that a lamda functor may be the most promising path as I'd prefer not to allocate memory for additional arrays or vectors of data or pairs.

In particular, this answer seems very promising.

However, I cannot work out how to adapt it to my situation where I'm processing the raw pointers to the BSTRs that begin at pData.

I have tried the following:

long * p = (long*)pData; 

long ndx[5];

for (int i = 0; i < 5; i++) ndx[i] = i + 1;

std::sort(ndx[0], ndx[4], [&p](long i1, long i2) { comparestring((*p) + i1, (*p) + i2); })

I am using VC++ 2015 and the above results in the following error:

Error C2893 Failed to specialize function template 'iterator_traits<_Iter>::iterator_category std::_Iter_cat(const _Iter &)'

My C programming days are ancient history (predating the existence of C++) and so I'm struggling a little. Appreciate any assistance.

UPDATE

The code now looks like this.. and it compiles, but the order of ndx is incorrect after execution:

long * p = (long*)pData; 

long ndx[5];

for (int i = 0; i < 5; i++) ndx[i] = i + 1;

std::sort(ndx, ndx + 5, [&p](long i1, long i2) { return comparestring(*p + i1, *p + i2); })

Solution

  • This code:

    long ndx[5];
    for (int i = 0; i < 5; i++) ndx[i] = i + 1;
    std::sort(ndx[0], ndx[4], [&p](long i1, long i2) { comparestring((*p) + i1, (*p) + i2); })
    

    should probably be:

    long ndx[5];
    for (int i = 0; i < 5; i++) ndx[i] = i;
    std::sort(ndx, ndx + 5, [&](long i1, long i2) { return comparestring(*(p + i1), *(p + i2)); }
    

    The first two arguments to std::sort are an iterator range. It'd be better style (assuming your compiler is C++11-compatible) to use std::begin(ndx) and std::end(ndx).

    Also, the second line can be written std::iota( std::begin(ndx), std::end(ndx), 0 );