Я сортирую 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);