Сортировка OLE SafeArray вариантов в C ++ - PullRequest
3 голосов
/ 17 марта 2019

Я сортирую OLE SafeArray Вариантов. Я решил использовать тип C ++ _variant_t, чтобы упростить процесс.

Массив создается в MS Excel в VBA, и я передаю его первый элемент в мою C / C ++ DLL, которая содержит следующие фрагменты кода.

Все работает хорошо, и я получаю нужные результаты с хорошим ускорением сортировки из DLL.

Однако я не доволен тем, сколько кода потребовалось. Мой вопрос: как это можно упростить?

В Excel VBA очень часто работает с массивами вариантов, где каждый элемент может иметь свой подтип. Например, один элемент может быть указателем BString, а следующий может быть двойным, а следующий может быть валютой, а следующий - длинным, а следующий - кодом ошибки Variant. На самом деле, существует много возможных типов, и одна структура SafeArray может содержать сразу несколько различных типов вариантов.

Когда я решил использовать тип _variant_t в C ++, я надеялся, что при сортировке это приведет к порядку сортировки, который точно соответствует порядку сортировки, который MS Excel создает при сортировке данных на листе.

Увы, это было совсем не так. Поэтому я создал собственную функцию компаратора, которая сортирует ТОЧНО, как в Excel, когда передается в std::sort() или std::stable_sort(). Однако функция компаратора намного длиннее, чем я ожидал.

И мне понадобилась вторая версия для сортировки по убыванию, потому что точный порядок сортировки в Excel довольно удивителен как для возрастания, так и для убывания, и простая обратная логическая переменная не подходит.

Так что теперь у меня ДВА раздражающих функции компаратора.

Есть ли способ сделать это умнее?

Вот две мои функции сравнения (опять же, они отлично работают):

bool CompareVariantsAscending(const _variant_t lhs, const _variant_t rhs) {

    switch (lhs.vt) {

    case VT_R8: case VT_I4: case VT_DATE: case VT_I2: case VT_R4: case VT_CY: case VT_UI1: case VT_DECIMAL: 
        switch (rhs.vt) {
        default:
            return 0 == VarCmp(&(_variant_t)lhs, &(_variant_t)rhs, LOCALE_USER_DEFAULT, NORM_IGNORECASE);
            break;
        case VT_NULL: return false; break;
        case VT_EMPTY: case VT_BSTR: case VT_ERROR: case VT_BOOL: return true; break;
        }

    case VT_BSTR:
        switch (rhs.vt) {
        case VT_BSTR:
            return 0 == VarCmp(&(_variant_t)lhs, &(_variant_t)rhs, LOCALE_USER_DEFAULT, NORM_IGNORECASE);
            break;
        case VT_EMPTY: case VT_ERROR: case VT_BOOL: return true; break;
        default: return false; break;
        }

    case VT_BOOL:
        switch (rhs.vt) {
        case VT_EMPTY: case VT_ERROR: return true; break;
        case VT_BOOL: return lhs.boolVal > rhs.boolVal; break;
        default: return false; break;
        }

    case VT_ERROR:
        switch (rhs.vt) {
        default: return false; break;
        case VT_EMPTY:  return true; break;
        }

    case VT_EMPTY:  // always last
        return false; break;

    case VT_NULL:   // always first
        return true; break; 

    default: return false;

    }

}


bool CompareVariantsDescending(const _variant_t lhs, const _variant_t rhs) {

    switch (lhs.vt) {

    case VT_R8: case VT_I4: case VT_DATE: case VT_I2: case VT_R4: case VT_CY: case VT_UI1: case VT_DECIMAL: 
        switch (rhs.vt) {
        default:
            return 0 < VarCmp(&(_variant_t)lhs, &(_variant_t)rhs, LOCALE_USER_DEFAULT, NORM_IGNORECASE);
            break;
        case VT_NULL: case VT_BSTR: case VT_BOOL: case VT_ERROR: return false; break;
        case VT_EMPTY: return true; break;
        }

    case VT_BSTR:
        switch (rhs.vt) {
        case VT_BSTR:
            return 0 < VarCmp(&(_variant_t)lhs, &(_variant_t)rhs, LOCALE_USER_DEFAULT, NORM_IGNORECASE);
            break;
        case VT_ERROR: case VT_BOOL: case VT_NULL: return false; break;
        default: return true; break;
        }

    case VT_BOOL:
        switch (rhs.vt) {       
        case VT_BOOL: return lhs.boolVal < rhs.boolVal; break;
        case VT_ERROR: case VT_NULL: return false; break;
        default: return true; break;
        }

    case VT_ERROR:
        switch (rhs.vt) {
        default: return true; break;
        case VT_NULL: return false; break;
        case VT_ERROR: return false; break;
        }

    case VT_EMPTY:  // always last
        return false; break;

    case VT_NULL:   // always first
        return true; break; 

    default: return false;

    }

}

Пожалуйста, обратитесь к следующей таблице образцов данных.

В первом столбце показаны элементы массива с несортированными данными.

Во втором столбце показано, как сортировать данные в порядке возрастания.

В третьем столбце показано, как сортировать данные в порядке убывания.

+-----------------+-----------------+-----------------+
|  Data In Array  | Asc Sort Order  | Desc Sort Order |
+-----------------+-----------------+-----------------+
| anchorage       | -78.96          | #NAME?          |
| 123             | -1              | #N/A            |
| FALSE           | 0               | #DIV/0!         |
| #NAME?          | 0.60625         | TRUE            |
| 0               | 1               | FALSE           |
| 2/18/2019 11:55 | 99.01           | zimmer          |
| #N/A            | 123             | Major Tom       |
| 99.01           | 4/15/2017       | anchorage       |
|                 | 2/18/2019 11:55 | ABC             |
| #DIV/0!         |                 | 888.87          |
|                 | $%^%$^          | $%^%$^          |
| ABC             | 888.87          |                 |
| -78.96          | ABC             | 2/18/2019 11:55 |
| Major Tom       | anchorage       | 4/15/2017       |
| TRUE            | Major Tom       | 123             |
| 4/15/2017       | zimmer          | 99.01           |
| zimmer          | FALSE           | 1               |
| 1               | TRUE            | 0.60625         |
|                 | #NAME?          | 0               |
| -1              | #N/A            | -1              |
| 0.60625         | #DIV/0!         | -78.96          |
| 888.87          |                 |                 |
| $%^%$^          |                 |                 |
+-----------------+-----------------+-----------------+

Обратите внимание, что пустой элемент в 10-й позиции Порядка сортировки Asc представляет собой VT_BSTR нулевой длины, а два пустых элемента в нижней части столбца - VT_EMPTY.

Мои функции компаратора, приведенные выше, преуспели в создании этих точных порядков сортировки Но я хотел бы знать, можно ли сделать это более кратко и / или более эффективно.

Функции компаратора в настоящее время называются так:

   std::stable_sort(Data, Data + Rows, CompareVariantsDescending);
...