我正在排序OLE SafeArray的变体。我选择使用C ++类型_variant_t
来简化事情。
该数组是在VBA的MS Excel中创建的,我将其第一个元素传递给包含以下代码段的C / C ++ DLL。
一切正常,通过DLL的排序速度得到了很好的提高,我得到了想要的正确结果。
但是,我对需要多少代码不满意。我的问题是如何简化这一点?
在Excel VBA中,使用每个元素可以具有不同子类型的Variants数组是很常见的。例如,一个元素可能是BString指针,下一个可能是Double,下一个可能是Currency,下一个是Long,下一个是Variant错误代码。实际上,有很多可能的类型,并且一个SafeArray结构可以很好地一次容纳许多不同的Variant类型。
当我选择在C ++中使用_variant_t
类型时,我的希望是排序时会导致排序顺序与MS Excel在对工作表中的数据进行排序时所产生的排序顺序紧密匹配。
A,并非如此。因此,我制作了一个自定义比较器函数,该函数完全类似于Excel提供给std::sort()
或std::stable_sort()
时的排序。但是,比较器功能比我预期的要长得多。
我需要第二个版本的Descending排序,因为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 | | |
| $%^%$^ | | |
+-----------------+-----------------+-----------------+
请注意,“ Asc排序顺序”第10位的空白元素为零长度VT_BSTR,而列底部的两个空白元素为VT_EMPTY。
我上面的比较器功能成功产生了这些确切的排序顺序。但是我想知道是否可以更简洁和/或更有效地完成此操作。
比较器函数当前被这样调用:
std::stable_sort(Data, Data + Rows, CompareVariantsDescending);