这些把安全数组转成std::list对象的方法能转成模板函数吗?
Can these methods that convert safe arrays into std::list objects be turned into a template function?
采用这三种方法,每种方法都从安全数组中构建不同的 std::list
对象:
void CMSATools::ConvertSAFEARRAY_DISCUSSIONITEMS(SAFEARRAY* psaDiscussionItems, ListDiscussionItems& rListDiscussionItems)
{
MSAToolsLibrary::IDiscussionItemPtr* pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaDiscussionItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaDiscussionItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaDiscussionItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListDiscussionItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListDiscussionItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaDiscussionItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaDiscussionItems);
if (FAILED(hr))
throw _com_error(hr);
}
void CMSATools::ConvertSAFEARRAY_STUDENTITEMS(SAFEARRAY* psaStudentItems, ListStudentItems& rListStudentItems)
{
MSAToolsLibrary::IStudentItemPtr *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaStudentItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaStudentItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaStudentItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListStudentItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListStudentItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaStudentItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaStudentItems);
if (FAILED(hr))
throw _com_error(hr);
}
void CMSATools::ConvertSAFEARRAY_DUTYHISTORYITEMS(SAFEARRAY* psaHistoryItems, ListDutyHistoryLookupItems& rListHistoryItems)
{
MSAToolsLibrary::IDutyAssignmentLookupPtr *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaHistoryItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaHistoryItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaHistoryItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListHistoryItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListHistoryItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaHistoryItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaHistoryItems);
if (FAILED(hr))
throw _com_error(hr);
}
它们都可以正常工作。但它们有很多共同点。可以在这里使用“模板”吗?这不是我以前做过的事情。
我不介意拥有三种不同的方法,但如果它们可以调用一种通用的模板化方法来转换安全数组,那将使代码维护更容易。
有道理吗?
试试这个(没有编译它):
template<typename from, typename to>
void CMSATools::ConvertSAFEARRAY_DUTYHISTORYITEMS(SAFEARRAY* psaItems, to& rItems)
{
from *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
是的,看起来这些函数的主体非常相似,很容易将它们全部折叠成一个函数模板。
但是不,我不认为这是正确的方法。相反,我会创建一个小代理 class 让 SAFEARRAY
像一个范围一样。
#include <windows.h>
#include <oleauto.h>
template <class F, class ...Args>
void check(F f, Args ...args) {
auto hr = f(args...);
if (FAILED(hr))
throw hr;
}
template <class T>
class SafeArrayRange {
SAFEARRAY *data;
T* pVals {nullptr};
long count;
public:
using value_type = T;
using reference = T&;
using pointer = T*;
using difference_type = std::ptrdiff_t;
using iterator_tag = std::random_access_iterator_tag;
SafeArrayRange(SAFEARRAY *array) : data(array) {
check(SafeArrayAccessData, data, (void**)&pVals);
long lowerBound;
check(SafeArrayGetLBound, data, 1, &lowerBound);
long upperBound;
check(SafeArrayGetUBound, data, 1, &upperBound);
count = upperBound - lowerBound + 1;
}
using iterator = T*;
iterator begin() const { return pVals; }
iterator end() const { return pVals + count; }
std::size_t size() const { return count; }
~SafeArrayRange() { SafeArrayUnaccessData(data); }
};
有了它,我可以像对待 vector
或其他普通容器一样对待 SAFEARRAY
。例如,我至少使用以下代码对范围适配器进行了最小测试:
int main() {
SAFEARRAY* array = SafeArrayCreateVector(VT_I4, 0, 20);
SafeArrayRange<uint32_t> range(array);
// put data into the array:
std::iota(range.begin(), range.end(), 0);
// check that our idea of its size matches what we specified:
std::cout << "range size: " << range.size() << "\n";
std::vector<uint32_t> result;
// get data back out of the array:
std::copy(range.begin(), range.end(), std::back_inserter(result));
// print out the contents of the array:
std::copy(range.begin(), range.end(), std::ostream_iterator<uint32_t>(std::cout, "\t"));
}
所以,我将范围适配器放入 header 并在需要的地方包含它,所以大多数时候我可以忽略 SAFEARRAY
的大部分内部细节。警告:这只是最低限度的测试,所以这里和那里可能有一些我遗漏的点点滴滴。
采用这三种方法,每种方法都从安全数组中构建不同的 std::list
对象:
void CMSATools::ConvertSAFEARRAY_DISCUSSIONITEMS(SAFEARRAY* psaDiscussionItems, ListDiscussionItems& rListDiscussionItems)
{
MSAToolsLibrary::IDiscussionItemPtr* pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaDiscussionItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaDiscussionItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaDiscussionItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListDiscussionItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListDiscussionItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaDiscussionItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaDiscussionItems);
if (FAILED(hr))
throw _com_error(hr);
}
void CMSATools::ConvertSAFEARRAY_STUDENTITEMS(SAFEARRAY* psaStudentItems, ListStudentItems& rListStudentItems)
{
MSAToolsLibrary::IStudentItemPtr *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaStudentItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaStudentItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaStudentItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListStudentItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListStudentItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaStudentItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaStudentItems);
if (FAILED(hr))
throw _com_error(hr);
}
void CMSATools::ConvertSAFEARRAY_DUTYHISTORYITEMS(SAFEARRAY* psaHistoryItems, ListDutyHistoryLookupItems& rListHistoryItems)
{
MSAToolsLibrary::IDutyAssignmentLookupPtr *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaHistoryItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaHistoryItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaHistoryItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rListHistoryItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rListHistoryItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaHistoryItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaHistoryItems);
if (FAILED(hr))
throw _com_error(hr);
}
它们都可以正常工作。但它们有很多共同点。可以在这里使用“模板”吗?这不是我以前做过的事情。
我不介意拥有三种不同的方法,但如果它们可以调用一种通用的模板化方法来转换安全数组,那将使代码维护更容易。
有道理吗?
试试这个(没有编译它):
template<typename from, typename to>
void CMSATools::ConvertSAFEARRAY_DUTYHISTORYITEMS(SAFEARRAY* psaItems, to& rItems)
{
from *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
rItems.clear();
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
rItems.push_back(pVals[i]);
}
hr = SafeArrayUnaccessData(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
是的,看起来这些函数的主体非常相似,很容易将它们全部折叠成一个函数模板。
但是不,我不认为这是正确的方法。相反,我会创建一个小代理 class 让 SAFEARRAY
像一个范围一样。
#include <windows.h>
#include <oleauto.h>
template <class F, class ...Args>
void check(F f, Args ...args) {
auto hr = f(args...);
if (FAILED(hr))
throw hr;
}
template <class T>
class SafeArrayRange {
SAFEARRAY *data;
T* pVals {nullptr};
long count;
public:
using value_type = T;
using reference = T&;
using pointer = T*;
using difference_type = std::ptrdiff_t;
using iterator_tag = std::random_access_iterator_tag;
SafeArrayRange(SAFEARRAY *array) : data(array) {
check(SafeArrayAccessData, data, (void**)&pVals);
long lowerBound;
check(SafeArrayGetLBound, data, 1, &lowerBound);
long upperBound;
check(SafeArrayGetUBound, data, 1, &upperBound);
count = upperBound - lowerBound + 1;
}
using iterator = T*;
iterator begin() const { return pVals; }
iterator end() const { return pVals + count; }
std::size_t size() const { return count; }
~SafeArrayRange() { SafeArrayUnaccessData(data); }
};
有了它,我可以像对待 vector
或其他普通容器一样对待 SAFEARRAY
。例如,我至少使用以下代码对范围适配器进行了最小测试:
int main() {
SAFEARRAY* array = SafeArrayCreateVector(VT_I4, 0, 20);
SafeArrayRange<uint32_t> range(array);
// put data into the array:
std::iota(range.begin(), range.end(), 0);
// check that our idea of its size matches what we specified:
std::cout << "range size: " << range.size() << "\n";
std::vector<uint32_t> result;
// get data back out of the array:
std::copy(range.begin(), range.end(), std::back_inserter(result));
// print out the contents of the array:
std::copy(range.begin(), range.end(), std::ostream_iterator<uint32_t>(std::cout, "\t"));
}
所以,我将范围适配器放入 header 并在需要的地方包含它,所以大多数时候我可以忽略 SAFEARRAY
的大部分内部细节。警告:这只是最低限度的测试,所以这里和那里可能有一些我遗漏的点点滴滴。