From be738b08b7ff56ab73dfac4ac2cda486af22b3a5 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Mon, 3 Aug 2020 14:10:39 -0700 Subject: [PATCH 01/34] Start of basic safearray_t type --- include/wil/Safearrays.h | 177 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 include/wil/Safearrays.h diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h new file mode 100644 index 00000000..c7373ef5 --- /dev/null +++ b/include/wil/Safearrays.h @@ -0,0 +1,177 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_SAFEARRAYS_INCLUDED +#define __WIL_SAFEARRAYS_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include // new(std::nothrow) +#include "resource.h" // unique_hkey +#include + +namespace wil +{ +#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) +#define __WIL_OLEAUTO_ + /// @cond + namespace details + { + template + struct TypeTraits{}; + + template<> struct TypeTraits { enum { type = VT_I1 }; }; + template<> struct TypeTraits { enum { type = VT_I2 }; }; + template<> struct TypeTraits { enum { type = VT_I4 }; }; + template<> struct TypeTraits { enum { type = VT_I4 }; }; + template<> struct TypeTraits { enum { type = VT_I8 }; }; + template<> struct TypeTraits<__int64> { enum { type = VT_I8 }; }; + template<> struct TypeTraits { enum { type = VT_UI1 }; }; + template<> struct TypeTraits { enum { type = VT_UI2 }; }; + template<> struct TypeTraits { enum { type = VT_UI4 }; }; + template<> struct TypeTraits { enum { type = VT_UI4 }; }; + template<> struct TypeTraits { enum { type = VT_UI8 }; }; + template<> struct TypeTraits { enum { type = VT_UI8 }; }; + template<> struct TypeTraits { enum { type = VT_R4 }; }; + template<> struct TypeTraits { enum { type = VT_R8 }; }; + + template + struct TypeTraits> { enum { type = VT_UNKNOWN }; }; + + inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT + { + FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); + } + + inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT + { + FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); + } + + inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT + { + FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); + } + + typedef resource_policy safearray_resource_policy; + //typedef resource_policy safearray_lock_resource_policy; + //typedef resource_policy safearray_accessdata_resource_policy; + + } + /// @endcond + + typedef unique_any safearray_unlock_scope_exit; + + // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope + // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + return safearray_unlock_scope_exit(psa); + } + + template + class safearray_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based construction + safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(vt, cElements, lowerBound); + } + + // Low-level, arbitrary number of dimensions + result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + HRESULT hr = [&]() + { + RETURN_HR_IF_NULL(E_INVALIDARG, sab); + RETURN_HR_IF(E_INVALIDARG, cDims < 1); + SAFEARRAY* sa = ::SafeArrayCreate(vt, cDims, sab); + RETURN_LAST_ERROR_IF_NULL(sa); + storage_t::reset(sa); + return S_OK; + }(); + + return err_policy::HResult(hr); + } + + // Single Dimension specialization + result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0 ) + { + auto bounds = SAFEARRAYBOUND{cElements, lowerBound}; + + return err_policy::HResult(create(vt, 1, &bounds)); + } + + ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + + result lbound(UINT nDim, LONG* pLbound) const + { + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); + } + result lbound(LONG* pLbound) const + { + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); + } + result ubound(UINT nDim, LONG* pUbound) const + { + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); + } + result ubound(LONG* pUbound) const + { + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); + } + + // Lock Helper + WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT + { + return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + } + + // Exception-based helper functions + LONG lbound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG result = 0; + lbound(nDim, &result); + return result; + } + LONG ubound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG result = 0; + ubound(nDim, &result); + return result; + } + + + private: + + }; + + + +#ifdef WIL_ENABLE_EXCEPTIONS +#endif // WIL_ENABLE_EXCEPTIONS +} // namespace wil + +#endif +#endif From 5a0f8009c13b9154960c3328a658959bdea824a1 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Wed, 5 Aug 2020 13:41:02 -0700 Subject: [PATCH 02/34] Started fleshing out access data class --- include/wil/Safearrays.h | 80 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index c7373ef5..81e1a6c2 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -62,6 +62,30 @@ namespace wil FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } + inline HRESULT SafeArrayCountElements( SAFEARRAY* psa, ULONG* pCount ) + { + RETURN_HR_IF_NULL(E_INVALIDARG, psa); + RETURN_HR_IF_NULL(E_POINTER, pCount); + ULONG nDims = ::SafeArrayGetDim(psa); + ULONGLONG result = 1; + LONG nLbound = 0; + LONG nUbound = 0; + for ( UINT i = 1 ; i <= nDims ; ++i ) + { + RETURN_IF_FAILED(::SafeArrayGetLBound(psa, i, &nLbound)); + RETURN_IF_FAILED(::SafeArrayGetUBound(psa, i, &nUbound)); + + result *= (nUbound - nLbound + 1); + if ( result > ULONG_MAX ) + { + RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + } + } + + *pCount = static_cast(result); + return S_OK; + } + typedef resource_policy safearray_resource_policy; //typedef resource_policy safearray_lock_resource_policy; //typedef resource_policy safearray_accessdata_resource_policy; @@ -79,6 +103,53 @@ namespace wil return safearray_unlock_scope_exit(psa); } + template + class safearraydata_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit safearraydata_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based construction + safearraydata_t() + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + //create(vt, cElements, lowerBound); + } + + result create(storage_t::pointer psa) + { + HRESULT hr = [&]() + { + RETURN_HR_IF(E_INVALIDARG, !storage_t::is_valid(psa)); + RETURN_IF_FAILED(::SafeArrayAccessData(psa, &m_pBegin)); + storage_t::reset(sa); + RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nCount)); + m_pEnd = m_pBegin + m_nCount; + return S_OK; + }(); + + return err_policy::HResult(hr); + } + + T* begin() WI_NOEXCEPT { return m_pBegin; } + T* end() WI_NOEXCEPT { return m_pEnd; } + const T* begin() const WI_NOEXCEPT { return m_pBegin; } + const T* end() const WI_NOEXCEPT { return m_pEnd; } + ULONG count() const WI_NOEXCEPT { return m_nCount; } + + // TODO: Implement index operator to support [] syntax + + private: + T* m_pBegin = nullptr; + T* m_pEnd = nullptr; + ULONG m_nCount = 0; + }; + template class safearray_t : public storage_t { @@ -140,6 +211,11 @@ namespace wil return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); } + result count(ULONG* pCount) const + { + return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); + } + // Lock Helper WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT { @@ -147,14 +223,14 @@ namespace wil } // Exception-based helper functions - LONG lbound(UINT nDim = 1) const + WI_NODISCARD LONG lbound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); LONG result = 0; lbound(nDim, &result); return result; } - LONG ubound(UINT nDim = 1) const + WI_NODISCARD LONG ubound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); LONG result = 0; From 345d5646feb35036a803fda57e94562525cc47aa Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Wed, 5 Aug 2020 19:07:24 -0700 Subject: [PATCH 03/34] Added array index operators and some other goodness --- include/wil/Safearrays.h | 57 +++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 81e1a6c2..1d5085aa 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -88,7 +88,7 @@ namespace wil typedef resource_policy safearray_resource_policy; //typedef resource_policy safearray_lock_resource_policy; - //typedef resource_policy safearray_accessdata_resource_policy; + typedef resource_policy safearray_accessdata_resource_policy; } /// @endcond @@ -115,10 +115,10 @@ namespace wil typedef typename err_policy::result result; // Exception-based construction - safearraydata_t() + safearraydata_t(storage_t::pointer psa) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - //create(vt, cElements, lowerBound); + create(psa); } result create(storage_t::pointer psa) @@ -136,13 +136,16 @@ namespace wil return err_policy::HResult(hr); } + // Ranged-for style T* begin() WI_NOEXCEPT { return m_pBegin; } T* end() WI_NOEXCEPT { return m_pEnd; } - const T* begin() const WI_NOEXCEPT { return m_pBegin; } - const T* end() const WI_NOEXCEPT { return m_pEnd; } - ULONG count() const WI_NOEXCEPT { return m_nCount; } + const T* begin() const WI_NOEXCEPT { return m_pBegin; } + const T* end() const WI_NOEXCEPT { return m_pEnd; } - // TODO: Implement index operator to support [] syntax + // Old style iteration + ULONG count() const WI_NOEXCEPT { return m_nCount; } + WI_NODISCARD T& operator[](ULONG i) { WI_ASSERT(i < m_nCount); return *(m_pBegin + i); } + WI_NODISCARD const T& operator[](ULONG i) const { WI_ASSERT(i < m_nCount); return *(m_pBegin +i); } private: T* m_pBegin = nullptr; @@ -150,9 +153,24 @@ namespace wil ULONG m_nCount = 0; }; +// template +// using unique_safearrayaccess_nothrow = unique_any_t, err_returncode_policy>>; + +// template +// using unique_safearrayaccess_failfast = unique_any_t, err_failfast_policy>>; + +// #ifdef WIL_ENABLE_EXCEPTIONS +// template +// using unique_safearrayaccess = unique_any_t, err_exception_policy>>; +// #endif + + template class safearray_t : public storage_t { + template + using unique_accessdata_t = unique_any_t, err_policy>>; + public: // forward all base class constructors... template @@ -175,7 +193,7 @@ namespace wil { RETURN_HR_IF_NULL(E_INVALIDARG, sab); RETURN_HR_IF(E_INVALIDARG, cDims < 1); - SAFEARRAY* sa = ::SafeArrayCreate(vt, cDims, sab); + auto* sa = ::SafeArrayCreate(vt, cDims, sab); RETURN_LAST_ERROR_IF_NULL(sa); storage_t::reset(sa); return S_OK; @@ -216,6 +234,20 @@ namespace wil return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); } + result copy(storage_t::pointer psa) + { + HRESULT hr = [&]() + { + auto dest = invalid_value(); + + RETURN_IF_FAILED(::SafeArrayCopy(psa, &dest)); + storage_t::reset(dest); + return S_OK; + }(); + + return err_policy::HResult(hr); + } + // Lock Helper WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT { @@ -237,10 +269,15 @@ namespace wil ubound(nDim, &result); return result; } - + WI_NODISCARD ULONG count() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG result = 0; + count(&result); + return result; + } private: - }; From 57f39f8db3e89bfcc8834e70ddc887b720634e97 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Thu, 6 Aug 2020 11:21:30 -0700 Subject: [PATCH 04/34] Renamed Traits Class --- include/wil/Safearrays.h | 43 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 1d5085aa..1ab36790 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -27,25 +27,26 @@ namespace wil namespace details { template - struct TypeTraits{}; - - template<> struct TypeTraits { enum { type = VT_I1 }; }; - template<> struct TypeTraits { enum { type = VT_I2 }; }; - template<> struct TypeTraits { enum { type = VT_I4 }; }; - template<> struct TypeTraits { enum { type = VT_I4 }; }; - template<> struct TypeTraits { enum { type = VT_I8 }; }; - template<> struct TypeTraits<__int64> { enum { type = VT_I8 }; }; - template<> struct TypeTraits { enum { type = VT_UI1 }; }; - template<> struct TypeTraits { enum { type = VT_UI2 }; }; - template<> struct TypeTraits { enum { type = VT_UI4 }; }; - template<> struct TypeTraits { enum { type = VT_UI4 }; }; - template<> struct TypeTraits { enum { type = VT_UI8 }; }; - template<> struct TypeTraits { enum { type = VT_UI8 }; }; - template<> struct TypeTraits { enum { type = VT_R4 }; }; - template<> struct TypeTraits { enum { type = VT_R8 }; }; + struct VarTraits{}; + + template<> struct VarTraits { enum { type = VT_I1 }; }; + template<> struct VarTraits { enum { type = VT_I2 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I8 }; }; + template<> struct VarTraits<__int64> { enum { type = VT_I8 }; }; + template<> struct VarTraits { enum { type = VT_UI1 }; }; + template<> struct VarTraits { enum { type = VT_UI2 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI8 }; }; + template<> struct VarTraits { enum { type = VT_UI8 }; }; + template<> struct VarTraits { enum { type = VT_R4 }; }; + template<> struct VarTraits { enum { type = VT_R8 }; }; + template<> struct VarTraits { enum { type = VT_BSTR }; }; template - struct TypeTraits> { enum { type = VT_UNKNOWN }; }; + struct VarTraits> { enum { type = VT_UNKNOWN }; }; inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT { @@ -210,6 +211,14 @@ namespace wil return err_policy::HResult(create(vt, 1, &bounds)); } + template + result create(UINT cElements, LONG lowerBound = 0) + { + constexpr auto vt = static_cast(details::VarTraits::type); + + return err_policy::Hresult(create(vt, cElements, lowerBound)); + } + ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } result lbound(UINT nDim, LONG* pLbound) const From 72f7832c26e97cb4ae4cde95113fe4edb6ed4dbf Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Tue, 11 Aug 2020 18:50:40 -0700 Subject: [PATCH 05/34] Basic implementation of safearray_t, safearray_data_t, and some unit tests --- include/wil/Safearrays.h | 229 ++++++++++++++++++++++----------- tests/SafearrayTests.cpp | 77 +++++++++++ tests/app/CMakeLists.txt | 1 + tests/cpplatest/CMakeLists.txt | 1 + tests/noexcept/CMakeLists.txt | 1 + tests/normal/CMakeLists.txt | 1 + tests/win7/CMakeLists.txt | 1 + 7 files changed, 239 insertions(+), 72 deletions(-) create mode 100644 tests/SafearrayTests.cpp diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 1ab36790..880aac7f 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -17,7 +17,6 @@ #include // new(std::nothrow) #include "resource.h" // unique_hkey -#include namespace wil { @@ -34,61 +33,77 @@ namespace wil template<> struct VarTraits { enum { type = VT_I4 }; }; template<> struct VarTraits { enum { type = VT_I4 }; }; template<> struct VarTraits { enum { type = VT_I8 }; }; - template<> struct VarTraits<__int64> { enum { type = VT_I8 }; }; template<> struct VarTraits { enum { type = VT_UI1 }; }; template<> struct VarTraits { enum { type = VT_UI2 }; }; template<> struct VarTraits { enum { type = VT_UI4 }; }; template<> struct VarTraits { enum { type = VT_UI4 }; }; template<> struct VarTraits { enum { type = VT_UI8 }; }; - template<> struct VarTraits { enum { type = VT_UI8 }; }; template<> struct VarTraits { enum { type = VT_R4 }; }; template<> struct VarTraits { enum { type = VT_R8 }; }; template<> struct VarTraits { enum { type = VT_BSTR }; }; - - template - struct VarTraits> { enum { type = VT_UNKNOWN }; }; + template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; + template<> struct VarTraits { enum { type = VT_VARIANT }; }; inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT { + __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); } + inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); + } + inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT { + __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); } inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT { + __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } - inline HRESULT SafeArrayCountElements( SAFEARRAY* psa, ULONG* pCount ) + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT + { + WI_ASSERT(sab != nullptr); + WI_ASSERT(cDims > 0); + psa = ::SafeArrayCreate(vt, cDims, sab); + RETURN_LAST_ERROR_IF_NULL(psa); + return S_OK; + } + + inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT { - RETURN_HR_IF_NULL(E_INVALIDARG, psa); RETURN_HR_IF_NULL(E_POINTER, pCount); - ULONG nDims = ::SafeArrayGetDim(psa); - ULONGLONG result = 1; - LONG nLbound = 0; - LONG nUbound = 0; - for ( UINT i = 1 ; i <= nDims ; ++i ) - { - RETURN_IF_FAILED(::SafeArrayGetLBound(psa, i, &nLbound)); - RETURN_IF_FAILED(::SafeArrayGetUBound(psa, i, &nUbound)); - result *= (nUbound - nLbound + 1); - if ( result > ULONG_MAX ) + if ( psa != nullptr ) + { + ULONGLONG result = 1; + for (UINT i = 0; i < psa->cDims; ++i) { - RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + result *= psa->rgsabound[i].cElements; + if (result > ULONG_MAX) + { + RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + } } - } - *pCount = static_cast(result); + *pCount = static_cast(result); + } + else + { + // If it's invalid, it doesn't contain any elements + *pCount = 0; + } return S_OK; } typedef resource_policy safearray_resource_policy; - //typedef resource_policy safearray_lock_resource_policy; typedef resource_policy safearray_accessdata_resource_policy; } @@ -100,37 +115,38 @@ namespace wil // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); + details::SafeArrayLock(psa); return safearray_unlock_scope_exit(psa); } template class safearraydata_t : public storage_t { + public: + typedef typename err_policy::result result; + typedef typename storage_t::pointer pointer; + public: // forward all base class constructors... template explicit safearraydata_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} - // HRESULT or void error handling... - typedef typename err_policy::result result; - // Exception-based construction - safearraydata_t(storage_t::pointer psa) + safearraydata_t(pointer psa) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(psa); } - result create(storage_t::pointer psa) + result create(pointer psa) { HRESULT hr = [&]() { + WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); RETURN_HR_IF(E_INVALIDARG, !storage_t::is_valid(psa)); RETURN_IF_FAILED(::SafeArrayAccessData(psa, &m_pBegin)); - storage_t::reset(sa); + storage_t::reset(psa); RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nCount)); - m_pEnd = m_pBegin + m_nCount; return S_OK; }(); @@ -139,47 +155,45 @@ namespace wil // Ranged-for style T* begin() WI_NOEXCEPT { return m_pBegin; } - T* end() WI_NOEXCEPT { return m_pEnd; } + T* end() WI_NOEXCEPT { return m_pBegin+m_nCount; } const T* begin() const WI_NOEXCEPT { return m_pBegin; } - const T* end() const WI_NOEXCEPT { return m_pEnd; } + const T* end() const WI_NOEXCEPT { return m_pBegin+m_nCount; } // Old style iteration ULONG count() const WI_NOEXCEPT { return m_nCount; } - WI_NODISCARD T& operator[](ULONG i) { WI_ASSERT(i < m_nCount); return *(m_pBegin + i); } + WI_NODISCARD T& operator[](ULONG i) { WI_ASSERT(i < m_nCount); return *(m_pBegin + i); } WI_NODISCARD const T& operator[](ULONG i) const { WI_ASSERT(i < m_nCount); return *(m_pBegin +i); } private: T* m_pBegin = nullptr; - T* m_pEnd = nullptr; ULONG m_nCount = 0; }; -// template -// using unique_safearrayaccess_nothrow = unique_any_t, err_returncode_policy>>; - -// template -// using unique_safearrayaccess_failfast = unique_any_t, err_failfast_policy>>; + template + using unique_safearrayaccess_nothrow = unique_any_t, err_returncode_policy>>; + template + using unique_safearrayaccess_failfast = unique_any_t, err_failfast_policy>>; -// #ifdef WIL_ENABLE_EXCEPTIONS -// template -// using unique_safearrayaccess = unique_any_t, err_exception_policy>>; -// #endif +#ifdef WIL_ENABLE_EXCEPTIONS + template + using unique_safearrayaccess = unique_any_t, err_exception_policy>>; +#endif template class safearray_t : public storage_t { + public: template using unique_accessdata_t = unique_any_t, err_policy>>; + typedef typename err_policy::result result; + typedef typename storage_t::pointer pointer; public: // forward all base class constructors... template explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} - // HRESULT or void error handling... - typedef typename err_policy::result result; - // Exception-based construction safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { @@ -192,11 +206,10 @@ namespace wil { HRESULT hr = [&]() { - RETURN_HR_IF_NULL(E_INVALIDARG, sab); - RETURN_HR_IF(E_INVALIDARG, cDims < 1); - auto* sa = ::SafeArrayCreate(vt, cDims, sab); - RETURN_LAST_ERROR_IF_NULL(sa); - storage_t::reset(sa); + auto psa = storage_t::policy::invalid_value(); + + RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); + storage_t::reset(psa); return S_OK; }(); @@ -204,29 +217,62 @@ namespace wil } // Single Dimension specialization - result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0 ) + result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { - auto bounds = SAFEARRAYBOUND{cElements, lowerBound}; + HRESULT hr = [&]() + { + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + auto psa = storage_t::policy::invalid_value(); - return err_policy::HResult(create(vt, 1, &bounds)); - } + RETURN_IF_FAILED(details::SafeArrayCreate(vt, 1, &bounds, psa)); + storage_t::reset(psa); + return S_OK; + }(); + return err_policy::HResult(hr); + } template result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); + HRESULT hr = [&]() + { + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + auto psa = storage_t::policy::invalid_value(); + + RETURN_IF_FAILED(details::SafeArrayCreate(vt, 1, &bounds, psa)); + storage_t::reset(psa); + return S_OK; + }(); - return err_policy::Hresult(create(vt, cElements, lowerBound)); + return err_policy::HResult(hr); } - ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + // Create a Copy + result create(pointer psaSrc) + { + HRESULT hr = [&]() + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); + storage_t::reset(psa); + return S_OK; + }(); + + return err_policy::HResult(hr); + } + + // Dimensions, Sizes, Boundaries + ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } result lbound(UINT nDim, LONG* pLbound) const { return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); } result lbound(LONG* pLbound) const { + WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); } result ubound(UINT nDim, LONG* pUbound) const @@ -235,32 +281,64 @@ namespace wil } result ubound(LONG* pUbound) const { + WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); } - result count(ULONG* pCount) const { return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); } - result copy(storage_t::pointer psa) + // Lock Helper + WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT { - HRESULT hr = [&]() - { - auto dest = invalid_value(); - - RETURN_IF_FAILED(::SafeArrayCopy(psa, &dest)); - storage_t::reset(dest); - return S_OK; - }(); + return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + } - return err_policy::HResult(hr); + result put_element(void* pv, LONG* pIndices) + { + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); + } + result put_element(void* pv, LONG nIndex) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + } + template + result put_element(T t, LONG* pIndices) + { + WI_ASSERT(sizeof(t) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, &t)); + } + template + result put_element(T t, LONG nIndex) + { + WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); } - // Lock Helper - WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT + result get_element(void* pv, LONG* pIndices) { - return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + result get_element(void* pv, LONG nIndex) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + } + template + result get_element(T& t, LONG* pIndices) + { + WI_ASSERT(sizeof(t) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + } + template + result get_element(T& t, LONG nIndex) + { + WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } // Exception-based helper functions @@ -289,11 +367,18 @@ namespace wil private: }; + using unique_safearray_nothrow = unique_any_t, err_returncode_policy>>; + using unique_safearray_failfast = unique_any_t, err_failfast_policy>>; +#ifdef WIL_ENABLE_EXCEPTIONS + using unique_safearray = unique_any_t, err_exception_policy>>; +#endif + +#endif #ifdef WIL_ENABLE_EXCEPTIONS #endif // WIL_ENABLE_EXCEPTIONS } // namespace wil #endif -#endif + diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp new file mode 100644 index 00000000..107ada6b --- /dev/null +++ b/tests/SafearrayTests.cpp @@ -0,0 +1,77 @@ + +#include +#include + +#include "common.h" + + +TEST_CASE("SafearrayTests::Create", "[safearray][create]") +{ + SECTION("Create SafeArray - No Throw") + { + constexpr auto SIZE = 256; + + wil::unique_safearray_nothrow sa; + LONG val = 0; + ULONG count = 0; + REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == 1); + REQUIRE_SUCCEEDED(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_SUCCEEDED(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_SUCCEEDED(sa.ubound(&val)); + REQUIRE(val == SIZE-1); + + sa.reset(); + REQUIRE(!sa); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create SafeArray - Exceptions") + { + constexpr auto SIZE = 256; + + wil::unique_safearray sa; + + REQUIRE_NOTHROW(sa.create(VT_UI1, SIZE)); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == 1); + REQUIRE(sa.count() == SIZE); + REQUIRE(sa.lbound() == 0); + REQUIRE(sa.ubound() == SIZE-1); + + sa.reset(); + REQUIRE(!sa); + } +#endif +} + + +TEST_CASE("SafearrayTests::Lock", "[safearray][lock]") +{ + SECTION("Lock SafeArray") + { + constexpr auto SIZE = 256; + + wil::unique_safearray_nothrow sa; + + REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); + + const auto startingLocks = sa.get()->cLocks; + + // Control lifetime of Lock + { + auto lock = sa.scope_lock(); + + // Verify Lock Count increased + REQUIRE(sa.get()->cLocks > startingLocks); + } + + const auto endingLocks = sa.get()->cLocks; + + REQUIRE(startingLocks == endingLocks); + } + +} diff --git a/tests/app/CMakeLists.txt b/tests/app/CMakeLists.txt index 1118f235..d85335fc 100644 --- a/tests/app/CMakeLists.txt +++ b/tests/app/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(witest.app PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafearrayTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp diff --git a/tests/cpplatest/CMakeLists.txt b/tests/cpplatest/CMakeLists.txt index 7ac24aeb..4cb95a09 100644 --- a/tests/cpplatest/CMakeLists.txt +++ b/tests/cpplatest/CMakeLists.txt @@ -26,6 +26,7 @@ target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafearrayTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp diff --git a/tests/noexcept/CMakeLists.txt b/tests/noexcept/CMakeLists.txt index 76feacdd..2d59888e 100644 --- a/tests/noexcept/CMakeLists.txt +++ b/tests/noexcept/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources(witest.noexcept PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafearrayTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp diff --git a/tests/normal/CMakeLists.txt b/tests/normal/CMakeLists.txt index b146e8e4..d7097729 100644 --- a/tests/normal/CMakeLists.txt +++ b/tests/normal/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(witest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafearrayTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp diff --git a/tests/win7/CMakeLists.txt b/tests/win7/CMakeLists.txt index 8eebc498..bfb93f3c 100644 --- a/tests/win7/CMakeLists.txt +++ b/tests/win7/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(witest.win7 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafearrayTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp From 961d0cd8488930b594cafc9ee01cfc0052083a9b Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Tue, 11 Aug 2020 19:20:12 -0700 Subject: [PATCH 06/34] Added copy() operation for exception based classes --- include/wil/Safearrays.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 880aac7f..b63e01d8 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -186,6 +186,8 @@ namespace wil public: template using unique_accessdata_t = unique_any_t, err_policy>>; + + using unique_safearray_t = unique_any_t>; typedef typename err_policy::result result; typedef typename storage_t::pointer pointer; @@ -363,6 +365,14 @@ namespace wil count(&result); return result; } + WI_NODISCARD unique_safearray_t copy() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto result = unique_safearray_t{}; + result.create(storage_t::get()); + return result; + } private: }; From c3a930bb03b4549c3313a526900989ad2f63972a Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Wed, 12 Aug 2020 17:45:10 -0700 Subject: [PATCH 07/34] Got Access Data working --- .gitignore | 8 ++++++++ include/wil/Safearrays.h | 44 ++++++++++++++++++++++++++++++---------- tests/SafearrayTests.cpp | 29 +++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 3973a16b..3de71a71 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,11 @@ ASALocalRun/ # CMake/Build output build/ +/packaging +/build_tools/nuget.exe +*.vcxproj +*.filters +*.sln +/CMakeCache.txt +cmake_install.cmake +CMakeFiles/ \ No newline at end of file diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index b63e01d8..a8eb6d20 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -68,6 +68,13 @@ namespace wil FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } + template + inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); + } + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT { WI_ASSERT(sab != nullptr); @@ -143,8 +150,7 @@ namespace wil HRESULT hr = [&]() { WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - RETURN_HR_IF(E_INVALIDARG, !storage_t::is_valid(psa)); - RETURN_IF_FAILED(::SafeArrayAccessData(psa, &m_pBegin)); + details::SafeArrayAccessData(psa, m_pBegin); storage_t::reset(psa); RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nCount)); return S_OK; @@ -343,27 +349,34 @@ namespace wil return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } + // Data Access + template + result access_data(unique_accessdata_t& data) + { + return err_policy::HResult(data.create(storage_t::get())); + } + // Exception-based helper functions WI_NODISCARD LONG lbound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG result = 0; - lbound(nDim, &result); - return result; + LONG nResult = 0; + lbound(nDim, &nResult); + return nResult; } WI_NODISCARD LONG ubound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG result = 0; - ubound(nDim, &result); - return result; + LONG nResult = 0; + ubound(nDim, &nResult); + return nResult; } WI_NODISCARD ULONG count() const { static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG result = 0; - count(&result); - return result; + ULONG nResult = 0; + count(&nResult); + return nResult; } WI_NODISCARD unique_safearray_t copy() const { @@ -373,6 +386,15 @@ namespace wil result.create(storage_t::get()); return result; } + template + WI_NODISCARD unique_accessdata_t access_data() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto data = unique_accessdata_t{}; + data.create(storage_t::get()); + return data; + } private: }; diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 107ada6b..e991768b 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -48,7 +48,6 @@ TEST_CASE("SafearrayTests::Create", "[safearray][create]") #endif } - TEST_CASE("SafearrayTests::Lock", "[safearray][lock]") { SECTION("Lock SafeArray") @@ -75,3 +74,31 @@ TEST_CASE("SafearrayTests::Lock", "[safearray][lock]") } } + +TEST_CASE("SafearrayTests::AccessData", "[safearray][data]") +{ + SECTION("Basic") + { + constexpr auto SIZE = 32; + + wil::unique_safearray_nothrow sa; + + REQUIRE_SUCCEEDED(sa.create(VT_UI4, SIZE)); + + for (auto i = 0; i < SIZE; ++i) + { + sa.put_element((1 << i), i); + } + + { + wil::unique_safearray_nothrow::unique_accessdata_t data; + UINT counter = 0; + REQUIRE_SUCCEEDED(sa.access_data(data)); + for (auto& n : data) + { + REQUIRE(n == static_cast(1 << counter++)); + } + } + } + +} From dae0f559d527befa1bd767a2dba79acdc5cf65dc Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Thu, 13 Aug 2020 17:14:15 -0700 Subject: [PATCH 08/34] Added element type to safearray_t which can be set to VOID and SFINAE will expose functions that require a type or it can be another type and SFINAE will only expose functions assuming that type Test functions are a mess. --- include/wil/Safearrays.h | 149 +++++++++++++++++++++++---------- tests/SafearrayTests.cpp | 173 ++++++++++++++++++++++++++++----------- 2 files changed, 234 insertions(+), 88 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index a8eb6d20..93e4675b 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -16,6 +16,7 @@ #endif #include // new(std::nothrow) +#include "wistd_type_traits.h" #include "resource.h" // unique_hkey namespace wil @@ -62,17 +63,17 @@ namespace wil FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); } - inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT + template + inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT { __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); + FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); } - template - inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT + inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT { __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); + FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT @@ -185,17 +186,27 @@ namespace wil using unique_safearrayaccess = unique_any_t, err_exception_policy>>; #endif - - template + // Add safearray functionality to the given storage type using the given error policy + // element_t is the C++ type for the elements in the safearray OR use void to enable + // the methods that take the type as a parameter + template class safearray_t : public storage_t { + private: + // SFINAE Helpers to improve readability + // Filters functions that don't require a type because a type was provided by the class type + template using IsSafeArrayTyped = typename wistd::enable_if::value, int>::type; + // Filters functions that require a type because the class type doesn't provide one (element_t == void) + template using IsSafeArrayNotTyped = typename wistd::enable_if::value, int>::type; + public: template using unique_accessdata_t = unique_any_t, err_policy>>; - using unique_safearray_t = unique_any_t>; + using unique_safearray_t = unique_any_t>; typedef typename err_policy::result result; typedef typename storage_t::pointer pointer; + typedef element_t elemtype; public: // forward all base class constructors... @@ -203,6 +214,7 @@ namespace wil explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} // Exception-based construction + template::value, int>::type = 0> safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); @@ -210,50 +222,51 @@ namespace wil } // Low-level, arbitrary number of dimensions + template::value, int>::type = 0> result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) { - HRESULT hr = [&]() - { - auto psa = storage_t::policy::invalid_value(); - - RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); - storage_t::reset(psa); - return S_OK; - }(); - - return err_policy::HResult(hr); + return err_policy::HResult(_create(vt, cDims, sab)); } // Single Dimension specialization + template::value, int>::type = 0> result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { - HRESULT hr = [&]() - { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - auto psa = storage_t::policy::invalid_value(); - - RETURN_IF_FAILED(details::SafeArrayCreate(vt, 1, &bounds, psa)); - storage_t::reset(psa); - return S_OK; - }(); - - return err_policy::HResult(hr); + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); } - template + template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); - HRESULT hr = [&]() - { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - auto psa = storage_t::policy::invalid_value(); + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } - RETURN_IF_FAILED(details::SafeArrayCreate(vt, 1, &bounds, psa)); - storage_t::reset(psa); - return S_OK; - }(); + // Uses the fixed element type defined by the class + // Exception-based construction + template::value, int>::type = 0> + safearray_t(UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(cElements, lowerBound); + } - return err_policy::HResult(hr); + // Low-level, arbitrary number of dimensions + template::value, int>::type = 0> + result create(UINT cDims, SAFEARRAYBOUND* sab) + { + constexpr auto vt = static_cast(details::VarTraits::type); + return err_policy::HResult(_create(vt, cDims, sab)); + } + + // Single Dimension specialization + template::value, int>::type = 0> + result create(UINT cElements, LONG lowerBound = 0) + { + constexpr auto vt = static_cast(details::VarTraits::type); + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); } // Create a Copy @@ -397,13 +410,65 @@ namespace wil } private: + HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); + storage_t::reset(psa); + return S_OK; + } }; - using unique_safearray_nothrow = unique_any_t, err_returncode_policy>>; - using unique_safearray_failfast = unique_any_t, err_failfast_policy>>; + using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; + using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; + using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; + using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; + using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; + using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; + using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; + using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; + using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; + using unique_int_safearray_failfast = unique_any_t, err_failfast_policy, int>>; + using unique_longlong_safearray_nothrow = unique_any_t, err_returncode_policy, long long>>; + using unique_longlong_safearray_failfast = unique_any_t, err_failfast_policy, long long>>; + using unique_byte_safearray_nothrow = unique_any_t, err_returncode_policy, byte>>; + using unique_byte_safearray_failfast = unique_any_t, err_failfast_policy, byte>>; + using unique_word_safearray_nothrow = unique_any_t, err_returncode_policy, WORD>>; + using unique_word_safearray_failfast = unique_any_t, err_failfast_policy, WORD>>; + using unique_dword_safearray_nothrow = unique_any_t, err_returncode_policy, DWORD>>; + using unique_dword_safearray_failfast = unique_any_t, err_failfast_policy, DWORD>>; + using unique_ulonglong_safearray_nothrow = unique_any_t, err_returncode_policy, ULONGLONG>>; + using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; + using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; + using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; + using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; + using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; + using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; + using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; + using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; + using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; + using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; + using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; + using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; + using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; #ifdef WIL_ENABLE_EXCEPTIONS - using unique_safearray = unique_any_t, err_exception_policy>>; + using unique_safearray = unique_any_t, err_exception_policy, void>>; + using unique_char_safearray = unique_any_t, err_exception_policy, char>>; + using unique_short_safearray = unique_any_t, err_exception_policy, short>>; + using unique_long_safearray = unique_any_t, err_exception_policy, long>>; + using unique_int_safearray = unique_any_t, err_exception_policy, int>>; + using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; + using unique_byte_safearray = unique_any_t, err_exception_policy, byte>>; + using unique_word_safearray = unique_any_t, err_exception_policy, WORD>>; + using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; + using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; + using unique_float_safearray = unique_any_t, err_exception_policy, float>>; + using unique_double_safearray = unique_any_t, err_exception_policy, double>>; + using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; + using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; + using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; + using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif #endif diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index e991768b..4c5488de 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -4,73 +4,154 @@ #include "common.h" +#define TEST_ALL_SA_TYPES(fn) \ + (fn)(); \ + fn +inline void TestCreate() { - SECTION("Create SafeArray - No Throw") - { - constexpr auto SIZE = 256; + constexpr auto SIZE = 256; - wil::unique_safearray_nothrow sa; - LONG val = 0; - ULONG count = 0; + auto sa = safearray_t{}; + LONG val = 0; + ULONG count = 0; + if constexpr (wistd::is_same::value) + { REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); - REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == 1); + } + else + { + REQUIRE_SUCCEEDED(sa.create(SIZE)); + } + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == 1); + if constexpr (wistd::is_same::value) + { + REQUIRE_NOTHROW(sa.count() == SIZE); + REQUIRE_NOTHROW(sa.lbound() == 0); + REQUIRE_NOTHROW(sa.ubound() == SIZE - 1); + } + else if constexpr (wistd::is_same::value) + { + REQUIRE_NOCRASH(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_NOCRASH(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_NOCRASH(sa.ubound(&val)); + REQUIRE(val == SIZE - 1); + } + else + { REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_SUCCEEDED(sa.ubound(&val)); - REQUIRE(val == SIZE-1); - - sa.reset(); - REQUIRE(!sa); + REQUIRE(val == SIZE - 1); } -#ifdef WIL_ENABLE_EXCEPTIONS - SECTION("Create SafeArray - Exceptions") - { - constexpr auto SIZE = 256; + sa.reset(); + REQUIRE(!sa); +} - wil::unique_safearray sa; +//template +//void TestCreate() +//{ +// constexpr auto SIZE = 256; +// +// auto sa = safearray_t{}; +// LONG val = 0; +// ULONG count = 0; +// REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); +// REQUIRE(sa); +// REQUIRE(sa.dim() == 1); +// REQUIRE(sa.elemsize() == 1); +// REQUIRE_SUCCEEDED(sa.count(&count)); +// REQUIRE(count == SIZE); +// REQUIRE_SUCCEEDED(sa.lbound(&val)); +// REQUIRE(val == 0); +// REQUIRE_SUCCEEDED(sa.ubound(&val)); +// REQUIRE(val == SIZE - 1); +// +// sa.reset(); +// REQUIRE(!sa); +//} - REQUIRE_NOTHROW(sa.create(VT_UI1, SIZE)); - REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == 1); - REQUIRE(sa.count() == SIZE); - REQUIRE(sa.lbound() == 0); - REQUIRE(sa.ubound() == SIZE-1); +TEST_CASE("SafearrayTests::Create", "[safearray][create]") +{ + TEST_ALL_SA_TYPES(TestCreate); +// SECTION("Create SafeArray - No Throw") +// { +// } +// +//#ifdef WIL_ENABLE_EXCEPTIONS +// SECTION("Create SafeArray - Exceptions") +// { +// constexpr auto SIZE = 256; +// +// wil::unique_safearray sa; +// +// REQUIRE_NOTHROW(sa.create(VT_UI1, SIZE)); +// REQUIRE(sa.dim() == 1); +// REQUIRE(sa.elemsize() == 1); +// REQUIRE(sa.count() == SIZE); +// REQUIRE(sa.lbound() == 0); +// REQUIRE(sa.ubound() == SIZE-1); +// +// sa.reset(); +// REQUIRE(!sa); +// } +//#endif +// +// SECTION("Create Typed SafeArray - No Throw") +// { +// constexpr auto SIZE = 256; +// auto fn = [](auto& sa) +// { +// REQUIRE_SUCCEEDED(sa.create(SIZE)); +// LONG val = 0; +// ULONG count = 0; +// REQUIRE(sa.dim() == 1); +// REQUIRE(sa.elemsize() == 1); +// REQUIRE_SUCCEEDED(sa.count(&count)); +// REQUIRE(count == SIZE); +// REQUIRE_SUCCEEDED(sa.lbound(&val)); +// REQUIRE(val == 0); +// REQUIRE_SUCCEEDED(sa.ubound(&val)); +// REQUIRE(val == SIZE - 1); +// +// sa.reset(); +// REQUIRE(!sa); +// }; +// } +} - sa.reset(); - REQUIRE(!sa); +template +void SafeArrayLockTests() +{ + auto sa = safearray_t{}; + sa.create(VT_UI1, 16); + REQUIRE(sa); + const auto startingLocks = sa.get()->cLocks; + { + auto lock = sa.scope_lock(); + REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased } -#endif + REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down } TEST_CASE("SafearrayTests::Lock", "[safearray][lock]") { SECTION("Lock SafeArray") { - constexpr auto SIZE = 256; - - wil::unique_safearray_nothrow sa; - - REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); - - const auto startingLocks = sa.get()->cLocks; - - // Control lifetime of Lock - { - auto lock = sa.scope_lock(); - - // Verify Lock Count increased - REQUIRE(sa.get()->cLocks > startingLocks); - } - - const auto endingLocks = sa.get()->cLocks; - - REQUIRE(startingLocks == endingLocks); + SafeArrayLockTests(); + SafeArrayLockTests(); +#ifdef WIL_ENABLE_EXCEPTIONS + SafeArrayLockTests(); +#endif } } From 07245283c9d53cb4a10a0c1330afd85b3c331dc2 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Fri, 14 Aug 2020 14:08:57 -0700 Subject: [PATCH 09/34] Created way to run tests against many safearray types. Cleaned up the SFINAE. --- include/wil/Safearrays.h | 31 +++--- tests/SafearrayTests.cpp | 218 +++++++++++++++++++++++++-------------- 2 files changed, 154 insertions(+), 95 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 93e4675b..db42f33c 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -43,6 +43,7 @@ namespace wil template<> struct VarTraits { enum { type = VT_R8 }; }; template<> struct VarTraits { enum { type = VT_BSTR }; }; template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; + template<> struct VarTraits { enum { type = VT_DISPATCH }; }; template<> struct VarTraits { enum { type = VT_VARIANT }; }; inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT @@ -195,9 +196,9 @@ namespace wil private: // SFINAE Helpers to improve readability // Filters functions that don't require a type because a type was provided by the class type - template using IsSafeArrayTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfTyped = typename wistd::enable_if::value, int>::type; // Filters functions that require a type because the class type doesn't provide one (element_t == void) - template using IsSafeArrayNotTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; public: template @@ -214,28 +215,34 @@ namespace wil explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} // Exception-based construction - template::value, int>::type = 0> + template = 0> safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(vt, cElements, lowerBound); } + template = 0> + safearray_t(UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(cElements, lowerBound); + } // Low-level, arbitrary number of dimensions - template::value, int>::type = 0> + template = 0> result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) { return err_policy::HResult(_create(vt, cDims, sab)); } // Single Dimension specialization - template::value, int>::type = 0> + template = 0> result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; return err_policy::HResult(_create(vt, 1, &bounds)); } - template::value, int>::type = 0> + template = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -244,16 +251,8 @@ namespace wil } // Uses the fixed element type defined by the class - // Exception-based construction - template::value, int>::type = 0> - safearray_t(UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(cElements, lowerBound); - } - // Low-level, arbitrary number of dimensions - template::value, int>::type = 0> + template = 0> result create(UINT cDims, SAFEARRAYBOUND* sab) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -261,7 +260,7 @@ namespace wil } // Single Dimension specialization - template::value, int>::type = 0> + template = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 4c5488de..630e549f 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -4,107 +4,167 @@ #include "common.h" -#define TEST_ALL_SA_TYPES(fn) \ - (fn)(); \ - fn -inline void TestCreate() +void TestCreateTyped_NoThrow() { constexpr auto SIZE = 256; auto sa = safearray_t{}; LONG val = 0; ULONG count = 0; - if constexpr (wistd::is_same::value) - { - REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); - } - else - { - REQUIRE_SUCCEEDED(sa.create(SIZE)); - } + REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == 1); - if constexpr (wistd::is_same::value) + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE_SUCCEEDED(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_SUCCEEDED(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_SUCCEEDED(sa.ubound(&val)); + REQUIRE(val == SIZE - 1); { - REQUIRE_NOTHROW(sa.count() == SIZE); - REQUIRE_NOTHROW(sa.lbound() == 0); - REQUIRE_NOTHROW(sa.ubound() == SIZE - 1); + const auto startingLocks = sa.get()->cLocks; + { + auto lock = sa.scope_lock(); + REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased + } + REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down } - else if constexpr (wistd::is_same::value) + sa.reset(); + REQUIRE(!sa); +} + +template +void TestCreateTyped_FailFast() +{ + constexpr auto SIZE = 256; + + auto sa = safearray_t{}; + LONG val = 0; + ULONG count = 0; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE_NOCRASH(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_NOCRASH(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_NOCRASH(sa.ubound(&val)); + REQUIRE(val == SIZE - 1); { - REQUIRE_NOCRASH(sa.count(&count)); - REQUIRE(count == SIZE); - REQUIRE_NOCRASH(sa.lbound(&val)); - REQUIRE(val == 0); - REQUIRE_NOCRASH(sa.ubound(&val)); - REQUIRE(val == SIZE - 1); + const auto startingLocks = sa.get()->cLocks; + { + auto lock = sa.scope_lock(); + REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased + } + REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down } - else + sa.reset(); + REQUIRE(!sa); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void TestCreateTyped() +{ + constexpr auto SIZE = 256U; + + safearray_t sa; + REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE_NOTHROW(sa.count() == SIZE); + REQUIRE_NOTHROW(sa.lbound() == 0); + REQUIRE_NOTHROW(sa.ubound() == SIZE-1); { - REQUIRE_SUCCEEDED(sa.count(&count)); - REQUIRE(count == SIZE); - REQUIRE_SUCCEEDED(sa.lbound(&val)); - REQUIRE(val == 0); - REQUIRE_SUCCEEDED(sa.ubound(&val)); - REQUIRE(val == SIZE - 1); + const auto startingLocks = sa.get()->cLocks; + { + auto lock = sa.scope_lock(); + REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased + } + REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down } - sa.reset(); REQUIRE(!sa); } +#endif + +#define _CREATE_TYPED_NOTHROW(type) TestCreateTyped_NoThrow(); +#define _CREATE_TYPED_FAILFAST(type) TestCreateTyped_FailFast(); +#define _CREATE_TYPED(type) TestCreateTyped(); -//template -//void TestCreate() -//{ -// constexpr auto SIZE = 256; -// -// auto sa = safearray_t{}; -// LONG val = 0; -// ULONG count = 0; -// REQUIRE_SUCCEEDED(sa.create(VT_UI1, SIZE)); -// REQUIRE(sa); -// REQUIRE(sa.dim() == 1); -// REQUIRE(sa.elemsize() == 1); -// REQUIRE_SUCCEEDED(sa.count(&count)); -// REQUIRE(count == SIZE); -// REQUIRE_SUCCEEDED(sa.lbound(&val)); -// REQUIRE(val == 0); -// REQUIRE_SUCCEEDED(sa.ubound(&val)); -// REQUIRE(val == SIZE - 1); -// -// sa.reset(); -// REQUIRE(!sa); -//} TEST_CASE("SafearrayTests::Create", "[safearray][create]") { - TEST_ALL_SA_TYPES(TestCreate); -// SECTION("Create SafeArray - No Throw") -// { -// } -// -//#ifdef WIL_ENABLE_EXCEPTIONS -// SECTION("Create SafeArray - Exceptions") -// { -// constexpr auto SIZE = 256; -// -// wil::unique_safearray sa; -// -// REQUIRE_NOTHROW(sa.create(VT_UI1, SIZE)); -// REQUIRE(sa.dim() == 1); -// REQUIRE(sa.elemsize() == 1); -// REQUIRE(sa.count() == SIZE); -// REQUIRE(sa.lbound() == 0); -// REQUIRE(sa.ubound() == SIZE-1); -// -// sa.reset(); -// REQUIRE(!sa); -// } -//#endif + SECTION("Create SafeArray - No Throw") + { + RUN_TEST_TYPED_NOTHROW(_CREATE_TYPED_NOTHROW); + } + + SECTION("Create SafeArray - FailFast") + { + RUN_TEST_TYPED_FAILFAST(_CREATE_TYPED_FAILFAST); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create SafeArray - Exceptions") + { + RUN_TEST_TYPED(_CREATE_TYPED); + } +#endif // // SECTION("Create Typed SafeArray - No Throw") // { From 6453ccfc999f4ffcc987fa41c626997daf54f30a Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sat, 15 Aug 2020 16:14:32 -0700 Subject: [PATCH 10/34] More Progress --- include/wil/Safearrays.h | 106 +++++++++++++++++---- tests/SafearrayTests.cpp | 201 +++++++++++++++------------------------ 2 files changed, 167 insertions(+), 140 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index db42f33c..e6544cbe 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -40,7 +40,10 @@ namespace wil template<> struct VarTraits { enum { type = VT_UI4 }; }; template<> struct VarTraits { enum { type = VT_UI8 }; }; template<> struct VarTraits { enum { type = VT_R4 }; }; - template<> struct VarTraits { enum { type = VT_R8 }; }; + template<> struct VarTraits { enum { type = VT_BOOL }; }; + template<> struct VarTraits { enum { type = VT_DATE }; }; + template<> struct VarTraits { enum { type = VT_CY }; }; + template<> struct VarTraits { enum { type = VT_DECIMAL }; }; template<> struct VarTraits { enum { type = VT_BSTR }; }; template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; template<> struct VarTraits { enum { type = VT_DISPATCH }; }; @@ -115,6 +118,42 @@ namespace wil typedef resource_policy safearray_resource_policy; typedef resource_policy safearray_accessdata_resource_policy; + //template + //class safearray_index : private std::array + //{ + //public: + // safearray_index() : std::array{ Indexes... }; + // LPLONG get() { return data(); } + // ULONG count() { return static_cast(size()); } + //}; + + //template + //class safearray_index + //{ + //public: + // safearray_index() : _value{ Index } {} + // LPLONG get() { return &_value; } + // ULONG count() { return 1; } + //private: + // LONG _value; + //}; + + //template + //class safearray_index + //{ + //public: + // safearray_index() : _indexes{Indexes} + // LPLONG get() { return _indexes; } + // ULONG count() { return 0; } + //private: + // LPLONG _indexes: + //}; + + //template + //DebugContextT& Format(const wchar_t* const lpszFormat, Args... args) noexcept + // template + // constexpr auto chars = std::array{ cs... }; + } /// @endcond @@ -315,6 +354,8 @@ namespace wil return wil::SafeArrayUnlock_scope_exit(storage_t::get()); } + // Lowest-Level functions for those who know what they're doing + // TODO: Finish safearray_index type to collapse both versions into one functioon result put_element(void* pv, LONG* pIndices) { return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); @@ -324,37 +365,54 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); } - template - result put_element(T t, LONG* pIndices) + result get_element(void* pv, LONG* pIndices) { - WI_ASSERT(sizeof(t) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, &t)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); } - template - result put_element(T t, LONG nIndex) + result get_element(void* pv, LONG nIndex) { - WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); } - result get_element(void* pv, LONG* pIndices) + //template + //result put_element(const T& t, LONG* pIndices) + //{ + // WI_ASSERT(sizeof(t) == elemsize()); + // return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, &t)); + //} + //template + //result get_element(T& t, LONG* pIndices) + //{ + // WI_ASSERT(sizeof(t) == elemsize()); + // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + //} + template = 0> + result put_element(const T& t, LONG nIndex) { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); } - result get_element(void* pv, LONG nIndex) + + template = 0> + result get_element(T& t, LONG nIndex) { + WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } - template - result get_element(T& t, LONG* pIndices) + + template = 0> + result put_element(const element_t& t, LONG nIndex) { WI_ASSERT(sizeof(t) == elemsize()); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); } - template - result get_element(T& t, LONG nIndex) + + template = 0> + result get_element(element_t& t, LONG nIndex) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); @@ -450,6 +508,14 @@ namespace wil using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; + using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; + using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; + using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; + using unique_date_safearray_failfast = unique_any_t, err_failfast_policy, DATE>>; + using unique_currency_safearray_nothrow = unique_any_t, err_returncode_policy, CURRENCY>>; + using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; + using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; + using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; #ifdef WIL_ENABLE_EXCEPTIONS using unique_safearray = unique_any_t, err_exception_policy, void>>; @@ -468,6 +534,10 @@ namespace wil using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; + using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; + using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; + using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; + using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; #endif #endif diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 630e549f..16ef1e3e 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -4,56 +4,83 @@ #include "common.h" -#define RUN_TEST_TYPED_NOTHROW(test) WI_FOREACH(test, \ - wil::unique_char_safearray_nothrow, \ - wil::unique_short_safearray_nothrow, \ - wil::unique_long_safearray_nothrow, \ - wil::unique_int_safearray_nothrow, \ - wil::unique_longlong_safearray_nothrow, \ - wil::unique_byte_safearray_nothrow, \ - wil::unique_word_safearray_nothrow, \ - wil::unique_dword_safearray_nothrow, \ - wil::unique_ulonglong_safearray_nothrow, \ - wil::unique_float_safearray_nothrow, \ - wil::unique_double_safearray_nothrow, \ - wil::unique_bstr_safearray_nothrow, \ - wil::unique_unknown_safearray_nothrow, \ - wil::unique_dispatch_safearray_nothrow, \ - wil::unique_variant_safearray_nothrow) +#define RUN_TEST_TYPED_NOTHROW(test) WI_FOREACH(test \ + , wil::unique_char_safearray_nothrow \ + , wil::unique_short_safearray_nothrow \ + , wil::unique_long_safearray_nothrow \ + , wil::unique_int_safearray_nothrow \ + , wil::unique_longlong_safearray_nothrow \ + , wil::unique_byte_safearray_nothrow \ + , wil::unique_word_safearray_nothrow \ + , wil::unique_dword_safearray_nothrow \ + , wil::unique_ulonglong_safearray_nothrow \ + , wil::unique_float_safearray_nothrow \ + , wil::unique_double_safearray_nothrow \ + , wil::unique_bstr_safearray_nothrow \ + , wil::unique_unknown_safearray_nothrow \ + , wil::unique_dispatch_safearray_nothrow \ + , wil::unique_variant_safearray_nothrow \ + , wil::unique_varbool_safearray_nothrow \ + , wil::unique_date_safearray_nothrow \ + , wil::unique_currency_safearray_nothrow \ + , wil::unique_decimal_safearray_nothrow \ + ) -#define RUN_TEST_TYPED_FAILFAST(test) WI_FOREACH(test, \ - wil::unique_char_safearray_failfast, \ - wil::unique_short_safearray_failfast, \ - wil::unique_long_safearray_failfast, \ - wil::unique_int_safearray_failfast, \ - wil::unique_longlong_safearray_failfast, \ - wil::unique_byte_safearray_failfast, \ - wil::unique_word_safearray_failfast, \ - wil::unique_dword_safearray_failfast, \ - wil::unique_ulonglong_safearray_failfast, \ - wil::unique_float_safearray_failfast, \ - wil::unique_double_safearray_failfast, \ - wil::unique_bstr_safearray_failfast, \ - wil::unique_unknown_safearray_failfast, \ - wil::unique_dispatch_safearray_failfast, \ - wil::unique_variant_safearray_failfast) +#define RUN_TEST_TYPED_FAILFAST(test) WI_FOREACH(test \ + , wil::unique_char_safearray_failfast \ + , wil::unique_short_safearray_failfast \ + , wil::unique_long_safearray_failfast \ + , wil::unique_int_safearray_failfast \ + , wil::unique_longlong_safearray_failfast \ + , wil::unique_byte_safearray_failfast \ + , wil::unique_word_safearray_failfast \ + , wil::unique_dword_safearray_failfast \ + , wil::unique_ulonglong_safearray_failfast \ + , wil::unique_float_safearray_failfast \ + , wil::unique_double_safearray_failfast \ + , wil::unique_bstr_safearray_failfast \ + , wil::unique_unknown_safearray_failfast \ + , wil::unique_dispatch_safearray_failfast \ + , wil::unique_variant_safearray_failfast \ + , wil::unique_varbool_safearray_failfast \ + , wil::unique_date_safearray_failfast \ + , wil::unique_currency_safearray_failfast \ + , wil::unique_decimal_safearray_failfast \ + ) -#define RUN_TEST_TYPED(test) WI_FOREACH(test, \ - wil::unique_char_safearray, \ - wil::unique_short_safearray, \ - wil::unique_long_safearray, \ - wil::unique_int_safearray, \ - wil::unique_longlong_safearray, \ - wil::unique_byte_safearray, \ - wil::unique_word_safearray, \ - wil::unique_dword_safearray, \ - wil::unique_ulonglong_safearray, \ - wil::unique_float_safearray, \ - wil::unique_double_safearray, \ - wil::unique_bstr_safearray, \ - wil::unique_unknown_safearray, \ - wil::unique_dispatch_safearray, \ - wil::unique_variant_safearray) +#define RUN_TEST_TYPED(test) WI_FOREACH(test \ + , wil::unique_char_safearray \ + , wil::unique_short_safearray \ + , wil::unique_long_safearray \ + , wil::unique_int_safearray \ + , wil::unique_longlong_safearray \ + , wil::unique_byte_safearray \ + , wil::unique_word_safearray \ + , wil::unique_dword_safearray \ + , wil::unique_ulonglong_safearray \ + , wil::unique_float_safearray \ + , wil::unique_double_safearray \ + , wil::unique_bstr_safearray \ + , wil::unique_unknown_safearray \ + , wil::unique_dispatch_safearray \ + , wil::unique_variant_safearray \ + , wil::unique_varbool_safearray \ + , wil::unique_date_safearray \ + , wil::unique_currency_safearray \ + , wil::unique_decimal_safearray \ + ) + +template +void TestLock(safearray_t& sa) +{ + REQUIRE(sa); + const auto startingLocks = sa.get()->cLocks; + { + auto lock = sa.scope_lock(); + REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased + } + REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down +} template void TestCreateTyped_NoThrow() @@ -73,14 +100,7 @@ void TestCreateTyped_NoThrow() REQUIRE(val == 0); REQUIRE_SUCCEEDED(sa.ubound(&val)); REQUIRE(val == SIZE - 1); - { - const auto startingLocks = sa.get()->cLocks; - { - auto lock = sa.scope_lock(); - REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased - } - REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down - } + TestLock(sa); sa.reset(); REQUIRE(!sa); } @@ -103,14 +123,7 @@ void TestCreateTyped_FailFast() REQUIRE(val == 0); REQUIRE_NOCRASH(sa.ubound(&val)); REQUIRE(val == SIZE - 1); - { - const auto startingLocks = sa.get()->cLocks; - { - auto lock = sa.scope_lock(); - REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased - } - REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down - } + TestLock(sa); sa.reset(); REQUIRE(!sa); } @@ -121,7 +134,7 @@ void TestCreateTyped() { constexpr auto SIZE = 256U; - safearray_t sa; + auto sa = safearray_t{}; REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); REQUIRE(sa); REQUIRE(sa.dim() == 1); @@ -129,14 +142,7 @@ void TestCreateTyped() REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE-1); - { - const auto startingLocks = sa.get()->cLocks; - { - auto lock = sa.scope_lock(); - REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased - } - REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down - } + TestLock(sa); sa.reset(); REQUIRE(!sa); } @@ -165,55 +171,6 @@ TEST_CASE("SafearrayTests::Create", "[safearray][create]") RUN_TEST_TYPED(_CREATE_TYPED); } #endif -// -// SECTION("Create Typed SafeArray - No Throw") -// { -// constexpr auto SIZE = 256; -// auto fn = [](auto& sa) -// { -// REQUIRE_SUCCEEDED(sa.create(SIZE)); -// LONG val = 0; -// ULONG count = 0; -// REQUIRE(sa.dim() == 1); -// REQUIRE(sa.elemsize() == 1); -// REQUIRE_SUCCEEDED(sa.count(&count)); -// REQUIRE(count == SIZE); -// REQUIRE_SUCCEEDED(sa.lbound(&val)); -// REQUIRE(val == 0); -// REQUIRE_SUCCEEDED(sa.ubound(&val)); -// REQUIRE(val == SIZE - 1); -// -// sa.reset(); -// REQUIRE(!sa); -// }; -// } -} - -template -void SafeArrayLockTests() -{ - auto sa = safearray_t{}; - sa.create(VT_UI1, 16); - REQUIRE(sa); - const auto startingLocks = sa.get()->cLocks; - { - auto lock = sa.scope_lock(); - REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased - } - REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down -} - -TEST_CASE("SafearrayTests::Lock", "[safearray][lock]") -{ - SECTION("Lock SafeArray") - { - SafeArrayLockTests(); - SafeArrayLockTests(); -#ifdef WIL_ENABLE_EXCEPTIONS - SafeArrayLockTests(); -#endif - } - } TEST_CASE("SafearrayTests::AccessData", "[safearray][data]") From 89d5650e0077fd0410bc9fa4e5cb94da43da5310 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sun, 16 Aug 2020 16:45:34 -0700 Subject: [PATCH 11/34] Set up Access Data functionality and implemented sample data for unit tests --- include/wil/Safearrays.h | 121 ++++++++++++++++++++-------- tests/SafearrayTests.cpp | 167 +++++++++++++++++++++++++++++++++------ 2 files changed, 231 insertions(+), 57 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index e6544cbe..ab851be1 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -40,6 +40,7 @@ namespace wil template<> struct VarTraits { enum { type = VT_UI4 }; }; template<> struct VarTraits { enum { type = VT_UI8 }; }; template<> struct VarTraits { enum { type = VT_R4 }; }; + template<> struct VarTraits { enum { type = VT_R8 }; }; template<> struct VarTraits { enum { type = VT_BOOL }; }; template<> struct VarTraits { enum { type = VT_DATE }; }; template<> struct VarTraits { enum { type = VT_CY }; }; @@ -80,12 +81,30 @@ namespace wil FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } + inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) + { + // Safearrays cannot hold type VT_NULL, so this value + // means the SAFEARRAY was null + VARTYPE vt = VT_NULL; + if (psa != nullptr) + { + if (FAILED(::SafeArrayGetVartype(psa, &vt))) + { + // Safearrays cannot hold type VT_EMPTY, so this value + // means the type couldn't be determined + vt = VT_EMPTY; + } + } + return vt; + } + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT { WI_ASSERT(sab != nullptr); WI_ASSERT(cDims > 0); psa = ::SafeArrayCreate(vt, cDims, sab); RETURN_LAST_ERROR_IF_NULL(psa); + WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); return S_OK; } @@ -173,6 +192,13 @@ namespace wil public: typedef typename err_policy::result result; typedef typename storage_t::pointer pointer; + typedef T value_type; + typedef value_type* value_pointer; + typedef const value_type* const_value_pointer; + typedef value_type& value_reference; + typedef const value_type& const_value_reference; + typedef value_type* iterator; + typedef const value_type* const_iterator; public: // forward all base class constructors... @@ -190,7 +216,9 @@ namespace wil { HRESULT hr = [&]() { + constexpr auto vt = static_cast(details::VarTraits::type); WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); + WI_ASSERT(vt == detail::SafeArrayGetVartype(psa)); details::SafeArrayAccessData(psa, m_pBegin); storage_t::reset(psa); RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nCount)); @@ -201,19 +229,24 @@ namespace wil } // Ranged-for style - T* begin() WI_NOEXCEPT { return m_pBegin; } - T* end() WI_NOEXCEPT { return m_pBegin+m_nCount; } - const T* begin() const WI_NOEXCEPT { return m_pBegin; } - const T* end() const WI_NOEXCEPT { return m_pBegin+m_nCount; } + iterator begin() WI_NOEXCEPT { return m_pBegin; } + iterator end() WI_NOEXCEPT { return m_pBegin+m_nSize; } + const_iterator begin() const WI_NOEXCEPT { return m_pBegin; } + const_iterator end() const WI_NOEXCEPT { return m_pBegin+m_nSize; } + const_iterator cbegin() const WI_NOEXCEPT { return begin(); } + const_iterator cend() const WI_NOEXCEPT { return end(); } // Old style iteration - ULONG count() const WI_NOEXCEPT { return m_nCount; } - WI_NODISCARD T& operator[](ULONG i) { WI_ASSERT(i < m_nCount); return *(m_pBegin + i); } - WI_NODISCARD const T& operator[](ULONG i) const { WI_ASSERT(i < m_nCount); return *(m_pBegin +i); } + ULONG size() const WI_NOEXCEPT { return m_nSize; } + WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } + WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin +i); } + + // Utilities + bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } private: - T* m_pBegin = nullptr; - ULONG m_nCount = 0; + value_pointer m_pBegin = nullptr; + ULONG m_nSize = 0; }; template @@ -226,24 +259,31 @@ namespace wil using unique_safearrayaccess = unique_any_t, err_exception_policy>>; #endif + // Add safearray functionality to the given storage type using the given error policy - // element_t is the C++ type for the elements in the safearray OR use void to enable - // the methods that take the type as a parameter + // element_t is the either: + // A) The C++ type for the elements in the safearray and all methods are typesafe + // B) void to make the safearray generic (and not as typesafe) template class safearray_t : public storage_t { private: // SFINAE Helpers to improve readability // Filters functions that don't require a type because a type was provided by the class type - template using EnableIfTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfTyped = typename wistd::enable_if::value, int>::type; // Filters functions that require a type because the class type doesn't provide one (element_t == void) - template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; public: + // AccessData type that still requires a type (used when not typed) template using unique_accessdata_t = unique_any_t, err_policy>>; + // AccessData type that uses the safearray's type (used when a type is provided) + using unique_accessdata = unique_any_t, err_policy>>; + + // Represents this type + using unique_type = unique_any_t>; - using unique_safearray_t = unique_any_t>; typedef typename err_policy::result result; typedef typename storage_t::pointer pointer; typedef element_t elemtype; @@ -394,7 +434,6 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); } - template = 0> result get_element(T& t, LONG nIndex) { @@ -402,7 +441,6 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } - template = 0> result put_element(const element_t& t, LONG nIndex) { @@ -410,7 +448,6 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); } - template = 0> result get_element(element_t& t, LONG nIndex) { @@ -420,8 +457,15 @@ namespace wil } // Data Access - template + // Use with non-typed safearrays after the type has been determined + template = 0> result access_data(unique_accessdata_t& data) + { + return err_policy::HResult(data.create(storage_t::get())); + } + // Use with type safearrays + template = 0> + result access_data(unique_accessdata& data) { return err_policy::HResult(data.create(storage_t::get())); } @@ -448,21 +492,30 @@ namespace wil count(&nResult); return nResult; } - WI_NODISCARD unique_safearray_t copy() const + WI_NODISCARD unique_type copy() const { static_assert(wistd::is_same::value, "this method requires exceptions"); - auto result = unique_safearray_t{}; + auto result = unique_type{}; result.create(storage_t::get()); return result; } - template + template = 0> WI_NODISCARD unique_accessdata_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); auto data = unique_accessdata_t{}; - data.create(storage_t::get()); + access_data(data); + return data; + } + template = 0> + WI_NODISCARD unique_accessdata access_data() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto data = unique_accessdata{}; + access_data(data); return data; } @@ -500,14 +553,6 @@ namespace wil using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; - using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; - using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; - using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; - using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; - using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; - using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; - using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; - using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; @@ -516,6 +561,14 @@ namespace wil using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; + using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; + using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; + using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; + using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; + using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; + using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; + using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; + using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; #ifdef WIL_ENABLE_EXCEPTIONS using unique_safearray = unique_any_t, err_exception_policy, void>>; @@ -530,14 +583,14 @@ namespace wil using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; using unique_float_safearray = unique_any_t, err_exception_policy, float>>; using unique_double_safearray = unique_any_t, err_exception_policy, double>>; - using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; - using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; - using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; - using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; + using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; + using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; + using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; + using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif #endif diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 16ef1e3e..623ec787 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,8 +1,10 @@ #include #include +#include #include "common.h" +#include #define RUN_TEST_TYPED_NOTHROW(test) WI_FOREACH(test \ , wil::unique_char_safearray_nothrow \ @@ -16,14 +18,14 @@ , wil::unique_ulonglong_safearray_nothrow \ , wil::unique_float_safearray_nothrow \ , wil::unique_double_safearray_nothrow \ - , wil::unique_bstr_safearray_nothrow \ - , wil::unique_unknown_safearray_nothrow \ - , wil::unique_dispatch_safearray_nothrow \ - , wil::unique_variant_safearray_nothrow \ , wil::unique_varbool_safearray_nothrow \ , wil::unique_date_safearray_nothrow \ , wil::unique_currency_safearray_nothrow \ , wil::unique_decimal_safearray_nothrow \ + , wil::unique_bstr_safearray_nothrow \ + , wil::unique_unknown_safearray_nothrow \ + , wil::unique_dispatch_safearray_nothrow \ + , wil::unique_variant_safearray_nothrow \ ) #define RUN_TEST_TYPED_FAILFAST(test) WI_FOREACH(test \ @@ -38,14 +40,14 @@ , wil::unique_ulonglong_safearray_failfast \ , wil::unique_float_safearray_failfast \ , wil::unique_double_safearray_failfast \ - , wil::unique_bstr_safearray_failfast \ - , wil::unique_unknown_safearray_failfast \ - , wil::unique_dispatch_safearray_failfast \ - , wil::unique_variant_safearray_failfast \ , wil::unique_varbool_safearray_failfast \ , wil::unique_date_safearray_failfast \ , wil::unique_currency_safearray_failfast \ , wil::unique_decimal_safearray_failfast \ + , wil::unique_bstr_safearray_failfast \ + , wil::unique_unknown_safearray_failfast \ + , wil::unique_dispatch_safearray_failfast \ + , wil::unique_variant_safearray_failfast \ ) #define RUN_TEST_TYPED(test) WI_FOREACH(test \ @@ -60,16 +62,109 @@ , wil::unique_ulonglong_safearray \ , wil::unique_float_safearray \ , wil::unique_double_safearray \ - , wil::unique_bstr_safearray \ - , wil::unique_unknown_safearray \ - , wil::unique_dispatch_safearray \ - , wil::unique_variant_safearray \ , wil::unique_varbool_safearray \ , wil::unique_date_safearray \ , wil::unique_currency_safearray \ , wil::unique_decimal_safearray \ + , wil::unique_bstr_safearray \ + , wil::unique_unknown_safearray \ + , wil::unique_dispatch_safearray \ + , wil::unique_variant_safearray \ ) +template::value, int>::type> +std::vector GetSampleData() +{ + constexpr auto BIT_COUNT = sizeof(T) * 8; + + auto result = std::vector{}; + result.reserve(BIT_COUNT); + for (auto i = 0; i < BIT_COUNT; ++i) + { + result.emplace_back(1 << i); + } + return result; +} + +template::value, int>::type> +std::vector GetSampleData() +{ + auto result = std::vector{}; + result.reserve(32); + for (auto i = 1; i <= 16; ++i) + { + result.emplace_back(1 / i); + result.emplace_back(2 * i); + } + return result; +} + +template::value, int>::type> +std::vector GetSampleData() +{ + auto result = std::vector{}; + result.reserve(32); + for (auto i = 0; i < 32; ++i) + { + wil::unique_bstr temp{}; + switch (i % 4) + { + case 0: + temp.reset(::SysAllocString(L"Sample Data")); + break; + case 1: + temp.reset(::SysAllocString(L"Larger Sample Data")); + break; + case 2: + temp.reset(::SysAllocString(L"This is much much larger Sample Data")); + break; + case 3: + temp.reset(::SysAllocString(L"This is the longest Sample Data. It's the longest by a lot. I mean a lot.")); + break; + } + result.emplace_back(std::move(temp)); + } + return result; +} + +template::value, int>::type> +std::vector GetSampleData() +{ + auto result = std::vector{}; + result.reserve(32); + for (auto i = 0; i < 32; ++i) + { + result.push_back({}); + auto& var = result.back(); + switch (i % 4) + { + case 0: + V_VT(&var) = VT_I4; + V_I4(&var) = 37; + break; + case 1: + V_VT(&var) = VT_I1; + V_I1(&var) = 0x40; + break; + case 2: + V_VT(&var) = VT_UI2; + V_UI2(&var) = 0x4000; + break; + case 3: + V_VT(&var) = VT_R4; + V_R4(&var) = 98.6f; + break; + } + } + return result; +} + +template::value, int>::type> +std::vector> GetSampleData() { return {} }; + +template::value, int>::type> +std::vector> GetSampleData() { return {} }; + template void TestLock(safearray_t& sa) { @@ -83,9 +178,9 @@ void TestLock(safearray_t& sa) } template -void TestCreateTyped_NoThrow() +void TestTyped_Create_NoThrow() { - constexpr auto SIZE = 256; + constexpr auto SIZE = 256U; auto sa = safearray_t{}; LONG val = 0; @@ -106,9 +201,9 @@ void TestCreateTyped_NoThrow() } template -void TestCreateTyped_FailFast() +void TestTyped_Create_FailFast() { - constexpr auto SIZE = 256; + constexpr auto SIZE = 256U; auto sa = safearray_t{}; LONG val = 0; @@ -130,7 +225,7 @@ void TestCreateTyped_FailFast() #ifdef WIL_ENABLE_EXCEPTIONS template -void TestCreateTyped() +void TestTyped_Create() { constexpr auto SIZE = 256U; @@ -148,27 +243,53 @@ void TestCreateTyped() } #endif -#define _CREATE_TYPED_NOTHROW(type) TestCreateTyped_NoThrow(); -#define _CREATE_TYPED_FAILFAST(type) TestCreateTyped_FailFast(); -#define _CREATE_TYPED(type) TestCreateTyped(); +#define _TYPED_CREATE_NOTHROW(type) TestTyped_Create_NoThrow(); +#define _TYPED_CREATE_FAILFAST(type) TestTyped_Create_FailFast(); +#define _TYPED_CREATE(type) TestTyped_Create(); + +template +void TestTyped_DirectElementAccess_NoThrow() +{ + constexpr auto SIZE = 256U; + + const auto sample_data = GetSampleData(); + auto sa = safearray_t{}; + LONG val = 0; + ULONG count = 0; + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE_SUCCEEDED(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_SUCCEEDED(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_SUCCEEDED(sa.ubound(&val)); + REQUIRE(val == SIZE - 1); + TestLock(sa); + sa.reset(); + REQUIRE(!sa); +} + + TEST_CASE("SafearrayTests::Create", "[safearray][create]") { SECTION("Create SafeArray - No Throw") { - RUN_TEST_TYPED_NOTHROW(_CREATE_TYPED_NOTHROW); + RUN_TEST_TYPED_NOTHROW(_TYPED_CREATE_NOTHROW); } SECTION("Create SafeArray - FailFast") { - RUN_TEST_TYPED_FAILFAST(_CREATE_TYPED_FAILFAST); + RUN_TEST_TYPED_FAILFAST(_TYPED_CREATE_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Create SafeArray - Exceptions") { - RUN_TEST_TYPED(_CREATE_TYPED); + RUN_TEST_TYPED(_TYPED_CREATE); } #endif } From 3831e4764b4885806486f78783196c8159add6cb Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Mon, 17 Aug 2020 15:34:26 -0700 Subject: [PATCH 12/34] I guess it's progress --- include/wil/Safearrays.h | 85 ++++++--- tests/SafearrayTests.cpp | 382 ++++++++++++++++++++++++++------------- 2 files changed, 311 insertions(+), 156 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index ab851be1..57ee1eea 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -30,7 +30,7 @@ namespace wil struct VarTraits{}; template<> struct VarTraits { enum { type = VT_I1 }; }; - template<> struct VarTraits { enum { type = VT_I2 }; }; + //template<> struct VarTraits { enum { type = VT_I2 }; }; template<> struct VarTraits { enum { type = VT_I4 }; }; template<> struct VarTraits { enum { type = VT_I4 }; }; template<> struct VarTraits { enum { type = VT_I8 }; }; @@ -40,7 +40,7 @@ namespace wil template<> struct VarTraits { enum { type = VT_UI4 }; }; template<> struct VarTraits { enum { type = VT_UI8 }; }; template<> struct VarTraits { enum { type = VT_R4 }; }; - template<> struct VarTraits { enum { type = VT_R8 }; }; + //template<> struct VarTraits { enum { type = VT_R8 }; }; template<> struct VarTraits { enum { type = VT_BOOL }; }; template<> struct VarTraits { enum { type = VT_DATE }; }; template<> struct VarTraits { enum { type = VT_CY }; }; @@ -218,10 +218,10 @@ namespace wil { constexpr auto vt = static_cast(details::VarTraits::type); WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - WI_ASSERT(vt == detail::SafeArrayGetVartype(psa)); + WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); details::SafeArrayAccessData(psa, m_pBegin); storage_t::reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nCount)); + RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nSize)); return S_OK; }(); @@ -270,9 +270,9 @@ namespace wil private: // SFINAE Helpers to improve readability // Filters functions that don't require a type because a type was provided by the class type - template using EnableIfTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfTyped = typename wistd::enable_if::value, int>::type; // Filters functions that require a type because the class type doesn't provide one (element_t == void) - template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; + template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; public: // AccessData type that still requires a type (used when not typed) @@ -300,6 +300,12 @@ namespace wil static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(vt, cElements, lowerBound); } + template = 0> + safearray_t(UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(cElements, lowerBound); + } template = 0> safearray_t(UINT cElements, LONG lowerBound = 0) { @@ -321,13 +327,6 @@ namespace wil auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; return err_policy::HResult(_create(vt, 1, &bounds)); } - template = 0> - result create(UINT cElements, LONG lowerBound = 0) - { - constexpr auto vt = static_cast(details::VarTraits::type); - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } // Uses the fixed element type defined by the class // Low-level, arbitrary number of dimensions @@ -339,7 +338,7 @@ namespace wil } // Single Dimension specialization - template = 0> + template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -348,7 +347,7 @@ namespace wil } // Create a Copy - result create(pointer psaSrc) + result create_copy(pointer psaSrc) { HRESULT hr = [&]() { @@ -396,20 +395,20 @@ namespace wil // Lowest-Level functions for those who know what they're doing // TODO: Finish safearray_index type to collapse both versions into one functioon - result put_element(void* pv, LONG* pIndices) + result put_element(LONG* pIndices, void* pv) { return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); } - result put_element(void* pv, LONG nIndex) + result put_element(LONG nIndex, void* pv) { WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); } - result get_element(void* pv, LONG* pIndices) + result get_element(LONG* pIndices, void* pv) { return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); } - result get_element(void* pv, LONG nIndex) + result get_element(LONG nIndex, void* pv) { WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); @@ -427,34 +426,66 @@ namespace wil // WI_ASSERT(sizeof(t) == elemsize()); // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); //} - template = 0> - result put_element(const T& t, LONG nIndex) + template::value && !wistd::is_same::value, int>::type = 0> + result put_element(LONG nIndex, const T& t) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&t)))); } template = 0> - result get_element(T& t, LONG nIndex) + result get_element(LONG nIndex, T& t) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } - template = 0> - result put_element(const element_t& t, LONG nIndex) + template::value && !wistd::is_same::value, int>::type = 0> + result put_element(LONG nIndex, const T& t) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, &t)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&t)))); } template = 0> - result get_element(element_t& t, LONG nIndex) + result get_element(LONG nIndex, T& t) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } + //template using EnableIfTyped = typename wistd::enable_if::value, int>::type; + //// Filters functions that require a type because the class type doesn't provide one (element_t == void) + //template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; + + template::value&& wistd::is_same::value, int>::type = 0> + result put_element(LONG nIndex, BSTR bstr) + { + WI_ASSERT(sizeof(bstr) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, bstr)); + } + //template::value && wistd::is_same::value, int>::type = 0> + //result get_element(LONG nIndex, T& t) + //{ + // WI_ASSERT(sizeof(t) == elemsize()); + // WI_ASSERT(dim() == 1); + // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); + //} + template::value&& wistd::is_same::value, int>::type = 0> + result put_element(LONG nIndex, BSTR bstr) + { + WI_ASSERT(sizeof(bstr) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, bstr)); + } + //template = 0> + //result get_element(LONG nIndex, T& t) + //{ + // WI_ASSERT(sizeof(t) == elemsize()); + // WI_ASSERT(dim() == 1); + // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); + //} // Data Access // Use with non-typed safearrays after the type has been determined diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 623ec787..dad66b7c 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,141 +1,148 @@ - #include #include #include #include "common.h" #include +#include +#include + +#pragma comment( lib, "Propsys.lib" ) -#define RUN_TEST_TYPED_NOTHROW(test) WI_FOREACH(test \ - , wil::unique_char_safearray_nothrow \ - , wil::unique_short_safearray_nothrow \ - , wil::unique_long_safearray_nothrow \ - , wil::unique_int_safearray_nothrow \ - , wil::unique_longlong_safearray_nothrow \ - , wil::unique_byte_safearray_nothrow \ - , wil::unique_word_safearray_nothrow \ - , wil::unique_dword_safearray_nothrow \ - , wil::unique_ulonglong_safearray_nothrow \ - , wil::unique_float_safearray_nothrow \ - , wil::unique_double_safearray_nothrow \ - , wil::unique_varbool_safearray_nothrow \ - , wil::unique_date_safearray_nothrow \ - , wil::unique_currency_safearray_nothrow \ - , wil::unique_decimal_safearray_nothrow \ - , wil::unique_bstr_safearray_nothrow \ - , wil::unique_unknown_safearray_nothrow \ - , wil::unique_dispatch_safearray_nothrow \ - , wil::unique_variant_safearray_nothrow \ +#define RUN_TYPED_TEST_NOTHROW(test) WI_FOREACH(test \ + , wil::unique_char_safearray_nothrow \ + , wil::unique_long_safearray_nothrow \ + , wil::unique_int_safearray_nothrow \ + , wil::unique_longlong_safearray_nothrow \ + , wil::unique_byte_safearray_nothrow \ + , wil::unique_word_safearray_nothrow \ + , wil::unique_dword_safearray_nothrow \ + , wil::unique_ulonglong_safearray_nothrow \ + , wil::unique_float_safearray_nothrow \ + , wil::unique_varbool_safearray_nothrow \ + , wil::unique_date_safearray_nothrow \ + , wil::unique_bstr_safearray_nothrow \ + , wil::unique_unknown_safearray_nothrow \ + , wil::unique_dispatch_safearray_nothrow \ + , wil::unique_variant_safearray_nothrow \ ) -#define RUN_TEST_TYPED_FAILFAST(test) WI_FOREACH(test \ - , wil::unique_char_safearray_failfast \ - , wil::unique_short_safearray_failfast \ - , wil::unique_long_safearray_failfast \ - , wil::unique_int_safearray_failfast \ - , wil::unique_longlong_safearray_failfast \ - , wil::unique_byte_safearray_failfast \ - , wil::unique_word_safearray_failfast \ - , wil::unique_dword_safearray_failfast \ - , wil::unique_ulonglong_safearray_failfast \ - , wil::unique_float_safearray_failfast \ - , wil::unique_double_safearray_failfast \ - , wil::unique_varbool_safearray_failfast \ - , wil::unique_date_safearray_failfast \ - , wil::unique_currency_safearray_failfast \ - , wil::unique_decimal_safearray_failfast \ - , wil::unique_bstr_safearray_failfast \ - , wil::unique_unknown_safearray_failfast \ - , wil::unique_dispatch_safearray_failfast \ - , wil::unique_variant_safearray_failfast \ - ) +#define RUN_TYPED_TEST_FAILFAST(test) WI_FOREACH(test \ + , wil::unique_char_safearray_failfast \ + , wil::unique_long_safearray_failfast \ + , wil::unique_int_safearray_failfast \ + , wil::unique_longlong_safearray_failfast \ + , wil::unique_byte_safearray_failfast \ + , wil::unique_word_safearray_failfast \ + , wil::unique_dword_safearray_failfast \ + , wil::unique_ulonglong_safearray_failfast \ + , wil::unique_float_safearray_failfast \ + , wil::unique_varbool_safearray_failfast \ + , wil::unique_date_safearray_failfast \ + , wil::unique_bstr_safearray_failfast \ + , wil::unique_unknown_safearray_failfast \ + , wil::unique_dispatch_safearray_failfast \ + , wil::unique_variant_safearray_failfast \ + ) -#define RUN_TEST_TYPED(test) WI_FOREACH(test \ - , wil::unique_char_safearray \ - , wil::unique_short_safearray \ - , wil::unique_long_safearray \ - , wil::unique_int_safearray \ - , wil::unique_longlong_safearray \ - , wil::unique_byte_safearray \ - , wil::unique_word_safearray \ - , wil::unique_dword_safearray \ - , wil::unique_ulonglong_safearray \ - , wil::unique_float_safearray \ - , wil::unique_double_safearray \ - , wil::unique_varbool_safearray \ - , wil::unique_date_safearray \ - , wil::unique_currency_safearray \ - , wil::unique_decimal_safearray \ - , wil::unique_bstr_safearray \ - , wil::unique_unknown_safearray \ - , wil::unique_dispatch_safearray \ - , wil::unique_variant_safearray \ - ) +#define RUN_TYPED_TEST(test) WI_FOREACH(test \ + , wil::unique_char_safearray \ + , wil::unique_long_safearray \ + , wil::unique_int_safearray \ + , wil::unique_longlong_safearray \ + , wil::unique_byte_safearray \ + , wil::unique_word_safearray \ + , wil::unique_dword_safearray \ + , wil::unique_ulonglong_safearray \ + , wil::unique_float_safearray \ + , wil::unique_varbool_safearray \ + , wil::unique_date_safearray \ + , wil::unique_bstr_safearray \ + , wil::unique_unknown_safearray \ + , wil::unique_dispatch_safearray \ + , wil::unique_variant_safearray \ + ) + +#define RUN_TEST(test) WI_FOREACH(test \ + , char \ + , short \ + , long \ + , int \ + , long long \ + , unsigned char \ + , unsigned short \ + , unsigned long \ + , unsigned int \ + , unsigned long long \ + , float \ + , double \ + , VARIANT_BOOL \ + , DATE \ + , BSTR \ + , LPUNKNOWN \ + , LPDISPATCH \ + , VARIANT \ + ) + +constexpr auto DEFAULT_SAMPLE_SIZE = 32; template::value, int>::type> -std::vector GetSampleData() +auto GetSampleData() -> std::array { constexpr auto BIT_COUNT = sizeof(T) * 8; - auto result = std::vector{}; - result.reserve(BIT_COUNT); + auto result = std::array{}; for (auto i = 0; i < BIT_COUNT; ++i) { - result.emplace_back(1 << i); + result[i] = (static_cast(1) << i); } return result; } template::value, int>::type> -std::vector GetSampleData() +std::array GetSampleData() { - auto result = std::vector{}; - result.reserve(32); - for (auto i = 1; i <= 16; ++i) + auto result = std::array{}; + for (auto i = 0; i < DEFAULT_SAMPLE_SIZE/2; ++i) { - result.emplace_back(1 / i); - result.emplace_back(2 * i); + result[i] = (i != 0) ? static_cast(1 / i) : 0; + result[i + (DEFAULT_SAMPLE_SIZE/2)] = 2* static_cast(i); } return result; } template::value, int>::type> -std::vector GetSampleData() +std::array GetSampleData() { - auto result = std::vector{}; - result.reserve(32); - for (auto i = 0; i < 32; ++i) + auto result = std::array{}; + for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) { - wil::unique_bstr temp{}; switch (i % 4) { case 0: - temp.reset(::SysAllocString(L"Sample Data")); + result[i].reset(::SysAllocString(L"Sample Data")); break; case 1: - temp.reset(::SysAllocString(L"Larger Sample Data")); + result[i].reset(::SysAllocString(L"Larger Sample Data")); break; case 2: - temp.reset(::SysAllocString(L"This is much much larger Sample Data")); + result[i].reset(::SysAllocString(L"This is much much larger Sample Data")); break; case 3: - temp.reset(::SysAllocString(L"This is the longest Sample Data. It's the longest by a lot. I mean a lot.")); + result[i].reset(::SysAllocString(L"This is the longest Sample Data. It's the longest by a lot. I mean a lot.")); break; } - result.emplace_back(std::move(temp)); } return result; } template::value, int>::type> -std::vector GetSampleData() +std::array GetSampleData() { - auto result = std::vector{}; - result.reserve(32); - for (auto i = 0; i < 32; ++i) + auto result = std::array{}; + for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) { - result.push_back({}); - auto& var = result.back(); + auto& var = result[i]; switch (i % 4) { case 0: @@ -147,8 +154,8 @@ std::vector GetSampleData() V_I1(&var) = 0x40; break; case 2: - V_VT(&var) = VT_UI2; - V_UI2(&var) = 0x4000; + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = ::SysAllocString(L"String in a variant"); break; case 3: V_VT(&var) = VT_R4; @@ -160,10 +167,22 @@ std::vector GetSampleData() } template::value, int>::type> -std::vector> GetSampleData() { return {} }; +std::array, 1> GetSampleData() { return {}; } template::value, int>::type> -std::vector> GetSampleData() { return {} }; +std::array, 1> GetSampleData() { return {}; } + +template +bool PerformCompare(const T& left, const T& right) +{ + return (left == right); +} + +template<> +bool PerformCompare(const wil::unique_variant& left, const wil::unique_variant& right) +{ + return (::VariantCompare(left, right) == 0); +} template void TestLock(safearray_t& sa) @@ -242,82 +261,187 @@ void TestTyped_Create() REQUIRE(!sa); } #endif - -#define _TYPED_CREATE_NOTHROW(type) TestTyped_Create_NoThrow(); -#define _TYPED_CREATE_FAILFAST(type) TestTyped_Create_FailFast(); -#define _TYPED_CREATE(type) TestTyped_Create(); -template -void TestTyped_DirectElementAccess_NoThrow() +template +void Test_Create_NoThrow() { constexpr auto SIZE = 256U; - const auto sample_data = GetSampleData(); - auto sa = safearray_t{}; + auto sa = wil::unique_safearray_nothrow{}; LONG val = 0; ULONG count = 0; - REQUIRE_SUCCEEDED(sa.create(SIZE)); + //REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(T)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_SUCCEEDED(sa.ubound(&val)); REQUIRE(val == SIZE - 1); - TestLock(sa); + TestLock(sa); sa.reset(); REQUIRE(!sa); } +template +void Test_Create_FailFast() +{ + constexpr auto SIZE = 256U; + + auto sa = wil::unique_safearray_failfast{}; + LONG val = 0; + ULONG count = 0; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == sizeof(T)); + REQUIRE_NOCRASH(sa.count(&count)); + REQUIRE(count == SIZE); + REQUIRE_NOCRASH(sa.lbound(&val)); + REQUIRE(val == 0); + REQUIRE_NOCRASH(sa.ubound(&val)); + REQUIRE(val == SIZE - 1); + TestLock(sa); + sa.reset(); + REQUIRE(!sa); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void Test_Create() +{ + constexpr auto SIZE = 256U; + + wil::unique_safearray sa{}; + REQUIRE_NOTHROW(sa.create( SIZE )); + REQUIRE(sa); + REQUIRE(sa.dim() == 1); + REQUIRE(sa.elemsize() == sizeof(T)); + REQUIRE_NOTHROW(sa.count() == SIZE); + REQUIRE_NOTHROW(sa.lbound() == 0); + REQUIRE_NOTHROW(sa.ubound() == SIZE - 1); + TestLock(sa); + sa.reset(); + REQUIRE(!sa); +} +#endif + +#define _TYPED_CREATE_NOTHROW(type) TestTyped_Create_NoThrow(); +#define _TYPED_CREATE_FAILFAST(type) TestTyped_Create_FailFast(); +#define _TYPED_CREATE(type) TestTyped_Create(); +#define _CREATE_NOTHROW(type) Test_Create_NoThrow(); +#define _CREATE_FAILFAST(type) Test_Create_FailFast(); +#define _CREATE(type) Test_Create(); + +template +auto GetReadable(const T& t) -> const T& +{ + return t; +} + +template::value, int>::type> +auto GetReadable(const wil::unique_bstr& t) -> BSTR +{ + return t.get(); +} + +template +auto GetWritable(T& t) -> T& +{ + return t; +} + +template::value, int>::type> +auto GetWritable(wil::unique_bstr& t) -> BSTR& +{ + return *(t.addressof()); +} + +template +void TestTyped_DirectElement_NoThrow() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = safearray_t{}; + + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} +#define _TYPED_DIRECT_NOTHROW(type) TestTyped_DirectElement_NoThrow(); -TEST_CASE("SafearrayTests::Create", "[safearray][create]") + +TEST_CASE("Safearray::Create", "[safearray]") { SECTION("Create SafeArray - No Throw") { - RUN_TEST_TYPED_NOTHROW(_TYPED_CREATE_NOTHROW); + RUN_TYPED_TEST_NOTHROW(_TYPED_CREATE_NOTHROW); + RUN_TEST(_CREATE_NOTHROW); } SECTION("Create SafeArray - FailFast") { - RUN_TEST_TYPED_FAILFAST(_TYPED_CREATE_FAILFAST); + RUN_TYPED_TEST_FAILFAST(_TYPED_CREATE_FAILFAST); + RUN_TEST(_CREATE_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Create SafeArray - Exceptions") { - RUN_TEST_TYPED(_TYPED_CREATE); + RUN_TYPED_TEST(_TYPED_CREATE); + RUN_TEST(_CREATE); } #endif } -TEST_CASE("SafearrayTests::AccessData", "[safearray][data]") + +TEST_CASE("Safearray::Put/Get", "[safearray]") { - SECTION("Basic") + SECTION("Direct Element Access - No Throw") { - constexpr auto SIZE = 32; - - wil::unique_safearray_nothrow sa; - - REQUIRE_SUCCEEDED(sa.create(VT_UI4, SIZE)); - - for (auto i = 0; i < SIZE; ++i) - { - sa.put_element((1 << i), i); - } + RUN_TYPED_TEST_NOTHROW(_TYPED_DIRECT_NOTHROW); + //RUN_TEST(_CREATE_NOTHROW); + } - { - wil::unique_safearray_nothrow::unique_accessdata_t data; - UINT counter = 0; - REQUIRE_SUCCEEDED(sa.access_data(data)); - for (auto& n : data) - { - REQUIRE(n == static_cast(1 << counter++)); - } - } + SECTION("Direct Element Access - FailFast") + { + //RUN_TYPED_TEST_FAILFAST(_TYPED_CREATE_FAILFAST); + //RUN_TEST(_CREATE_FAILFAST); } +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Direct Element Access - Exceptions") + { + //RUN_TYPED_TEST(_TYPED_CREATE); + //RUN_TEST(_CREATE); + } +#endif } + From 048e1084eda9cdfe90d5a0b8228e60d6c97bf9cc Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Mon, 17 Aug 2020 18:16:49 -0700 Subject: [PATCH 13/34] Finished most of the test harness Whiped up some fresh sfinae helpers --- include/wil/Safearrays.h | 74 +++----- tests/SafearrayTests.cpp | 388 ++++++++++++++++++++++++++++++--------- 2 files changed, 326 insertions(+), 136 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 57ee1eea..1feeb833 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -351,13 +351,11 @@ namespace wil { HRESULT hr = [&]() { - auto psa = storage_t::policy::invalid_value(); - + auto psa = storage_t::policy::invalid_value(); RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); storage_t::reset(psa); return S_OK; }(); - return err_policy::HResult(hr); } @@ -414,40 +412,33 @@ namespace wil return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); } - //template - //result put_element(const T& t, LONG* pIndices) - //{ - // WI_ASSERT(sizeof(t) == elemsize()); - // return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, &t)); - //} - //template - //result get_element(T& t, LONG* pIndices) - //{ - // WI_ASSERT(sizeof(t) == elemsize()); - // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); - //} - template::value && !wistd::is_same::value, int>::type = 0> - result put_element(LONG nIndex, const T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&t)))); - } - template = 0> - result get_element(LONG nIndex, T& t) + template + using is_special_type = wistd::disjunction, + wistd::is_same, + wistd::is_same>; + + template + using is_valid_type = wistd::negation>; + + template + using is_nonspecial_type = wistd::conjunction>,is_valid_type>; + + template::value, int>::type = 0> + result put_element(LONG nIndex, const T& val) { - WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(sizeof(val) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); } - template::value && !wistd::is_same::value, int>::type = 0> - result put_element(LONG nIndex, const T& t) + template::value, int>::type = 0> + result put_element(LONG nIndex, T val) { - WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(sizeof(val) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&t)))); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); } - template = 0> + + template::value, int>::type = 0> result get_element(LONG nIndex, T& t) { WI_ASSERT(sizeof(t) == elemsize()); @@ -458,27 +449,6 @@ namespace wil //// Filters functions that require a type because the class type doesn't provide one (element_t == void) //template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; - template::value&& wistd::is_same::value, int>::type = 0> - result put_element(LONG nIndex, BSTR bstr) - { - WI_ASSERT(sizeof(bstr) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, bstr)); - } - //template::value && wistd::is_same::value, int>::type = 0> - //result get_element(LONG nIndex, T& t) - //{ - // WI_ASSERT(sizeof(t) == elemsize()); - // WI_ASSERT(dim() == 1); - // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); - //} - template::value&& wistd::is_same::value, int>::type = 0> - result put_element(LONG nIndex, BSTR bstr) - { - WI_ASSERT(sizeof(bstr) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, bstr)); - } //template = 0> //result get_element(LONG nIndex, T& t) //{ diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index dad66b7c..8a3e0100 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -10,57 +10,57 @@ #pragma comment( lib, "Propsys.lib" ) #define RUN_TYPED_TEST_NOTHROW(test) WI_FOREACH(test \ - , wil::unique_char_safearray_nothrow \ - , wil::unique_long_safearray_nothrow \ - , wil::unique_int_safearray_nothrow \ - , wil::unique_longlong_safearray_nothrow \ - , wil::unique_byte_safearray_nothrow \ - , wil::unique_word_safearray_nothrow \ - , wil::unique_dword_safearray_nothrow \ - , wil::unique_ulonglong_safearray_nothrow \ - , wil::unique_float_safearray_nothrow \ + , wil::unique_char_safearray_nothrow \ + , wil::unique_long_safearray_nothrow \ + , wil::unique_int_safearray_nothrow \ + , wil::unique_longlong_safearray_nothrow \ + , wil::unique_byte_safearray_nothrow \ + , wil::unique_word_safearray_nothrow \ + , wil::unique_dword_safearray_nothrow \ + , wil::unique_ulonglong_safearray_nothrow \ + , wil::unique_float_safearray_nothrow \ , wil::unique_varbool_safearray_nothrow \ , wil::unique_date_safearray_nothrow \ - , wil::unique_bstr_safearray_nothrow \ - , wil::unique_unknown_safearray_nothrow \ - , wil::unique_dispatch_safearray_nothrow \ - , wil::unique_variant_safearray_nothrow \ + , wil::unique_bstr_safearray_nothrow \ + , wil::unique_unknown_safearray_nothrow \ + , wil::unique_dispatch_safearray_nothrow \ + , wil::unique_variant_safearray_nothrow \ ) - + #define RUN_TYPED_TEST_FAILFAST(test) WI_FOREACH(test \ - , wil::unique_char_safearray_failfast \ - , wil::unique_long_safearray_failfast \ - , wil::unique_int_safearray_failfast \ - , wil::unique_longlong_safearray_failfast \ - , wil::unique_byte_safearray_failfast \ - , wil::unique_word_safearray_failfast \ - , wil::unique_dword_safearray_failfast \ - , wil::unique_ulonglong_safearray_failfast \ - , wil::unique_float_safearray_failfast \ + , wil::unique_char_safearray_failfast \ + , wil::unique_long_safearray_failfast \ + , wil::unique_int_safearray_failfast \ + , wil::unique_longlong_safearray_failfast \ + , wil::unique_byte_safearray_failfast \ + , wil::unique_word_safearray_failfast \ + , wil::unique_dword_safearray_failfast \ + , wil::unique_ulonglong_safearray_failfast \ + , wil::unique_float_safearray_failfast \ , wil::unique_varbool_safearray_failfast \ , wil::unique_date_safearray_failfast \ - , wil::unique_bstr_safearray_failfast \ - , wil::unique_unknown_safearray_failfast \ - , wil::unique_dispatch_safearray_failfast \ - , wil::unique_variant_safearray_failfast \ + , wil::unique_bstr_safearray_failfast \ + , wil::unique_unknown_safearray_failfast \ + , wil::unique_dispatch_safearray_failfast \ + , wil::unique_variant_safearray_failfast \ ) - + #define RUN_TYPED_TEST(test) WI_FOREACH(test \ - , wil::unique_char_safearray \ - , wil::unique_long_safearray \ - , wil::unique_int_safearray \ - , wil::unique_longlong_safearray \ - , wil::unique_byte_safearray \ - , wil::unique_word_safearray \ - , wil::unique_dword_safearray \ - , wil::unique_ulonglong_safearray \ - , wil::unique_float_safearray \ + , wil::unique_char_safearray \ + , wil::unique_long_safearray \ + , wil::unique_int_safearray \ + , wil::unique_longlong_safearray \ + , wil::unique_byte_safearray \ + , wil::unique_word_safearray \ + , wil::unique_dword_safearray \ + , wil::unique_ulonglong_safearray \ + , wil::unique_float_safearray \ , wil::unique_varbool_safearray \ , wil::unique_date_safearray \ - , wil::unique_bstr_safearray \ - , wil::unique_unknown_safearray \ - , wil::unique_dispatch_safearray \ - , wil::unique_variant_safearray \ + , wil::unique_bstr_safearray \ + , wil::unique_unknown_safearray \ + , wil::unique_dispatch_safearray \ + , wil::unique_variant_safearray \ ) #define RUN_TEST(test) WI_FOREACH(test \ @@ -172,6 +172,58 @@ std::array, 1> GetSampleData() { return {}; } template::value, int>::type> std::array, 1> GetSampleData() { return {}; } +template ::value + && !wistd::is_same>::value + && !wistd::is_same>::value, int>::type> +auto GetReadable(const T& t) -> const T& +{ + return t; +} + +template::value, int>::type> +auto GetReadable(const T& t) -> BSTR +{ + return t.get(); +} + +template>::value, int>::type> +auto GetReadable(const T& t) -> LPUNKNOWN +{ + return t.get(); +} + +template>::value, int>::type> +auto GetReadable(const T& t) -> LPDISPATCH +{ + return t.get(); +} + +template ::value + && !wistd::is_same>::value + && !wistd::is_same>::value, int>::type> +auto GetWritable(T& t) -> T& +{ + return t; +} + +template::value, int>::type> +auto GetWritable(T& t) -> BSTR& +{ + return *(t.addressof()); +} + +template>::value, int>::type> +auto GetWritable(T& t) -> LPUNKNOWN& +{ + return *(t.addressof()); +} + +template>::value, int>::type> +auto GetWritable(T& t) -> LPDISPATCH& +{ + return *(t.addressof()); +} + template bool PerformCompare(const T& left, const T& right) { @@ -184,6 +236,13 @@ bool PerformCompare(const wil::unique_variant& left, const wil::unique_variant& return (::VariantCompare(left, right) == 0); } +template<> +bool PerformCompare(const wil::unique_bstr& left, const wil::unique_bstr& right) +{ + return (::SysStringLen(left.get()) == ::SysStringLen(right.get())) + && (wcscmp(left.get(), right.get()) == 0); +} + template void TestLock(safearray_t& sa) { @@ -195,7 +254,7 @@ void TestLock(safearray_t& sa) } REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down } - + template void TestTyped_Create_NoThrow() { @@ -261,7 +320,11 @@ void TestTyped_Create() REQUIRE(!sa); } #endif - + +#define _TYPED_CREATE_NOTHROW(type) TestTyped_Create_NoThrow(); +#define _TYPED_CREATE_FAILFAST(type) TestTyped_Create_FailFast(); +#define _TYPED_CREATE(type) TestTyped_Create(); + template void Test_Create_NoThrow() { @@ -270,7 +333,7 @@ void Test_Create_NoThrow() auto sa = wil::unique_safearray_nothrow{}; LONG val = 0; ULONG count = 0; - //REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); REQUIRE(sa.elemsize() == sizeof(T)); @@ -328,38 +391,10 @@ void Test_Create() } #endif -#define _TYPED_CREATE_NOTHROW(type) TestTyped_Create_NoThrow(); -#define _TYPED_CREATE_FAILFAST(type) TestTyped_Create_FailFast(); -#define _TYPED_CREATE(type) TestTyped_Create(); -#define _CREATE_NOTHROW(type) Test_Create_NoThrow(); -#define _CREATE_FAILFAST(type) Test_Create_FailFast(); -#define _CREATE(type) Test_Create(); - -template -auto GetReadable(const T& t) -> const T& -{ - return t; -} - -template::value, int>::type> -auto GetReadable(const wil::unique_bstr& t) -> BSTR -{ - return t.get(); -} - -template -auto GetWritable(T& t) -> T& -{ - return t; -} - -template::value, int>::type> -auto GetWritable(wil::unique_bstr& t) -> BSTR& -{ - return *(t.addressof()); -} - - +#define _CREATE_NOTHROW(type) Test_Create_NoThrow(); +#define _CREATE_FAILFAST(type) Test_Create_FailFast(); +#define _CREATE(type) Test_Create(); + template void TestTyped_DirectElement_NoThrow() { @@ -395,9 +430,194 @@ void TestTyped_DirectElement_NoThrow() } } -#define _TYPED_DIRECT_NOTHROW(type) TestTyped_DirectElement_NoThrow(); +template +void TestTyped_DirectElement_Failfast() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = safearray_t{}; + + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void TestTyped_DirectElement() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = safearray_t{}; + + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} +#endif + +#define _TYPED_DIRECT_NOTHROW(type) TestTyped_DirectElement_NoThrow(); +#define _TYPED_DIRECT_FAILFAST(type) TestTyped_DirectElement_Failfast(); +#define _TYPED_DIRECT(type) TestTyped_DirectElement(); + +template +void Test_DirectElement_NoThrow() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = wil::unique_safearray_nothrow{}; + + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} + +template +void Test_DirectElement_Failfast() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = wil::unique_safearray_failfast{}; + + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void Test_DirectElement() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = wil::unique_safearray{}; + + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + { + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); + } + } + } +} +#endif + +#define _DIRECT_NOTHROW(type) Test_DirectElement_NoThrow(); +#define _DIRECT_FAILFAST(type) Test_DirectElement_Failfast(); +#define _DIRECT(type) Test_DirectElement(); + - TEST_CASE("Safearray::Create", "[safearray]") { SECTION("Create SafeArray - No Throw") @@ -421,26 +641,26 @@ TEST_CASE("Safearray::Create", "[safearray]") #endif } - + TEST_CASE("Safearray::Put/Get", "[safearray]") { SECTION("Direct Element Access - No Throw") { RUN_TYPED_TEST_NOTHROW(_TYPED_DIRECT_NOTHROW); - //RUN_TEST(_CREATE_NOTHROW); + RUN_TEST(_DIRECT_NOTHROW); } SECTION("Direct Element Access - FailFast") { - //RUN_TYPED_TEST_FAILFAST(_TYPED_CREATE_FAILFAST); - //RUN_TEST(_CREATE_FAILFAST); + RUN_TYPED_TEST_FAILFAST(_TYPED_DIRECT_FAILFAST); + RUN_TEST(_DIRECT_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Direct Element Access - Exceptions") { - //RUN_TYPED_TEST(_TYPED_CREATE); - //RUN_TEST(_CREATE); + RUN_TYPED_TEST(_TYPED_DIRECT); + RUN_TEST(_DIRECT); } #endif } From 342897189cf5f7bd41e6752059d068e3d432e825 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Mon, 17 Aug 2020 19:22:25 -0700 Subject: [PATCH 14/34] Fixed Tests --- tests/SafearrayTests.cpp | 152 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 8a3e0100..f7aa097c 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -224,8 +224,8 @@ auto GetWritable(T& t) -> LPDISPATCH& return *(t.addressof()); } -template -bool PerformCompare(const T& left, const T& right) +template +bool PerformCompare(const T& left, const U& right) { return (left == right); } @@ -236,11 +236,73 @@ bool PerformCompare(const wil::unique_variant& left, const wil::unique_variant& return (::VariantCompare(left, right) == 0); } +template<> +bool PerformCompare(const BSTR& left, const BSTR& right) +{ + return (::SysStringLen(left) == ::SysStringLen(right)) + && (wcscmp(left, right) == 0); +} + +template<> +bool PerformCompare(const BSTR& left, const wil::unique_bstr& right) +{ + return PerformCompare(left, right.get()); +} + +template<> +bool PerformCompare(const wil::unique_bstr& left, const BSTR& right) +{ + return PerformCompare(left.get(), right); +} + template<> bool PerformCompare(const wil::unique_bstr& left, const wil::unique_bstr& right) { - return (::SysStringLen(left.get()) == ::SysStringLen(right.get())) - && (wcscmp(left.get(), right.get()) == 0); + return PerformCompare(left.get(), right.get()); +} + +template<> +bool PerformCompare(const wil::unique_variant& left, const VARIANT& right) +{ + return (::VariantCompare(left, right) == 0); +} + +template<> +bool PerformCompare(const VARIANT& left, const wil::unique_variant& right) +{ + return (::VariantCompare(left, right) == 0); +} + +template +void PerfromAssignment(T& t, const U& u) +{ + t = u; +} + +template<> +void PerfromAssignment(VARIANT& t, const wil::unique_variant& u) +{ + ::VariantCopy(&t, &u); +} + +template<> +void PerfromAssignment(BSTR& t, const wil::unique_bstr& u) +{ + t = ::SysAllocString(u.get()); +} + +template +void PerfromAssignment(I*& t, const wil::com_ptr& u) +{ + if (t) + { + t->Release(); + t = nullptr; + } + if (u) + { + REQUIRE_SUCCEEDED(u->QueryInterface(&t)); + } } template @@ -617,6 +679,63 @@ void Test_DirectElement() #define _DIRECT_FAILFAST(type) Test_DirectElement_Failfast(); #define _DIRECT(type) Test_DirectElement(); +template +void TestTyped_AccessData_NoThrow() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + if (SIZE > 1) + { + auto sa = safearray_t{}; + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + { + ULONG counter = {}; + typename safearray_t::unique_accessdata data; + REQUIRE_SUCCEEDED(sa.access_data(data)); + REQUIRE(data); + for (auto& elem : data) + { + PerfromAssignment(elem, sample_data[counter++]); + } + } + + auto sa2 = safearray_t{}; + REQUIRE_SUCCEEDED(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + ULONG counter = {}; + typename safearray_t::unique_accessdata data; + REQUIRE_SUCCEEDED(sa2.access_data(data)); + REQUIRE(data); + for (auto& elem : data) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + } + } +} + +template +void TestTyped_AccessData_Failfast() +{ +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void TestTyped_AccessData() +{ +} +#endif + +#define _TYPED_ACCESSDATA_NOTHROW(type) TestTyped_AccessData_NoThrow(); +#define _TYPED_ACCESSDATA_FAILFAST(type) TestTyped_AccessData_Failfast(); +#define _TYPED_ACCESSDATA(type) TestTyped_AccessData(); + TEST_CASE("Safearray::Create", "[safearray]") { @@ -641,7 +760,6 @@ TEST_CASE("Safearray::Create", "[safearray]") #endif } - TEST_CASE("Safearray::Put/Get", "[safearray]") { SECTION("Direct Element Access - No Throw") @@ -665,3 +783,27 @@ TEST_CASE("Safearray::Put/Get", "[safearray]") #endif } + +TEST_CASE("Safearray::AccessData", "[safearray]") +{ + SECTION("Access Data - No Throw") + { + RUN_TYPED_TEST_NOTHROW(_TYPED_ACCESSDATA_NOTHROW); + //RUN_TEST(_DIRECT_NOTHROW); + } + + SECTION("Access Data - FailFast") + { + //RUN_TYPED_TEST_FAILFAST(_TYPED_ACCESSDATA_FAILFAST); + //RUN_TEST(_DIRECT_FAILFAST); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Access Data - Exceptions") + { + //RUN_TYPED_TEST(_TYPED_ACCESSDATA); + //RUN_TEST(_DIRECT); + } +#endif +} + From bbd9e1d2ad0aa6313892a15d51e8d5df9f0ef4e2 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Tue, 18 Aug 2020 16:26:28 -0700 Subject: [PATCH 15/34] Lots of progress but the safearraydata_t type is all wrong because the unique_any types can't have their own members. --- include/wil/Safearrays.h | 98 +++--- tests/SafearrayTests.cpp | 669 ++++++++++++++++++++++++++++++--------- 2 files changed, 579 insertions(+), 188 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 1feeb833..8e4f7dde 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -211,6 +211,15 @@ namespace wil static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(psa); } + safearraydata_t(safearraydata_t&& other) : storage_t(wistd::move(other)) + { + m_pBegin = wistd::exchange(other.m_pBegin, nullptr); + m_nSize = wistd::exchange(other.m_nSize, 0L); + } + ~safearraydata_t() + { + + } result create(pointer psa) { @@ -229,16 +238,14 @@ namespace wil } // Ranged-for style - iterator begin() WI_NOEXCEPT { return m_pBegin; } - iterator end() WI_NOEXCEPT { return m_pBegin+m_nSize; } + iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } const_iterator begin() const WI_NOEXCEPT { return m_pBegin; } const_iterator end() const WI_NOEXCEPT { return m_pBegin+m_nSize; } - const_iterator cbegin() const WI_NOEXCEPT { return begin(); } - const_iterator cend() const WI_NOEXCEPT { return end(); } // Old style iteration ULONG size() const WI_NOEXCEPT { return m_nSize; } - WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } + WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin +i); } // Utilities @@ -273,6 +280,20 @@ namespace wil template using EnableIfTyped = typename wistd::enable_if::value, int>::type; // Filters functions that require a type because the class type doesn't provide one (element_t == void) template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; + template + using is_pointer_type = wistd::disjunction, + wistd::is_same, + wistd::is_same>; + + template + using is_type_not_set = wistd::is_same; + + template + using is_valid_type = wistd::negation>; + + template + using is_value_type = wistd::conjunction>, is_valid_type>; + public: // AccessData type that still requires a type (used when not typed) @@ -294,19 +315,19 @@ namespace wil explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} // Exception-based construction - template = 0> + template::value, int>::type = 0> safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(vt, cElements, lowerBound); } - template = 0> + template::value, int>::type = 0> safearray_t(UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(cElements, lowerBound); } - template = 0> + template::value, int>::type = 0> safearray_t(UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); @@ -314,14 +335,14 @@ namespace wil } // Low-level, arbitrary number of dimensions - template = 0> + template::value, int>::type = 0> result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) { return err_policy::HResult(_create(vt, cDims, sab)); } // Single Dimension specialization - template = 0> + template::value, int>::type = 0> result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) { auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; @@ -330,7 +351,7 @@ namespace wil // Uses the fixed element type defined by the class // Low-level, arbitrary number of dimensions - template = 0> + template::value, int>::type = 0> result create(UINT cDims, SAFEARRAYBOUND* sab) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -338,7 +359,7 @@ namespace wil } // Single Dimension specialization - template::value, int>::type = 0> + template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -412,25 +433,14 @@ namespace wil return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); } - template - using is_special_type = wistd::disjunction, - wistd::is_same, - wistd::is_same>; - - template - using is_valid_type = wistd::negation>; - - template - using is_nonspecial_type = wistd::conjunction>,is_valid_type>; - - template::value, int>::type = 0> + template::value, int>::type = 0> result put_element(LONG nIndex, const T& val) { WI_ASSERT(sizeof(val) == elemsize()); WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); } - template::value, int>::type = 0> + template::value, int>::type = 0> result put_element(LONG nIndex, T val) { WI_ASSERT(sizeof(val) == elemsize()); @@ -459,17 +469,17 @@ namespace wil // Data Access // Use with non-typed safearrays after the type has been determined - template = 0> - result access_data(unique_accessdata_t& data) - { - return err_policy::HResult(data.create(storage_t::get())); - } - // Use with type safearrays - template = 0> - result access_data(unique_accessdata& data) - { - return err_policy::HResult(data.create(storage_t::get())); - } + //template = 0> + //result access_data(unique_accessdata_t& data) + //{ + // return err_policy::HResult(data.create(storage_t::get())); + //} + // // Use with type safearrays + //template = 0> + //result access_data(unique_accessdata& data) + //{ + // return err_policy::HResult(data.create(storage_t::get())); + //} // Exception-based helper functions WI_NODISCARD LONG lbound(UINT nDim = 1) const @@ -493,31 +503,31 @@ namespace wil count(&nResult); return nResult; } - WI_NODISCARD unique_type copy() const + WI_NODISCARD unique_type create_copy() const { static_assert(wistd::is_same::value, "this method requires exceptions"); auto result = unique_type{}; - result.create(storage_t::get()); + result.create_copy(storage_t::get()); return result; } - template = 0> + template::value, int>::type = 0> WI_NODISCARD unique_accessdata_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); auto data = unique_accessdata_t{}; - access_data(data); - return data; + data.create(storage_t::get()); + return wistd::move(data); } - template = 0> + template::value, int>::type = 0> WI_NODISCARD unique_accessdata access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); auto data = unique_accessdata{}; - access_data(data); - return data; + data.create(storage_t::get()); + return wistd::move(data); } private: diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index f7aa097c..0a4f4093 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -84,7 +84,163 @@ , VARIANT \ ) -constexpr auto DEFAULT_SAMPLE_SIZE = 32; +// {5D80EC64-6694-4F49-B0B9-CCAA65467D12} +static const GUID IID_IAmForTesting = +{ 0x5d80ec64, 0x6694, 0x4f49, { 0xb0, 0xb9, 0xcc, 0xaa, 0x65, 0x46, 0x7d, 0x12 } }; + +struct __declspec(uuid("5D80EC64-6694-4F49-B0B9-CCAA65467D12")) + IAmForTesting : public IUnknown +{ + virtual HRESULT GetID(LONG* pOut) = 0; +}; + +class TestComObject : virtual public IAmForTesting, + virtual public IDispatch +{ +private: + TestComObject() + { + ::InterlockedIncrement(&s_ObjectCounter); + ::InterlockedIncrement(&s_IDCounter); + m_nID = s_IDCounter; + } + + virtual ~TestComObject() + { + ::InterlockedDecrement(&s_ObjectCounter); + } + + // IUnknown +public: + ULONG AddRef() + { + ::InterlockedIncrement(&m_nRefCount); + return m_nRefCount; + } + ULONG Release() + { + ULONG ulRefCount = ::InterlockedDecrement(&m_nRefCount); + if (0 == m_nRefCount) + { + delete this; + } + return ulRefCount; + } + HRESULT QueryInterface(REFIID riid, LPVOID* ppvObj) + { + if (!ppvObj) + return E_INVALIDARG; + *ppvObj = NULL; + if (riid == IID_IUnknown) + { + *ppvObj = (LPVOID)(IAmForTesting*)this; + AddRef(); + return NOERROR; + } + else if (riid == IID_IDispatch) + { + *ppvObj = (LPVOID)(IDispatch*)this; + AddRef(); + return NOERROR; + } + else if (riid == IID_IAmForTesting) + { + *ppvObj = (LPVOID)(IAmForTesting*)this; + AddRef(); + return NOERROR; + } + return E_NOINTERFACE; + } + + // IDispatch +public: + STDMETHODIMP GetTypeInfoCount(UINT* /*pctinfo*/) + { + return E_NOTIMPL; + } + + STDMETHODIMP GetTypeInfo(UINT /*itinfo*/, LCID /*lcid*/, ITypeInfo** /*pptinfo*/) + { + return E_NOTIMPL; + } + + STDMETHODIMP GetIDsOfNames(REFIID /*riid*/, LPOLESTR* /*rgszNames*/, UINT /*cNames*/, LCID /*lcid*/, DISPID* /*rgdispid*/) + { + return E_NOTIMPL; + } + + STDMETHODIMP Invoke(DISPID /*dispidMember*/, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* /*pdispparams*/, VARIANT* /*pvarResult*/, EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/) + { + return E_NOTIMPL; + } + + // IAmForTesting +public: + STDMETHODIMP GetID(LONG* pOut) + { + if (!pOut) return E_POINTER; + *pOut = m_nID; + return S_OK; + } + + // Factory Method +public: + static HRESULT Create(REFIID riid, LPVOID* ppvObj) + { + auto* p = new (std::nothrow) TestComObject(); + if (!p) return E_OUTOFMEMORY; + auto hr = p->QueryInterface(riid, ppvObj); + if (FAILED(hr)) + { + delete p; + } + return hr; + } + + static bool Compare(LPUNKNOWN left, LPUNKNOWN right) + { + if (left == nullptr && right == nullptr) + { + return true; + } + if (left == nullptr || right == nullptr) + { + return false; + } + + wil::com_ptr_nothrow spLeft; + wil::com_ptr_nothrow spRight; + + if (SUCCEEDED(left->QueryInterface(&spLeft)) && SUCCEEDED(right->QueryInterface(&spRight))) + { + LONG nLeft = 0; + LONG nRight = 0; + + REQUIRE_SUCCEEDED(spLeft->GetID(&nLeft)); + REQUIRE_SUCCEEDED(spRight->GetID(&nRight)); + + return (nLeft == nRight); + } + else + { + return false; + } + } + + static LONG GetObjectCounter() { return s_ObjectCounter; } + +private: + LONG m_nRefCount{}; + LONG m_nID{}; + + static LONG s_ObjectCounter; + static LONG s_IDCounter; +}; + +LONG TestComObject::s_ObjectCounter = 0; +LONG TestComObject::s_IDCounter = 0; + +constexpr auto DEFAULT_SAMPLE_SIZE = 32U; template::value, int>::type> auto GetSampleData() -> std::array @@ -106,7 +262,7 @@ std::array GetSampleData() for (auto i = 0; i < DEFAULT_SAMPLE_SIZE/2; ++i) { result[i] = (i != 0) ? static_cast(1 / i) : 0; - result[i + (DEFAULT_SAMPLE_SIZE/2)] = 2* static_cast(i); + result[i + (DEFAULT_SAMPLE_SIZE/2)] = 2 * static_cast(i); } return result; } @@ -167,14 +323,32 @@ std::array GetSampleData() } template::value, int>::type> -std::array, 1> GetSampleData() { return {}; } +std::array, DEFAULT_SAMPLE_SIZE> GetSampleData() +{ + auto result = std::array, DEFAULT_SAMPLE_SIZE>{}; + for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + { + auto& var = result[i]; + REQUIRE_SUCCEEDED(TestComObject::Create(IID_IAmForTesting, (LPVOID*)var.put())); + } + return result; +} template::value, int>::type> -std::array, 1> GetSampleData() { return {}; } +std::array, DEFAULT_SAMPLE_SIZE> GetSampleData() +{ + auto result = std::array, DEFAULT_SAMPLE_SIZE>{}; + for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + { + auto& var = result[i]; + REQUIRE_SUCCEEDED(TestComObject::Create(IID_IDispatch, (LPVOID*)var.put())); + } + return result; +} template ::value - && !wistd::is_same>::value - && !wistd::is_same>::value, int>::type> + && !wistd::is_same>::value + && !wistd::is_same>::value, int>::type> auto GetReadable(const T& t) -> const T& { return t; @@ -186,21 +360,21 @@ auto GetReadable(const T& t) -> BSTR return t.get(); } -template>::value, int>::type> +template>::value, int>::type> auto GetReadable(const T& t) -> LPUNKNOWN { return t.get(); } -template>::value, int>::type> +template>::value, int>::type> auto GetReadable(const T& t) -> LPDISPATCH { return t.get(); } template ::value - && !wistd::is_same>::value - && !wistd::is_same>::value, int>::type> + && !wistd::is_same>::value + && !wistd::is_same>::value, int>::type> auto GetWritable(T& t) -> T& { return t; @@ -212,13 +386,13 @@ auto GetWritable(T& t) -> BSTR& return *(t.addressof()); } -template>::value, int>::type> +template>::value, int>::type> auto GetWritable(T& t) -> LPUNKNOWN& { - return *(t.addressof()); + return *((LPUNKNOWN*)t.addressof()); } -template>::value, int>::type> +template>::value, int>::type> auto GetWritable(T& t) -> LPDISPATCH& { return *(t.addressof()); @@ -231,13 +405,7 @@ bool PerformCompare(const T& left, const U& right) } template<> -bool PerformCompare(const wil::unique_variant& left, const wil::unique_variant& right) -{ - return (::VariantCompare(left, right) == 0); -} - -template<> -bool PerformCompare(const BSTR& left, const BSTR& right) +bool PerformCompare(_In_z_ const BSTR& left, _In_z_ const BSTR& right) { return (::SysStringLen(left) == ::SysStringLen(right)) && (wcscmp(left, right) == 0); @@ -262,7 +430,7 @@ bool PerformCompare(const wil::unique_bstr& left, const wil::unique_bstr& right) } template<> -bool PerformCompare(const wil::unique_variant& left, const VARIANT& right) +bool PerformCompare(const wil::unique_variant& left, const wil::unique_variant& right) { return (::VariantCompare(left, right) == 0); } @@ -273,6 +441,30 @@ bool PerformCompare(const VARIANT& left, const wil::unique_variant& right) return (::VariantCompare(left, right) == 0); } +template<> +bool PerformCompare(const VARIANT& left, const VARIANT& right) +{ + return (::VariantCompare(left, right) == 0); +} + +template<> +bool PerformCompare(const LPUNKNOWN& left, const LPUNKNOWN& right) +{ + return TestComObject::Compare(left, right); +} + +template<> +bool PerformCompare(const wil::com_ptr_nothrow& left, const LPUNKNOWN& right) +{ + return TestComObject::Compare(left.get(), right); +} + +template<> +bool PerformCompare(const LPUNKNOWN& left, const wil::com_ptr_nothrow& right) +{ + return TestComObject::Compare(left, right.get()); +} + template void PerfromAssignment(T& t, const U& u) { @@ -282,7 +474,7 @@ void PerfromAssignment(T& t, const U& u) template<> void PerfromAssignment(VARIANT& t, const wil::unique_variant& u) { - ::VariantCopy(&t, &u); + FAIL_FAST_IF_FAILED(::VariantCopy(&t, &u)); } template<> @@ -291,8 +483,22 @@ void PerfromAssignment(BSTR& t, const wil::unique_bstr& u) t = ::SysAllocString(u.get()); } +template<> +void PerfromAssignment(LPUNKNOWN& t, const wil::com_ptr_nothrow& u) +{ + if (t) + { + t->Release(); + t = nullptr; + } + if (u) + { + REQUIRE_SUCCEEDED(u->QueryInterface(&t)); + } +} + template -void PerfromAssignment(I*& t, const wil::com_ptr& u) +void PerfromAssignment(I*& t, const wil::com_ptr_nothrow& u) { if (t) { @@ -320,7 +526,7 @@ void TestLock(safearray_t& sa) template void TestTyped_Create_NoThrow() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; auto sa = safearray_t{}; LONG val = 0; @@ -343,7 +549,7 @@ void TestTyped_Create_NoThrow() template void TestTyped_Create_FailFast() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; auto sa = safearray_t{}; LONG val = 0; @@ -367,7 +573,7 @@ void TestTyped_Create_FailFast() template void TestTyped_Create() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; auto sa = safearray_t{}; REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); @@ -390,7 +596,7 @@ void TestTyped_Create() template void Test_Create_NoThrow() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; auto sa = wil::unique_safearray_nothrow{}; LONG val = 0; @@ -413,7 +619,7 @@ void Test_Create_NoThrow() template void Test_Create_FailFast() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; auto sa = wil::unique_safearray_failfast{}; LONG val = 0; @@ -437,9 +643,9 @@ void Test_Create_FailFast() template void Test_Create() { - constexpr auto SIZE = 256U; + constexpr auto SIZE = DEFAULT_SAMPLE_SIZE; - wil::unique_safearray sa{}; + auto sa = wil::unique_safearray{}; REQUIRE_NOTHROW(sa.create( SIZE )); REQUIRE(sa); REQUIRE(sa.dim() == 1); @@ -466,28 +672,25 @@ void TestTyped_DirectElement_NoThrow() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = safearray_t{}; + auto sa = safearray_t{}; - REQUIRE_SUCCEEDED(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -501,28 +704,25 @@ void TestTyped_DirectElement_Failfast() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = safearray_t{}; + auto sa = safearray_t{}; - REQUIRE_NOCRASH(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -537,28 +737,25 @@ void TestTyped_DirectElement() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = safearray_t{}; + auto sa = safearray_t{}; - REQUIRE_NOTHROW(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -577,28 +774,25 @@ void Test_DirectElement_NoThrow() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = wil::unique_safearray_nothrow{}; + auto sa = wil::unique_safearray_nothrow{}; - REQUIRE_SUCCEEDED(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_SUCCEEDED(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_SUCCEEDED(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -612,28 +806,25 @@ void Test_DirectElement_Failfast() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = wil::unique_safearray_failfast{}; + auto sa = wil::unique_safearray_failfast{}; - REQUIRE_NOCRASH(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOCRASH(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOCRASH(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -648,28 +839,25 @@ void Test_DirectElement() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) - { - auto sa = wil::unique_safearray{}; + auto sa = wil::unique_safearray{}; - REQUIRE_NOTHROW(sa.create(SIZE)); - REQUIRE(sa); + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); - // Loop through and set the values with put_element - for (ULONG i = 0; i < SIZE; ++i) - { - REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); - } + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + REQUIRE_NOTHROW(sa.put_element(i, GetReadable(sample_data[i]))); + } - // Loop through and get the values with get_element - for (ULONG i = 0; i < SIZE; ++i) + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { { - { - auto temp = data_type{}; - // And make sure it was the value that was set - REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); - REQUIRE(PerformCompare(temp, sample_data[i])); - } + auto temp = data_type{}; + // And make sure it was the value that was set + REQUIRE_NOTHROW(sa.get_element(i, GetWritable(temp))); + REQUIRE(PerformCompare(temp, sample_data[i])); } } } @@ -688,15 +876,16 @@ void TestTyped_AccessData_NoThrow() using array_type = decltype(sample_data); using data_type = typename array_type::value_type; - if (SIZE > 1) + // Operate on the AccessData class using ranged-for { + // Create a new SA and copy the sample data into it auto sa = safearray_t{}; REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { ULONG counter = {}; typename safearray_t::unique_accessdata data; - REQUIRE_SUCCEEDED(sa.access_data(data)); + REQUIRE_SUCCEEDED(data.create(sa.get())); REQUIRE(data); for (auto& elem : data) { @@ -704,31 +893,219 @@ void TestTyped_AccessData_NoThrow() } } + // Duplicate the SA to make sure create_copy works auto sa2 = safearray_t{}; REQUIRE_SUCCEEDED(sa2.create_copy(sa.get())); REQUIRE(sa2); { + // Verify the values in the copy are the same as + // the values that were placed into the original ULONG counter = {}; typename safearray_t::unique_accessdata data; - REQUIRE_SUCCEEDED(sa2.access_data(data)); + REQUIRE_SUCCEEDED(data.create(sa2.get())); REQUIRE(data); - for (auto& elem : data) + for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); } } } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = safearray_t{}; + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + { + typename safearray_t::unique_accessdata data; + REQUIRE_SUCCEEDED(data.create(sa.get())); + REQUIRE(data); + for (ULONG i = 0 ; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = safearray_t{}; + REQUIRE_SUCCEEDED(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + typename safearray_t::unique_accessdata data; + REQUIRE_SUCCEEDED(data.create(sa2.get())); + REQUIRE(data); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + } + } } template void TestTyped_AccessData_Failfast() { + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + // Operate on the AccessData class using ranged-for + { + // Create a new SA and copy the sample data into it + auto sa = safearray_t{}; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + { + ULONG counter = {}; + typename safearray_t::unique_accessdata data; + REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE(data); + for (auto& elem : data) + { + PerfromAssignment(elem, sample_data[counter++]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = safearray_t{}; + REQUIRE_NOCRASH(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + ULONG counter = {}; + typename safearray_t::unique_accessdata data; + REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE(data); + for (const auto& elem : data) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + } + } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = safearray_t{}; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + { + typename safearray_t::unique_accessdata data; + REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE(data); + for (ULONG i = 0; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = safearray_t{}; + REQUIRE_NOCRASH(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + typename safearray_t::unique_accessdata data; + REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE(data); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + } + } } #ifdef WIL_ENABLE_EXCEPTIONS template void TestTyped_AccessData() { + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + // Operate on the AccessData class using ranged-for + { + // Create a new SA and copy the sample data into it + auto sa = safearray_t{}; + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + REQUIRE_NOTHROW([&]() + { + ULONG counter = {}; + auto data = sa.access_data(); + for (auto& elem : data) + { + PerfromAssignment(elem, sample_data[counter++]); + } + }()); + + // Duplicate the SA to make sure copy works + auto sa2 = safearray_t{}; + REQUIRE_NOTHROW([&]() + { + sa2 = sa.create_copy(); + REQUIRE(sa2); + }()); + + REQUIRE_NOTHROW([&]() + { + // Verify the values in the copy are the same as + // the values that were placed into the original + ULONG counter = {}; + auto data = sa.access_data(); + for (const auto& elem : data) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + }()); + } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = safearray_t{}; + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + REQUIRE_NOTHROW([&]() + { + auto data = sa.access_data(); + for (ULONG i = 0; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + }()); + + // Duplicate the SA to make sure copy works + auto sa2 = safearray_t{}; + REQUIRE_NOTHROW([&]() + { + sa2 = sa.create_copy(); + REQUIRE(sa2); + }()); + + REQUIRE_NOTHROW([&]() + { + // Verify the values in the copy are the same as + // the values that were placed into the original + auto data = sa2.access_data(); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + }()); + } } #endif @@ -781,6 +1158,8 @@ TEST_CASE("Safearray::Put/Get", "[safearray]") RUN_TEST(_DIRECT); } #endif + + REQUIRE(TestComObject::GetObjectCounter() == 0); } @@ -794,16 +1173,18 @@ TEST_CASE("Safearray::AccessData", "[safearray]") SECTION("Access Data - FailFast") { - //RUN_TYPED_TEST_FAILFAST(_TYPED_ACCESSDATA_FAILFAST); + RUN_TYPED_TEST_FAILFAST(_TYPED_ACCESSDATA_FAILFAST); //RUN_TEST(_DIRECT_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Access Data - Exceptions") { - //RUN_TYPED_TEST(_TYPED_ACCESSDATA); + RUN_TYPED_TEST(_TYPED_ACCESSDATA); //RUN_TEST(_DIRECT); } #endif + + REQUIRE(TestComObject::GetObjectCounter() == 0); } From b4814bb57d5ff688dbda2c4a7c5c9151848fb451 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Tue, 18 Aug 2020 17:58:29 -0700 Subject: [PATCH 16/34] Everything is working. All the unit tests pass. All the syntax works the way that I want. All that is missing is some helper functions to copy/move to/from vectors/arrays to safearrays. --- include/wil/Safearrays.h | 105 ++++----------- tests/SafearrayTests.cpp | 283 +++++++++++++++++++++++++++++++++++---- 2 files changed, 283 insertions(+), 105 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 8e4f7dde..b091afa8 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -186,12 +186,15 @@ namespace wil return safearray_unlock_scope_exit(psa); } - template - class safearraydata_t : public storage_t + template + class safearraydata_t { + private: + typedef unique_any safearray_unaccess_data; + public: typedef typename err_policy::result result; - typedef typename storage_t::pointer pointer; + typedef typename safearray_unaccess_data::pointer pointer; typedef T value_type; typedef value_type* value_pointer; typedef const value_type* const_value_pointer; @@ -201,21 +204,14 @@ namespace wil typedef const value_type* const_iterator; public: - // forward all base class constructors... - template - explicit safearraydata_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} - // Exception-based construction safearraydata_t(pointer psa) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(psa); } - safearraydata_t(safearraydata_t&& other) : storage_t(wistd::move(other)) - { - m_pBegin = wistd::exchange(other.m_pBegin, nullptr); - m_nSize = wistd::exchange(other.m_nSize, 0L); - } + safearraydata_t() = default; + safearraydata_t(safearraydata_t&& other) = default; ~safearraydata_t() { @@ -229,8 +225,8 @@ namespace wil WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); details::SafeArrayAccessData(psa, m_pBegin); - storage_t::reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(storage_t::get(), &m_nSize)); + m_unaccess.reset(psa); + RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); return S_OK; }(); @@ -252,18 +248,19 @@ namespace wil bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } private: + safearray_unaccess_data m_unaccess; value_pointer m_pBegin = nullptr; ULONG m_nSize = 0; }; template - using unique_safearrayaccess_nothrow = unique_any_t, err_returncode_policy>>; + using safearrayaccess_nothrow = safearraydata_t; template - using unique_safearrayaccess_failfast = unique_any_t, err_failfast_policy>>; + using safearrayaccess_failfast = safearraydata_t; #ifdef WIL_ENABLE_EXCEPTIONS - template - using unique_safearrayaccess = unique_any_t, err_exception_policy>>; + template + using safearrayaccess = safearraydata_t; #endif @@ -276,31 +273,20 @@ namespace wil { private: // SFINAE Helpers to improve readability - // Filters functions that don't require a type because a type was provided by the class type - template using EnableIfTyped = typename wistd::enable_if::value, int>::type; - // Filters functions that require a type because the class type doesn't provide one (element_t == void) - template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; template using is_pointer_type = wistd::disjunction, wistd::is_same, wistd::is_same>; - template using is_type_not_set = wistd::is_same; - template - using is_valid_type = wistd::negation>; - + using is_type_set = wistd::negation>; template - using is_value_type = wistd::conjunction>, is_valid_type>; - + using is_value_type = wistd::conjunction>, is_type_set>; public: - // AccessData type that still requires a type (used when not typed) template - using unique_accessdata_t = unique_any_t, err_policy>>; - // AccessData type that uses the safearray's type (used when a type is provided) - using unique_accessdata = unique_any_t, err_policy>>; + using safearrayaccess_t = safearraydata_t; // Represents this type using unique_type = unique_any_t>; @@ -321,18 +307,12 @@ namespace wil static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(vt, cElements, lowerBound); } - template::value, int>::type = 0> + template::value, int>::type = 0> safearray_t(UINT cElements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); create(cElements, lowerBound); } - template::value, int>::type = 0> - safearray_t(UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(cElements, lowerBound); - } // Low-level, arbitrary number of dimensions template::value, int>::type = 0> @@ -351,7 +331,7 @@ namespace wil // Uses the fixed element type defined by the class // Low-level, arbitrary number of dimensions - template::value, int>::type = 0> + template::value, int>::type = 0> result create(UINT cDims, SAFEARRAYBOUND* sab) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -359,7 +339,7 @@ namespace wil } // Single Dimension specialization - template::value, int>::type = 0> + template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { constexpr auto vt = static_cast(details::VarTraits::type); @@ -413,7 +393,6 @@ namespace wil } // Lowest-Level functions for those who know what they're doing - // TODO: Finish safearray_index type to collapse both versions into one functioon result put_element(LONG* pIndices, void* pv) { return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); @@ -448,38 +427,13 @@ namespace wil return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); } - template::value, int>::type = 0> + template::value, int>::type = 0> result get_element(LONG nIndex, T& t) { WI_ASSERT(sizeof(t) == elemsize()); WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } - //template using EnableIfTyped = typename wistd::enable_if::value, int>::type; - //// Filters functions that require a type because the class type doesn't provide one (element_t == void) - //template using EnableIfNotTyped = typename wistd::enable_if::value, int>::type; - - //template = 0> - //result get_element(LONG nIndex, T& t) - //{ - // WI_ASSERT(sizeof(t) == elemsize()); - // WI_ASSERT(dim() == 1); - // return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); - //} - - // Data Access - // Use with non-typed safearrays after the type has been determined - //template = 0> - //result access_data(unique_accessdata_t& data) - //{ - // return err_policy::HResult(data.create(storage_t::get())); - //} - // // Use with type safearrays - //template = 0> - //result access_data(unique_accessdata& data) - //{ - // return err_policy::HResult(data.create(storage_t::get())); - //} // Exception-based helper functions WI_NODISCARD LONG lbound(UINT nDim = 1) const @@ -511,21 +465,12 @@ namespace wil result.create_copy(storage_t::get()); return result; } - template::value, int>::type = 0> - WI_NODISCARD unique_accessdata_t access_data() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - - auto data = unique_accessdata_t{}; - data.create(storage_t::get()); - return wistd::move(data); - } - template::value, int>::type = 0> - WI_NODISCARD unique_accessdata access_data() const + template::value, int>::type = 0> + WI_NODISCARD safearrayaccess_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); - auto data = unique_accessdata{}; + auto data = safearrayaccess_t{}; data.create(storage_t::get()); return wistd::move(data); } diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 0a4f4093..400947b4 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -240,7 +240,7 @@ class TestComObject : virtual public IAmForTesting, LONG TestComObject::s_ObjectCounter = 0; LONG TestComObject::s_IDCounter = 0; -constexpr auto DEFAULT_SAMPLE_SIZE = 32U; +constexpr auto DEFAULT_SAMPLE_SIZE = 192U; template::value, int>::type> auto GetSampleData() -> std::array @@ -299,7 +299,7 @@ std::array GetSampleData() for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; - switch (i % 4) + switch (i % 6) { case 0: V_VT(&var) = VT_I4; @@ -317,6 +317,14 @@ std::array GetSampleData() V_VT(&var) = VT_R4; V_R4(&var) = 98.6f; break; + case 4: + V_VT(&var) = VT_UNKNOWN; + REQUIRE_SUCCEEDED(TestComObject::Create(IID_IUnknown, (LPVOID*)&(V_UNKNOWN(&var)))); + break; + case 5: + V_VT(&var) = VT_DISPATCH; + REQUIRE_SUCCEEDED(TestComObject::Create(IID_IDispatch, (LPVOID*)&(V_DISPATCH(&var)))); + break; } } return result; @@ -884,9 +892,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); - REQUIRE(data); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -901,9 +908,8 @@ void TestTyped_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); - REQUIRE(data); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -918,9 +924,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); - REQUIRE(data); for (ULONG i = 0 ; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -934,9 +939,8 @@ void TestTyped_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); - REQUIRE(data); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -962,9 +966,8 @@ void TestTyped_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); - REQUIRE(data); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -979,9 +982,8 @@ void TestTyped_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); - REQUIRE(data); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -996,9 +998,8 @@ void TestTyped_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); - REQUIRE(data); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1012,9 +1013,8 @@ void TestTyped_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - typename safearray_t::unique_accessdata data; + wil::safearrayaccess_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); - REQUIRE(data); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1043,8 +1043,7 @@ void TestTyped_AccessData() REQUIRE_NOTHROW([&]() { ULONG counter = {}; - auto data = sa.access_data(); - for (auto& elem : data) + for (auto& elem : sa.access_data()) { PerfromAssignment(elem, sample_data[counter++]); } @@ -1063,8 +1062,7 @@ void TestTyped_AccessData() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - auto data = sa.access_data(); - for (const auto& elem : data) + for (const auto& elem : sa.access_data()) { REQUIRE(PerformCompare(elem, sample_data[counter++])); } @@ -1113,6 +1111,241 @@ void TestTyped_AccessData() #define _TYPED_ACCESSDATA_FAILFAST(type) TestTyped_AccessData_Failfast(); #define _TYPED_ACCESSDATA(type) TestTyped_AccessData(); +template +void Test_AccessData_NoThrow() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + // Operate on the AccessData class using ranged-for + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray_nothrow{}; + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + { + ULONG counter = {}; + wil::safearrayaccess_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa.get())); + for (auto& elem : data) + { + PerfromAssignment(elem, sample_data[counter++]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = wil::unique_safearray_nothrow{}; + REQUIRE_SUCCEEDED(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + ULONG counter = {}; + wil::safearrayaccess_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa2.get())); + for (const auto& elem : data) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + } + } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray_nothrow{}; + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + { + wil::safearrayaccess_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa.get())); + for (ULONG i = 0; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = wil::unique_safearray_nothrow{}; + REQUIRE_SUCCEEDED(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + wil::safearrayaccess_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa2.get())); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + } + } +} + +template +void Test_AccessData_Failfast() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + // Operate on the AccessData class using ranged-for + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray_failfast{}; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + { + ULONG counter = {}; + wil::safearrayaccess_failfast data; + REQUIRE_NOCRASH(data.create(sa.get())); + for (auto& elem : data) + { + PerfromAssignment(elem, sample_data[counter++]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = wil::unique_safearray_failfast{}; + REQUIRE_NOCRASH(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + ULONG counter = {}; + wil::safearrayaccess_failfast data; + REQUIRE_NOCRASH(data.create(sa2.get())); + for (const auto& elem : data) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + } + } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray_failfast{}; + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + { + wil::safearrayaccess_failfast data; + REQUIRE_NOCRASH(data.create(sa.get())); + for (ULONG i = 0; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + } + + // Duplicate the SA to make sure create_copy works + auto sa2 = wil::unique_safearray_failfast{}; + REQUIRE_NOCRASH(sa2.create_copy(sa.get())); + REQUIRE(sa2); + { + // Verify the values in the copy are the same as + // the values that were placed into the original + wil::safearrayaccess_failfast data; + REQUIRE_NOCRASH(data.create(sa2.get())); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + } + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void Test_AccessData() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + // Operate on the AccessData class using ranged-for + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray{}; + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + REQUIRE_NOTHROW([&]() + { + ULONG counter = {}; + for (auto& elem : sa.access_data()) + { + PerfromAssignment(elem, sample_data[counter++]); + } + }()); + + // Duplicate the SA to make sure copy works + auto sa2 = wil::unique_safearray{}; + REQUIRE_NOTHROW([&]() + { + sa2 = sa.create_copy(); + REQUIRE(sa2); + }()); + + REQUIRE_NOTHROW([&]() + { + // Verify the values in the copy are the same as + // the values that were placed into the original + ULONG counter = {}; + for (const auto& elem : sa.access_data()) + { + REQUIRE(PerformCompare(elem, sample_data[counter++])); + } + }()); + } + + // Operate on the AccessData class using regular for-loop + { + // Create a new SA and copy the sample data into it + auto sa = wil::unique_safearray{}; + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + REQUIRE_NOTHROW([&]() + { + auto data = sa.access_data(); + for (ULONG i = 0; i < data.size(); ++i) + { + PerfromAssignment(data[i], sample_data[i]); + } + }()); + + // Duplicate the SA to make sure copy works + auto sa2 = wil::unique_safearray{}; + REQUIRE_NOTHROW([&]() + { + sa2 = sa.create_copy(); + REQUIRE(sa2); + }()); + + REQUIRE_NOTHROW([&]() + { + // Verify the values in the copy are the same as + // the values that were placed into the original + auto data = sa2.access_data(); + for (ULONG i = 0; i < data.size(); ++i) + { + REQUIRE(PerformCompare(data[i], sample_data[i])); + } + }()); + } +} +#endif + +#define _ACCESSDATA_NOTHROW(type) Test_AccessData_NoThrow(); +#define _ACCESSDATA_FAILFAST(type) Test_AccessData_Failfast(); +#define _ACCESSDATA(type) Test_AccessData(); TEST_CASE("Safearray::Create", "[safearray]") { @@ -1168,20 +1401,20 @@ TEST_CASE("Safearray::AccessData", "[safearray]") SECTION("Access Data - No Throw") { RUN_TYPED_TEST_NOTHROW(_TYPED_ACCESSDATA_NOTHROW); - //RUN_TEST(_DIRECT_NOTHROW); + RUN_TEST(_ACCESSDATA_NOTHROW); } SECTION("Access Data - FailFast") { RUN_TYPED_TEST_FAILFAST(_TYPED_ACCESSDATA_FAILFAST); - //RUN_TEST(_DIRECT_FAILFAST); + RUN_TEST(_ACCESSDATA_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Access Data - Exceptions") { RUN_TYPED_TEST(_TYPED_ACCESSDATA); - //RUN_TEST(_DIRECT); + RUN_TEST(_ACCESSDATA); } #endif From 331893c147f034f13c7a282db703cee684dddc63 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Wed, 19 Aug 2020 15:29:39 -0700 Subject: [PATCH 17/34] Code Cleanup Fixed the safearraydata class Added function to look up size of a single dimension Added retrieval of vartype and safer assertions --- include/wil/Safearrays.h | 238 ++++++++++++++++++++++++++++++--------- tests/SafearrayTests.cpp | 45 ++++---- 2 files changed, 212 insertions(+), 71 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index b091afa8..89ec56ab 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -19,6 +19,13 @@ #include "wistd_type_traits.h" #include "resource.h" // unique_hkey +namespace std +{ + // Forward Defines + template class allocator; + template> class vector; +} + namespace wil { #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) @@ -83,16 +90,12 @@ namespace wil inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) { - // Safearrays cannot hold type VT_NULL, so this value - // means the SAFEARRAY was null - VARTYPE vt = VT_NULL; + VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null if (psa != nullptr) { if (FAILED(::SafeArrayGetVartype(psa, &vt))) { - // Safearrays cannot hold type VT_EMPTY, so this value - // means the type couldn't be determined - vt = VT_EMPTY; + vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined } } return vt; @@ -108,10 +111,19 @@ namespace wil return S_OK; } - inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT { - RETURN_HR_IF_NULL(E_POINTER, pCount); + __FAIL_FAST_ASSERT__(psa != nullptr); + __FAIL_FAST_ASSERT__(pCount != nullptr); + RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); + // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order + *pCount = psa->rgsabound[psa->cDims - nDim].cElements; + return S_OK; + } + inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(pCount != nullptr); if ( psa != nullptr ) { ULONGLONG result = 1; @@ -123,7 +135,6 @@ namespace wil RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); } } - *pCount = static_cast(result); } else @@ -211,11 +222,11 @@ namespace wil create(psa); } safearraydata_t() = default; - safearraydata_t(safearraydata_t&& other) = default; - ~safearraydata_t() - { - - } + safearraydata_t(safearraydata_t&&) = default; + ~safearraydata_t() = default; + safearraydata_t& operator=(safearraydata_t&&) = default; + safearraydata_t(const safearraydata_t&) = delete; + safearraydata_t& operator=(const safearraydata_t&) = delete; result create(pointer psa) { @@ -236,8 +247,8 @@ namespace wil // Ranged-for style iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } - const_iterator begin() const WI_NOEXCEPT { return m_pBegin; } - const_iterator end() const WI_NOEXCEPT { return m_pBegin+m_nSize; } + const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } // Old style iteration ULONG size() const WI_NOEXCEPT { return m_nSize; } @@ -248,19 +259,19 @@ namespace wil bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } private: - safearray_unaccess_data m_unaccess; - value_pointer m_pBegin = nullptr; - ULONG m_nSize = 0; + safearray_unaccess_data m_unaccess{}; + value_pointer m_pBegin{}; + ULONG m_nSize{}; }; template - using safearrayaccess_nothrow = safearraydata_t; + using safearraydata_nothrow = safearraydata_t; template - using safearrayaccess_failfast = safearraydata_t; + using safearraydata_failfast = safearraydata_t; #ifdef WIL_ENABLE_EXCEPTIONS template - using safearrayaccess = safearraydata_t; + using safearraydata = safearraydata_t; #endif @@ -286,7 +297,7 @@ namespace wil public: template - using safearrayaccess_t = safearraydata_t; + using safearraydata_t = safearraydata_t; // Represents this type using unique_type = unique_any_t>; @@ -315,22 +326,13 @@ namespace wil } // Low-level, arbitrary number of dimensions + // Uses Explicit Type template::value, int>::type = 0> result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) { return err_policy::HResult(_create(vt, cDims, sab)); } - - // Single Dimension specialization - template::value, int>::type = 0> - result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) - { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } - - // Uses the fixed element type defined by the class - // Low-level, arbitrary number of dimensions + // Uses Inferred Type template::value, int>::type = 0> result create(UINT cDims, SAFEARRAYBOUND* sab) { @@ -338,7 +340,15 @@ namespace wil return err_policy::HResult(_create(vt, cDims, sab)); } - // Single Dimension specialization + // Single Dimension Specialization + // Uses Explicit Type + template::value, int>::type = 0> + result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } + // Uses Inferred Type template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { @@ -365,26 +375,47 @@ namespace wil ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } result lbound(UINT nDim, LONG* pLbound) const { + WI_ASSERT(pLbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); } result lbound(LONG* pLbound) const { WI_ASSERT(dim() == 1); + WI_ASSERT(pLbound != nullptr); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); } result ubound(UINT nDim, LONG* pUbound) const { + WI_ASSERT(pUbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); } result ubound(LONG* pUbound) const { WI_ASSERT(dim() == 1); + WI_ASSERT(pUbound != nullptr); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); } result count(ULONG* pCount) const { + WI_ASSERT(pCount != nullptr); return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); } + // Same as, but much faster than, the typical UBOUND - LBOUND + 1 + result dim_elements(UINT nDim, ULONG* pCount) const + { + WI_ASSERT(pCount != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); + } + + // Retrieve the stored type (when not working with a fixed type) + template::value, int>::type = 0> + VARTYPE vartype() + { + return details::SafeArrayGetVartype(storage_t::get()); + } // Lock Helper WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT @@ -393,25 +424,26 @@ namespace wil } // Lowest-Level functions for those who know what they're doing - result put_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); - } result put_element(LONG nIndex, void* pv) { WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); } - result get_element(LONG* pIndices, void* pv) + result put_element(LONG* pIndices, void* pv) { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); } result get_element(LONG nIndex, void* pv) { WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); } + result get_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + // Single Dimension Specialization template::value, int>::type = 0> result put_element(LONG nIndex, const T& val) { @@ -426,7 +458,6 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); } - template::value, int>::type = 0> result get_element(LONG nIndex, T& t) { @@ -435,6 +466,26 @@ namespace wil return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } + // Multi Dimension Support + template::value, int>::type = 0> + result put_element(LONG* pIndices, const T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); + } + template::value, int>::type = 0> + result put_element(LONG* pIndices, T val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); + } + template::value, int>::type = 0> + result get_element(LONG* pIndices, T& t) + { + WI_ASSERT(sizeof(t) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + } + // Exception-based helper functions WI_NODISCARD LONG lbound(UINT nDim = 1) const { @@ -457,6 +508,13 @@ namespace wil count(&nResult); return nResult; } + WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG nResult = 0; + dim_elements(nDim, &nResult); + return nResult; + } WI_NODISCARD unique_type create_copy() const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -466,11 +524,12 @@ namespace wil return result; } template::value, int>::type = 0> - WI_NODISCARD safearrayaccess_t access_data() const + WI_NODISCARD safearraydata_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); + WI_ASSERT(static_cast(details::VarTraits::type) == details::SafeArrayGetVartype(storage_t::get())); - auto data = safearrayaccess_t{}; + auto data = safearraydata_t{}; data.create(storage_t::get()); return wistd::move(data); } @@ -489,8 +548,8 @@ namespace wil using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; - using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; - using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; + //using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; + //using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; @@ -507,8 +566,8 @@ namespace wil using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; - using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; - using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; + //using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; + //using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; @@ -529,7 +588,7 @@ namespace wil #ifdef WIL_ENABLE_EXCEPTIONS using unique_safearray = unique_any_t, err_exception_policy, void>>; using unique_char_safearray = unique_any_t, err_exception_policy, char>>; - using unique_short_safearray = unique_any_t, err_exception_policy, short>>; + //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; using unique_long_safearray = unique_any_t, err_exception_policy, long>>; using unique_int_safearray = unique_any_t, err_exception_policy, int>>; using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; @@ -538,7 +597,7 @@ namespace wil using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; using unique_float_safearray = unique_any_t, err_exception_policy, float>>; - using unique_double_safearray = unique_any_t, err_exception_policy, double>>; + //using unique_double_safearray = unique_any_t, err_exception_policy, double>>; using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; @@ -549,11 +608,86 @@ namespace wil using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif -#endif +template +typename err_policy::result copy_safearray_to_container(SAFEARRAY* psa, container_type& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} + +template +typename err_policy::result copy_container_to_safearray(const container_type& cont, SAFEARRAY** ppsa) +{ + WI_ASSERT(ppsa != nullptr); + return err_policy::HResult([&]() + { + auto sa = wil::unique_safearray_nothrow{}; + RETURN_IF_FAILED(sa.create(cont.size())); + + { + auto data = wil::safearraydata_nothrow{}; + RETURN_IF_FAILED(data.create(sa.get())); + + auto src = cont.begin(); + auto dest = data.begin(); + while (src != cont.end()) + { + *(dest++) = *(src++); + } + } + + *ppsa = sa.release(); + return S_OK; + }()); +} + +template +typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector>& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.reserve(cont.size() + data.size()); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} + +template +typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.reserve(cont.size() + data.size()); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} #ifdef WIL_ENABLE_EXCEPTIONS +template +std::vector> copy_safearray_to_vector(SAFEARRAY* psa) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata{ psa }; + return { data.begin(), data.end() }; +} + #endif // WIL_ENABLE_EXCEPTIONS + +#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) } // namespace wil -#endif +#endif // #ifndef __WIL_SAFEARRAYS_INCLUDED diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 400947b4..ecc82ddc 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -2,8 +2,7 @@ #include #include -#include "common.h" -#include +//#include "common.h" #include #include @@ -892,7 +891,7 @@ void TestTyped_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); for (auto& elem : data) { @@ -908,7 +907,7 @@ void TestTyped_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); for (const auto& elem : data) { @@ -924,7 +923,7 @@ void TestTyped_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); for (ULONG i = 0 ; i < data.size(); ++i) { @@ -939,7 +938,7 @@ void TestTyped_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -966,7 +965,7 @@ void TestTyped_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); for (auto& elem : data) { @@ -982,7 +981,7 @@ void TestTyped_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); for (const auto& elem : data) { @@ -998,7 +997,7 @@ void TestTyped_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1013,7 +1012,7 @@ void TestTyped_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1128,7 +1127,7 @@ void Test_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); for (auto& elem : data) { @@ -1144,7 +1143,7 @@ void Test_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); for (const auto& elem : data) { @@ -1160,7 +1159,7 @@ void Test_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1175,7 +1174,7 @@ void Test_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearrayaccess_nothrow data; + wil::safearraydata_nothrow data; REQUIRE_SUCCEEDED(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1202,7 +1201,7 @@ void Test_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); for (auto& elem : data) { @@ -1218,7 +1217,7 @@ void Test_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); for (const auto& elem : data) { @@ -1234,7 +1233,7 @@ void Test_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1249,7 +1248,7 @@ void Test_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearrayaccess_failfast data; + wil::safearraydata_failfast data; REQUIRE_NOCRASH(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { @@ -1395,7 +1394,6 @@ TEST_CASE("Safearray::Put/Get", "[safearray]") REQUIRE(TestComObject::GetObjectCounter() == 0); } - TEST_CASE("Safearray::AccessData", "[safearray]") { SECTION("Access Data - No Throw") @@ -1421,3 +1419,12 @@ TEST_CASE("Safearray::AccessData", "[safearray]") REQUIRE(TestComObject::GetObjectCounter() == 0); } +TEST_CASE("Safearray Helper Functions", "[safearray]") +{ + std::vector test; + +#ifdef WIL_ENABLE_EXCEPTIONS +#endif + + REQUIRE(TestComObject::GetObjectCounter() == 0); +} From d7c4d8ca131d72805c2eb3fdf1d5992c67854369 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Fri, 21 Aug 2020 17:07:00 -0700 Subject: [PATCH 18/34] Made changes to make clang happy Moved the Safearray code into Resource.h --- include/wil/Safearrays.h | 693 --------------------------------------- include/wil/resource.h | 538 ++++++++++++++++++++++++++++++ tests/SafearrayTests.cpp | 83 ++--- 3 files changed, 574 insertions(+), 740 deletions(-) delete mode 100644 include/wil/Safearrays.h diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h deleted file mode 100644 index 89ec56ab..00000000 --- a/include/wil/Safearrays.h +++ /dev/null @@ -1,693 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. -// -//********************************************************* -#ifndef __WIL_SAFEARRAYS_INCLUDED -#define __WIL_SAFEARRAYS_INCLUDED - -#ifdef _KERNEL_MODE -#error This header is not supported in kernel-mode. -#endif - -#include // new(std::nothrow) -#include "wistd_type_traits.h" -#include "resource.h" // unique_hkey - -namespace std -{ - // Forward Defines - template class allocator; - template> class vector; -} - -namespace wil -{ -#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) -#define __WIL_OLEAUTO_ - /// @cond - namespace details - { - template - struct VarTraits{}; - - template<> struct VarTraits { enum { type = VT_I1 }; }; - //template<> struct VarTraits { enum { type = VT_I2 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I8 }; }; - template<> struct VarTraits { enum { type = VT_UI1 }; }; - template<> struct VarTraits { enum { type = VT_UI2 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI8 }; }; - template<> struct VarTraits { enum { type = VT_R4 }; }; - //template<> struct VarTraits { enum { type = VT_R8 }; }; - template<> struct VarTraits { enum { type = VT_BOOL }; }; - template<> struct VarTraits { enum { type = VT_DATE }; }; - template<> struct VarTraits { enum { type = VT_CY }; }; - template<> struct VarTraits { enum { type = VT_DECIMAL }; }; - template<> struct VarTraits { enum { type = VT_BSTR }; }; - template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; - template<> struct VarTraits { enum { type = VT_DISPATCH }; }; - template<> struct VarTraits { enum { type = VT_VARIANT }; }; - - inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); - } - - inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); - } - - inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); - } - - template - inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); - } - - inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); - } - - inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) - { - VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null - if (psa != nullptr) - { - if (FAILED(::SafeArrayGetVartype(psa, &vt))) - { - vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined - } - } - return vt; - } - - inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT - { - WI_ASSERT(sab != nullptr); - WI_ASSERT(cDims > 0); - psa = ::SafeArrayCreate(vt, cDims, sab); - RETURN_LAST_ERROR_IF_NULL(psa); - WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); - return S_OK; - } - - inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - __FAIL_FAST_ASSERT__(pCount != nullptr); - RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); - // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order - *pCount = psa->rgsabound[psa->cDims - nDim].cElements; - return S_OK; - } - - inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(pCount != nullptr); - if ( psa != nullptr ) - { - ULONGLONG result = 1; - for (UINT i = 0; i < psa->cDims; ++i) - { - result *= psa->rgsabound[i].cElements; - if (result > ULONG_MAX) - { - RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); - } - } - *pCount = static_cast(result); - } - else - { - // If it's invalid, it doesn't contain any elements - *pCount = 0; - } - return S_OK; - } - - typedef resource_policy safearray_resource_policy; - typedef resource_policy safearray_accessdata_resource_policy; - - //template - //class safearray_index : private std::array - //{ - //public: - // safearray_index() : std::array{ Indexes... }; - // LPLONG get() { return data(); } - // ULONG count() { return static_cast(size()); } - //}; - - //template - //class safearray_index - //{ - //public: - // safearray_index() : _value{ Index } {} - // LPLONG get() { return &_value; } - // ULONG count() { return 1; } - //private: - // LONG _value; - //}; - - //template - //class safearray_index - //{ - //public: - // safearray_index() : _indexes{Indexes} - // LPLONG get() { return _indexes; } - // ULONG count() { return 0; } - //private: - // LPLONG _indexes: - //}; - - //template - //DebugContextT& Format(const wchar_t* const lpszFormat, Args... args) noexcept - // template - // constexpr auto chars = std::array{ cs... }; - - } - /// @endcond - - typedef unique_any safearray_unlock_scope_exit; - - // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope - // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method - WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT - { - details::SafeArrayLock(psa); - return safearray_unlock_scope_exit(psa); - } - - template - class safearraydata_t - { - private: - typedef unique_any safearray_unaccess_data; - - public: - typedef typename err_policy::result result; - typedef typename safearray_unaccess_data::pointer pointer; - typedef T value_type; - typedef value_type* value_pointer; - typedef const value_type* const_value_pointer; - typedef value_type& value_reference; - typedef const value_type& const_value_reference; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - public: - // Exception-based construction - safearraydata_t(pointer psa) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(psa); - } - safearraydata_t() = default; - safearraydata_t(safearraydata_t&&) = default; - ~safearraydata_t() = default; - safearraydata_t& operator=(safearraydata_t&&) = default; - safearraydata_t(const safearraydata_t&) = delete; - safearraydata_t& operator=(const safearraydata_t&) = delete; - - result create(pointer psa) - { - HRESULT hr = [&]() - { - constexpr auto vt = static_cast(details::VarTraits::type); - WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); - details::SafeArrayAccessData(psa, m_pBegin); - m_unaccess.reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); - return S_OK; - }(); - - return err_policy::HResult(hr); - } - - // Ranged-for style - iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } - const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } - - // Old style iteration - ULONG size() const WI_NOEXCEPT { return m_nSize; } - WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } - WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin +i); } - - // Utilities - bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } - - private: - safearray_unaccess_data m_unaccess{}; - value_pointer m_pBegin{}; - ULONG m_nSize{}; - }; - - template - using safearraydata_nothrow = safearraydata_t; - template - using safearraydata_failfast = safearraydata_t; - -#ifdef WIL_ENABLE_EXCEPTIONS - template - using safearraydata = safearraydata_t; -#endif - - - // Add safearray functionality to the given storage type using the given error policy - // element_t is the either: - // A) The C++ type for the elements in the safearray and all methods are typesafe - // B) void to make the safearray generic (and not as typesafe) - template - class safearray_t : public storage_t - { - private: - // SFINAE Helpers to improve readability - template - using is_pointer_type = wistd::disjunction, - wistd::is_same, - wistd::is_same>; - template - using is_type_not_set = wistd::is_same; - template - using is_type_set = wistd::negation>; - template - using is_value_type = wistd::conjunction>, is_type_set>; - - public: - template - using safearraydata_t = safearraydata_t; - - // Represents this type - using unique_type = unique_any_t>; - - typedef typename err_policy::result result; - typedef typename storage_t::pointer pointer; - typedef element_t elemtype; - - public: - // forward all base class constructors... - template - explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} - - // Exception-based construction - template::value, int>::type = 0> - safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(vt, cElements, lowerBound); - } - template::value, int>::type = 0> - safearray_t(UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(cElements, lowerBound); - } - - // Low-level, arbitrary number of dimensions - // Uses Explicit Type - template::value, int>::type = 0> - result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) - { - return err_policy::HResult(_create(vt, cDims, sab)); - } - // Uses Inferred Type - template::value, int>::type = 0> - result create(UINT cDims, SAFEARRAYBOUND* sab) - { - constexpr auto vt = static_cast(details::VarTraits::type); - return err_policy::HResult(_create(vt, cDims, sab)); - } - - // Single Dimension Specialization - // Uses Explicit Type - template::value, int>::type = 0> - result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) - { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } - // Uses Inferred Type - template::value, int>::type = 0> - result create(UINT cElements, LONG lowerBound = 0) - { - constexpr auto vt = static_cast(details::VarTraits::type); - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } - - // Create a Copy - result create_copy(pointer psaSrc) - { - HRESULT hr = [&]() - { - auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); - storage_t::reset(psa); - return S_OK; - }(); - return err_policy::HResult(hr); - } - - // Dimensions, Sizes, Boundaries - ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } - ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } - result lbound(UINT nDim, LONG* pLbound) const - { - WI_ASSERT(pLbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); - } - result lbound(LONG* pLbound) const - { - WI_ASSERT(dim() == 1); - WI_ASSERT(pLbound != nullptr); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); - } - result ubound(UINT nDim, LONG* pUbound) const - { - WI_ASSERT(pUbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); - } - result ubound(LONG* pUbound) const - { - WI_ASSERT(dim() == 1); - WI_ASSERT(pUbound != nullptr); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); - } - result count(ULONG* pCount) const - { - WI_ASSERT(pCount != nullptr); - return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); - } - // Same as, but much faster than, the typical UBOUND - LBOUND + 1 - result dim_elements(UINT nDim, ULONG* pCount) const - { - WI_ASSERT(pCount != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); - } - - // Retrieve the stored type (when not working with a fixed type) - template::value, int>::type = 0> - VARTYPE vartype() - { - return details::SafeArrayGetVartype(storage_t::get()); - } - - // Lock Helper - WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT - { - return wil::SafeArrayUnlock_scope_exit(storage_t::get()); - } - - // Lowest-Level functions for those who know what they're doing - result put_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); - } - result put_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); - } - result get_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); - } - result get_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); - } - - // Single Dimension Specialization - template::value, int>::type = 0> - result put_element(LONG nIndex, const T& val) - { - WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); - } - template::value, int>::type = 0> - result put_element(LONG nIndex, T val) - { - WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); - } - template::value, int>::type = 0> - result get_element(LONG nIndex, T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); - } - - // Multi Dimension Support - template::value, int>::type = 0> - result put_element(LONG* pIndices, const T& val) - { - WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); - } - template::value, int>::type = 0> - result put_element(LONG* pIndices, T val) - { - WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); - } - template::value, int>::type = 0> - result get_element(LONG* pIndices, T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); - } - - // Exception-based helper functions - WI_NODISCARD LONG lbound(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - lbound(nDim, &nResult); - return nResult; - } - WI_NODISCARD LONG ubound(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - ubound(nDim, &nResult); - return nResult; - } - WI_NODISCARD ULONG count() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - count(&nResult); - return nResult; - } - WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - dim_elements(nDim, &nResult); - return nResult; - } - WI_NODISCARD unique_type create_copy() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - - auto result = unique_type{}; - result.create_copy(storage_t::get()); - return result; - } - template::value, int>::type = 0> - WI_NODISCARD safearraydata_t access_data() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - WI_ASSERT(static_cast(details::VarTraits::type) == details::SafeArrayGetVartype(storage_t::get())); - - auto data = safearraydata_t{}; - data.create(storage_t::get()); - return wistd::move(data); - } - - private: - HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) - { - auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); - storage_t::reset(psa); - return S_OK; - } - }; - - using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; - using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; - using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; - using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; - //using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; - //using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; - using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; - using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; - using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; - using unique_int_safearray_failfast = unique_any_t, err_failfast_policy, int>>; - using unique_longlong_safearray_nothrow = unique_any_t, err_returncode_policy, long long>>; - using unique_longlong_safearray_failfast = unique_any_t, err_failfast_policy, long long>>; - using unique_byte_safearray_nothrow = unique_any_t, err_returncode_policy, byte>>; - using unique_byte_safearray_failfast = unique_any_t, err_failfast_policy, byte>>; - using unique_word_safearray_nothrow = unique_any_t, err_returncode_policy, WORD>>; - using unique_word_safearray_failfast = unique_any_t, err_failfast_policy, WORD>>; - using unique_dword_safearray_nothrow = unique_any_t, err_returncode_policy, DWORD>>; - using unique_dword_safearray_failfast = unique_any_t, err_failfast_policy, DWORD>>; - using unique_ulonglong_safearray_nothrow = unique_any_t, err_returncode_policy, ULONGLONG>>; - using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; - using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; - using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; - //using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; - //using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; - using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; - using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; - using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; - using unique_date_safearray_failfast = unique_any_t, err_failfast_policy, DATE>>; - using unique_currency_safearray_nothrow = unique_any_t, err_returncode_policy, CURRENCY>>; - using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; - using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; - using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; - using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; - using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; - using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; - using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; - using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; - using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; - using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; - using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; - -#ifdef WIL_ENABLE_EXCEPTIONS - using unique_safearray = unique_any_t, err_exception_policy, void>>; - using unique_char_safearray = unique_any_t, err_exception_policy, char>>; - //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; - using unique_long_safearray = unique_any_t, err_exception_policy, long>>; - using unique_int_safearray = unique_any_t, err_exception_policy, int>>; - using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; - using unique_byte_safearray = unique_any_t, err_exception_policy, byte>>; - using unique_word_safearray = unique_any_t, err_exception_policy, WORD>>; - using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; - using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; - using unique_float_safearray = unique_any_t, err_exception_policy, float>>; - //using unique_double_safearray = unique_any_t, err_exception_policy, double>>; - using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; - using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; - using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; - using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; - using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; - using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; - using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; - using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; -#endif - -template -typename err_policy::result copy_safearray_to_container(SAFEARRAY* psa, container_type& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -template -typename err_policy::result copy_container_to_safearray(const container_type& cont, SAFEARRAY** ppsa) -{ - WI_ASSERT(ppsa != nullptr); - return err_policy::HResult([&]() - { - auto sa = wil::unique_safearray_nothrow{}; - RETURN_IF_FAILED(sa.create(cont.size())); - - { - auto data = wil::safearraydata_nothrow{}; - RETURN_IF_FAILED(data.create(sa.get())); - - auto src = cont.begin(); - auto dest = data.begin(); - while (src != cont.end()) - { - *(dest++) = *(src++); - } - } - - *ppsa = sa.release(); - return S_OK; - }()); -} - -template -typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector>& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.reserve(cont.size() + data.size()); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -template -typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.reserve(cont.size() + data.size()); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -#ifdef WIL_ENABLE_EXCEPTIONS -template -std::vector> copy_safearray_to_vector(SAFEARRAY* psa) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata{ psa }; - return { data.begin(), data.end() }; -} - -#endif // WIL_ENABLE_EXCEPTIONS - -#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) -} // namespace wil - -#endif // #ifndef __WIL_SAFEARRAYS_INCLUDED - diff --git a/include/wil/resource.h b/include/wil/resource.h index d679382d..db3e737b 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5572,6 +5572,544 @@ namespace wil typedef weak_any weak_package_info_reference; #endif // __WIL_APPMODEL_H_STL +#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) +#define __WIL_OLEAUTO_ + /// @cond + namespace details + { + template + struct VarTraits {}; + + template<> struct VarTraits { enum { type = VT_I1 }; }; + //template<> struct VarTraits { enum { type = VT_I2 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I8 }; }; + template<> struct VarTraits { enum { type = VT_UI1 }; }; + template<> struct VarTraits { enum { type = VT_UI2 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI8 }; }; + template<> struct VarTraits { enum { type = VT_R4 }; }; + //template<> struct VarTraits { enum { type = VT_R8 }; }; + template<> struct VarTraits { enum { type = VT_BOOL }; }; + template<> struct VarTraits { enum { type = VT_DATE }; }; + template<> struct VarTraits { enum { type = VT_CY }; }; + template<> struct VarTraits { enum { type = VT_DECIMAL }; }; + template<> struct VarTraits { enum { type = VT_BSTR }; }; + template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; + template<> struct VarTraits { enum { type = VT_DISPATCH }; }; + template<> struct VarTraits { enum { type = VT_VARIANT }; }; + + inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); + } + + inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); + } + + inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); + } + + template + inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); + } + + inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); + } + + inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) + { + VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null + if ((psa != nullptr) && FAILED(::SafeArrayGetVartype(psa, &vt))) + { + vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined + } + return vt; + } + + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT + { + WI_ASSERT(sab != nullptr); + WI_ASSERT(cDims > 0); + psa = ::SafeArrayCreate(vt, cDims, sab); + RETURN_LAST_ERROR_IF_NULL(psa); + WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); + return S_OK; + } + + inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + __FAIL_FAST_ASSERT__(pCount != nullptr); + RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); + // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order + *pCount = psa->rgsabound[psa->cDims - nDim].cElements; + return S_OK; + } + + inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(pCount != nullptr); + if (psa != nullptr) + { + ULONGLONG result = 1; + for (UINT i = 0; i < psa->cDims; ++i) + { + result *= psa->rgsabound[i].cElements; + if (result > ULONG_MAX) + { + RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + } + } + *pCount = static_cast(result); + } + else + { + // If it's invalid, it doesn't contain any elements + *pCount = 0; + } + return S_OK; + } + + typedef resource_policy safearray_resource_policy; + } + /// @endcond + + typedef unique_any safearray_unlock_scope_exit; + + // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope + // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT + { + details::SafeArrayLock(psa); + return safearray_unlock_scope_exit(psa); + } + + template + class safearraydata_t + { + private: + typedef unique_any safearray_unaccess_data; + + public: + typedef typename err_policy::result result; + typedef typename safearray_unaccess_data::pointer pointer; + typedef T value_type; + typedef value_type* value_pointer; + typedef const value_type* const_value_pointer; + typedef value_type& value_reference; + typedef const value_type& const_value_reference; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + public: + // Exception-based construction + safearraydata_t(pointer psa) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + access(psa); + } + safearraydata_t() = default; + safearraydata_t(safearraydata_t&&) = default; + ~safearraydata_t() = default; + safearraydata_t& operator=(safearraydata_t&&) = default; + safearraydata_t(const safearraydata_t&) = delete; + safearraydata_t& operator=(const safearraydata_t&) = delete; + + result access(pointer psa) + { + HRESULT hr = [&]() + { + WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); + details::SafeArrayAccessData(psa, m_pBegin); + m_unaccess.reset(psa); + RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); + return S_OK; + }(); + + return err_policy::HResult(hr); + } + + // Ranged-for style + iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin + m_nSize; } + const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin + m_nSize; } + + // Old style iteration + ULONG size() const WI_NOEXCEPT { return m_nSize; } + WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } + WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } + + // Utilities + bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } + + private: + safearray_unaccess_data m_unaccess{}; + value_pointer m_pBegin{}; + ULONG m_nSize{}; + }; + + template + using safearraydata_nothrow = safearraydata_t; + template + using safearraydata_failfast = safearraydata_t; + +#ifdef WIL_ENABLE_EXCEPTIONS + template + using safearraydata = safearraydata_t; +#endif + + + // Add safearray functionality to the given storage type using the given error policy + // element_t is the either: + // A) The C++ type for the elements in the safearray and all methods are typesafe + // B) void to make the safearray generic (and not as typesafe) + template + class safearray_t : public storage_t + { + private: + // SFINAE Helpers to improve readability + template + using is_pointer_type = wistd::disjunction, + wistd::is_same, + wistd::is_same>; + template + using is_type_not_set = wistd::is_same; + template + using is_type_set = wistd::negation>; + template + using is_value_type = wistd::conjunction>, is_type_set>; + + public: + template + using safearraydata_t = safearraydata_t; + + // Represents this type + using unique_type = unique_any_t>; + + typedef typename err_policy::result result; + typedef typename storage_t::pointer pointer; + typedef element_t elemtype; + + public: + // forward all base class constructors... + template + explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // Exception-based construction + template::value, int>::type = 0> + safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(vt, cElements, lowerBound); + } + template::value, int>::type = 0> + safearray_t(UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(cElements, lowerBound); + } + + // Low-level, arbitrary number of dimensions + // Uses Explicit Type + template::value, int>::type = 0> + result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + return err_policy::HResult(_create(vt, cDims, sab)); + } + // Uses Inferred Type + template::value, int>::type = 0> + result create(UINT cDims, SAFEARRAYBOUND* sab) + { + constexpr auto vt = static_cast(details::VarTraits::type); + return err_policy::HResult(_create(vt, cDims, sab)); + } + + // Single Dimension Specialization + // Uses Explicit Type + template::value, int>::type = 0> + result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } + // Uses Inferred Type + template::value, int>::type = 0> + result create(UINT cElements, LONG lowerBound = 0) + { + constexpr auto vt = static_cast(details::VarTraits::type); + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } + + // Create a Copy + result create_copy(pointer psaSrc) + { + HRESULT hr = [&]() + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); + storage_t::reset(psa); + return S_OK; + }(); + return err_policy::HResult(hr); + } + + // Dimensions, Sizes, Boundaries + ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } + result lbound(UINT nDim, LONG* pLbound) const + { + WI_ASSERT(pLbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); + } + result lbound(LONG* pLbound) const + { + WI_ASSERT(dim() == 1); + WI_ASSERT(pLbound != nullptr); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); + } + result ubound(UINT nDim, LONG* pUbound) const + { + WI_ASSERT(pUbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); + } + result ubound(LONG* pUbound) const + { + WI_ASSERT(dim() == 1); + WI_ASSERT(pUbound != nullptr); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); + } + result count(ULONG* pCount) const + { + WI_ASSERT(pCount != nullptr); + return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); + } + // Same as, but much faster than, the typical UBOUND - LBOUND + 1 + result dim_elements(UINT nDim, ULONG* pCount) const + { + WI_ASSERT(pCount != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); + } + + // Retrieve the stored type (when not working with a fixed type) + template::value, int>::type = 0> + VARTYPE vartype() + { + return details::SafeArrayGetVartype(storage_t::get()); + } + + // Lock Helper + WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT + { + return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + } + + // Lowest-Level functions for those who know what they're doing + result put_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + } + result put_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); + } + result get_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + } + result get_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + + // Single Dimension Specialization + template::value, int>::type = 0> + result put_element(LONG nIndex, const T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); + } + template::value, int>::type = 0> + result put_element(LONG nIndex, T val) + { + WI_ASSERT(sizeof(val) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); + } + template::value, int>::type = 0> + result get_element(LONG nIndex, T& t) + { + WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); + } + + // Multi Dimension Support + template::value, int>::type = 0> + result put_element(LONG* pIndices, const T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); + } + template::value, int>::type = 0> + result put_element(LONG* pIndices, T val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); + } + template::value, int>::type = 0> + result get_element(LONG* pIndices, T& t) + { + WI_ASSERT(sizeof(t) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + } + + // Exception-based helper functions + WI_NODISCARD LONG lbound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG nResult = 0; + lbound(nDim, &nResult); + return nResult; + } + WI_NODISCARD LONG ubound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG nResult = 0; + ubound(nDim, &nResult); + return nResult; + } + WI_NODISCARD ULONG count() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG nResult = 0; + count(&nResult); + return nResult; + } + WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG nResult = 0; + dim_elements(nDim, &nResult); + return nResult; + } + WI_NODISCARD unique_type create_copy() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto result = unique_type{}; + result.create_copy(storage_t::get()); + return result; + } + template::value, int>::type = 0> + WI_NODISCARD safearraydata_t access_data() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto data = safearraydata_t{}; + data.access(storage_t::get()); + return wistd::move(data); + } + + private: + HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); + storage_t::reset(psa); + return S_OK; + } + }; + + using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; + using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; + using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; + using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; + //using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; + //using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; + using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; + using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; + using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; + using unique_int_safearray_failfast = unique_any_t, err_failfast_policy, int>>; + using unique_longlong_safearray_nothrow = unique_any_t, err_returncode_policy, long long>>; + using unique_longlong_safearray_failfast = unique_any_t, err_failfast_policy, long long>>; + using unique_byte_safearray_nothrow = unique_any_t, err_returncode_policy, byte>>; + using unique_byte_safearray_failfast = unique_any_t, err_failfast_policy, byte>>; + using unique_word_safearray_nothrow = unique_any_t, err_returncode_policy, WORD>>; + using unique_word_safearray_failfast = unique_any_t, err_failfast_policy, WORD>>; + using unique_dword_safearray_nothrow = unique_any_t, err_returncode_policy, DWORD>>; + using unique_dword_safearray_failfast = unique_any_t, err_failfast_policy, DWORD>>; + using unique_ulonglong_safearray_nothrow = unique_any_t, err_returncode_policy, ULONGLONG>>; + using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; + using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; + using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; + //using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; + //using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; + using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; + using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; + using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; + using unique_date_safearray_failfast = unique_any_t, err_failfast_policy, DATE>>; + using unique_currency_safearray_nothrow = unique_any_t, err_returncode_policy, CURRENCY>>; + using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; + using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; + using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; + using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; + using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; + using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; + using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; + using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; + using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; + using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; + using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; + +#ifdef WIL_ENABLE_EXCEPTIONS + using unique_safearray = unique_any_t, err_exception_policy, void>>; + using unique_char_safearray = unique_any_t, err_exception_policy, char>>; + //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; + using unique_long_safearray = unique_any_t, err_exception_policy, long>>; + using unique_int_safearray = unique_any_t, err_exception_policy, int>>; + using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; + using unique_byte_safearray = unique_any_t, err_exception_policy, byte>>; + using unique_word_safearray = unique_any_t, err_exception_policy, WORD>>; + using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; + using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; + using unique_float_safearray = unique_any_t, err_exception_policy, float>>; + //using unique_double_safearray = unique_any_t, err_exception_policy, double>>; + using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; + using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; + using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; + using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; + using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; + using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; + using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; + using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; +#endif + +#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) + #if defined(WDFAPI) && !defined(__WIL_WDFAPI) #define __WIL_WDFAPI diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index ecc82ddc..9404bf54 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,8 +1,7 @@ -#include -#include #include +#include -//#include "common.h" +#include "common.h" #include #include @@ -514,7 +513,7 @@ void PerfromAssignment(I*& t, const wil::com_ptr_nothrow& u) } if (u) { - REQUIRE_SUCCEEDED(u->QueryInterface(&t)); + REQUIRE_SUCCEEDED(u->QueryInterface(IID_PPV_ARGS(&t))); } } @@ -541,7 +540,7 @@ void TestTyped_Create_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); @@ -564,7 +563,7 @@ void TestTyped_Create_FailFast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOCRASH(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_NOCRASH(sa.lbound(&val)); @@ -586,7 +585,7 @@ void TestTyped_Create() REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE-1); @@ -673,7 +672,7 @@ void Test_Create() template void TestTyped_DirectElement_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -705,7 +704,7 @@ void TestTyped_DirectElement_NoThrow() template void TestTyped_DirectElement_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -738,7 +737,7 @@ void TestTyped_DirectElement_Failfast() template void TestTyped_DirectElement() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -877,7 +876,7 @@ void Test_DirectElement() template void TestTyped_AccessData_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -891,8 +890,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -907,8 +906,8 @@ void TestTyped_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -923,8 +922,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa.get())); for (ULONG i = 0 ; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -938,8 +937,8 @@ void TestTyped_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -951,7 +950,7 @@ void TestTyped_AccessData_NoThrow() template void TestTyped_AccessData_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -965,8 +964,8 @@ void TestTyped_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -981,8 +980,8 @@ void TestTyped_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -997,8 +996,8 @@ void TestTyped_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1012,8 +1011,8 @@ void TestTyped_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1026,7 +1025,7 @@ void TestTyped_AccessData_Failfast() template void TestTyped_AccessData() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -1128,7 +1127,7 @@ void Test_AccessData_NoThrow() { ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + REQUIRE_SUCCEEDED(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1144,7 +1143,7 @@ void Test_AccessData_NoThrow() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1160,7 +1159,7 @@ void Test_AccessData_NoThrow() REQUIRE(sa); { wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + REQUIRE_SUCCEEDED(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1175,7 +1174,7 @@ void Test_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1202,7 +1201,7 @@ void Test_AccessData_Failfast() { ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE_NOCRASH(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1218,7 +1217,7 @@ void Test_AccessData_Failfast() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE_NOCRASH(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1234,7 +1233,7 @@ void Test_AccessData_Failfast() REQUIRE(sa); { wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE_NOCRASH(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1249,7 +1248,7 @@ void Test_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE_NOCRASH(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1418,13 +1417,3 @@ TEST_CASE("Safearray::AccessData", "[safearray]") REQUIRE(TestComObject::GetObjectCounter() == 0); } - -TEST_CASE("Safearray Helper Functions", "[safearray]") -{ - std::vector test; - -#ifdef WIL_ENABLE_EXCEPTIONS -#endif - - REQUIRE(TestComObject::GetObjectCounter() == 0); -} From b7b4ea88d5f672ea53cc0a4bdf964e3fa6ed2362 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Fri, 21 Aug 2020 17:16:23 -0700 Subject: [PATCH 19/34] Rollback changes to gitignore --- .gitignore | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.gitignore b/.gitignore index 3de71a71..3973a16b 100644 --- a/.gitignore +++ b/.gitignore @@ -334,11 +334,3 @@ ASALocalRun/ # CMake/Build output build/ -/packaging -/build_tools/nuget.exe -*.vcxproj -*.filters -*.sln -/CMakeCache.txt -cmake_install.cmake -CMakeFiles/ \ No newline at end of file From bec3d8efd88d3be5fecfe8887daab9f1b85f244d Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sat, 22 Aug 2020 12:31:52 -0700 Subject: [PATCH 20/34] Revert "Made changes to make clang happy" This reverts commit d7c4d8ca131d72805c2eb3fdf1d5992c67854369. --- include/wil/Safearrays.h | 693 +++++++++++++++++++++++++++++++++++++++ include/wil/resource.h | 538 ------------------------------ tests/SafearrayTests.cpp | 83 +++-- 3 files changed, 740 insertions(+), 574 deletions(-) create mode 100644 include/wil/Safearrays.h diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h new file mode 100644 index 00000000..89ec56ab --- /dev/null +++ b/include/wil/Safearrays.h @@ -0,0 +1,693 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_SAFEARRAYS_INCLUDED +#define __WIL_SAFEARRAYS_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include // new(std::nothrow) +#include "wistd_type_traits.h" +#include "resource.h" // unique_hkey + +namespace std +{ + // Forward Defines + template class allocator; + template> class vector; +} + +namespace wil +{ +#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) +#define __WIL_OLEAUTO_ + /// @cond + namespace details + { + template + struct VarTraits{}; + + template<> struct VarTraits { enum { type = VT_I1 }; }; + //template<> struct VarTraits { enum { type = VT_I2 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I4 }; }; + template<> struct VarTraits { enum { type = VT_I8 }; }; + template<> struct VarTraits { enum { type = VT_UI1 }; }; + template<> struct VarTraits { enum { type = VT_UI2 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI4 }; }; + template<> struct VarTraits { enum { type = VT_UI8 }; }; + template<> struct VarTraits { enum { type = VT_R4 }; }; + //template<> struct VarTraits { enum { type = VT_R8 }; }; + template<> struct VarTraits { enum { type = VT_BOOL }; }; + template<> struct VarTraits { enum { type = VT_DATE }; }; + template<> struct VarTraits { enum { type = VT_CY }; }; + template<> struct VarTraits { enum { type = VT_DECIMAL }; }; + template<> struct VarTraits { enum { type = VT_BSTR }; }; + template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; + template<> struct VarTraits { enum { type = VT_DISPATCH }; }; + template<> struct VarTraits { enum { type = VT_VARIANT }; }; + + inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); + } + + inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); + } + + inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); + } + + template + inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); + } + + inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); + } + + inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) + { + VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null + if (psa != nullptr) + { + if (FAILED(::SafeArrayGetVartype(psa, &vt))) + { + vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined + } + } + return vt; + } + + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT + { + WI_ASSERT(sab != nullptr); + WI_ASSERT(cDims > 0); + psa = ::SafeArrayCreate(vt, cDims, sab); + RETURN_LAST_ERROR_IF_NULL(psa); + WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); + return S_OK; + } + + inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(psa != nullptr); + __FAIL_FAST_ASSERT__(pCount != nullptr); + RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); + // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order + *pCount = psa->rgsabound[psa->cDims - nDim].cElements; + return S_OK; + } + + inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(pCount != nullptr); + if ( psa != nullptr ) + { + ULONGLONG result = 1; + for (UINT i = 0; i < psa->cDims; ++i) + { + result *= psa->rgsabound[i].cElements; + if (result > ULONG_MAX) + { + RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); + } + } + *pCount = static_cast(result); + } + else + { + // If it's invalid, it doesn't contain any elements + *pCount = 0; + } + return S_OK; + } + + typedef resource_policy safearray_resource_policy; + typedef resource_policy safearray_accessdata_resource_policy; + + //template + //class safearray_index : private std::array + //{ + //public: + // safearray_index() : std::array{ Indexes... }; + // LPLONG get() { return data(); } + // ULONG count() { return static_cast(size()); } + //}; + + //template + //class safearray_index + //{ + //public: + // safearray_index() : _value{ Index } {} + // LPLONG get() { return &_value; } + // ULONG count() { return 1; } + //private: + // LONG _value; + //}; + + //template + //class safearray_index + //{ + //public: + // safearray_index() : _indexes{Indexes} + // LPLONG get() { return _indexes; } + // ULONG count() { return 0; } + //private: + // LPLONG _indexes: + //}; + + //template + //DebugContextT& Format(const wchar_t* const lpszFormat, Args... args) noexcept + // template + // constexpr auto chars = std::array{ cs... }; + + } + /// @endcond + + typedef unique_any safearray_unlock_scope_exit; + + // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope + // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT + { + details::SafeArrayLock(psa); + return safearray_unlock_scope_exit(psa); + } + + template + class safearraydata_t + { + private: + typedef unique_any safearray_unaccess_data; + + public: + typedef typename err_policy::result result; + typedef typename safearray_unaccess_data::pointer pointer; + typedef T value_type; + typedef value_type* value_pointer; + typedef const value_type* const_value_pointer; + typedef value_type& value_reference; + typedef const value_type& const_value_reference; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + public: + // Exception-based construction + safearraydata_t(pointer psa) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(psa); + } + safearraydata_t() = default; + safearraydata_t(safearraydata_t&&) = default; + ~safearraydata_t() = default; + safearraydata_t& operator=(safearraydata_t&&) = default; + safearraydata_t(const safearraydata_t&) = delete; + safearraydata_t& operator=(const safearraydata_t&) = delete; + + result create(pointer psa) + { + HRESULT hr = [&]() + { + constexpr auto vt = static_cast(details::VarTraits::type); + WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); + WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); + details::SafeArrayAccessData(psa, m_pBegin); + m_unaccess.reset(psa); + RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); + return S_OK; + }(); + + return err_policy::HResult(hr); + } + + // Ranged-for style + iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } + const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } + const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } + + // Old style iteration + ULONG size() const WI_NOEXCEPT { return m_nSize; } + WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } + WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin +i); } + + // Utilities + bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } + + private: + safearray_unaccess_data m_unaccess{}; + value_pointer m_pBegin{}; + ULONG m_nSize{}; + }; + + template + using safearraydata_nothrow = safearraydata_t; + template + using safearraydata_failfast = safearraydata_t; + +#ifdef WIL_ENABLE_EXCEPTIONS + template + using safearraydata = safearraydata_t; +#endif + + + // Add safearray functionality to the given storage type using the given error policy + // element_t is the either: + // A) The C++ type for the elements in the safearray and all methods are typesafe + // B) void to make the safearray generic (and not as typesafe) + template + class safearray_t : public storage_t + { + private: + // SFINAE Helpers to improve readability + template + using is_pointer_type = wistd::disjunction, + wistd::is_same, + wistd::is_same>; + template + using is_type_not_set = wistd::is_same; + template + using is_type_set = wistd::negation>; + template + using is_value_type = wistd::conjunction>, is_type_set>; + + public: + template + using safearraydata_t = safearraydata_t; + + // Represents this type + using unique_type = unique_any_t>; + + typedef typename err_policy::result result; + typedef typename storage_t::pointer pointer; + typedef element_t elemtype; + + public: + // forward all base class constructors... + template + explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // Exception-based construction + template::value, int>::type = 0> + safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(vt, cElements, lowerBound); + } + template::value, int>::type = 0> + safearray_t(UINT cElements, LONG lowerBound = 0) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(cElements, lowerBound); + } + + // Low-level, arbitrary number of dimensions + // Uses Explicit Type + template::value, int>::type = 0> + result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + return err_policy::HResult(_create(vt, cDims, sab)); + } + // Uses Inferred Type + template::value, int>::type = 0> + result create(UINT cDims, SAFEARRAYBOUND* sab) + { + constexpr auto vt = static_cast(details::VarTraits::type); + return err_policy::HResult(_create(vt, cDims, sab)); + } + + // Single Dimension Specialization + // Uses Explicit Type + template::value, int>::type = 0> + result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + { + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } + // Uses Inferred Type + template::value, int>::type = 0> + result create(UINT cElements, LONG lowerBound = 0) + { + constexpr auto vt = static_cast(details::VarTraits::type); + auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + return err_policy::HResult(_create(vt, 1, &bounds)); + } + + // Create a Copy + result create_copy(pointer psaSrc) + { + HRESULT hr = [&]() + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); + storage_t::reset(psa); + return S_OK; + }(); + return err_policy::HResult(hr); + } + + // Dimensions, Sizes, Boundaries + ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } + result lbound(UINT nDim, LONG* pLbound) const + { + WI_ASSERT(pLbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); + } + result lbound(LONG* pLbound) const + { + WI_ASSERT(dim() == 1); + WI_ASSERT(pLbound != nullptr); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); + } + result ubound(UINT nDim, LONG* pUbound) const + { + WI_ASSERT(pUbound != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); + } + result ubound(LONG* pUbound) const + { + WI_ASSERT(dim() == 1); + WI_ASSERT(pUbound != nullptr); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); + } + result count(ULONG* pCount) const + { + WI_ASSERT(pCount != nullptr); + return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); + } + // Same as, but much faster than, the typical UBOUND - LBOUND + 1 + result dim_elements(UINT nDim, ULONG* pCount) const + { + WI_ASSERT(pCount != nullptr); + WI_ASSERT((nDim > 0) && (nDim <= dim())); + return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); + } + + // Retrieve the stored type (when not working with a fixed type) + template::value, int>::type = 0> + VARTYPE vartype() + { + return details::SafeArrayGetVartype(storage_t::get()); + } + + // Lock Helper + WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT + { + return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + } + + // Lowest-Level functions for those who know what they're doing + result put_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + } + result put_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); + } + result get_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + } + result get_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + + // Single Dimension Specialization + template::value, int>::type = 0> + result put_element(LONG nIndex, const T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); + } + template::value, int>::type = 0> + result put_element(LONG nIndex, T val) + { + WI_ASSERT(sizeof(val) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); + } + template::value, int>::type = 0> + result get_element(LONG nIndex, T& t) + { + WI_ASSERT(sizeof(t) == elemsize()); + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); + } + + // Multi Dimension Support + template::value, int>::type = 0> + result put_element(LONG* pIndices, const T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); + } + template::value, int>::type = 0> + result put_element(LONG* pIndices, T val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); + } + template::value, int>::type = 0> + result get_element(LONG* pIndices, T& t) + { + WI_ASSERT(sizeof(t) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + } + + // Exception-based helper functions + WI_NODISCARD LONG lbound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG nResult = 0; + lbound(nDim, &nResult); + return nResult; + } + WI_NODISCARD LONG ubound(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + LONG nResult = 0; + ubound(nDim, &nResult); + return nResult; + } + WI_NODISCARD ULONG count() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG nResult = 0; + count(&nResult); + return nResult; + } + WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + ULONG nResult = 0; + dim_elements(nDim, &nResult); + return nResult; + } + WI_NODISCARD unique_type create_copy() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + + auto result = unique_type{}; + result.create_copy(storage_t::get()); + return result; + } + template::value, int>::type = 0> + WI_NODISCARD safearraydata_t access_data() const + { + static_assert(wistd::is_same::value, "this method requires exceptions"); + WI_ASSERT(static_cast(details::VarTraits::type) == details::SafeArrayGetVartype(storage_t::get())); + + auto data = safearraydata_t{}; + data.create(storage_t::get()); + return wistd::move(data); + } + + private: + HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); + storage_t::reset(psa); + return S_OK; + } + }; + + using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; + using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; + using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; + using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; + //using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; + //using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; + using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; + using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; + using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; + using unique_int_safearray_failfast = unique_any_t, err_failfast_policy, int>>; + using unique_longlong_safearray_nothrow = unique_any_t, err_returncode_policy, long long>>; + using unique_longlong_safearray_failfast = unique_any_t, err_failfast_policy, long long>>; + using unique_byte_safearray_nothrow = unique_any_t, err_returncode_policy, byte>>; + using unique_byte_safearray_failfast = unique_any_t, err_failfast_policy, byte>>; + using unique_word_safearray_nothrow = unique_any_t, err_returncode_policy, WORD>>; + using unique_word_safearray_failfast = unique_any_t, err_failfast_policy, WORD>>; + using unique_dword_safearray_nothrow = unique_any_t, err_returncode_policy, DWORD>>; + using unique_dword_safearray_failfast = unique_any_t, err_failfast_policy, DWORD>>; + using unique_ulonglong_safearray_nothrow = unique_any_t, err_returncode_policy, ULONGLONG>>; + using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; + using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; + using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; + //using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; + //using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; + using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; + using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; + using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; + using unique_date_safearray_failfast = unique_any_t, err_failfast_policy, DATE>>; + using unique_currency_safearray_nothrow = unique_any_t, err_returncode_policy, CURRENCY>>; + using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; + using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; + using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; + using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; + using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; + using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; + using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; + using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; + using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; + using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; + using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; + +#ifdef WIL_ENABLE_EXCEPTIONS + using unique_safearray = unique_any_t, err_exception_policy, void>>; + using unique_char_safearray = unique_any_t, err_exception_policy, char>>; + //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; + using unique_long_safearray = unique_any_t, err_exception_policy, long>>; + using unique_int_safearray = unique_any_t, err_exception_policy, int>>; + using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; + using unique_byte_safearray = unique_any_t, err_exception_policy, byte>>; + using unique_word_safearray = unique_any_t, err_exception_policy, WORD>>; + using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; + using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; + using unique_float_safearray = unique_any_t, err_exception_policy, float>>; + //using unique_double_safearray = unique_any_t, err_exception_policy, double>>; + using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; + using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; + using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; + using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; + using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; + using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; + using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; + using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; +#endif + +template +typename err_policy::result copy_safearray_to_container(SAFEARRAY* psa, container_type& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} + +template +typename err_policy::result copy_container_to_safearray(const container_type& cont, SAFEARRAY** ppsa) +{ + WI_ASSERT(ppsa != nullptr); + return err_policy::HResult([&]() + { + auto sa = wil::unique_safearray_nothrow{}; + RETURN_IF_FAILED(sa.create(cont.size())); + + { + auto data = wil::safearraydata_nothrow{}; + RETURN_IF_FAILED(data.create(sa.get())); + + auto src = cont.begin(); + auto dest = data.begin(); + while (src != cont.end()) + { + *(dest++) = *(src++); + } + } + + *ppsa = sa.release(); + return S_OK; + }()); +} + +template +typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector>& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.reserve(cont.size() + data.size()); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} + +template +typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector& cont) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata_nothrow{}; + return err_policy::HResult([&]() + { + RETURN_IF_FAILED(data.create(psa)); + cont.reserve(cont.size() + data.size()); + cont.insert(cont.end(), data.begin(), data.end()); + return S_OK; + }()); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +std::vector> copy_safearray_to_vector(SAFEARRAY* psa) +{ + WI_ASSERT(psa != nullptr); + auto data = wil::safearraydata{ psa }; + return { data.begin(), data.end() }; +} + +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) +} // namespace wil + +#endif // #ifndef __WIL_SAFEARRAYS_INCLUDED + diff --git a/include/wil/resource.h b/include/wil/resource.h index db3e737b..d679382d 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5572,544 +5572,6 @@ namespace wil typedef weak_any weak_package_info_reference; #endif // __WIL_APPMODEL_H_STL -#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) -#define __WIL_OLEAUTO_ - /// @cond - namespace details - { - template - struct VarTraits {}; - - template<> struct VarTraits { enum { type = VT_I1 }; }; - //template<> struct VarTraits { enum { type = VT_I2 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I8 }; }; - template<> struct VarTraits { enum { type = VT_UI1 }; }; - template<> struct VarTraits { enum { type = VT_UI2 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI8 }; }; - template<> struct VarTraits { enum { type = VT_R4 }; }; - //template<> struct VarTraits { enum { type = VT_R8 }; }; - template<> struct VarTraits { enum { type = VT_BOOL }; }; - template<> struct VarTraits { enum { type = VT_DATE }; }; - template<> struct VarTraits { enum { type = VT_CY }; }; - template<> struct VarTraits { enum { type = VT_DECIMAL }; }; - template<> struct VarTraits { enum { type = VT_BSTR }; }; - template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; - template<> struct VarTraits { enum { type = VT_DISPATCH }; }; - template<> struct VarTraits { enum { type = VT_VARIANT }; }; - - inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); - } - - inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); - } - - inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); - } - - template - inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); - } - - inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); - } - - inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) - { - VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null - if ((psa != nullptr) && FAILED(::SafeArrayGetVartype(psa, &vt))) - { - vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined - } - return vt; - } - - inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT - { - WI_ASSERT(sab != nullptr); - WI_ASSERT(cDims > 0); - psa = ::SafeArrayCreate(vt, cDims, sab); - RETURN_LAST_ERROR_IF_NULL(psa); - WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); - return S_OK; - } - - inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(psa != nullptr); - __FAIL_FAST_ASSERT__(pCount != nullptr); - RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); - // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order - *pCount = psa->rgsabound[psa->cDims - nDim].cElements; - return S_OK; - } - - inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT - { - __FAIL_FAST_ASSERT__(pCount != nullptr); - if (psa != nullptr) - { - ULONGLONG result = 1; - for (UINT i = 0; i < psa->cDims; ++i) - { - result *= psa->rgsabound[i].cElements; - if (result > ULONG_MAX) - { - RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); - } - } - *pCount = static_cast(result); - } - else - { - // If it's invalid, it doesn't contain any elements - *pCount = 0; - } - return S_OK; - } - - typedef resource_policy safearray_resource_policy; - } - /// @endcond - - typedef unique_any safearray_unlock_scope_exit; - - // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope - // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method - WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT - { - details::SafeArrayLock(psa); - return safearray_unlock_scope_exit(psa); - } - - template - class safearraydata_t - { - private: - typedef unique_any safearray_unaccess_data; - - public: - typedef typename err_policy::result result; - typedef typename safearray_unaccess_data::pointer pointer; - typedef T value_type; - typedef value_type* value_pointer; - typedef const value_type* const_value_pointer; - typedef value_type& value_reference; - typedef const value_type& const_value_reference; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - public: - // Exception-based construction - safearraydata_t(pointer psa) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - access(psa); - } - safearraydata_t() = default; - safearraydata_t(safearraydata_t&&) = default; - ~safearraydata_t() = default; - safearraydata_t& operator=(safearraydata_t&&) = default; - safearraydata_t(const safearraydata_t&) = delete; - safearraydata_t& operator=(const safearraydata_t&) = delete; - - result access(pointer psa) - { - HRESULT hr = [&]() - { - WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - details::SafeArrayAccessData(psa, m_pBegin); - m_unaccess.reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); - return S_OK; - }(); - - return err_policy::HResult(hr); - } - - // Ranged-for style - iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin + m_nSize; } - const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin + m_nSize; } - - // Old style iteration - ULONG size() const WI_NOEXCEPT { return m_nSize; } - WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } - WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } - - // Utilities - bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } - - private: - safearray_unaccess_data m_unaccess{}; - value_pointer m_pBegin{}; - ULONG m_nSize{}; - }; - - template - using safearraydata_nothrow = safearraydata_t; - template - using safearraydata_failfast = safearraydata_t; - -#ifdef WIL_ENABLE_EXCEPTIONS - template - using safearraydata = safearraydata_t; -#endif - - - // Add safearray functionality to the given storage type using the given error policy - // element_t is the either: - // A) The C++ type for the elements in the safearray and all methods are typesafe - // B) void to make the safearray generic (and not as typesafe) - template - class safearray_t : public storage_t - { - private: - // SFINAE Helpers to improve readability - template - using is_pointer_type = wistd::disjunction, - wistd::is_same, - wistd::is_same>; - template - using is_type_not_set = wistd::is_same; - template - using is_type_set = wistd::negation>; - template - using is_value_type = wistd::conjunction>, is_type_set>; - - public: - template - using safearraydata_t = safearraydata_t; - - // Represents this type - using unique_type = unique_any_t>; - - typedef typename err_policy::result result; - typedef typename storage_t::pointer pointer; - typedef element_t elemtype; - - public: - // forward all base class constructors... - template - explicit safearray_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} - - // Exception-based construction - template::value, int>::type = 0> - safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(vt, cElements, lowerBound); - } - template::value, int>::type = 0> - safearray_t(UINT cElements, LONG lowerBound = 0) - { - static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(cElements, lowerBound); - } - - // Low-level, arbitrary number of dimensions - // Uses Explicit Type - template::value, int>::type = 0> - result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) - { - return err_policy::HResult(_create(vt, cDims, sab)); - } - // Uses Inferred Type - template::value, int>::type = 0> - result create(UINT cDims, SAFEARRAYBOUND* sab) - { - constexpr auto vt = static_cast(details::VarTraits::type); - return err_policy::HResult(_create(vt, cDims, sab)); - } - - // Single Dimension Specialization - // Uses Explicit Type - template::value, int>::type = 0> - result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) - { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } - // Uses Inferred Type - template::value, int>::type = 0> - result create(UINT cElements, LONG lowerBound = 0) - { - constexpr auto vt = static_cast(details::VarTraits::type); - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; - return err_policy::HResult(_create(vt, 1, &bounds)); - } - - // Create a Copy - result create_copy(pointer psaSrc) - { - HRESULT hr = [&]() - { - auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); - storage_t::reset(psa); - return S_OK; - }(); - return err_policy::HResult(hr); - } - - // Dimensions, Sizes, Boundaries - ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } - ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } - result lbound(UINT nDim, LONG* pLbound) const - { - WI_ASSERT(pLbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); - } - result lbound(LONG* pLbound) const - { - WI_ASSERT(dim() == 1); - WI_ASSERT(pLbound != nullptr); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); - } - result ubound(UINT nDim, LONG* pUbound) const - { - WI_ASSERT(pUbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); - } - result ubound(LONG* pUbound) const - { - WI_ASSERT(dim() == 1); - WI_ASSERT(pUbound != nullptr); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); - } - result count(ULONG* pCount) const - { - WI_ASSERT(pCount != nullptr); - return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); - } - // Same as, but much faster than, the typical UBOUND - LBOUND + 1 - result dim_elements(UINT nDim, ULONG* pCount) const - { - WI_ASSERT(pCount != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); - } - - // Retrieve the stored type (when not working with a fixed type) - template::value, int>::type = 0> - VARTYPE vartype() - { - return details::SafeArrayGetVartype(storage_t::get()); - } - - // Lock Helper - WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT - { - return wil::SafeArrayUnlock_scope_exit(storage_t::get()); - } - - // Lowest-Level functions for those who know what they're doing - result put_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); - } - result put_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); - } - result get_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); - } - result get_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); - } - - // Single Dimension Specialization - template::value, int>::type = 0> - result put_element(LONG nIndex, const T& val) - { - WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); - } - template::value, int>::type = 0> - result put_element(LONG nIndex, T val) - { - WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); - } - template::value, int>::type = 0> - result get_element(LONG nIndex, T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); - } - - // Multi Dimension Support - template::value, int>::type = 0> - result put_element(LONG* pIndices, const T& val) - { - WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); - } - template::value, int>::type = 0> - result put_element(LONG* pIndices, T val) - { - WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); - } - template::value, int>::type = 0> - result get_element(LONG* pIndices, T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); - } - - // Exception-based helper functions - WI_NODISCARD LONG lbound(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - lbound(nDim, &nResult); - return nResult; - } - WI_NODISCARD LONG ubound(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - ubound(nDim, &nResult); - return nResult; - } - WI_NODISCARD ULONG count() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - count(&nResult); - return nResult; - } - WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - dim_elements(nDim, &nResult); - return nResult; - } - WI_NODISCARD unique_type create_copy() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - - auto result = unique_type{}; - result.create_copy(storage_t::get()); - return result; - } - template::value, int>::type = 0> - WI_NODISCARD safearraydata_t access_data() const - { - static_assert(wistd::is_same::value, "this method requires exceptions"); - - auto data = safearraydata_t{}; - data.access(storage_t::get()); - return wistd::move(data); - } - - private: - HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) - { - auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); - storage_t::reset(psa); - return S_OK; - } - }; - - using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; - using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; - using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; - using unique_char_safearray_failfast = unique_any_t, err_failfast_policy, char>>; - //using unique_short_safearray_nothrow = unique_any_t, err_returncode_policy, short>>; - //using unique_short_safearray_failfast = unique_any_t, err_failfast_policy, short>>; - using unique_long_safearray_nothrow = unique_any_t, err_returncode_policy, long>>; - using unique_long_safearray_failfast = unique_any_t, err_failfast_policy, long>>; - using unique_int_safearray_nothrow = unique_any_t, err_returncode_policy, int>>; - using unique_int_safearray_failfast = unique_any_t, err_failfast_policy, int>>; - using unique_longlong_safearray_nothrow = unique_any_t, err_returncode_policy, long long>>; - using unique_longlong_safearray_failfast = unique_any_t, err_failfast_policy, long long>>; - using unique_byte_safearray_nothrow = unique_any_t, err_returncode_policy, byte>>; - using unique_byte_safearray_failfast = unique_any_t, err_failfast_policy, byte>>; - using unique_word_safearray_nothrow = unique_any_t, err_returncode_policy, WORD>>; - using unique_word_safearray_failfast = unique_any_t, err_failfast_policy, WORD>>; - using unique_dword_safearray_nothrow = unique_any_t, err_returncode_policy, DWORD>>; - using unique_dword_safearray_failfast = unique_any_t, err_failfast_policy, DWORD>>; - using unique_ulonglong_safearray_nothrow = unique_any_t, err_returncode_policy, ULONGLONG>>; - using unique_ulonglong_safearray_failfast = unique_any_t, err_failfast_policy, ULONGLONG>>; - using unique_float_safearray_nothrow = unique_any_t, err_returncode_policy, float>>; - using unique_float_safearray_failfast = unique_any_t, err_failfast_policy, float>>; - //using unique_double_safearray_nothrow = unique_any_t, err_returncode_policy, double>>; - //using unique_double_safearray_failfast = unique_any_t, err_failfast_policy, double>>; - using unique_varbool_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT_BOOL>>; - using unique_varbool_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT_BOOL>>; - using unique_date_safearray_nothrow = unique_any_t, err_returncode_policy, DATE>>; - using unique_date_safearray_failfast = unique_any_t, err_failfast_policy, DATE>>; - using unique_currency_safearray_nothrow = unique_any_t, err_returncode_policy, CURRENCY>>; - using unique_currency_safearray_failfast = unique_any_t, err_failfast_policy, CURRENCY>>; - using unique_decimal_safearray_nothrow = unique_any_t, err_returncode_policy, DECIMAL>>; - using unique_decimal_safearray_failfast = unique_any_t, err_failfast_policy, DECIMAL>>; - using unique_bstr_safearray_nothrow = unique_any_t, err_returncode_policy, BSTR>>; - using unique_bstr_safearray_failfast = unique_any_t, err_failfast_policy, BSTR>>; - using unique_unknown_safearray_nothrow = unique_any_t, err_returncode_policy, LPUNKNOWN>>; - using unique_unknown_safearray_failfast = unique_any_t, err_failfast_policy, LPUNKNOWN>>; - using unique_dispatch_safearray_nothrow = unique_any_t, err_returncode_policy, LPDISPATCH>>; - using unique_dispatch_safearray_failfast = unique_any_t, err_failfast_policy, LPDISPATCH>>; - using unique_variant_safearray_nothrow = unique_any_t, err_returncode_policy, VARIANT>>; - using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; - -#ifdef WIL_ENABLE_EXCEPTIONS - using unique_safearray = unique_any_t, err_exception_policy, void>>; - using unique_char_safearray = unique_any_t, err_exception_policy, char>>; - //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; - using unique_long_safearray = unique_any_t, err_exception_policy, long>>; - using unique_int_safearray = unique_any_t, err_exception_policy, int>>; - using unique_longlong_safearray = unique_any_t, err_exception_policy, long long>>; - using unique_byte_safearray = unique_any_t, err_exception_policy, byte>>; - using unique_word_safearray = unique_any_t, err_exception_policy, WORD>>; - using unique_dword_safearray = unique_any_t, err_exception_policy, DWORD>>; - using unique_ulonglong_safearray = unique_any_t, err_exception_policy, ULONGLONG>>; - using unique_float_safearray = unique_any_t, err_exception_policy, float>>; - //using unique_double_safearray = unique_any_t, err_exception_policy, double>>; - using unique_varbool_safearray = unique_any_t, err_exception_policy, VARIANT_BOOL>>; - using unique_date_safearray = unique_any_t, err_exception_policy, DATE>>; - using unique_currency_safearray = unique_any_t, err_exception_policy, CURRENCY>>; - using unique_decimal_safearray = unique_any_t, err_exception_policy, DECIMAL>>; - using unique_bstr_safearray = unique_any_t, err_exception_policy, BSTR>>; - using unique_unknown_safearray = unique_any_t, err_exception_policy, LPUNKNOWN>>; - using unique_dispatch_safearray = unique_any_t, err_exception_policy, LPDISPATCH>>; - using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; -#endif - -#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) - #if defined(WDFAPI) && !defined(__WIL_WDFAPI) #define __WIL_WDFAPI diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 9404bf54..ecc82ddc 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,7 +1,8 @@ -#include #include +#include +#include -#include "common.h" +//#include "common.h" #include #include @@ -513,7 +514,7 @@ void PerfromAssignment(I*& t, const wil::com_ptr_nothrow& u) } if (u) { - REQUIRE_SUCCEEDED(u->QueryInterface(IID_PPV_ARGS(&t))); + REQUIRE_SUCCEEDED(u->QueryInterface(&t)); } } @@ -540,7 +541,7 @@ void TestTyped_Create_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); @@ -563,7 +564,7 @@ void TestTyped_Create_FailFast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); REQUIRE_NOCRASH(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_NOCRASH(sa.lbound(&val)); @@ -585,7 +586,7 @@ void TestTyped_Create() REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE-1); @@ -672,7 +673,7 @@ void Test_Create() template void TestTyped_DirectElement_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -704,7 +705,7 @@ void TestTyped_DirectElement_NoThrow() template void TestTyped_DirectElement_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -737,7 +738,7 @@ void TestTyped_DirectElement_Failfast() template void TestTyped_DirectElement() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -876,7 +877,7 @@ void Test_DirectElement() template void TestTyped_AccessData_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -890,8 +891,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -906,8 +907,8 @@ void TestTyped_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -922,8 +923,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa.get())); for (ULONG i = 0 ; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -937,8 +938,8 @@ void TestTyped_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -950,7 +951,7 @@ void TestTyped_AccessData_NoThrow() template void TestTyped_AccessData_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -964,8 +965,8 @@ void TestTyped_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.create(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -980,8 +981,8 @@ void TestTyped_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.create(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -996,8 +997,8 @@ void TestTyped_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1011,8 +1012,8 @@ void TestTyped_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1025,7 +1026,7 @@ void TestTyped_AccessData_Failfast() template void TestTyped_AccessData() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -1127,7 +1128,7 @@ void Test_AccessData_NoThrow() { ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa.get())); + REQUIRE_SUCCEEDED(data.create(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1143,7 +1144,7 @@ void Test_AccessData_NoThrow() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa2.get())); + REQUIRE_SUCCEEDED(data.create(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1159,7 +1160,7 @@ void Test_AccessData_NoThrow() REQUIRE(sa); { wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa.get())); + REQUIRE_SUCCEEDED(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1174,7 +1175,7 @@ void Test_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.access(sa2.get())); + REQUIRE_SUCCEEDED(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1201,7 +1202,7 @@ void Test_AccessData_Failfast() { ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa.get())); + REQUIRE_NOCRASH(data.create(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1217,7 +1218,7 @@ void Test_AccessData_Failfast() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa2.get())); + REQUIRE_NOCRASH(data.create(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1233,7 +1234,7 @@ void Test_AccessData_Failfast() REQUIRE(sa); { wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa.get())); + REQUIRE_NOCRASH(data.create(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1248,7 +1249,7 @@ void Test_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.access(sa2.get())); + REQUIRE_NOCRASH(data.create(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1417,3 +1418,13 @@ TEST_CASE("Safearray::AccessData", "[safearray]") REQUIRE(TestComObject::GetObjectCounter() == 0); } + +TEST_CASE("Safearray Helper Functions", "[safearray]") +{ + std::vector test; + +#ifdef WIL_ENABLE_EXCEPTIONS +#endif + + REQUIRE(TestComObject::GetObjectCounter() == 0); +} From 0d7e4f4fe7b9159c2c61e383056997cbaa326fd3 Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sat, 22 Aug 2020 17:59:51 -0700 Subject: [PATCH 21/34] Some feedback from the PR: 1) Put safearray code back into it's own header 2) Changed var_traits to use better naming convention and use static constexpr over enum 3) Inline documentation and sample usage 4) Some attempts to make clang happy --- include/wil/Safearrays.h | 419 +++++++++++++++++++++------------------ tests/SafearrayTests.cpp | 128 ++++++------ 2 files changed, 288 insertions(+), 259 deletions(-) diff --git a/include/wil/Safearrays.h b/include/wil/Safearrays.h index 89ec56ab..ac2a9dae 100644 --- a/include/wil/Safearrays.h +++ b/include/wil/Safearrays.h @@ -19,13 +19,6 @@ #include "wistd_type_traits.h" #include "resource.h" // unique_hkey -namespace std -{ - // Forward Defines - template class allocator; - template> class vector; -} - namespace wil { #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) @@ -34,30 +27,30 @@ namespace wil namespace details { template - struct VarTraits{}; - - template<> struct VarTraits { enum { type = VT_I1 }; }; - //template<> struct VarTraits { enum { type = VT_I2 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I4 }; }; - template<> struct VarTraits { enum { type = VT_I8 }; }; - template<> struct VarTraits { enum { type = VT_UI1 }; }; - template<> struct VarTraits { enum { type = VT_UI2 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI4 }; }; - template<> struct VarTraits { enum { type = VT_UI8 }; }; - template<> struct VarTraits { enum { type = VT_R4 }; }; - //template<> struct VarTraits { enum { type = VT_R8 }; }; - template<> struct VarTraits { enum { type = VT_BOOL }; }; - template<> struct VarTraits { enum { type = VT_DATE }; }; - template<> struct VarTraits { enum { type = VT_CY }; }; - template<> struct VarTraits { enum { type = VT_DECIMAL }; }; - template<> struct VarTraits { enum { type = VT_BSTR }; }; - template<> struct VarTraits { enum { type = VT_UNKNOWN }; }; - template<> struct VarTraits { enum { type = VT_DISPATCH }; }; - template<> struct VarTraits { enum { type = VT_VARIANT }; }; - - inline void __stdcall SafeArrayDestory(SAFEARRAY* psa) WI_NOEXCEPT + struct var_traits{}; + + template<> struct var_traits { static constexpr auto vartype = VT_I1; }; + //template<> struct var_traits { static constexpr auto vartype = VT_I2; }; + template<> struct var_traits { static constexpr auto vartype = VT_I4; }; + template<> struct var_traits { static constexpr auto vartype = VT_I4; }; + template<> struct var_traits { static constexpr auto vartype = VT_I8; }; + template<> struct var_traits { static constexpr auto vartype = VT_UI1; }; + template<> struct var_traits { static constexpr auto vartype = VT_UI2; }; + template<> struct var_traits { static constexpr auto vartype = VT_UI4; }; + template<> struct var_traits { static constexpr auto vartype = VT_UI4; }; + template<> struct var_traits { static constexpr auto vartype = VT_UI8; }; + template<> struct var_traits { static constexpr auto vartype = VT_R4; }; + //template<> struct var_traits { static constexpr auto vartype = VT_R8; }; + template<> struct var_traits { static constexpr auto vartype = VT_BOOL; }; + template<> struct var_traits { static constexpr auto vartype = VT_DATE; }; + template<> struct var_traits { static constexpr auto vartype = VT_CY; }; + template<> struct var_traits { static constexpr auto vartype = VT_DECIMAL; }; + template<> struct var_traits { static constexpr auto vartype = VT_BSTR; }; + template<> struct var_traits { static constexpr auto vartype = VT_UNKNOWN; }; + template<> struct var_traits { static constexpr auto vartype = VT_DISPATCH; }; + template<> struct var_traits { static constexpr auto vartype = VT_VARIANT; }; + + inline void __stdcall SafeArrayDestroy(SAFEARRAY* psa) WI_NOEXCEPT { __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); @@ -91,16 +84,18 @@ namespace wil inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) { VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null - if (psa != nullptr) + if ((psa != nullptr) && FAILED(::SafeArrayGetVartype(psa, &vt))) { - if (FAILED(::SafeArrayGetVartype(psa, &vt))) - { - vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined - } + vt = VT_EMPTY; // Invalid for SAs, use to mean type couldn't be determined } return vt; } + inline ULONG __stdcall SafeArrayGetLockCount(SAFEARRAY* psa) WI_NOEXCEPT + { + return (psa != nullptr) ? psa->cLocks : 0U; + } + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT { WI_ASSERT(sab != nullptr); @@ -124,7 +119,7 @@ namespace wil inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT { __FAIL_FAST_ASSERT__(pCount != nullptr); - if ( psa != nullptr ) + if (psa != nullptr) { ULONGLONG result = 1; for (UINT i = 0; i < psa->cDims; ++i) @@ -145,58 +140,82 @@ namespace wil return S_OK; } - typedef resource_policy safearray_resource_policy; - typedef resource_policy safearray_accessdata_resource_policy; - - //template - //class safearray_index : private std::array - //{ - //public: - // safearray_index() : std::array{ Indexes... }; - // LPLONG get() { return data(); } - // ULONG count() { return static_cast(size()); } - //}; - - //template - //class safearray_index - //{ - //public: - // safearray_index() : _value{ Index } {} - // LPLONG get() { return &_value; } - // ULONG count() { return 1; } - //private: - // LONG _value; - //}; - - //template - //class safearray_index - //{ - //public: - // safearray_index() : _indexes{Indexes} - // LPLONG get() { return _indexes; } - // ULONG count() { return 0; } - //private: - // LPLONG _indexes: - //}; - - //template - //DebugContextT& Format(const wchar_t* const lpszFormat, Args... args) noexcept - // template - // constexpr auto chars = std::array{ cs... }; - + typedef resource_policy safearray_resource_policy; } /// @endcond typedef unique_any safearray_unlock_scope_exit; - // Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope - // Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method + //! Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope + //! Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method WI_NODISCARD inline safearray_unlock_scope_exit SafeArrayUnlock_scope_exit(SAFEARRAY* psa) WI_NOEXCEPT { details::SafeArrayLock(psa); return safearray_unlock_scope_exit(psa); } + //! Class that facilitates the direct access to the contents of a SAFEARRAY and "unaccessing" it when done + //! It enables treating the safearray like an regular array, or doing a ranged-for on the contents. + //! Accessing a safearray increases it's lock count, so attempts to destroy the safearray will fail until it is unaccessed. + //! NOTE: This class does not manage the lifetime of the SAFEARRAY itself. See @ref safearray_t. + //! ~~~~ + //! HRESULT copy_to_bstr_vector(SAFEARRAY* psa, std::vector& result) + //! { + //! auto data = wil::safearraydata_nothrow{}; + //! RETURN_IF_FAILED(data.access(psa)); + //! result.reserve(result.size() + data.size()); + //! for(BSTR& bstr : data) + //! { + //! result.emplace_back(bstr); + //! } + //! return S_OK; + //! } + //! ~~~~ + //! unique_bstr_safearray move_to_safearray(std::vector& source) + //! { + //! auto sa = wil::unique_bstr_safearray{source.size()}; + //! auto current = std::begin(source); + //! for(BSTR& bstr : sa.access_data()) + //! { + //! // Transfer ownership of the BSTR into the safearray + //! bstr = (*current++).release(); + //! } + //! return sa; + //! } + //! ~~~~ + //! void handle_safearray_of_bools(SAFEARRAY* psa, std::function fnHandler) + //! { + //! auto data = wil::safearraydata{psa}; + //! for (auto i = 0U; i < data.size(); ++i) + //! { + //! fnHandler(data[i] != VARIANT_FALSE, i); + //! } + //! } + //! ~~~~ + //! template + //! std::vector extract_all_values(SAFEARRAY* psa) + //! { + //! auto data = wil::safearraydata{psa}; + //! // Construct the vector from the iterators + //! return {data.begin(), data.end()}; + //! } + //! ~~~~ + //! template + //! std::vector> extract_all_interfaces(SAFEARRAY* psa) + //! { + //! auto result = std::vector>{}; + //! auto data = wil::safearraydata{psa}; + //! result.reserve(data.size()); + //! for(auto& p : data) // type of P is LPUNKNOWN + //! { + //! // Use "tag_com_query" if you want failure instead of nullptr if INTERFACE not supported + //! result.emplace_back(p, details::tag_try_com_query); + //! } + //! return result; + //! } + //! ~~~~ + //! @tparam T The underlying datatype expected in the SAFEARRAY. It should be a scalar type, BSTR, VARIANT_BOOL, LPUNKNOWN/LPDISPATCH, etc. + //! @tparam err_policy Represents the error policy for the class (error codes, exceptions, or fail fast; see @ref page_errors) template class safearraydata_t { @@ -219,7 +238,7 @@ namespace wil safearraydata_t(pointer psa) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(psa); + access(psa); } safearraydata_t() = default; safearraydata_t(safearraydata_t&&) = default; @@ -228,13 +247,11 @@ namespace wil safearraydata_t(const safearraydata_t&) = delete; safearraydata_t& operator=(const safearraydata_t&) = delete; - result create(pointer psa) + result access(pointer psa) { HRESULT hr = [&]() { - constexpr auto vt = static_cast(details::VarTraits::type); WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); details::SafeArrayAccessData(psa, m_pBegin); m_unaccess.reset(psa); RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); @@ -274,11 +291,54 @@ namespace wil using safearraydata = safearraydata_t; #endif - - // Add safearray functionality to the given storage type using the given error policy - // element_t is the either: - // A) The C++ type for the elements in the safearray and all methods are typesafe - // B) void to make the safearray generic (and not as typesafe) + //! RAII class for SAFEARRAYs that lives between unique_any_t and unique_storage to provide extra SAFEARRAY functionality. + //! SAFEARRAYs provide a convenient way of passing an array of values across APIs and can be useful because they will clean + //! up their resources (interface ref counts, BSTRs, etc.) when properly destroyed. If you have a SAFEARRAY of interface + //! pointers or BSTRs, there is no need to call Release or SysFreeString on each element (that is done automatically + //! when the array is destroyed in SafeArrayDestroy), so the only resource that needs to be managed is the SAFEARRAY + //! itself. + //! ~~~~ + //! // Return a SAFEARRAY from an API + //! HRESULT GetWonderfulData(SAFEARRAY** ppsa) + //! { + //! wil::unique_bstr_safearray_nothrow sa{}; + //! RETURN_IF_FAILED(sa.create(32)); + //! { + //! wil::safearraydata_nothrow data{}; + //! RETURN_IF_FAILED(data.access(sa.get()); + //! for(auto& bstr : data) + //! { + //! // SAFEARRAY will own this string and clean it up + //! bstr = ::SysAllocString(L"Wonderful!"); + //! // Even if we bail early, nothing will leak + //! RETURN_HR_IF_NULL(E_OUTOFMEMORY, bstr); + //! } + //! } + //! *ppsa = sa.release(); + //! return S_OK; + //! } + //! ~~~~ + //! // Obtain a SAFEARRAY from an API + //! HRESULT ProcessWonderful() + //! { + //! wil::unique_safearray sa{}; + //! + //! // Invoke the API + //! RETURN_IF_FAILED(::GetWonderfulData(sa.put())); + //! // Verify the output is expected + //! RETURN_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); + //! for(auto& bstr : sa.access_data()) + //! { + //! // Use the BSTR, no clean up necessary + //! DoSomethingWithBSTR(bstr); + //! } + //! return S_OK; + //! } + //! ~~~~ + //! @tparam storage_t Storage class that manages the SAFEARRAY pointer + //! @tparam err_policy Represents the error policy for the class (error codes, exceptions, or fail fast; see @ref page_errors) + //! @tparam element_t The underlying datatype that is contained in the safearray OR + //! it can be void to make the safearray generic (and not as typesafe) template class safearray_t : public storage_t { @@ -336,7 +396,7 @@ namespace wil template::value, int>::type = 0> result create(UINT cDims, SAFEARRAYBOUND* sab) { - constexpr auto vt = static_cast(details::VarTraits::type); + constexpr auto vt = static_cast(details::var_traits::vartype); return err_policy::HResult(_create(vt, cDims, sab)); } @@ -352,7 +412,7 @@ namespace wil template::value, int>::type = 0> result create(UINT cElements, LONG lowerBound = 0) { - constexpr auto vt = static_cast(details::VarTraits::type); + constexpr auto vt = static_cast(details::var_traits::vartype); auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; return err_policy::HResult(_create(vt, 1, &bounds)); } @@ -410,40 +470,62 @@ namespace wil return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); } - // Retrieve the stored type (when not working with a fixed type) + //! Retrieves the stored type (when not working with a fixed type) template::value, int>::type = 0> VARTYPE vartype() { return details::SafeArrayGetVartype(storage_t::get()); } - // Lock Helper - WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT + //! Returns the current number of locks on the SAFEARRAY + ULONG lock_count() const { - return wil::SafeArrayUnlock_scope_exit(storage_t::get()); + return details::SafeArrayGetLockCount(storage_t::get()); } - // Lowest-Level functions for those who know what they're doing - result put_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); - } - result put_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); - } - result get_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); - } - result get_element(LONG* pIndices, void* pv) + //! Returns an object that keeps a lock count on the safearray until it goes out of scope + //! Use to keep the SAFEARRAY from being destroyed but without access the contents + //! Not needed when using safearraydata_t because accessing the data also holds a lock + //! ~~~~ + //! void Process(wil::unique_safearray& sa) + //! { + //! auto lock = sa.scope_lock(); + //! LongRunningFunction(sa); + //! } + WI_NODISCARD safearray_unlock_scope_exit scope_lock() const WI_NOEXCEPT { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + return wil::SafeArrayUnlock_scope_exit(storage_t::get()); } - // Single Dimension Specialization + + //! Element Access + //! Instead of using the safearraydata_t to access the entire collection of elements + //! at once, it is also possible to access them one at a time. However, using this + //! functionality is less efficient because it deals with copies of the elements + //! that the caller is then responsible for cleaning up, so use RAII data types + //! for items that require clean up. + //! ~~~~ + //! HRESULT ProcessWonderful_UsingElements() + //! { + //! wil::unique_safearray_nothrow sa{}; + //! wil::unique_bstr bstr{}; + //! ULONG count{}; + //! + //! // Invoke the API + //! RETURN_IF_FAILED(::GetWonderfulData(sa.put())); + //! // Verify the output is expected + //! RETURN_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); + //! RETURN_HR_IF(E_UNEXPECTED, sa.dims() != 1); + //! RETURN_IF_FAILED(sa.count(&count)); + //! for(auto i = 0U; i < count; ++i) + //! { + //! // Copy from the safearray to the unique_bstr + //! sa.get_element(i, bstr.put()); + //! DoSomethingWithBSTR(bstr); + //! } + //! return S_OK; + //! } + //! ~~~~ template::value, int>::type = 0> result put_element(LONG nIndex, const T& val) { @@ -466,6 +548,26 @@ namespace wil return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); } + // Lowest-Level functions for those who know what they're doing + result put_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + } + result put_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); + } + result get_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + } + result get_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + // Multi Dimension Support template::value, int>::type = 0> result put_element(LONG* pIndices, const T& val) @@ -523,14 +625,28 @@ namespace wil result.create_copy(storage_t::get()); return result; } + //! Returns a safearraydata_t that provides direct access to this SAFEARRAYs contents + //! ~~~~ + //! unique_bstr_safearray copy_to_safearray(std::vector& source) + //! { + //! auto sa = wil::unique_bstr_safearray{source.size()}; + //! auto current = std::begin(source); + //! for(BSTR& bstr : sa.access_data()) + //! { + //! // Create a copy for the safearray to own + //! bstr = ::SysAllocString((*current++).get()); + //! } + //! return sa; + //! } + //! ~~~~ template::value, int>::type = 0> WI_NODISCARD safearraydata_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); - WI_ASSERT(static_cast(details::VarTraits::type) == details::SafeArrayGetVartype(storage_t::get())); + WI_ASSERT(static_cast(details::var_traits::vartype) == details::SafeArrayGetVartype(storage_t::get())); auto data = safearraydata_t{}; - data.create(storage_t::get()); + data.access(storage_t::get()); return wistd::move(data); } @@ -608,83 +724,6 @@ namespace wil using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif -template -typename err_policy::result copy_safearray_to_container(SAFEARRAY* psa, container_type& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -template -typename err_policy::result copy_container_to_safearray(const container_type& cont, SAFEARRAY** ppsa) -{ - WI_ASSERT(ppsa != nullptr); - return err_policy::HResult([&]() - { - auto sa = wil::unique_safearray_nothrow{}; - RETURN_IF_FAILED(sa.create(cont.size())); - - { - auto data = wil::safearraydata_nothrow{}; - RETURN_IF_FAILED(data.create(sa.get())); - - auto src = cont.begin(); - auto dest = data.begin(); - while (src != cont.end()) - { - *(dest++) = *(src++); - } - } - - *ppsa = sa.release(); - return S_OK; - }()); -} - -template -typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector>& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.reserve(cont.size() + data.size()); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -template -typename err_policy::result copy_safearray_to_vector(SAFEARRAY* psa, std::vector& cont) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata_nothrow{}; - return err_policy::HResult([&]() - { - RETURN_IF_FAILED(data.create(psa)); - cont.reserve(cont.size() + data.size()); - cont.insert(cont.end(), data.begin(), data.end()); - return S_OK; - }()); -} - -#ifdef WIL_ENABLE_EXCEPTIONS -template -std::vector> copy_safearray_to_vector(SAFEARRAY* psa) -{ - WI_ASSERT(psa != nullptr); - auto data = wil::safearraydata{ psa }; - return { data.begin(), data.end() }; -} - -#endif // WIL_ENABLE_EXCEPTIONS #endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) } // namespace wil diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index ecc82ddc..f6220702 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,8 +1,8 @@ -#include -#include #include +#include +#include -//#include "common.h" +#include "common.h" #include #include @@ -90,7 +90,7 @@ static const GUID IID_IAmForTesting = struct __declspec(uuid("5D80EC64-6694-4F49-B0B9-CCAA65467D12")) IAmForTesting : public IUnknown { - virtual HRESULT GetID(LONG* pOut) = 0; + STDMETHODIMP GetID(LONG* pOut) = 0; }; class TestComObject : virtual public IAmForTesting, @@ -111,12 +111,12 @@ class TestComObject : virtual public IAmForTesting, // IUnknown public: - ULONG AddRef() + STDMETHODIMP_(ULONG) AddRef() { ::InterlockedIncrement(&m_nRefCount); return m_nRefCount; } - ULONG Release() + STDMETHODIMP_(ULONG) Release() { ULONG ulRefCount = ::InterlockedDecrement(&m_nRefCount); if (0 == m_nRefCount) @@ -125,7 +125,7 @@ class TestComObject : virtual public IAmForTesting, } return ulRefCount; } - HRESULT QueryInterface(REFIID riid, LPVOID* ppvObj) + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) { if (!ppvObj) return E_INVALIDARG; @@ -247,7 +247,7 @@ auto GetSampleData() -> std::array constexpr auto BIT_COUNT = sizeof(T) * 8; auto result = std::array{}; - for (auto i = 0; i < BIT_COUNT; ++i) + for (auto i = 0U; i < BIT_COUNT; ++i) { result[i] = (static_cast(1) << i); } @@ -258,7 +258,7 @@ template GetSampleData() { auto result = std::array{}; - for (auto i = 0; i < DEFAULT_SAMPLE_SIZE/2; ++i) + for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE/2; ++i) { result[i] = (i != 0) ? static_cast(1 / i) : 0; result[i + (DEFAULT_SAMPLE_SIZE/2)] = 2 * static_cast(i); @@ -270,7 +270,7 @@ template GetSampleData() { auto result = std::array{}; - for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { switch (i % 4) { @@ -295,7 +295,7 @@ template GetSampleData() { auto result = std::array{}; - for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; switch (i % 6) @@ -333,7 +333,7 @@ template, DEFAULT_SAMPLE_SIZE> GetSampleData() { auto result = std::array, DEFAULT_SAMPLE_SIZE>{}; - for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; REQUIRE_SUCCEEDED(TestComObject::Create(IID_IAmForTesting, (LPVOID*)var.put())); @@ -345,7 +345,7 @@ template, DEFAULT_SAMPLE_SIZE> GetSampleData() { auto result = std::array, DEFAULT_SAMPLE_SIZE>{}; - for (auto i = 0; i < DEFAULT_SAMPLE_SIZE; ++i) + for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; REQUIRE_SUCCEEDED(TestComObject::Create(IID_IDispatch, (LPVOID*)var.put())); @@ -514,7 +514,7 @@ void PerfromAssignment(I*& t, const wil::com_ptr_nothrow& u) } if (u) { - REQUIRE_SUCCEEDED(u->QueryInterface(&t)); + REQUIRE_SUCCEEDED(u->QueryInterface(IID_PPV_ARGS(&t))); } } @@ -541,7 +541,7 @@ void TestTyped_Create_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); @@ -564,7 +564,7 @@ void TestTyped_Create_FailFast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOCRASH(sa.count(&count)); REQUIRE(count == SIZE); REQUIRE_NOCRASH(sa.lbound(&val)); @@ -586,7 +586,7 @@ void TestTyped_Create() REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); REQUIRE(sa); REQUIRE(sa.dim() == 1); - REQUIRE(sa.elemsize() == sizeof(safearray_t::elemtype)); + REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE-1); @@ -671,9 +671,9 @@ void Test_Create() #define _CREATE(type) Test_Create(); template -void TestTyped_DirectElement_NoThrow() +void TestTyped_Element_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -703,9 +703,9 @@ void TestTyped_DirectElement_NoThrow() } template -void TestTyped_DirectElement_Failfast() +void TestTyped_Element_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -736,9 +736,9 @@ void TestTyped_DirectElement_Failfast() #ifdef WIL_ENABLE_EXCEPTIONS template -void TestTyped_DirectElement() +void TestTyped_Element() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -768,12 +768,12 @@ void TestTyped_DirectElement() } #endif -#define _TYPED_DIRECT_NOTHROW(type) TestTyped_DirectElement_NoThrow(); -#define _TYPED_DIRECT_FAILFAST(type) TestTyped_DirectElement_Failfast(); -#define _TYPED_DIRECT(type) TestTyped_DirectElement(); +#define _TYPED_DIRECT_NOTHROW(type) TestTyped_Element_NoThrow(); +#define _TYPED_DIRECT_FAILFAST(type) TestTyped_Element_Failfast(); +#define _TYPED_DIRECT(type) TestTyped_Element(); template -void Test_DirectElement_NoThrow() +void Test_Element_NoThrow() { auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; @@ -805,7 +805,7 @@ void Test_DirectElement_NoThrow() } template -void Test_DirectElement_Failfast() +void Test_Element_Failfast() { auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; @@ -838,7 +838,7 @@ void Test_DirectElement_Failfast() #ifdef WIL_ENABLE_EXCEPTIONS template -void Test_DirectElement() +void Test_Element() { auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; @@ -870,14 +870,14 @@ void Test_DirectElement() } #endif -#define _DIRECT_NOTHROW(type) Test_DirectElement_NoThrow(); -#define _DIRECT_FAILFAST(type) Test_DirectElement_Failfast(); -#define _DIRECT(type) Test_DirectElement(); +#define _DIRECT_NOTHROW(type) Test_Element_NoThrow(); +#define _DIRECT_FAILFAST(type) Test_Element_Failfast(); +#define _DIRECT(type) Test_Element(); template void TestTyped_AccessData_NoThrow() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -891,8 +891,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -907,8 +907,8 @@ void TestTyped_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -923,8 +923,8 @@ void TestTyped_AccessData_NoThrow() REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa.get())); for (ULONG i = 0 ; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -938,8 +938,8 @@ void TestTyped_AccessData_NoThrow() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + wil::safearraydata_nothrow data; + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -951,7 +951,7 @@ void TestTyped_AccessData_NoThrow() template void TestTyped_AccessData_Failfast() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -965,8 +965,8 @@ void TestTyped_AccessData_Failfast() REQUIRE(sa); { ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -981,8 +981,8 @@ void TestTyped_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original ULONG counter = {}; - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -997,8 +997,8 @@ void TestTyped_AccessData_Failfast() REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); { - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1012,8 +1012,8 @@ void TestTyped_AccessData_Failfast() { // Verify the values in the copy are the same as // the values that were placed into the original - wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + wil::safearraydata_failfast data; + REQUIRE_NOCRASH(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1026,7 +1026,7 @@ void TestTyped_AccessData_Failfast() template void TestTyped_AccessData() { - auto sample_data = GetSampleData(); + auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; using array_type = decltype(sample_data); @@ -1128,7 +1128,7 @@ void Test_AccessData_NoThrow() { ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + REQUIRE_SUCCEEDED(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1144,7 +1144,7 @@ void Test_AccessData_NoThrow() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1160,7 +1160,7 @@ void Test_AccessData_NoThrow() REQUIRE(sa); { wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa.get())); + REQUIRE_SUCCEEDED(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1175,7 +1175,7 @@ void Test_AccessData_NoThrow() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_nothrow data; - REQUIRE_SUCCEEDED(data.create(sa2.get())); + REQUIRE_SUCCEEDED(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1202,7 +1202,7 @@ void Test_AccessData_Failfast() { ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE_NOCRASH(data.access(sa.get())); for (auto& elem : data) { PerfromAssignment(elem, sample_data[counter++]); @@ -1218,7 +1218,7 @@ void Test_AccessData_Failfast() // the values that were placed into the original ULONG counter = {}; wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE_NOCRASH(data.access(sa2.get())); for (const auto& elem : data) { REQUIRE(PerformCompare(elem, sample_data[counter++])); @@ -1234,7 +1234,7 @@ void Test_AccessData_Failfast() REQUIRE(sa); { wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa.get())); + REQUIRE_NOCRASH(data.access(sa.get())); for (ULONG i = 0; i < data.size(); ++i) { PerfromAssignment(data[i], sample_data[i]); @@ -1249,7 +1249,7 @@ void Test_AccessData_Failfast() // Verify the values in the copy are the same as // the values that were placed into the original wil::safearraydata_failfast data; - REQUIRE_NOCRASH(data.create(sa2.get())); + REQUIRE_NOCRASH(data.access(sa2.get())); for (ULONG i = 0; i < data.size(); ++i) { REQUIRE(PerformCompare(data[i], sample_data[i])); @@ -1418,13 +1418,3 @@ TEST_CASE("Safearray::AccessData", "[safearray]") REQUIRE(TestComObject::GetObjectCounter() == 0); } - -TEST_CASE("Safearray Helper Functions", "[safearray]") -{ - std::vector test; - -#ifdef WIL_ENABLE_EXCEPTIONS -#endif - - REQUIRE(TestComObject::GetObjectCounter() == 0); -} From 2cd1322558494f5abcf8b3e417d820ad2f8a183e Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sun, 23 Aug 2020 12:55:53 -0700 Subject: [PATCH 22/34] Renamed new file to be all lower case --- include/wil/{Safearrays.h => safearrays.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/wil/{Safearrays.h => safearrays.h} (100%) diff --git a/include/wil/Safearrays.h b/include/wil/safearrays.h similarity index 100% rename from include/wil/Safearrays.h rename to include/wil/safearrays.h From 0be49fb7cae9ce19d388d21d5c1a59a479f8ebed Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sun, 23 Aug 2020 16:37:18 -0700 Subject: [PATCH 23/34] Added lots of documentation and examples Some code clean up / reorg for consistency Hopefully all the clang errors are fixed Defined shared/weak versions of SAFEARRAY types --- include/wil/safearrays.h | 309 ++++++++++++++++++++++++++++++--------- tests/SafearrayTests.cpp | 87 +++++------ 2 files changed, 283 insertions(+), 113 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index ac2a9dae..64c45db6 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -21,8 +21,8 @@ namespace wil { -#if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) -#define __WIL_OLEAUTO_ +#if defined( _OLEAUTO_H_ ) && !defined(__WIL_SAFEARRAY_) +#define __WIL_SAFEARRAY_ /// @cond namespace details { @@ -81,7 +81,7 @@ namespace wil FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } - inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) + inline VARTYPE __stdcall SafeArrayGetVartype(SAFEARRAY* psa) WI_NOEXCEPT { VARTYPE vt = VT_NULL; // Invalid for SAs, so use to mean SA was null if ((psa != nullptr) && FAILED(::SafeArrayGetVartype(psa, &vt))) @@ -157,6 +157,8 @@ namespace wil //! Class that facilitates the direct access to the contents of a SAFEARRAY and "unaccessing" it when done //! It enables treating the safearray like an regular array, or doing a ranged-for on the contents. //! Accessing a safearray increases it's lock count, so attempts to destroy the safearray will fail until it is unaccessed. + //! This class works even if the SAFEARRAY is multi-dimensional by treating it as a large single-dimension array, + //! but doesn't support access via a multi-dimensional index. //! NOTE: This class does not manage the lifetime of the SAFEARRAY itself. See @ref safearray_t. //! ~~~~ //! HRESULT copy_to_bstr_vector(SAFEARRAY* psa, std::vector& result) @@ -166,7 +168,7 @@ namespace wil //! result.reserve(result.size() + data.size()); //! for(BSTR& bstr : data) //! { - //! result.emplace_back(bstr); + //! result.emplace_back(wil::make_bstr_nothrow(bstr)); //! } //! return S_OK; //! } @@ -249,16 +251,14 @@ namespace wil result access(pointer psa) { - HRESULT hr = [&]() - { - WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - details::SafeArrayAccessData(psa, m_pBegin); - m_unaccess.reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); - return S_OK; - }(); - - return err_policy::HResult(hr); + return err_policy::HResult([&]() + { + WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); + details::SafeArrayAccessData(psa, m_pBegin); + m_unaccess.reset(psa); + RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); + return S_OK; + }()); } // Ranged-for style @@ -417,74 +417,91 @@ namespace wil return err_policy::HResult(_create(vt, 1, &bounds)); } - // Create a Copy - result create_copy(pointer psaSrc) + //! Create a Copy + //! Replaces the current SAFEARRAY (if any) with a new one that is created by duplicating the elements in + //! the SAFEARRAY* provided as a parameter in psaSource + result create_copy(pointer psaSource) { - HRESULT hr = [&]() - { - auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(::SafeArrayCopy(psaSrc, &psa)); - storage_t::reset(psa); - return S_OK; - }(); - return err_policy::HResult(hr); + WI_ASSERT(psaSource != nullptr); + return err_policy::HResult([&]() + { + auto psa = storage_t::policy::invalid_value(); + RETURN_IF_FAILED(::SafeArrayCopy(psaSource, &psa)); + storage_t::reset(psa); + return S_OK; + }()); } - // Dimensions, Sizes, Boundaries + //! @name Property Helpers + //! Functions to determine the size and shape of the SAFEARRAY, which + //! can have an arbitrary number of dimensions and each dimension can + //! it's own size and boundaries. + //! @{ + + //! Indicates the number of dimensions in the SAFEARRAY. The 1st Dimension + //! is always 1, and there never is a Dimension 0. ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } - ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } + //! Returns the lowest possible index for the given dimension. result lbound(UINT nDim, LONG* pLbound) const { WI_ASSERT(pLbound != nullptr); WI_ASSERT((nDim > 0) && (nDim <= dim())); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); } - result lbound(LONG* pLbound) const - { - WI_ASSERT(dim() == 1); - WI_ASSERT(pLbound != nullptr); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); - } + //! Returns the highest possible index for the given dimension. result ubound(UINT nDim, LONG* pUbound) const { WI_ASSERT(pUbound != nullptr); WI_ASSERT((nDim > 0) && (nDim <= dim())); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); } + //! 1D Specialization of lbound + result lbound(LONG* pLbound) const + { + WI_ASSERT(dim() == 1); + WI_ASSERT(pLbound != nullptr); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); + } + //! 1D Specialization of ubound result ubound(LONG* pUbound) const { WI_ASSERT(dim() == 1); WI_ASSERT(pUbound != nullptr); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); } + //! Indicates the total number of elements across all the dimensions result count(ULONG* pCount) const { WI_ASSERT(pCount != nullptr); return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); } - // Same as, but much faster than, the typical UBOUND - LBOUND + 1 + //! Returns the number of elements in the specified dimension + //! Same as, but much faster than, the typical ubound() - lbound() + 1 result dim_elements(UINT nDim, ULONG* pCount) const { WI_ASSERT(pCount != nullptr); WI_ASSERT((nDim > 0) && (nDim <= dim())); return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); } + //! Return the size, in bytes, of each element in the array + ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } //! Retrieves the stored type (when not working with a fixed type) template::value, int>::type = 0> - VARTYPE vartype() + VARTYPE vartype() WI_NOEXCEPT { return details::SafeArrayGetVartype(storage_t::get()); } //! Returns the current number of locks on the SAFEARRAY - ULONG lock_count() const + ULONG lock_count() const WI_NOEXCEPT { return details::SafeArrayGetLockCount(storage_t::get()); } + //! @} //! Returns an object that keeps a lock count on the safearray until it goes out of scope - //! Use to keep the SAFEARRAY from being destroyed but without access the contents + //! Use to keep the SAFEARRAY from being destroyed but without accessing the contents //! Not needed when using safearraydata_t because accessing the data also holds a lock //! ~~~~ //! void Process(wil::unique_safearray& sa) @@ -497,13 +514,12 @@ namespace wil return wil::SafeArrayUnlock_scope_exit(storage_t::get()); } - - //! Element Access + //! @name Element Access //! Instead of using the safearraydata_t to access the entire collection of elements //! at once, it is also possible to access them one at a time. However, using this //! functionality is less efficient because it deals with copies of the elements //! that the caller is then responsible for cleaning up, so use RAII data types - //! for items that require clean up. + //! for items that require clean up, like BSTR, VARIANT, or LPUNKNOWN //! ~~~~ //! HRESULT ProcessWonderful_UsingElements() //! { @@ -520,12 +536,16 @@ namespace wil //! for(auto i = 0U; i < count; ++i) //! { //! // Copy from the safearray to the unique_bstr - //! sa.get_element(i, bstr.put()); + //! sa.get_element(i, bstr.put()); // Will release current BSTR //! DoSomethingWithBSTR(bstr); //! } //! return S_OK; //! } //! ~~~~ + //! @{ + + //! Copies the specified element and puts it into the specified index in the SAFEARRAY (1D version) + //! The SAFEARRAY will own any resources and will clean up when it is destroyed template::value, int>::type = 0> result put_element(LONG nIndex, const T& val) { @@ -540,35 +560,17 @@ namespace wil WI_ASSERT(dim() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); } + //! Retrieves a copy of the element at the specified index. The caller owns this copy and must free + //! any resources with LPUNKNOWN->Release, SysFreeString, VariantClear, , etc. template::value, int>::type = 0> - result get_element(LONG nIndex, T& t) - { - WI_ASSERT(sizeof(t) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &t)); - } - - // Lowest-Level functions for those who know what they're doing - result put_element(LONG nIndex, void* pv) - { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); - } - result put_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); - } - result get_element(LONG nIndex, void* pv) + result get_element(LONG nIndex, T& val) { + WI_ASSERT(sizeof(val) == elemsize()); WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); - } - result get_element(LONG* pIndices, void* pv) - { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &val)); } - // Multi Dimension Support + //! Multiple Dimension version of put_element template::value, int>::type = 0> result put_element(LONG* pIndices, const T& val) { @@ -581,14 +583,39 @@ namespace wil WI_ASSERT(sizeof(val) == elemsize()); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); } + //! Multiple Dimension version of get_element template::value, int>::type = 0> - result get_element(LONG* pIndices, T& t) + result get_element(LONG* pIndices, T& val) + { + WI_ASSERT(sizeof(val) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &val)); + } + + //! Lowest-Level functions for those who know what they're doing. + result put_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + } + result get_element(LONG nIndex, void* pv) + { + WI_ASSERT(dim() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + } + result put_element(LONG* pIndices, void* pv) { - WI_ASSERT(sizeof(t) == elemsize()); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &t)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); } + result get_element(LONG* pIndices, void* pv) + { + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + } + //! @} + + //! Exception-based helper functions that make exception based code easier to read + //! @{ - // Exception-based helper functions + //! Returns the lower bound of the given dimension WI_NODISCARD LONG lbound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -596,6 +623,7 @@ namespace wil lbound(nDim, &nResult); return nResult; } + //! Returns the bupper bound of the given dimension WI_NODISCARD LONG ubound(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -603,6 +631,7 @@ namespace wil ubound(nDim, &nResult); return nResult; } + //! Return the total number of elements in the SAFEARRAY WI_NODISCARD ULONG count() const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -610,6 +639,7 @@ namespace wil count(&nResult); return nResult; } + //! Returns the number of elements in the given dimension WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -617,6 +647,7 @@ namespace wil dim_elements(nDim, &nResult); return nResult; } + //! Returns a copy of the SAFEARRAY, including all individual elements WI_NODISCARD unique_type create_copy() const { static_assert(wistd::is_same::value, "this method requires exceptions"); @@ -625,8 +656,10 @@ namespace wil result.create_copy(storage_t::get()); return result; } - //! Returns a safearraydata_t that provides direct access to this SAFEARRAYs contents + + //! Returns a safearraydata_t that provides direct access to this SAFEARRAYs contents. See @ref safearraydata_t. //! ~~~~ + //! // Create a new safearray by copying a vector of wil::unique_bstr //! unique_bstr_safearray copy_to_safearray(std::vector& source) //! { //! auto sa = wil::unique_bstr_safearray{source.size()}; @@ -636,19 +669,19 @@ namespace wil //! // Create a copy for the safearray to own //! bstr = ::SysAllocString((*current++).get()); //! } - //! return sa; + //! return std::move(sa); //! } //! ~~~~ template::value, int>::type = 0> WI_NODISCARD safearraydata_t access_data() const { static_assert(wistd::is_same::value, "this method requires exceptions"); - WI_ASSERT(static_cast(details::var_traits::vartype) == details::SafeArrayGetVartype(storage_t::get())); auto data = safearraydata_t{}; data.access(storage_t::get()); return wistd::move(data); } + //! @} private: HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) @@ -724,8 +757,140 @@ namespace wil using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif +#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_SAFEARRAY_) + +#if defined(__WIL_SAFEARRAY_) && !defined(__WIL_SAFEARRAY_SHARED_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_SAFEARRAY_SHARED_STL + using shared_safearray_nothrow = shared_any; + using shared_safearray_failfast = shared_any; + using shared_char_safearray_nothrow = shared_any; + using shared_char_safearray_failfast = shared_any; + //using shared_short_safearray_nothrow = shared_any; + //using shared_short_safearray_failfast = shared_any; + using shared_long_safearray_nothrow = shared_any; + using shared_long_safearray_failfast = shared_any; + using shared_int_safearray_nothrow = shared_any; + using shared_int_safearray_failfast = shared_any; + using shared_longlong_safearray_nothrow = shared_any; + using shared_longlong_safearray_failfast = shared_any; + using shared_byte_safearray_nothrow = shared_any; + using shared_byte_safearray_failfast = shared_any; + using shared_word_safearray_nothrow = shared_any; + using shared_word_safearray_failfast = shared_any; + using shared_dword_safearray_nothrow = shared_any; + using shared_dword_safearray_failfast = shared_any; + using shared_ulonglong_safearray_nothrow = shared_any; + using shared_ulonglong_safearray_failfast = shared_any; + using shared_float_safearray_nothrow = shared_any; + using shared_float_safearray_failfast = shared_any; + //using shared_double_safearray_nothrow = shared_any; + //using shared_double_safearray_failfast = shared_any; + using shared_varbool_safearray_nothrow = shared_any; + using shared_varbool_safearray_failfast = shared_any; + using shared_date_safearray_nothrow = shared_any; + using shared_date_safearray_failfast = shared_any; + using shared_currency_safearray_nothrow = shared_any; + using shared_currency_safearray_failfast = shared_any; + using shared_decimal_safearray_nothrow = shared_any; + using shared_decimal_safearray_failfast = shared_any; + using shared_bstr_safearray_nothrow = shared_any; + using shared_bstr_safearray_failfast = shared_any; + using shared_unknown_safearray_nothrow = shared_any; + using shared_unknown_safearray_failfast = shared_any; + using shared_dispatch_safearray_nothrow = shared_any; + using shared_dispatch_safearray_failfast = shared_any; + using shared_variant_safearray_nothrow = shared_any; + using shared_variant_safearray_failfast = shared_any; + +#ifdef WIL_ENABLE_EXCEPTIONS + using shared_safearray = shared_any; + using shared_char_safearray = shared_any; + //using shared_short_safearray = shared_any; + using shared_long_safearray = shared_any; + using shared_int_safearray = shared_any; + using shared_longlong_safearray = shared_any; + using shared_byte_safearray = shared_any; + using shared_word_safearray = shared_any; + using shared_dword_safearray = shared_any; + using shared_ulonglong_safearray = shared_any; + using shared_float_safearray = shared_any; + //using shared_double_safearray = shared_any; + using shared_varbool_safearray = shared_any; + using shared_date_safearray = shared_any; + using shared_currency_safearray = shared_any; + using shared_decimal_safearray = shared_any; + using shared_bstr_safearray = shared_any; + using shared_unknown_safearray = shared_any; + using shared_dispatch_safearray = shared_any; + using shared_variant_safearray = shared_any; +#endif + + using weak_safearray_nothrow = weak_any; + using weak_safearray_failfast = weak_any; + using weak_char_safearray_nothrow = weak_any; + using weak_char_safearray_failfast = weak_any; + //using weak_short_safearray_nothrow = weak_any; + //using weak_short_safearray_failfast = weak_any; + using weak_long_safearray_nothrow = weak_any; + using weak_long_safearray_failfast = weak_any; + using weak_int_safearray_nothrow = weak_any; + using weak_int_safearray_failfast = weak_any; + using weak_longlong_safearray_nothrow = weak_any; + using weak_longlong_safearray_failfast = weak_any; + using weak_byte_safearray_nothrow = weak_any; + using weak_byte_safearray_failfast = weak_any; + using weak_word_safearray_nothrow = weak_any; + using weak_word_safearray_failfast = weak_any; + using weak_dword_safearray_nothrow = weak_any; + using weak_dword_safearray_failfast = weak_any; + using weak_ulonglong_safearray_nothrow = weak_any; + using weak_ulonglong_safearray_failfast = weak_any; + using weak_float_safearray_nothrow = weak_any; + using weak_float_safearray_failfast = weak_any; + //using weak_double_safearray_nothrow = weak_any; + //using weak_double_safearray_failfast = weak_any; + using weak_varbool_safearray_nothrow = weak_any; + using weak_varbool_safearray_failfast = weak_any; + using weak_date_safearray_nothrow = weak_any; + using weak_date_safearray_failfast = weak_any; + using weak_currency_safearray_nothrow = weak_any; + using weak_currency_safearray_failfast = weak_any; + using weak_decimal_safearray_nothrow = weak_any; + using weak_decimal_safearray_failfast = weak_any; + using weak_bstr_safearray_nothrow = weak_any; + using weak_bstr_safearray_failfast = weak_any; + using weak_unknown_safearray_nothrow = weak_any; + using weak_unknown_safearray_failfast = weak_any; + using weak_dispatch_safearray_nothrow = weak_any; + using weak_dispatch_safearray_failfast = weak_any; + using weak_variant_safearray_nothrow = weak_any; + using weak_variant_safearray_failfast = weak_any; + +#ifdef WIL_ENABLE_EXCEPTIONS + using weak_safearray = weak_any; + using weak_char_safearray = weak_any; + //using weak_short_safearray = weak_any; + using weak_long_safearray = weak_any; + using weak_int_safearray = weak_any; + using weak_longlong_safearray = weak_any; + using weak_byte_safearray = weak_any; + using weak_word_safearray = weak_any; + using weak_dword_safearray = weak_any; + using weak_ulonglong_safearray = weak_any; + using weak_float_safearray = weak_any; + //using weak_double_safearray = weak_any; + using weak_varbool_safearray = weak_any; + using weak_date_safearray = weak_any; + using weak_currency_safearray = weak_any; + using weak_decimal_safearray = weak_any; + using weak_bstr_safearray = weak_any; + using weak_unknown_safearray = weak_any; + using weak_dispatch_safearray = weak_any; + using weak_variant_safearray = weak_any; +#endif + +#endif // __WIL_SAFEARRAY_SHARED_STL -#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_OLEAUTO_) } // namespace wil #endif // #ifndef __WIL_SAFEARRAYS_INCLUDED diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index f6220702..b9a0714f 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -5,6 +5,7 @@ #include "common.h" #include #include +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #pragma comment( lib, "Propsys.lib" ) @@ -84,13 +85,13 @@ ) // {5D80EC64-6694-4F49-B0B9-CCAA65467D12} -static const GUID IID_IAmForTesting = -{ 0x5d80ec64, 0x6694, 0x4f49, { 0xb0, 0xb9, 0xcc, 0xaa, 0x65, 0x46, 0x7d, 0x12 } }; +//static const GUID IID_IAmForTesting = +//{ 0x5d80ec64, 0x6694, 0x4f49, { 0xb0, 0xb9, 0xcc, 0xaa, 0x65, 0x46, 0x7d, 0x12 } }; -struct __declspec(uuid("5D80EC64-6694-4F49-B0B9-CCAA65467D12")) +interface __declspec(uuid("5D80EC64-6694-4F49-B0B9-CCAA65467D12")) IAmForTesting : public IUnknown { - STDMETHODIMP GetID(LONG* pOut) = 0; + STDMETHOD(GetID)(LONG* pOut) = 0; }; class TestComObject : virtual public IAmForTesting, @@ -99,25 +100,26 @@ class TestComObject : virtual public IAmForTesting, private: TestComObject() { - ::InterlockedIncrement(&s_ObjectCounter); - ::InterlockedIncrement(&s_IDCounter); - m_nID = s_IDCounter; + ::InterlockedIncrement(&s_ObjectCount); + m_nID = ::InterlockedIncrement(&s_IDCount); } virtual ~TestComObject() { - ::InterlockedDecrement(&s_ObjectCounter); + ::InterlockedDecrement(&s_ObjectCount); } // IUnknown public: - STDMETHODIMP_(ULONG) AddRef() + IFACEMETHODIMP_(ULONG) AddRef() { + ::InterlockedIncrement(&s_GlobalRefCount); ::InterlockedIncrement(&m_nRefCount); return m_nRefCount; } - STDMETHODIMP_(ULONG) Release() + IFACEMETHODIMP_(ULONG) Release() { + ::InterlockedDecrement(&s_GlobalRefCount); ULONG ulRefCount = ::InterlockedDecrement(&m_nRefCount); if (0 == m_nRefCount) { @@ -125,26 +127,20 @@ class TestComObject : virtual public IAmForTesting, } return ulRefCount; } - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) + IFACEMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv) { - if (!ppvObj) + if (!ppv) return E_INVALIDARG; - *ppvObj = NULL; - if (riid == IID_IUnknown) + *ppv = NULL; + if (riid == __uuidof(IUnknown) || riid == __uuidof(IAmForTesting)) { - *ppvObj = (LPVOID)(IAmForTesting*)this; + *ppv = (LPVOID)(IAmForTesting*)this; AddRef(); return NOERROR; } - else if (riid == IID_IDispatch) + else if (riid == __uuidof(IDispatch)) { - *ppvObj = (LPVOID)(IDispatch*)this; - AddRef(); - return NOERROR; - } - else if (riid == IID_IAmForTesting) - { - *ppvObj = (LPVOID)(IAmForTesting*)this; + *ppv = (LPVOID)(IDispatch*)this; AddRef(); return NOERROR; } @@ -153,29 +149,29 @@ class TestComObject : virtual public IAmForTesting, // IDispatch public: - STDMETHODIMP GetTypeInfoCount(UINT* /*pctinfo*/) + IFACEMETHODIMP GetTypeInfoCount(UINT* /*pctinfo*/) { return E_NOTIMPL; } - STDMETHODIMP GetTypeInfo(UINT /*itinfo*/, LCID /*lcid*/, ITypeInfo** /*pptinfo*/) + IFACEMETHODIMP GetTypeInfo(UINT /*itinfo*/, LCID /*lcid*/, ITypeInfo** /*pptinfo*/) { return E_NOTIMPL; } - STDMETHODIMP GetIDsOfNames(REFIID /*riid*/, LPOLESTR* /*rgszNames*/, UINT /*cNames*/, LCID /*lcid*/, DISPID* /*rgdispid*/) + IFACEMETHODIMP GetIDsOfNames(REFIID /*riid*/, LPOLESTR* /*rgszNames*/, UINT /*cNames*/, LCID /*lcid*/, DISPID* /*rgdispid*/) { return E_NOTIMPL; } - STDMETHODIMP Invoke(DISPID /*dispidMember*/, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* /*pdispparams*/, VARIANT* /*pvarResult*/, EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/) + IFACEMETHODIMP Invoke(DISPID /*dispidMember*/, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* /*pdispparams*/, VARIANT* /*pvarResult*/, EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/) { return E_NOTIMPL; } // IAmForTesting public: - STDMETHODIMP GetID(LONG* pOut) + IFACEMETHODIMP GetID(LONG* pOut) { if (!pOut) return E_POINTER; *pOut = m_nID; @@ -206,6 +202,10 @@ class TestComObject : virtual public IAmForTesting, { return false; } + if (left == right) + { + return true; + } wil::com_ptr_nothrow spLeft; wil::com_ptr_nothrow spRight; @@ -226,23 +226,26 @@ class TestComObject : virtual public IAmForTesting, } } - static LONG GetObjectCounter() { return s_ObjectCounter; } + static LONG ObjectCount() { return s_ObjectCount; } + static LONG GlobalRefCount() { return s_GlobalRefCount; } private: LONG m_nRefCount{}; LONG m_nID{}; - static LONG s_ObjectCounter; - static LONG s_IDCounter; + static LONG s_ObjectCount; + static LONG s_GlobalRefCount; + static LONG s_IDCount; }; -LONG TestComObject::s_ObjectCounter = 0; -LONG TestComObject::s_IDCounter = 0; +LONG TestComObject::s_ObjectCount = 0; +LONG TestComObject::s_GlobalRefCount = 0; +LONG TestComObject::s_IDCount = 0; constexpr auto DEFAULT_SAMPLE_SIZE = 192U; template::value, int>::type> -auto GetSampleData() -> std::array +std::array GetSampleData() { constexpr auto BIT_COUNT = sizeof(T) * 8; @@ -336,7 +339,7 @@ std::array, DEFAULT_SAMPLE_SIZE> GetSampleDa for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; - REQUIRE_SUCCEEDED(TestComObject::Create(IID_IAmForTesting, (LPVOID*)var.put())); + REQUIRE_SUCCEEDED(TestComObject::Create(__uuidof(IAmForTesting), (LPVOID*)var.put())); } return result; } @@ -348,7 +351,7 @@ std::array, DEFAULT_SAMPLE_SIZE> GetSampleData() for (auto i = 0U; i < DEFAULT_SAMPLE_SIZE; ++i) { auto& var = result[i]; - REQUIRE_SUCCEEDED(TestComObject::Create(IID_IDispatch, (LPVOID*)var.put())); + REQUIRE_SUCCEEDED(TestComObject::Create(__uuidof(IDispatch), (LPVOID*)var.put())); } return result; } @@ -522,12 +525,12 @@ template void TestLock(safearray_t& sa) { REQUIRE(sa); - const auto startingLocks = sa.get()->cLocks; + const auto startingLocks = sa.lock_count(); { auto lock = sa.scope_lock(); - REQUIRE(sa.get()->cLocks > startingLocks); // Verify Lock Count increased + REQUIRE(sa.lock_count() > startingLocks); // Verify Lock Count increased } - REQUIRE(startingLocks == sa.get()->cLocks); // Verify it dropped back down + REQUIRE(startingLocks == sa.lock_count()); // Verify it dropped back down } template @@ -1391,7 +1394,7 @@ TEST_CASE("Safearray::Put/Get", "[safearray]") } #endif - REQUIRE(TestComObject::GetObjectCounter() == 0); + REQUIRE(TestComObject::ObjectCount() == 0); } TEST_CASE("Safearray::AccessData", "[safearray]") @@ -1416,5 +1419,7 @@ TEST_CASE("Safearray::AccessData", "[safearray]") } #endif - REQUIRE(TestComObject::GetObjectCounter() == 0); + REQUIRE(TestComObject::ObjectCount() == 0); } + +#endif \ No newline at end of file From f4d36c14bd1710dc781cf9c09597576a7f5aedef Mon Sep 17 00:00:00 2001 From: Darrin Cullop Date: Sun, 23 Aug 2020 17:17:41 -0700 Subject: [PATCH 24/34] Make Clang Happy Wow, clang is super picky about unused local typedefs. It's ridiculous. --- tests/SafearrayTests.cpp | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index b9a0714f..e800ec3d 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -5,6 +5,7 @@ #include "common.h" #include #include + #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #pragma comment( lib, "Propsys.lib" ) @@ -127,7 +128,7 @@ class TestComObject : virtual public IAmForTesting, } return ulRefCount; } - IFACEMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv) + STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv) { if (!ppv) return E_INVALIDARG; @@ -149,29 +150,29 @@ class TestComObject : virtual public IAmForTesting, // IDispatch public: - IFACEMETHODIMP GetTypeInfoCount(UINT* /*pctinfo*/) + STDMETHOD(GetTypeInfoCount)(UINT* /*pctinfo*/) { return E_NOTIMPL; } - IFACEMETHODIMP GetTypeInfo(UINT /*itinfo*/, LCID /*lcid*/, ITypeInfo** /*pptinfo*/) + STDMETHOD(GetTypeInfo)(UINT /*itinfo*/, LCID /*lcid*/, ITypeInfo** /*pptinfo*/) { return E_NOTIMPL; } - IFACEMETHODIMP GetIDsOfNames(REFIID /*riid*/, LPOLESTR* /*rgszNames*/, UINT /*cNames*/, LCID /*lcid*/, DISPID* /*rgdispid*/) + STDMETHOD(GetIDsOfNames)(REFIID /*riid*/, LPOLESTR* /*rgszNames*/, UINT /*cNames*/, LCID /*lcid*/, DISPID* /*rgdispid*/) { return E_NOTIMPL; } - IFACEMETHODIMP Invoke(DISPID /*dispidMember*/, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* /*pdispparams*/, VARIANT* /*pvarResult*/, EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/) + STDMETHOD(Invoke)(DISPID /*dispidMember*/, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* /*pdispparams*/, VARIANT* /*pvarResult*/, EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/) { return E_NOTIMPL; } // IAmForTesting public: - IFACEMETHODIMP GetID(LONG* pOut) + STDMETHOD(GetID)(LONG* pOut) { if (!pOut) return E_POINTER; *pOut = m_nID; @@ -356,7 +357,7 @@ std::array, DEFAULT_SAMPLE_SIZE> GetSampleData() return result; } -template ::value +template ::value && !wistd::is_same>::value && !wistd::is_same>::value, int>::type> auto GetReadable(const T& t) -> const T& @@ -883,9 +884,6 @@ void TestTyped_AccessData_NoThrow() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it @@ -957,9 +955,6 @@ void TestTyped_AccessData_Failfast() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it @@ -1032,9 +1027,6 @@ void TestTyped_AccessData() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it @@ -1053,7 +1045,7 @@ void TestTyped_AccessData() // Duplicate the SA to make sure copy works auto sa2 = safearray_t{}; - REQUIRE_NOTHROW([&]() + REQUIRE_NOTHROW([&]() { sa2 = sa.create_copy(); REQUIRE(sa2); @@ -1119,9 +1111,6 @@ void Test_AccessData_NoThrow() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it @@ -1193,9 +1182,6 @@ void Test_AccessData_Failfast() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it @@ -1268,9 +1254,6 @@ void Test_AccessData() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - // Operate on the AccessData class using ranged-for { // Create a new SA and copy the sample data into it From 22da04b411c0b29e4960d5e881484143ad19b8ca Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Mon, 31 Aug 2020 18:37:07 -0700 Subject: [PATCH 25/34] Suggested changes from the PR: 1) Removed the outer compiler guards in Safearrays.h 2) Renamed var_traits to infer_safearray_type_traits 3) Removed nullptr check from shim functions 4) Removed anything that looks like it might be Hungarian 5) Changed "dim" function to "dims". 6) Explicit Constructors (where appropriate) 7) Improved comments/documentation 8) Explicitly include OleAuto.h instead of checking to see if it has been included before using it --- include/wil/safearrays.h | 268 +++++++++++++++++++-------------------- tests/SafearrayTests.cpp | 12 +- 2 files changed, 136 insertions(+), 144 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 64c45db6..315fc9a4 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -8,8 +8,6 @@ // PARTICULAR PURPOSE AND NONINFRINGEMENT. // //********************************************************* -#ifndef __WIL_SAFEARRAYS_INCLUDED -#define __WIL_SAFEARRAYS_INCLUDED #ifdef _KERNEL_MODE #error This header is not supported in kernel-mode. @@ -17,67 +15,63 @@ #include // new(std::nothrow) #include "wistd_type_traits.h" -#include "resource.h" // unique_hkey +#include "resource.h" +#include namespace wil { -#if defined( _OLEAUTO_H_ ) && !defined(__WIL_SAFEARRAY_) +#if !defined(__WIL_SAFEARRAY_) #define __WIL_SAFEARRAY_ /// @cond namespace details { template - struct var_traits{}; - - template<> struct var_traits { static constexpr auto vartype = VT_I1; }; - //template<> struct var_traits { static constexpr auto vartype = VT_I2; }; - template<> struct var_traits { static constexpr auto vartype = VT_I4; }; - template<> struct var_traits { static constexpr auto vartype = VT_I4; }; - template<> struct var_traits { static constexpr auto vartype = VT_I8; }; - template<> struct var_traits { static constexpr auto vartype = VT_UI1; }; - template<> struct var_traits { static constexpr auto vartype = VT_UI2; }; - template<> struct var_traits { static constexpr auto vartype = VT_UI4; }; - template<> struct var_traits { static constexpr auto vartype = VT_UI4; }; - template<> struct var_traits { static constexpr auto vartype = VT_UI8; }; - template<> struct var_traits { static constexpr auto vartype = VT_R4; }; - //template<> struct var_traits { static constexpr auto vartype = VT_R8; }; - template<> struct var_traits { static constexpr auto vartype = VT_BOOL; }; - template<> struct var_traits { static constexpr auto vartype = VT_DATE; }; - template<> struct var_traits { static constexpr auto vartype = VT_CY; }; - template<> struct var_traits { static constexpr auto vartype = VT_DECIMAL; }; - template<> struct var_traits { static constexpr auto vartype = VT_BSTR; }; - template<> struct var_traits { static constexpr auto vartype = VT_UNKNOWN; }; - template<> struct var_traits { static constexpr auto vartype = VT_DISPATCH; }; - template<> struct var_traits { static constexpr auto vartype = VT_VARIANT; }; + struct infer_safearray_type_traits{}; + + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I1; }; + //template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I2; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I4; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I4; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I8; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UI1; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UI2; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UI4; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UI4; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UI8; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_R4; }; + //template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_R8; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_BOOL; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_DATE; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_CY; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_DECIMAL; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_BSTR; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_UNKNOWN; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_DISPATCH; }; + template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_VARIANT; }; inline void __stdcall SafeArrayDestroy(SAFEARRAY* psa) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayDestroy(psa)); } inline void __stdcall SafeArrayLock(SAFEARRAY* psa) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayLock(psa)); } inline void __stdcall SafeArrayUnlock(SAFEARRAY* psa) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayUnlock(psa)); } template inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); } inline void __stdcall SafeArrayUnaccessData(SAFEARRAY* psa) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); FAIL_FAST_IF_FAILED(::SafeArrayUnaccessData(psa)); } @@ -96,46 +90,46 @@ namespace wil return (psa != nullptr) ? psa->cLocks : 0U; } - inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT dims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT { WI_ASSERT(sab != nullptr); - WI_ASSERT(cDims > 0); - psa = ::SafeArrayCreate(vt, cDims, sab); + WI_ASSERT(dims > 0); + psa = ::SafeArrayCreate(vt, dims, sab); RETURN_LAST_ERROR_IF_NULL(psa); WI_ASSERT(vt == details::SafeArrayGetVartype(psa)); return S_OK; } - inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT nDim, ULONG* pCount) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT dims, ULONG* result) WI_NOEXCEPT { __FAIL_FAST_ASSERT__(psa != nullptr); - __FAIL_FAST_ASSERT__(pCount != nullptr); - RETURN_HR_IF(E_INVALIDARG, ((nDim == 0) || (nDim > psa->cDims))); + __FAIL_FAST_ASSERT__(result != nullptr); + RETURN_HR_IF(E_INVALIDARG, ((dims == 0) || (dims > psa->cDims))); // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order - *pCount = psa->rgsabound[psa->cDims - nDim].cElements; + *result = psa->rgsabound[psa->cDims - dims].cElements; return S_OK; } - inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* pCount) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* result) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(pCount != nullptr); + __FAIL_FAST_ASSERT__(result != nullptr); if (psa != nullptr) { - ULONGLONG result = 1; + ULONGLONG elements = 1; for (UINT i = 0; i < psa->cDims; ++i) { - result *= psa->rgsabound[i].cElements; - if (result > ULONG_MAX) + elements *= psa->rgsabound[i].cElements; + if (elements > ULONG_MAX) { RETURN_HR(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); } } - *pCount = static_cast(result); + *result = static_cast(elements); } else { // If it's invalid, it doesn't contain any elements - *pCount = 0; + *result = 0; } return S_OK; } @@ -155,7 +149,7 @@ namespace wil } //! Class that facilitates the direct access to the contents of a SAFEARRAY and "unaccessing" it when done - //! It enables treating the safearray like an regular array, or doing a ranged-for on the contents. + //! It enables treating the safearray like an regular array and/or performing a ranged-for on the contents. //! Accessing a safearray increases it's lock count, so attempts to destroy the safearray will fail until it is unaccessed. //! This class works even if the SAFEARRAY is multi-dimensional by treating it as a large single-dimension array, //! but doesn't support access via a multi-dimensional index. @@ -203,12 +197,12 @@ namespace wil //! } //! ~~~~ //! template - //! std::vector> extract_all_interfaces(SAFEARRAY* psa) + //! std::vector> extract_all_interfaces(SAFEARRAY* psa) //! { //! auto result = std::vector>{}; //! auto data = wil::safearraydata{psa}; //! result.reserve(data.size()); - //! for(auto& p : data) // type of P is LPUNKNOWN + //! for(auto& p : data) // Type of p is LPUNKNOWN //! { //! // Use "tag_com_query" if you want failure instead of nullptr if INTERFACE not supported //! result.emplace_back(p, details::tag_try_com_query); @@ -226,7 +220,7 @@ namespace wil public: typedef typename err_policy::result result; - typedef typename safearray_unaccess_data::pointer pointer; + typedef safearray_unaccess_data::pointer pointer; typedef T value_type; typedef value_type* value_pointer; typedef const value_type* const_value_pointer; @@ -237,7 +231,7 @@ namespace wil public: // Exception-based construction - safearraydata_t(pointer psa) + explicit safearraydata_t(pointer psa) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); access(psa); @@ -254,31 +248,31 @@ namespace wil return err_policy::HResult([&]() { WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - details::SafeArrayAccessData(psa, m_pBegin); + details::SafeArrayAccessData(psa, m_begin); m_unaccess.reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_nSize)); + RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_size)); return S_OK; }()); } // Ranged-for style - iterator begin() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - iterator end() WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } - const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin; } - const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_pBegin != nullptr); return m_pBegin+m_nSize; } + iterator begin() WI_NOEXCEPT { WI_ASSERT(m_begin != nullptr); return m_begin; } + iterator end() WI_NOEXCEPT { WI_ASSERT(m_begin != nullptr); return m_begin+m_size; } + const_iterator begin() const WI_NOEXCEPT { WI_ASSERT(m_begin != nullptr); return m_begin; } + const_iterator end() const WI_NOEXCEPT { WI_ASSERT(m_begin != nullptr); return m_begin+m_size; } // Old style iteration - ULONG size() const WI_NOEXCEPT { return m_nSize; } - WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_nSize); return *(m_pBegin + i); } - WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_nSize); return *(m_pBegin +i); } + ULONG size() const WI_NOEXCEPT { return m_size; } + WI_NODISCARD value_reference operator[](ULONG i) { WI_ASSERT(i < m_size); return *(m_begin + i); } + WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_size); return *(m_begin +i); } // Utilities - bool empty() const WI_NOEXCEPT { return m_nSize == ULONG{}; } + bool empty() const WI_NOEXCEPT { return m_size == ULONG{}; } private: safearray_unaccess_data m_unaccess{}; - value_pointer m_pBegin{}; - ULONG m_nSize{}; + value_pointer m_begin{}; + ULONG m_size{}; }; template @@ -373,47 +367,47 @@ namespace wil // Exception-based construction template::value, int>::type = 0> - safearray_t(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + safearray_t(VARTYPE vt, UINT elements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(vt, cElements, lowerBound); + create(vt, elements, lowerBound); } template::value, int>::type = 0> - safearray_t(UINT cElements, LONG lowerBound = 0) + explicit safearray_t(UINT elements, LONG lowerBound = 0) { static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); - create(cElements, lowerBound); + create(elements, lowerBound); } // Low-level, arbitrary number of dimensions // Uses Explicit Type template::value, int>::type = 0> - result create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + result create(VARTYPE vt, UINT dims, SAFEARRAYBOUND* sab) { - return err_policy::HResult(_create(vt, cDims, sab)); + return err_policy::HResult(_create(vt, dims, sab)); } // Uses Inferred Type template::value, int>::type = 0> - result create(UINT cDims, SAFEARRAYBOUND* sab) + result create(UINT dims, SAFEARRAYBOUND* sab) { - constexpr auto vt = static_cast(details::var_traits::vartype); - return err_policy::HResult(_create(vt, cDims, sab)); + constexpr auto vt = static_cast(details::infer_safearray_type_traits::vartype); + return err_policy::HResult(_create(vt, dims, sab)); } // Single Dimension Specialization // Uses Explicit Type template::value, int>::type = 0> - result create(VARTYPE vt, UINT cElements, LONG lowerBound = 0) + result create(VARTYPE vt, UINT elements, LONG lowerBound = 0) { - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + auto bounds = SAFEARRAYBOUND{ elements, lowerBound }; return err_policy::HResult(_create(vt, 1, &bounds)); } // Uses Inferred Type template::value, int>::type = 0> - result create(UINT cElements, LONG lowerBound = 0) + result create(UINT elements, LONG lowerBound = 0) { - constexpr auto vt = static_cast(details::var_traits::vartype); - auto bounds = SAFEARRAYBOUND{ cElements, lowerBound }; + constexpr auto vt = static_cast(details::infer_safearray_type_traits::vartype); + auto bounds = SAFEARRAYBOUND{ elements, lowerBound }; return err_policy::HResult(_create(vt, 1, &bounds)); } @@ -440,48 +434,48 @@ namespace wil //! Indicates the number of dimensions in the SAFEARRAY. The 1st Dimension //! is always 1, and there never is a Dimension 0. - ULONG dim() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } + ULONG dims() const WI_NOEXCEPT { return ::SafeArrayGetDim(storage_t::get()); } //! Returns the lowest possible index for the given dimension. - result lbound(UINT nDim, LONG* pLbound) const + result lbound(UINT dim, LONG* result) const { - WI_ASSERT(pLbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), nDim, pLbound)); + WI_ASSERT(result != nullptr); + WI_ASSERT((dim > 0) && (dim <= dims())); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), dim, result)); } //! Returns the highest possible index for the given dimension. - result ubound(UINT nDim, LONG* pUbound) const + result ubound(UINT dim, LONG* result) const { - WI_ASSERT(pUbound != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), nDim, pUbound)); + WI_ASSERT(result != nullptr); + WI_ASSERT((dim > 0) && (dim <= dims())); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), dim, result)); } //! 1D Specialization of lbound - result lbound(LONG* pLbound) const + result lbound(LONG* result) const { - WI_ASSERT(dim() == 1); - WI_ASSERT(pLbound != nullptr); - return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, pLbound)); + WI_ASSERT(dims() == 1); + WI_ASSERT(result != nullptr); + return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, result)); } //! 1D Specialization of ubound - result ubound(LONG* pUbound) const + result ubound(LONG* result) const { - WI_ASSERT(dim() == 1); - WI_ASSERT(pUbound != nullptr); - return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, pUbound)); + WI_ASSERT(dims() == 1); + WI_ASSERT(result != nullptr); + return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, result)); } //! Indicates the total number of elements across all the dimensions - result count(ULONG* pCount) const + result count(ULONG* result) const { - WI_ASSERT(pCount != nullptr); - return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), pCount)); + WI_ASSERT(result != nullptr); + return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), result)); } //! Returns the number of elements in the specified dimension - //! Same as, but much faster than, the typical ubound() - lbound() + 1 - result dim_elements(UINT nDim, ULONG* pCount) const + //! Same as, but faster than, the typical ubound() - lbound() + 1 + result dim_elements(UINT dim, ULONG* result) const { - WI_ASSERT(pCount != nullptr); - WI_ASSERT((nDim > 0) && (nDim <= dim())); - return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), nDim, pCount)); + WI_ASSERT(result != nullptr); + WI_ASSERT((dim > 0) && (dim <= dims())); + return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), dim, result)); } //! Return the size, in bytes, of each element in the array ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } @@ -547,68 +541,68 @@ namespace wil //! Copies the specified element and puts it into the specified index in the SAFEARRAY (1D version) //! The SAFEARRAY will own any resources and will clean up when it is destroyed template::value, int>::type = 0> - result put_element(LONG nIndex, const T& val) + result put_element(LONG index, const T& val) { WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, reinterpret_cast(const_cast(&val)))); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, reinterpret_cast(const_cast(&val)))); } template::value, int>::type = 0> - result put_element(LONG nIndex, T val) + result put_element(LONG index, T val) { WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, val)); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, val)); } //! Retrieves a copy of the element at the specified index. The caller owns this copy and must free - //! any resources with LPUNKNOWN->Release, SysFreeString, VariantClear, , etc. + //! any resources with LPUNKNOWN->Release, SysFreeString, VariantClear, etc. template::value, int>::type = 0> - result get_element(LONG nIndex, T& val) + result get_element(LONG index, T& val) { WI_ASSERT(sizeof(val) == elemsize()); - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, &val)); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &index, &val)); } //! Multiple Dimension version of put_element template::value, int>::type = 0> - result put_element(LONG* pIndices, const T& val) + result put_element(LONG* indices, const T& val) { WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, reinterpret_cast(const_cast(&val)))); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, reinterpret_cast(const_cast(&val)))); } template::value, int>::type = 0> - result put_element(LONG* pIndices, T val) + result put_element(LONG* indices, T val) { WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, val)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, val)); } //! Multiple Dimension version of get_element template::value, int>::type = 0> - result get_element(LONG* pIndices, T& val) + result get_element(LONG* indices, T& val) { WI_ASSERT(sizeof(val) == elemsize()); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, &val)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, &val)); } //! Lowest-Level functions for those who know what they're doing. - result put_element(LONG nIndex, void* pv) + result put_element(LONG index, void* pv) { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &nIndex, pv)); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, pv)); } - result get_element(LONG nIndex, void* pv) + result get_element(LONG index, void* pv) { - WI_ASSERT(dim() == 1); - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &nIndex, pv)); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &index, pv)); } - result put_element(LONG* pIndices, void* pv) + result put_element(LONG* indices, void* pv) { - return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), pIndices, pv)); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, pv)); } - result get_element(LONG* pIndices, void* pv) + result get_element(LONG* indices, void* pv) { - return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), pIndices, pv)); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, pv)); } //! @} @@ -616,19 +610,19 @@ namespace wil //! @{ //! Returns the lower bound of the given dimension - WI_NODISCARD LONG lbound(UINT nDim = 1) const + WI_NODISCARD LONG lbound(UINT dim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); LONG nResult = 0; - lbound(nDim, &nResult); + lbound(dim, &nResult); return nResult; } //! Returns the bupper bound of the given dimension - WI_NODISCARD LONG ubound(UINT nDim = 1) const + WI_NODISCARD LONG ubound(UINT dim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); LONG nResult = 0; - ubound(nDim, &nResult); + ubound(dim, &nResult); return nResult; } //! Return the total number of elements in the SAFEARRAY @@ -640,11 +634,11 @@ namespace wil return nResult; } //! Returns the number of elements in the given dimension - WI_NODISCARD ULONG dim_elements(UINT nDim = 1) const + WI_NODISCARD ULONG dim_elements(UINT dim = 1) const { static_assert(wistd::is_same::value, "this method requires exceptions"); ULONG nResult = 0; - dim_elements(nDim, &nResult); + dim_elements(dim, &nResult); return nResult; } //! Returns a copy of the SAFEARRAY, including all individual elements @@ -684,10 +678,10 @@ namespace wil //! @} private: - HRESULT _create(VARTYPE vt, UINT cDims, SAFEARRAYBOUND* sab) + HRESULT _create(VARTYPE vt, UINT dims, SAFEARRAYBOUND* sab) { auto psa = storage_t::policy::invalid_value(); - RETURN_IF_FAILED(details::SafeArrayCreate(vt, cDims, sab, psa)); + RETURN_IF_FAILED(details::SafeArrayCreate(vt, dims, sab, psa)); storage_t::reset(psa); return S_OK; } @@ -757,7 +751,7 @@ namespace wil using unique_variant_safearray = unique_any_t, err_exception_policy, VARIANT>>; #endif -#endif // #if defined( _OLEAUTO_H_ ) && !defined(__WIL_SAFEARRAY_) +#endif // #if !defined(__WIL_SAFEARRAY_) #if defined(__WIL_SAFEARRAY_) && !defined(__WIL_SAFEARRAY_SHARED_STL) && defined(WIL_RESOURCE_STL) #define __WIL_SAFEARRAY_SHARED_STL @@ -893,5 +887,3 @@ namespace wil } // namespace wil -#endif // #ifndef __WIL_SAFEARRAYS_INCLUDED - diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index e800ec3d..3a3a1e0b 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -544,7 +544,7 @@ void TestTyped_Create_NoThrow() ULONG count = 0; REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); @@ -567,7 +567,7 @@ void TestTyped_Create_FailFast() ULONG count = 0; REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOCRASH(sa.count(&count)); REQUIRE(count == SIZE); @@ -589,7 +589,7 @@ void TestTyped_Create() auto sa = safearray_t{}; REQUIRE_NOTHROW(sa = safearray_t{ SIZE }); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); @@ -614,7 +614,7 @@ void Test_Create_NoThrow() ULONG count = 0; REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); REQUIRE_SUCCEEDED(sa.count(&count)); REQUIRE(count == SIZE); @@ -637,7 +637,7 @@ void Test_Create_FailFast() ULONG count = 0; REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); REQUIRE_NOCRASH(sa.count(&count)); REQUIRE(count == SIZE); @@ -659,7 +659,7 @@ void Test_Create() auto sa = wil::unique_safearray{}; REQUIRE_NOTHROW(sa.create( SIZE )); REQUIRE(sa); - REQUIRE(sa.dim() == 1); + REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); REQUIRE_NOTHROW(sa.count() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); From 1962a224d53046a293b09a681443e066448c4266 Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Tue, 1 Sep 2020 15:15:49 -0700 Subject: [PATCH 26/34] More feedback from the PR --- include/wil/safearrays.h | 333 +++++++++++++++++++++++++++++++-------- tests/SafearrayTests.cpp | 203 ++++++++++++++++++++++-- 2 files changed, 452 insertions(+), 84 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 315fc9a4..c4912480 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -25,9 +25,7 @@ namespace wil /// @cond namespace details { - template - struct infer_safearray_type_traits{}; - + template struct infer_safearray_type_traits{}; template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I1; }; //template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I2; }; template<> struct infer_safearray_type_traits { static constexpr auto vartype = VT_I4; }; @@ -67,6 +65,7 @@ namespace wil template inline void __stdcall SafeArrayAccessData(SAFEARRAY* psa, T*& p) WI_NOEXCEPT { + WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); FAIL_FAST_IF_FAILED(::SafeArrayAccessData(psa, reinterpret_cast(&p))); } @@ -92,7 +91,6 @@ namespace wil inline HRESULT __stdcall SafeArrayCreate(VARTYPE vt, UINT dims, SAFEARRAYBOUND* sab, SAFEARRAY*& psa) WI_NOEXCEPT { - WI_ASSERT(sab != nullptr); WI_ASSERT(dims > 0); psa = ::SafeArrayCreate(vt, dims, sab); RETURN_LAST_ERROR_IF_NULL(psa); @@ -100,24 +98,23 @@ namespace wil return S_OK; } - inline HRESULT __stdcall SafeArrayDimElementCount(SAFEARRAY* psa, UINT dims, ULONG* result) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayGetDimSize(SAFEARRAY* psa, UINT dims, ULONG* result) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(psa != nullptr); - __FAIL_FAST_ASSERT__(result != nullptr); RETURN_HR_IF(E_INVALIDARG, ((dims == 0) || (dims > psa->cDims))); // Read from the back of the array because SAFEARRAYs are stored with the dimensions in reverse order *result = psa->rgsabound[psa->cDims - dims].cElements; return S_OK; } - inline HRESULT __stdcall SafeArrayCountElements(SAFEARRAY* psa, ULONG* result) WI_NOEXCEPT + inline HRESULT __stdcall SafeArrayGetTotalSize(SAFEARRAY* psa, ULONG* result) WI_NOEXCEPT { - __FAIL_FAST_ASSERT__(result != nullptr); if (psa != nullptr) { ULONGLONG elements = 1; for (UINT i = 0; i < psa->cDims; ++i) { + // Determine the total size of the safearray by multiplying + // the sizes of each dimension together elements *= psa->rgsabound[i].cElements; if (elements > ULONG_MAX) { @@ -139,6 +136,7 @@ namespace wil /// @endcond typedef unique_any safearray_unlock_scope_exit; + typedef unique_any safearray_unaccess_scope_exit; //! Guarantees a SafeArrayUnlock call on the given object when the returned object goes out of scope //! Note: call SafeArrayUnlock early with the reset() method on the returned object or abort the call with the release() method @@ -148,6 +146,15 @@ namespace wil return safearray_unlock_scope_exit(psa); } + //! Guarantees a SafeArrayUnaccessData call on the given object when the returned object goes out of scope + //! Note: call SafeArrayUnaccessData early with the reset() method on the returned object or abort the call with the release() method + template + WI_NODISCARD inline safearray_unaccess_scope_exit SafeArrayAccessData_scope_exit(SAFEARRAY* psa, T*& p) WI_NOEXCEPT + { + details::SafeArrayAccessData(psa, p); + return safearray_unaccess_scope_exit(psa); + } + //! Class that facilitates the direct access to the contents of a SAFEARRAY and "unaccessing" it when done //! It enables treating the safearray like an regular array and/or performing a ranged-for on the contents. //! Accessing a safearray increases it's lock count, so attempts to destroy the safearray will fail until it is unaccessed. @@ -215,12 +222,9 @@ namespace wil template class safearraydata_t { - private: - typedef unique_any safearray_unaccess_data; - public: typedef typename err_policy::result result; - typedef safearray_unaccess_data::pointer pointer; + typedef safearray_unaccess_scope_exit::pointer pointer; typedef T value_type; typedef value_type* value_pointer; typedef const value_type* const_value_pointer; @@ -247,10 +251,8 @@ namespace wil { return err_policy::HResult([&]() { - WI_ASSERT(sizeof(T) == ::SafeArrayGetElemsize(psa)); - details::SafeArrayAccessData(psa, m_begin); - m_unaccess.reset(psa); - RETURN_IF_FAILED(details::SafeArrayCountElements(m_unaccess.get(), &m_size)); + m_unaccess = SafeArrayAccessData_scope_exit(psa, m_begin); + RETURN_IF_FAILED(details::SafeArrayGetTotalSize(m_unaccess.get(), &m_size)); return S_OK; }()); } @@ -267,10 +269,10 @@ namespace wil WI_NODISCARD const_value_reference operator[](ULONG i) const { WI_ASSERT(i < m_size); return *(m_begin +i); } // Utilities - bool empty() const WI_NOEXCEPT { return m_size == ULONG{}; } + bool empty() const WI_NOEXCEPT { return m_size == 0UL; } private: - safearray_unaccess_data m_unaccess{}; + safearray_unaccess_scope_exit m_unaccess{}; value_pointer m_begin{}; ULONG m_size{}; }; @@ -298,7 +300,7 @@ namespace wil //! wil::unique_bstr_safearray_nothrow sa{}; //! RETURN_IF_FAILED(sa.create(32)); //! { - //! wil::safearraydata_nothrow data{}; + //! wil::safearraydata_nothrow data{}; //! RETURN_IF_FAILED(data.access(sa.get()); //! for(auto& bstr : data) //! { @@ -339,9 +341,13 @@ namespace wil private: // SFINAE Helpers to improve readability template + using is_interface_type = wistd::disjunction, + wistd::is_same>; + template using is_pointer_type = wistd::disjunction, - wistd::is_same, - wistd::is_same>; + wistd::is_same, + wistd::is_same>; + template using is_type_not_set = wistd::is_same; template @@ -438,14 +444,12 @@ namespace wil //! Returns the lowest possible index for the given dimension. result lbound(UINT dim, LONG* result) const { - WI_ASSERT(result != nullptr); WI_ASSERT((dim > 0) && (dim <= dims())); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), dim, result)); } //! Returns the highest possible index for the given dimension. result ubound(UINT dim, LONG* result) const { - WI_ASSERT(result != nullptr); WI_ASSERT((dim > 0) && (dim <= dims())); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), dim, result)); } @@ -453,29 +457,25 @@ namespace wil result lbound(LONG* result) const { WI_ASSERT(dims() == 1); - WI_ASSERT(result != nullptr); return err_policy::HResult(::SafeArrayGetLBound(storage_t::get(), 1, result)); } //! 1D Specialization of ubound result ubound(LONG* result) const { WI_ASSERT(dims() == 1); - WI_ASSERT(result != nullptr); return err_policy::HResult(::SafeArrayGetUBound(storage_t::get(), 1, result)); } //! Indicates the total number of elements across all the dimensions - result count(ULONG* result) const + result size(ULONG* result) const { - WI_ASSERT(result != nullptr); - return err_policy::HResult(details::SafeArrayCountElements(storage_t::get(), result)); + return err_policy::HResult(details::SafeArrayGetTotalSize(storage_t::get(), result)); } //! Returns the number of elements in the specified dimension //! Same as, but faster than, the typical ubound() - lbound() + 1 - result dim_elements(UINT dim, ULONG* result) const + result dim_size(UINT dim, ULONG* result) const { - WI_ASSERT(result != nullptr); WI_ASSERT((dim > 0) && (dim <= dims())); - return err_policy::HResult(details::SafeArrayDimElementCount(storage_t::get(), dim, result)); + return err_policy::HResult(details::SafeArrayGetDimSize(storage_t::get(), dim, result)); } //! Return the size, in bytes, of each element in the array ULONG elemsize() const WI_NOEXCEPT { return ::SafeArrayGetElemsize(storage_t::get()); } @@ -519,15 +519,15 @@ namespace wil //! { //! wil::unique_safearray_nothrow sa{}; //! wil::unique_bstr bstr{}; - //! ULONG count{}; + //! ULONG size{}; //! //! // Invoke the API //! RETURN_IF_FAILED(::GetWonderfulData(sa.put())); //! // Verify the output is expected //! RETURN_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); //! RETURN_HR_IF(E_UNEXPECTED, sa.dims() != 1); - //! RETURN_IF_FAILED(sa.count(&count)); - //! for(auto i = 0U; i < count; ++i) + //! RETURN_IF_FAILED(sa.size(&size)); + //! for(auto i = 0U; i < size; ++i) //! { //! // Copy from the safearray to the unique_bstr //! sa.get_element(i, bstr.put()); // Will release current BSTR @@ -538,16 +538,17 @@ namespace wil //! ~~~~ //! @{ - //! Copies the specified element and puts it into the specified index in the SAFEARRAY (1D version) - //! The SAFEARRAY will own any resources and will clean up when it is destroyed - template::value, int>::type = 0> + //! Copies the specified element and puts it into the specified index in the SAFEARRAY. + //! The SAFEARRAY will own the copy and will clean up the resources when it is destroyed. + //! HOWEVER, the caller still owns the original and is responsible for it's clean up + template::value, int>::type = 0> result put_element(LONG index, const T& val) { WI_ASSERT(sizeof(val) == elemsize()); WI_ASSERT(dims() == 1); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, reinterpret_cast(const_cast(&val)))); } - template::value, int>::type = 0> + template::value, int>::type = 0> result put_element(LONG index, T val) { WI_ASSERT(sizeof(val) == elemsize()); @@ -556,7 +557,7 @@ namespace wil } //! Retrieves a copy of the element at the specified index. The caller owns this copy and must free //! any resources with LPUNKNOWN->Release, SysFreeString, VariantClear, etc. - template::value, int>::type = 0> + template::value, int>::type = 0> result get_element(LONG index, T& val) { WI_ASSERT(sizeof(val) == elemsize()); @@ -565,27 +566,27 @@ namespace wil } //! Multiple Dimension version of put_element - template::value, int>::type = 0> + template::value, int>::type = 0> result put_element(LONG* indices, const T& val) { WI_ASSERT(sizeof(val) == elemsize()); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, reinterpret_cast(const_cast(&val)))); } - template::value, int>::type = 0> + template::value, int>::type = 0> result put_element(LONG* indices, T val) { WI_ASSERT(sizeof(val) == elemsize()); return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, val)); } //! Multiple Dimension version of get_element - template::value, int>::type = 0> + template::value, int>::type = 0> result get_element(LONG* indices, T& val) { WI_ASSERT(sizeof(val) == elemsize()); return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, &val)); } - //! Lowest-Level functions for those who know what they're doing. + //! Lowest-Level, Type-Erased versions for those who know what they're doing. result put_element(LONG index, void* pv) { WI_ASSERT(dims() == 1); @@ -604,54 +605,156 @@ namespace wil { return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, pv)); } + + //! Safer versions "get_element/put_element" that returns the copy wrapped in the appropriate storage class +#if defined(__WIL_OLEAUTO_H_) + template::value, int>::type = 0> + result get_element(LONG index, wil::unique_bstr& val) + { + WI_ASSERT(sizeof(BSTR) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &index, val.put())); + } + template::value, int>::type = 0> + result put_element(LONG index, wil::unique_bstr& val) + { + WI_ASSERT(sizeof(BSTR) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, val.get())); + } + + template::value, int>::type = 0> + result get_element(LONG index, wil::unique_variant& val) + { + WI_ASSERT(sizeof(VARIANT) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), &index, val.reset_and_addressof())); + } + template::value, int>::type = 0> + result put_element(LONG index, wil::unique_variant& val) + { + WI_ASSERT(sizeof(VARIANT) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, val.addressof())); + } + + //! Multidimensional Versions + template::value, int>::type = 0> + result get_element(LONG* indices, wil::unique_bstr& val) + { + WI_ASSERT(sizeof(BSTR) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, val.put())); + } + template::value, int>::type = 0> + result put_element(LONG* indices, wil::unique_bstr& val) + { + WI_ASSERT(sizeof(BSTR) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, val.get())); + } + + template::value, int>::type = 0> + result get_element(LONG* indices, wil::unique_variant& val) + { + WI_ASSERT(sizeof(VARIANT) == elemsize()); + return err_policy::HResult(::SafeArrayGetElement(storage_t::get(), indices, val.reset_and_addressof())); + } + template::value, int>::type = 0> + result put_element(LONG* indices, wil::unique_variant& val) + { + WI_ASSERT(sizeof(VARIANT) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, val.addressof())); + } +#endif + +#if defined(__WIL_COM_INCLUDED) // Only leverage the COM functionality if it's been included + template::value, int>::type = 0> + result get_element(LONG index, wil::com_ptr_t& val) + { + WI_ASSERT(sizeof(T) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult([&]() + { + wil::com_ptr_nothrow unk{}; + RETURN_IF_FAILED(::SafeArrayGetElement(storage_t::get(), &index, unk.put())); + RETURN_IF_FAILED(unk.query_to(val.put())); + return S_OK; + }()); + } + template::value, int>::type = 0> + result put_element(LONG index, wil::com_ptr_t& val) + { + WI_ASSERT(sizeof(T) == elemsize()); + WI_ASSERT(dims() == 1); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), &index, val.get())); + } + + template::value, int>::type = 0> + result get_element(LONG* indices, wil::com_ptr_t& val) + { + WI_ASSERT(sizeof(T) == elemsize()); + return err_policy::HResult([&]() + { + wil::com_ptr_nothrow unk{}; + RETURN_IF_FAILED(::SafeArrayGetElement(storage_t::get(), indices, unk.put())); + RETURN_IF_FAILED(unk.query_to(val.put())); + return S_OK; + }()); + } + template::value, int>::type = 0> + result put_element(LONG* indices, wil::com_ptr_t& val) + { + WI_ASSERT(sizeof(T) == elemsize()); + return err_policy::HResult(::SafeArrayPutElement(storage_t::get(), indices, val.get())); + } +#endif //! @} - //! Exception-based helper functions that make exception based code easier to read + //! Non-error code based helper functions that are easier to read //! @{ //! Returns the lower bound of the given dimension WI_NODISCARD LONG lbound(UINT dim = 1) const { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - lbound(dim, &nResult); - return nResult; + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + LONG result = 0; + lbound(dim, &result); + return result; } //! Returns the bupper bound of the given dimension WI_NODISCARD LONG ubound(UINT dim = 1) const { - static_assert(wistd::is_same::value, "this method requires exceptions"); - LONG nResult = 0; - ubound(dim, &nResult); - return nResult; + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + LONG result = 0; + ubound(dim, &result); + return result; } //! Return the total number of elements in the SAFEARRAY - WI_NODISCARD ULONG count() const + WI_NODISCARD ULONG size() const { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - count(&nResult); - return nResult; + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + ULONG result = 0; + size(&result); + return result; } //! Returns the number of elements in the given dimension - WI_NODISCARD ULONG dim_elements(UINT dim = 1) const + WI_NODISCARD ULONG dim_size(UINT dim = 1) const { - static_assert(wistd::is_same::value, "this method requires exceptions"); - ULONG nResult = 0; - dim_elements(dim, &nResult); - return nResult; + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + ULONG result = 0; + dim_size(dim, &result); + return result; } //! Returns a copy of the SAFEARRAY, including all individual elements WI_NODISCARD unique_type create_copy() const { - static_assert(wistd::is_same::value, "this method requires exceptions"); + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); auto result = unique_type{}; result.create_copy(storage_t::get()); return result; } - //! Returns a safearraydata_t that provides direct access to this SAFEARRAYs contents. See @ref safearraydata_t. + //! Returns a safearraydata_t that provides direct access to this SAFEARRAY's contents. See @ref safearraydata_t. //! ~~~~ //! // Create a new safearray by copying a vector of wil::unique_bstr //! unique_bstr_safearray copy_to_safearray(std::vector& source) @@ -669,12 +772,80 @@ namespace wil template::value, int>::type = 0> WI_NODISCARD safearraydata_t access_data() const { - static_assert(wistd::is_same::value, "this method requires exceptions"); + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + return safearraydata_t{ storage_t::get() }; + } + + //! Special Get Functions +#if defined(__WIL_OLEAUTO_H_) + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG index) -> wil::unique_bstr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::unique_bstr result{}; + get_element(index, result); + return wistd::move(result); + } + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG index) -> wil::unique_variant + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::unique_variant result{}; + get_element(index, result); + return result; + } - auto data = safearraydata_t{}; - data.access(storage_t::get()); - return wistd::move(data); + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG* indices) -> wil::unique_bstr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::unique_bstr result{}; + get_element(indices, result); + return wistd::move(result); + } + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG* indices) -> wil::unique_variant + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::unique_variant result{}; + get_element(indices, result); + return result; + } +#endif +#if defined(__WIL_COM_INCLUDED) + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::com_ptr result{}; + get_element(index, result); + return wistd::move(result); + } + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::com_ptr result{}; + get_element(index, result); + return wistd::move(result); + } + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::com_ptr result{}; + get_element(indices, result); + return wistd::move(result); } + template::value, int>::type = 0> + WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr + { + static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); + wil::com_ptr result{}; + get_element(indices, result); + return wistd::move(result); + } +#endif //! @} private: @@ -687,6 +858,11 @@ namespace wil } }; + template + using unique_safearray_nothrow_t = unique_any_t, err_returncode_policy, T>>; + template + using unique_safearray_failfast_t = unique_any_t, err_failfast_policy, T>>; + using unique_safearray_nothrow = unique_any_t, err_returncode_policy, void>>; using unique_safearray_failfast = unique_any_t, err_failfast_policy, void>>; using unique_char_safearray_nothrow = unique_any_t, err_returncode_policy, char>>; @@ -729,6 +905,9 @@ namespace wil using unique_variant_safearray_failfast = unique_any_t, err_failfast_policy, VARIANT>>; #ifdef WIL_ENABLE_EXCEPTIONS + template + using unique_safearray_t = unique_any_t, err_exception_policy, T>>; + using unique_safearray = unique_any_t, err_exception_policy, void>>; using unique_char_safearray = unique_any_t, err_exception_policy, char>>; //using unique_short_safearray = unique_any_t, err_exception_policy, short>>; @@ -755,6 +934,11 @@ namespace wil #if defined(__WIL_SAFEARRAY_) && !defined(__WIL_SAFEARRAY_SHARED_STL) && defined(WIL_RESOURCE_STL) #define __WIL_SAFEARRAY_SHARED_STL + template + using shared_safearray_nothrow_t = shared_any>; + template + using shared_safearray_failfast_t = shared_any>; + using shared_safearray_nothrow = shared_any; using shared_safearray_failfast = shared_any; using shared_char_safearray_nothrow = shared_any; @@ -797,6 +981,9 @@ namespace wil using shared_variant_safearray_failfast = shared_any; #ifdef WIL_ENABLE_EXCEPTIONS + template + using shared_safearray_t = shared_any>; + using shared_safearray = shared_any; using shared_char_safearray = shared_any; //using shared_short_safearray = shared_any; @@ -819,6 +1006,11 @@ namespace wil using shared_variant_safearray = shared_any; #endif + template + using weak_safearray_nothrow_t = weak_any>; + template + using weak_safearray_failfast_t = weak_any>; + using weak_safearray_nothrow = weak_any; using weak_safearray_failfast = weak_any; using weak_char_safearray_nothrow = weak_any; @@ -861,6 +1053,9 @@ namespace wil using weak_variant_safearray_failfast = weak_any; #ifdef WIL_ENABLE_EXCEPTIONS + template + using weak_safearray_t = weak_any>; + using weak_safearray = weak_any; using weak_char_safearray = weak_any; //using weak_short_safearray = weak_any; diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 3a3a1e0b..23db7264 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include "common.h" @@ -85,6 +85,14 @@ , VARIANT \ ) +#define RUN_TEST_SPECIAL(test) WI_FOREACH(test \ + , BSTR \ + , LPUNKNOWN \ + , LPDISPATCH \ + , VARIANT \ + ) + + // {5D80EC64-6694-4F49-B0B9-CCAA65467D12} //static const GUID IID_IAmForTesting = //{ 0x5d80ec64, 0x6694, 0x4f49, { 0xb0, 0xb9, 0xcc, 0xaa, 0x65, 0x46, 0x7d, 0x12 } }; @@ -541,13 +549,13 @@ void TestTyped_Create_NoThrow() auto sa = safearray_t{}; LONG val = 0; - ULONG count = 0; + ULONG size = 0; REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); - REQUIRE_SUCCEEDED(sa.count(&count)); - REQUIRE(count == SIZE); + REQUIRE_SUCCEEDED(sa.size(&size)); + REQUIRE(size == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_SUCCEEDED(sa.ubound(&val)); @@ -564,13 +572,13 @@ void TestTyped_Create_FailFast() auto sa = safearray_t{}; LONG val = 0; - ULONG count = 0; + ULONG size = 0; REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); - REQUIRE_NOCRASH(sa.count(&count)); - REQUIRE(count == SIZE); + REQUIRE_NOCRASH(sa.size(&size)); + REQUIRE(size == SIZE); REQUIRE_NOCRASH(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_NOCRASH(sa.ubound(&val)); @@ -591,7 +599,7 @@ void TestTyped_Create() REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(typename safearray_t::elemtype)); - REQUIRE_NOTHROW(sa.count() == SIZE); + REQUIRE_NOTHROW(sa.size() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE-1); TestLock(sa); @@ -611,13 +619,13 @@ void Test_Create_NoThrow() auto sa = wil::unique_safearray_nothrow{}; LONG val = 0; - ULONG count = 0; + ULONG size = 0; REQUIRE_SUCCEEDED(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); - REQUIRE_SUCCEEDED(sa.count(&count)); - REQUIRE(count == SIZE); + REQUIRE_SUCCEEDED(sa.size(&size)); + REQUIRE(size == SIZE); REQUIRE_SUCCEEDED(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_SUCCEEDED(sa.ubound(&val)); @@ -634,13 +642,13 @@ void Test_Create_FailFast() auto sa = wil::unique_safearray_failfast{}; LONG val = 0; - ULONG count = 0; + ULONG size = 0; REQUIRE_NOCRASH(sa.create(SIZE)); REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); - REQUIRE_NOCRASH(sa.count(&count)); - REQUIRE(count == SIZE); + REQUIRE_NOCRASH(sa.size(&size)); + REQUIRE(size == SIZE); REQUIRE_NOCRASH(sa.lbound(&val)); REQUIRE(val == 0); REQUIRE_NOCRASH(sa.ubound(&val)); @@ -661,7 +669,7 @@ void Test_Create() REQUIRE(sa); REQUIRE(sa.dims() == 1); REQUIRE(sa.elemsize() == sizeof(T)); - REQUIRE_NOTHROW(sa.count() == SIZE); + REQUIRE_NOTHROW(sa.size() == SIZE); REQUIRE_NOTHROW(sa.lbound() == 0); REQUIRE_NOTHROW(sa.ubound() == SIZE - 1); TestLock(sa); @@ -1332,6 +1340,144 @@ void Test_AccessData() #define _ACCESSDATA_FAILFAST(type) Test_AccessData_Failfast(); #define _ACCESSDATA(type) Test_AccessData(); +template +void TestTyped_Element_Special_NoThrow() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + auto sa = wil::unique_safearray_nothrow_t{}; + + REQUIRE_SUCCEEDED(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + // No Need to call GetReadable + REQUIRE_SUCCEEDED(sa.put_element(i, sample_data[i])); + } + + // Can use a single instance because the wrapper classes clean up + auto temp = data_type{}; + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + // And make sure it was the value that was set + // No Need to Call GetWritable + REQUIRE_SUCCEEDED(sa.get_element(i, temp)); + REQUIRE(PerformCompare(temp, sample_data[i])); + } +} + +template +void TestTyped_Element_Special_Failfast() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + auto sa = wil::unique_safearray_failfast_t{}; + + REQUIRE_NOCRASH(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + // No Need to call GetReadable + REQUIRE_NOCRASH(sa.put_element(i, sample_data[i])); + } + + // Can use a single instance because the wrapper classes clean up + auto temp = data_type{}; + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + // And make sure it was the value that was set + // No Need to Call GetWritable + REQUIRE_NOCRASH(sa.get_element(i, temp)); + REQUIRE(PerformCompare(temp, sample_data[i])); + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void TestTyped_Element_Special() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + auto sa = wil::unique_safearray_t{}; + + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + // No Need to call GetReadable + REQUIRE_NOTHROW(sa.put_element(i, sample_data[i])); + } + + // Can use a single instance because the wrapper classes clean up + auto temp = data_type{}; + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + // And make sure it was the value that was set + // No Need to Call GetWritable + REQUIRE_NOTHROW(sa.get_element(i, temp)); + REQUIRE(PerformCompare(temp, sample_data[i])); + } +} + +template +void TestTyped_Element_Special_Get() +{ + auto sample_data = GetSampleData(); + auto SIZE = ULONG{ sample_data.size() }; + + using array_type = decltype(sample_data); + using data_type = typename array_type::value_type; + + auto sa = wil::unique_safearray_t{}; + + REQUIRE_NOTHROW(sa.create(SIZE)); + REQUIRE(sa); + + // Loop through and set the values with put_element + for (ULONG i = 0; i < SIZE; ++i) + { + // No Need to call GetReadable + REQUIRE_NOTHROW(sa.put_element(i, sample_data[i])); + } + + // Loop through and get the values with get_element + for (ULONG i = 0; i < SIZE; ++i) + { + // And make sure it was the value that was set via the special Get function + REQUIRE(PerformCompare(sa.get_element(i), sample_data[i])); + } +} +#endif + +#define _TYPED_DIRECT_SPECIAL_NOTHROW(type) TestTyped_Element_Special_NoThrow(); +#define _TYPED_DIRECT_SPECIAL_FAILFAST(type) TestTyped_Element_Special_Failfast(); +#define _TYPED_DIRECT_SPECIAL(type) TestTyped_Element_Special(); +#define _TYPED_DIRECT_SPECIAL_GET(type) TestTyped_Element_Special_Get(); + TEST_CASE("Safearray::Create", "[safearray]") { SECTION("Create SafeArray - No Throw") @@ -1405,4 +1551,31 @@ TEST_CASE("Safearray::AccessData", "[safearray]") REQUIRE(TestComObject::ObjectCount() == 0); } +TEST_CASE("Safearray::Put/Get Specials", "[safearray]") +{ + SECTION("Special Direct Element Access - No Throw") + { + RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_NOTHROW); + //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_NOTHROW); + } + + SECTION("Special Direct Element Access - FailFast") + { + RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_FAILFAST); + //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_FAILFAST); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Special Direct Element Access - Exceptions") + { + RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL); + //RUN_TEST_SPECIAL(_DIRECT_SPECIAL); + RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_GET); + //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_GET); + } +#endif + + REQUIRE(TestComObject::ObjectCount() == 0); +} + #endif \ No newline at end of file From 051548e592719830b082cb953c8c006c58729e76 Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Tue, 1 Sep 2020 15:20:54 -0700 Subject: [PATCH 27/34] More feedback from the PR --- tests/SafearrayTests.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 23db7264..5be4f5fc 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1556,22 +1556,18 @@ TEST_CASE("Safearray::Put/Get Specials", "[safearray]") SECTION("Special Direct Element Access - No Throw") { RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_NOTHROW); - //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_NOTHROW); } SECTION("Special Direct Element Access - FailFast") { RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_FAILFAST); - //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_FAILFAST); } #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Special Direct Element Access - Exceptions") { RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL); - //RUN_TEST_SPECIAL(_DIRECT_SPECIAL); RUN_TEST_SPECIAL(_TYPED_DIRECT_SPECIAL_GET); - //RUN_TEST_SPECIAL(_DIRECT_SPECIAL_GET); } #endif From c90a90ce0447e06f1c4a6ced3df4d6dfcd476a8f Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Tue, 1 Sep 2020 15:30:00 -0700 Subject: [PATCH 28/34] Changes to accomodate clang --- include/wil/safearrays.h | 16 ++++++++-------- tests/SafearrayTests.cpp | 3 --- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index c4912480..aadfe862 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -814,34 +814,34 @@ namespace wil #endif #if defined(__WIL_COM_INCLUDED) template::value, int>::type = 0> - WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr + WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr_t { static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); - wil::com_ptr result{}; + wil::com_ptr_t result{}; get_element(index, result); return wistd::move(result); } template::value, int>::type = 0> - WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr + WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr_t { static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); - wil::com_ptr result{}; + wil::com_ptr_t result{}; get_element(index, result); return wistd::move(result); } template::value, int>::type = 0> - WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr + WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr_t { static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); - wil::com_ptr result{}; + wil::com_ptr_t result{}; get_element(indices, result); return wistd::move(result); } template::value, int>::type = 0> - WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr + WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr_t { static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); - wil::com_ptr result{}; + wil::com_ptr_t result{}; get_element(indices, result); return wistd::move(result); } diff --git a/tests/SafearrayTests.cpp b/tests/SafearrayTests.cpp index 5be4f5fc..ec3b6ecb 100644 --- a/tests/SafearrayTests.cpp +++ b/tests/SafearrayTests.cpp @@ -1449,9 +1449,6 @@ void TestTyped_Element_Special_Get() auto sample_data = GetSampleData(); auto SIZE = ULONG{ sample_data.size() }; - using array_type = decltype(sample_data); - using data_type = typename array_type::value_type; - auto sa = wil::unique_safearray_t{}; REQUIRE_NOTHROW(sa.create(SIZE)); From 3d0c2f118ec8352b79fa96ecc2a3aefc8c2f24be Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Wed, 2 Sep 2020 15:10:25 -0700 Subject: [PATCH 29/34] 1) Re-Ordered Headers so that "OleAuto.h is before "resource.h" 2) Minor improvements to some of the inline samples --- include/wil/safearrays.h | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index aadfe862..d62145de 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -15,8 +15,8 @@ #include // new(std::nothrow) #include "wistd_type_traits.h" -#include "resource.h" #include +#include "resource.h" namespace wil { @@ -170,11 +170,12 @@ namespace wil //! for(BSTR& bstr : data) //! { //! result.emplace_back(wil::make_bstr_nothrow(bstr)); + //! RETURN_HR_IF_NULL(E_OUTOFMEMORY, result.rbegin()->get()); //! } //! return S_OK; //! } //! ~~~~ - //! unique_bstr_safearray move_to_safearray(std::vector& source) + //! wil::unique_bstr_safearray move_to_safearray(std::vector& source) //! { //! auto sa = wil::unique_bstr_safearray{source.size()}; //! auto current = std::begin(source); @@ -191,6 +192,7 @@ namespace wil //! auto data = wil::safearraydata{psa}; //! for (auto i = 0U; i < data.size(); ++i) //! { + //! // Translate to Regular Bool and Invoke the Handler //! fnHandler(data[i] != VARIANT_FALSE, i); //! } //! } @@ -204,15 +206,15 @@ namespace wil //! } //! ~~~~ //! template - //! std::vector> extract_all_interfaces(SAFEARRAY* psa) + //! std::vector> extract_all_interfaces(SAFEARRAY* psa) //! { - //! auto result = std::vector>{}; + //! auto result = std::vector>{}; //! auto data = wil::safearraydata{psa}; //! result.reserve(data.size()); //! for(auto& p : data) // Type of p is LPUNKNOWN //! { //! // Use "tag_com_query" if you want failure instead of nullptr if INTERFACE not supported - //! result.emplace_back(p, details::tag_try_com_query); + //! result.emplace_back(p, wil::details::tag_try_com_query); //! } //! return result; //! } @@ -297,11 +299,11 @@ namespace wil //! // Return a SAFEARRAY from an API //! HRESULT GetWonderfulData(SAFEARRAY** ppsa) //! { - //! wil::unique_bstr_safearray_nothrow sa{}; + //! wil::unique_bstr_safearray_nothrow sa{}; //! RETURN_IF_FAILED(sa.create(32)); //! { //! wil::safearraydata_nothrow data{}; - //! RETURN_IF_FAILED(data.access(sa.get()); + //! RETURN_IF_FAILED(data.access(sa.get())); //! for(auto& bstr : data) //! { //! // SAFEARRAY will own this string and clean it up @@ -757,16 +759,16 @@ namespace wil //! Returns a safearraydata_t that provides direct access to this SAFEARRAY's contents. See @ref safearraydata_t. //! ~~~~ //! // Create a new safearray by copying a vector of wil::unique_bstr - //! unique_bstr_safearray copy_to_safearray(std::vector& source) + //! wil::unique_bstr_safearray copy_to_safearray(std::vector& source) //! { //! auto sa = wil::unique_bstr_safearray{source.size()}; //! auto current = std::begin(source); //! for(BSTR& bstr : sa.access_data()) //! { //! // Create a copy for the safearray to own - //! bstr = ::SysAllocString((*current++).get()); + //! bstr = ::SysAllocString((current++)->get()); //! } - //! return std::move(sa); + //! return sa; //! } //! ~~~~ template::value, int>::type = 0> @@ -777,6 +779,25 @@ namespace wil } //! Special Get Functions + //! ~~~~ + //! // Create a new safearray by copying a vector of wil::unique_bstr + //! std::vector copy_safearray_to_vector_and_destroy(SAFEARRAY*& psa) + //! { + //! auto sa = wil::unique_bstr_safearray{}; + //! sa.reset(psa); // The SAFEARRAY is now owned by the instance of unique_safearray + //! psa = nullptr; // Signal to the caller the SA was cleaned up + //! + //! THROW_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); + //! + //! auto result = std::vector{}; + //! result.reserve(sa.size()); + //! for(auto i = 0; i < sa.size(); ++i) + //! { + //! result.emplace_back(sa.get_element(i)); + //! } + //! return result; + //! } + //! ~~~~ #if defined(__WIL_OLEAUTO_H_) template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG index) -> wil::unique_bstr @@ -812,6 +833,7 @@ namespace wil return result; } #endif + #if defined(__WIL_COM_INCLUDED) template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr_t From fc9e81de950bc83e6b21ff8b691f45188f68116e Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Tue, 8 Sep 2020 13:18:30 -0700 Subject: [PATCH 30/34] Remove some moves that aren't necessary and may inhibit the optimizer --- include/wil/safearrays.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index d62145de..45983373 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -805,7 +805,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::unique_bstr result{}; get_element(index, result); - return wistd::move(result); + return result; } template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG index) -> wil::unique_variant @@ -822,7 +822,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::unique_bstr result{}; get_element(indices, result); - return wistd::move(result); + return result; } template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG* indices) -> wil::unique_variant @@ -841,7 +841,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::com_ptr_t result{}; get_element(index, result); - return wistd::move(result); + return result; } template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG index) -> wil::com_ptr_t @@ -849,7 +849,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::com_ptr_t result{}; get_element(index, result); - return wistd::move(result); + return result; } template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr_t @@ -857,7 +857,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::com_ptr_t result{}; get_element(indices, result); - return wistd::move(result); + return result; } template::value, int>::type = 0> WI_NODISCARD auto get_element(LONG* indices) -> wil::com_ptr_t @@ -865,7 +865,7 @@ namespace wil static_assert(wistd::is_same::value, "this method requires exceptions or failfast"); wil::com_ptr_t result{}; get_element(indices, result); - return wistd::move(result); + return result; } #endif //! @} From cea4e55bcfb52d614167701b273eb1c901d5564a Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Mon, 14 Sep 2020 10:18:47 -0700 Subject: [PATCH 31/34] Correct Sample Code Co-authored-by: Chris Guzak --- include/wil/safearrays.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 45983373..5185b6bb 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -167,7 +167,7 @@ namespace wil //! auto data = wil::safearraydata_nothrow{}; //! RETURN_IF_FAILED(data.access(psa)); //! result.reserve(result.size() + data.size()); - //! for(BSTR& bstr : data) + //! for (BSTR& bstr : data) //! { //! result.emplace_back(wil::make_bstr_nothrow(bstr)); //! RETURN_HR_IF_NULL(E_OUTOFMEMORY, result.rbegin()->get()); @@ -1103,4 +1103,3 @@ namespace wil #endif // __WIL_SAFEARRAY_SHARED_STL } // namespace wil - From 4a75dafcc4f3e6efe42221cb38e4d8cbc1531681 Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Mon, 14 Sep 2020 10:18:47 -0700 Subject: [PATCH 32/34] Correct Sample Code Co-authored-by: Chris Guzak --- include/wil/safearrays.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 45983373..5e78e972 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -167,7 +167,7 @@ namespace wil //! auto data = wil::safearraydata_nothrow{}; //! RETURN_IF_FAILED(data.access(psa)); //! result.reserve(result.size() + data.size()); - //! for(BSTR& bstr : data) + //! for (BSTR& bstr : data) //! { //! result.emplace_back(wil::make_bstr_nothrow(bstr)); //! RETURN_HR_IF_NULL(E_OUTOFMEMORY, result.rbegin()->get()); @@ -179,7 +179,7 @@ namespace wil //! { //! auto sa = wil::unique_bstr_safearray{source.size()}; //! auto current = std::begin(source); - //! for(BSTR& bstr : sa.access_data()) + //! for (BSTR& bstr : sa.access_data()) //! { //! // Transfer ownership of the BSTR into the safearray //! bstr = (*current++).release(); @@ -211,7 +211,7 @@ namespace wil //! auto result = std::vector>{}; //! auto data = wil::safearraydata{psa}; //! result.reserve(data.size()); - //! for(auto& p : data) // Type of p is LPUNKNOWN + //! for (auto& p : data) // Type of p is LPUNKNOWN //! { //! // Use "tag_com_query" if you want failure instead of nullptr if INTERFACE not supported //! result.emplace_back(p, wil::details::tag_try_com_query); @@ -304,7 +304,7 @@ namespace wil //! { //! wil::safearraydata_nothrow data{}; //! RETURN_IF_FAILED(data.access(sa.get())); - //! for(auto& bstr : data) + //! for (auto& bstr : data) //! { //! // SAFEARRAY will own this string and clean it up //! bstr = ::SysAllocString(L"Wonderful!"); @@ -325,7 +325,7 @@ namespace wil //! RETURN_IF_FAILED(::GetWonderfulData(sa.put())); //! // Verify the output is expected //! RETURN_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); - //! for(auto& bstr : sa.access_data()) + //! for (auto& bstr : sa.access_data()) //! { //! // Use the BSTR, no clean up necessary //! DoSomethingWithBSTR(bstr); @@ -529,7 +529,7 @@ namespace wil //! RETURN_HR_IF(E_UNEXPECTED, sa.vartype() != VT_BSTR); //! RETURN_HR_IF(E_UNEXPECTED, sa.dims() != 1); //! RETURN_IF_FAILED(sa.size(&size)); - //! for(auto i = 0U; i < size; ++i) + //! for (auto i = 0U; i < size; ++i) //! { //! // Copy from the safearray to the unique_bstr //! sa.get_element(i, bstr.put()); // Will release current BSTR @@ -763,7 +763,7 @@ namespace wil //! { //! auto sa = wil::unique_bstr_safearray{source.size()}; //! auto current = std::begin(source); - //! for(BSTR& bstr : sa.access_data()) + //! for (BSTR& bstr : sa.access_data()) //! { //! // Create a copy for the safearray to own //! bstr = ::SysAllocString((current++)->get()); @@ -791,7 +791,7 @@ namespace wil //! //! auto result = std::vector{}; //! result.reserve(sa.size()); - //! for(auto i = 0; i < sa.size(); ++i) + //! for (auto i = 0; i < sa.size(); ++i) //! { //! result.emplace_back(sa.get_element(i)); //! } @@ -1103,4 +1103,3 @@ namespace wil #endif // __WIL_SAFEARRAY_SHARED_STL } // namespace wil - From 326e7a1f6f23e4e938d67d448a95fced7745a514 Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Sat, 2 Mar 2024 18:04:53 -0800 Subject: [PATCH 33/34] Fix comment Co-authored-by: Duncan Horn <40036384+dunhor@users.noreply.github.com> --- include/wil/safearrays.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 5e78e972..8a780860 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -157,7 +157,7 @@ namespace wil //! Class that facilitates the direct access to the contents of a SAFEARRAY and "unaccessing" it when done //! It enables treating the safearray like an regular array and/or performing a ranged-for on the contents. - //! Accessing a safearray increases it's lock count, so attempts to destroy the safearray will fail until it is unaccessed. + //! Accessing a safearray increases its lock count, so attempts to destroy the safearray will fail until it is unaccessed. //! This class works even if the SAFEARRAY is multi-dimensional by treating it as a large single-dimension array, //! but doesn't support access via a multi-dimensional index. //! NOTE: This class does not manage the lifetime of the SAFEARRAY itself. See @ref safearray_t. From 48cd647ac62099809007a8b5fd1c9d5fe79705f6 Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Fri, 16 Aug 2024 12:46:09 -0700 Subject: [PATCH 34/34] Update include/wil/safearrays.h Co-authored-by: Duncan Horn <40036384+dunhor@users.noreply.github.com> --- include/wil/safearrays.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/wil/safearrays.h b/include/wil/safearrays.h index 8a780860..544876e6 100644 --- a/include/wil/safearrays.h +++ b/include/wil/safearrays.h @@ -243,11 +243,6 @@ namespace wil access(psa); } safearraydata_t() = default; - safearraydata_t(safearraydata_t&&) = default; - ~safearraydata_t() = default; - safearraydata_t& operator=(safearraydata_t&&) = default; - safearraydata_t(const safearraydata_t&) = delete; - safearraydata_t& operator=(const safearraydata_t&) = delete; result access(pointer psa) {