зеркало из https://github.com/microsoft/mso.git
Formatted code with ClangFormat (#23)
* Formatted code with ClangFormat * Change files
This commit is contained in:
Родитель
68f50343a4
Коммит
67119c9c18
|
@ -0,0 +1,111 @@
|
|||
#
|
||||
# Clang formatting rules: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
# The clang-format npm package (https://github.com/angular/clang-format) uses
|
||||
# a pre-built clang-format.exe from http://llvm.org/builds/
|
||||
#
|
||||
---
|
||||
# We use defaults from the Microsoft style
|
||||
BasedOnStyle: Microsoft
|
||||
---
|
||||
Language: Cpp
|
||||
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterExternBlock: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakInheritanceList: BeforeComma
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces : true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, TEST_CLASS, TEST_CLASS_EX ]
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: 'pch.h'
|
||||
Priority: -1
|
||||
- Regex: '.*\.g\..*'
|
||||
Priority: 1
|
||||
- Regex: '^<.*\.h(pp)?>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 4
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Latest
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
...
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "Formatted code with ClangFormat",
|
||||
"packageName": "@microsoft/mso",
|
||||
"email": "vmorozov@microsoft.com",
|
||||
"dependentChangeType": "patch",
|
||||
"date": "2020-04-01T14:47:10.009Z"
|
||||
}
|
|
@ -14,53 +14,59 @@
|
|||
#include <comBaseApi.h>
|
||||
#include <unknwn.h>
|
||||
|
||||
namespace Mso {
|
||||
namespace ComUtil {
|
||||
namespace Mso { namespace ComUtil {
|
||||
|
||||
template<typename T>
|
||||
HRESULT HrCoCreateInstance(Mso::TCntPtr<T>& target, REFCLSID rclsid, _In_opt_ LPUNKNOWN pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept
|
||||
template <typename T>
|
||||
HRESULT HrCoCreateInstance(
|
||||
Mso::TCntPtr<T>& target,
|
||||
REFCLSID rclsid,
|
||||
_In_opt_ LPUNKNOWN pUnkOuter = nullptr,
|
||||
DWORD dwClsContext = CLSCTX_ALL) noexcept
|
||||
{
|
||||
return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, __uuidof(T), reinterpret_cast<void**>(target.GetAddressOf()));
|
||||
return ::CoCreateInstance(
|
||||
rclsid, pUnkOuter, dwClsContext, __uuidof(T), reinterpret_cast<void**>(target.GetAddressOf()));
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
template<typename T>
|
||||
HRESULT HrCoCreateInstance(Mso::TCntPtr<T>& target, _In_z_ LPCOLESTR szProgID, _In_opt_ LPUNKNOWN pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept
|
||||
template <typename T>
|
||||
HRESULT HrCoCreateInstance(
|
||||
Mso::TCntPtr<T>& target,
|
||||
_In_z_ LPCOLESTR szProgID,
|
||||
_In_opt_ LPUNKNOWN pUnkOuter = nullptr,
|
||||
DWORD dwClsContext = CLSCTX_ALL) noexcept
|
||||
{
|
||||
CLSID clsid;
|
||||
HRESULT hr = ::CLSIDFromProgID(szProgID, &clsid);
|
||||
return SUCCEEDED(hr) ? HrCoCreateInstance(target, clsid, pUnkOuter, dwClsContext) : hr;
|
||||
CLSID clsid;
|
||||
HRESULT hr = ::CLSIDFromProgID(szProgID, &clsid);
|
||||
return SUCCEEDED(hr) ? HrCoCreateInstance(target, clsid, pUnkOuter, dwClsContext) : hr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // ComUtil
|
||||
} // Mso
|
||||
}} // namespace Mso::ComUtil
|
||||
|
||||
namespace Mso {
|
||||
|
||||
template <typename T1, typename T2>
|
||||
__declspec(deprecated("Use Mso::ComUtil::AreEqualObjects")) bool AreEqualObjects(const T1* pLeft, const T2* pRight) noexcept
|
||||
__declspec(
|
||||
deprecated("Use Mso::ComUtil::AreEqualObjects")) bool AreEqualObjects(const T1* pLeft, const T2* pRight) noexcept
|
||||
{
|
||||
return Mso::ComUtil::AreEqualObjects(pLeft, pRight);
|
||||
return Mso::ComUtil::AreEqualObjects(pLeft, pRight);
|
||||
}
|
||||
|
||||
} // Mso
|
||||
} // namespace Mso
|
||||
|
||||
#endif // MS_TARGET_WINDOWS
|
||||
|
||||
namespace Mso {
|
||||
namespace Details {
|
||||
namespace Mso { namespace Details {
|
||||
|
||||
// Overloaded global function to provide to IID_PPV_ARGS that support Details::TCntPtrRef
|
||||
template <typename T>
|
||||
void** IID_PPV_ARGS_Helper(_Inout_ TCntPtrRef<T> pp) noexcept
|
||||
{
|
||||
static_assert(std::is_base_of<IUnknown, T>::value, "T has to derive from IUnknown");
|
||||
return pp;
|
||||
static_assert(std::is_base_of<IUnknown, T>::value, "T has to derive from IUnknown");
|
||||
return pp;
|
||||
}
|
||||
|
||||
} // Details
|
||||
} // Mso
|
||||
}} // namespace Mso::Details
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // LIBLET_COMUTIL_QICAST_H
|
||||
|
|
|
@ -30,188 +30,199 @@
|
|||
//============================================================================
|
||||
|
||||
/**
|
||||
qi_cast<Type>(source, optional riid)
|
||||
qi_cast<Type>(source, optional riid)
|
||||
|
||||
Primary version of the template. Generally speaking, it is preferable to
|
||||
add a MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") declaration to the class/interface, or use
|
||||
MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") if type is already defined. Otherwise it can be
|
||||
explicitly passed as a 2nd parameter.
|
||||
Primary version of the template. Generally speaking, it is preferable to
|
||||
add a MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") declaration to the class/interface, or use
|
||||
MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") if type is already defined. Otherwise it can be
|
||||
explicitly passed as a 2nd parameter.
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
Mso::TCntPtr< TTarget > qi_cast( TSource& piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
Mso::TCntPtr<TTarget> qi_cast(TSource& piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
Mso::TCntPtr< TTarget > spTarget;
|
||||
if( piSource != nullptr )
|
||||
{
|
||||
if( FAILED ( piSource->QueryInterface( riid, &spTarget ) ) )
|
||||
spTarget.Empty();
|
||||
}
|
||||
return spTarget;
|
||||
Mso::TCntPtr<TTarget> spTarget;
|
||||
if (piSource != nullptr)
|
||||
{
|
||||
if (FAILED(piSource->QueryInterface(riid, &spTarget)))
|
||||
spTarget.Empty();
|
||||
}
|
||||
return spTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
qi_cast<Type>(const source, optional riid)
|
||||
qi_cast<Type>(const source, optional riid)
|
||||
|
||||
Shorthand for classes and interfaces with an MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") attribute
|
||||
or a MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") assignment.
|
||||
TSource does not include the pointer type, which allows the compiler
|
||||
to disambiguate against the primary function.
|
||||
Shorthand for classes and interfaces with an MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") attribute
|
||||
or a MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") assignment.
|
||||
TSource does not include the pointer type, which allows the compiler
|
||||
to disambiguate against the primary function.
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
Mso::TCntPtr< TTarget > qi_cast( const TSource *piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
Mso::TCntPtr<TTarget> qi_cast(const TSource* piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
TSource *piSourceNonConst = const_cast< TSource* >( piSource );
|
||||
return qi_cast< TTarget, TSource* >( piSourceNonConst, riid );
|
||||
TSource* piSourceNonConst = const_cast<TSource*>(piSource);
|
||||
return qi_cast<TTarget, TSource*>(piSourceNonConst, riid);
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4995) // VerifyElseCrashSz gives "warning C4995: 'IsDebuggerPresent': name was marked as #pragma deprecated"
|
||||
#pragma warning(disable : 4995) // VerifyElseCrashSz gives "warning C4995: 'IsDebuggerPresent': name was marked as
|
||||
// #pragma deprecated"
|
||||
|
||||
/**
|
||||
qi_cast_or_crash<Type>(source, optional riid)
|
||||
qi_cast_or_crash<Type>(source, optional riid)
|
||||
|
||||
Calls qi_cast and crashes if it fails.
|
||||
This is useful when the qi_cast must succeed or otherwise it is a bug.
|
||||
Calls qi_cast and crashes if it fails.
|
||||
This is useful when the qi_cast must succeed or otherwise it is a bug.
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
Mso::TCntPtr< TTarget > qi_cast_or_crash( TSource& piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
Mso::TCntPtr<TTarget> qi_cast_or_crash(TSource& piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
Mso::TCntPtr< TTarget > target = qi_cast< TTarget >( piSource, riid );
|
||||
VerifyElseCrashSzTag( target, "Query interface failed." , 0x022054c3 /* tag_ciftd */);
|
||||
return target;
|
||||
Mso::TCntPtr<TTarget> target = qi_cast<TTarget>(piSource, riid);
|
||||
VerifyElseCrashSzTag(target, "Query interface failed.", 0x022054c3 /* tag_ciftd */);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
qi_cast_or_crash<Type>(const source, optional riid)
|
||||
qi_cast_or_crash<Type>(const source, optional riid)
|
||||
|
||||
Calls qi_cast and crashes if it fails.
|
||||
This is useful when the qi_cast must succeed or otherwise it is a bug.
|
||||
Calls qi_cast and crashes if it fails.
|
||||
This is useful when the qi_cast must succeed or otherwise it is a bug.
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
Mso::TCntPtr< TTarget > qi_cast_or_crash( const TSource *piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
Mso::TCntPtr<TTarget> qi_cast_or_crash(const TSource* piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
Mso::TCntPtr< TTarget > target = qi_cast< TTarget >( piSource, riid );
|
||||
VerifyElseCrashSzTag( target, "Query interface failed." , 0x022054c4 /* tag_cifte */);
|
||||
return target;
|
||||
Mso::TCntPtr<TTarget> target = qi_cast<TTarget>(piSource, riid);
|
||||
VerifyElseCrashSzTag(target, "Query interface failed.", 0x022054c4 /* tag_cifte */);
|
||||
return target;
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
/**
|
||||
simpleqi_cast<Type>(source, optional riid)
|
||||
simpleqi_cast<Type>(source, optional riid)
|
||||
|
||||
Primary version of the template. Generally speaking, it is preferable to
|
||||
add a MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") declaration to the class/interface, or use
|
||||
MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") if type is already defined. Otherwise it can be
|
||||
explicitly passed as a 2nd parameter.
|
||||
Primary version of the template. Generally speaking, it is preferable to
|
||||
add a MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") declaration to the class/interface, or use
|
||||
MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") if type is already defined. Otherwise it can be
|
||||
explicitly passed as a 2nd parameter.
|
||||
|
||||
Used for Simple interfaces that do not AddRef
|
||||
Used for Simple interfaces that do not AddRef
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
TTarget* simpleqi_cast( TSource& piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
TTarget* simpleqi_cast(TSource& piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
TTarget* pTarget = nullptr;
|
||||
if( piSource != nullptr )
|
||||
{
|
||||
if( FAILED( piSource->QueryInterface( riid, reinterpret_cast< void** >( &pTarget ) ) ) )
|
||||
pTarget = nullptr;
|
||||
}
|
||||
return pTarget;
|
||||
TTarget* pTarget = nullptr;
|
||||
if (piSource != nullptr)
|
||||
{
|
||||
if (FAILED(piSource->QueryInterface(riid, reinterpret_cast<void**>(&pTarget))))
|
||||
pTarget = nullptr;
|
||||
}
|
||||
return pTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
simpleqi_cast<Type>(const source, optional riid)
|
||||
simpleqi_cast<Type>(const source, optional riid)
|
||||
|
||||
Shorthand for classes and interfaces with an MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") attribute
|
||||
or a MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") assignment.
|
||||
TSource does not include the pointer type, which allows the compiler
|
||||
to disambiguate against the primary function.
|
||||
Shorthand for classes and interfaces with an MSO_CLASS_GUID(T, "") / MSO_STRUCT_GUID(T, "") attribute
|
||||
or a MSO_ASSIGN_CLASS_GUID(T, "") / MSO_ASSIGN_STRUCT_GUID(T, "") assignment.
|
||||
TSource does not include the pointer type, which allows the compiler
|
||||
to disambiguate against the primary function.
|
||||
*/
|
||||
template< typename TTarget, typename TSource >
|
||||
TTarget* simpleqi_cast( const TSource *piSource, const IID &riid = __uuidof( TTarget ) ) noexcept
|
||||
template <typename TTarget, typename TSource>
|
||||
TTarget* simpleqi_cast(const TSource* piSource, const IID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
TSource *piSourceNonConst = const_cast< TSource* >( piSource );
|
||||
return simpleqi_cast< TTarget, TSource *>( piSourceNonConst, riid );
|
||||
TSource* piSourceNonConst = const_cast<TSource*>(piSource);
|
||||
return simpleqi_cast<TTarget, TSource*>(piSourceNonConst, riid);
|
||||
}
|
||||
|
||||
namespace Mso {
|
||||
namespace ComUtil {
|
||||
namespace Mso { namespace ComUtil {
|
||||
|
||||
template <typename T, typename TOther>
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const TOther* pOther, const IID &riid = __uuidof(T)) noexcept
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const TOther* pOther, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
if (pOther == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<TOther*>(pOther)->QueryInterface(riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
}
|
||||
|
||||
template<typename T, typename TOther>
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const TOther& other, const IID &riid = __uuidof(T)) noexcept
|
||||
{
|
||||
if (other == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<TOther&>(other)->QueryInterface(riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
if (pOther == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<TOther*>(pOther)->QueryInterface(riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
}
|
||||
|
||||
template <typename T, typename TOther>
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const Mso::TCntPtr<TOther>& other, const IID &riid = __uuidof(T)) noexcept
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const TOther& other, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
if (other == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<Mso::TCntPtr<TOther>&>(other)->QueryInterface(riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
if (other == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<TOther&>(other)->QueryInterface(riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
}
|
||||
|
||||
template <typename T, typename TOther>
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const TOther* pOther, const IID &riid = __uuidof(T)) noexcept
|
||||
HRESULT HrQueryFrom(Mso::TCntPtr<T>& target, const Mso::TCntPtr<TOther>& other, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
return SUCCEEDED(HrQueryFrom(target, pOther, riid));
|
||||
if (other == nullptr)
|
||||
return E_POINTER;
|
||||
return const_cast<Mso::TCntPtr<TOther>&>(other)->QueryInterface(
|
||||
riid, reinterpret_cast<void**>(target.ClearAndGetAddressOf()));
|
||||
}
|
||||
|
||||
template <typename T, typename TOther>
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const TOther& other, const IID &riid = __uuidof(T)) noexcept
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const TOther* pOther, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
return SUCCEEDED(HrQueryFrom(target, other, riid));
|
||||
return SUCCEEDED(HrQueryFrom(target, pOther, riid));
|
||||
}
|
||||
|
||||
template<typename T, typename TOther>
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const Mso::TCntPtr<TOther>& other, const IID &riid = __uuidof(T)) noexcept
|
||||
template <typename T, typename TOther>
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const TOther& other, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
return SUCCEEDED(HrQueryFrom(target, other, riid));
|
||||
return SUCCEEDED(HrQueryFrom(target, other, riid));
|
||||
}
|
||||
|
||||
template <typename T, typename TOther>
|
||||
bool FQueryFrom(Mso::TCntPtr<T>& target, const Mso::TCntPtr<TOther>& other, const IID& riid = __uuidof(T)) noexcept
|
||||
{
|
||||
return SUCCEEDED(HrQueryFrom(target, other, riid));
|
||||
}
|
||||
|
||||
/// Checks if two IUnknown objects are equal.
|
||||
template <typename T1, typename T2>
|
||||
bool AreEqualObjects(const T1* pLeft, const T2* pRight) noexcept
|
||||
{
|
||||
if (reinterpret_cast<const void*>(pLeft) == reinterpret_cast<const void*>(pRight))
|
||||
return true; // Both pointers are the same
|
||||
if (pLeft == nullptr || pRight == nullptr)
|
||||
return false; // One is null the other is not
|
||||
if (reinterpret_cast<const void*>(pLeft) == reinterpret_cast<const void*>(pRight))
|
||||
return true; // Both pointers are the same
|
||||
if (pLeft == nullptr || pRight == nullptr)
|
||||
return false; // One is null the other is not
|
||||
|
||||
// Compare IUnknown pointers.
|
||||
auto punk1 = qi_cast<IUnknown>(pLeft);
|
||||
if (!punk1)
|
||||
return false;
|
||||
// Compare IUnknown pointers.
|
||||
auto punk1 = qi_cast<IUnknown>(pLeft);
|
||||
if (!punk1)
|
||||
return false;
|
||||
|
||||
auto punk2 = qi_cast<IUnknown>(pRight);
|
||||
if (!punk2)
|
||||
return false;
|
||||
auto punk2 = qi_cast<IUnknown>(pRight);
|
||||
if (!punk2)
|
||||
return false;
|
||||
|
||||
IUnknown* pComp1 = Details::TCntPtrAddRefStrategyImpl<Details::AddRefStrategyForType<T1>::TAddRefStrategy::Strategy>::GetIUnknownForObjectCompare(punk1.Get());
|
||||
IUnknown* pComp2 = Details::TCntPtrAddRefStrategyImpl<Details::AddRefStrategyForType<T2>::TAddRefStrategy::Strategy>::GetIUnknownForObjectCompare(punk2.Get());
|
||||
return (pComp1 == pComp2);
|
||||
IUnknown* pComp1 = Details::TCntPtrAddRefStrategyImpl<
|
||||
Details::AddRefStrategyForType<T1>::TAddRefStrategy::Strategy>::GetIUnknownForObjectCompare(punk1.Get());
|
||||
IUnknown* pComp2 = Details::TCntPtrAddRefStrategyImpl<
|
||||
Details::AddRefStrategyForType<T2>::TAddRefStrategy::Strategy>::GetIUnknownForObjectCompare(punk2.Get());
|
||||
return (pComp1 == pComp2);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool AreEqualObjects(const T1* pLeft, const Mso::TCntPtr<T2>& right) noexcept { return Mso::ComUtil::AreEqualObjects(pLeft, right.Get()); }
|
||||
bool AreEqualObjects(const T1* pLeft, const Mso::TCntPtr<T2>& right) noexcept
|
||||
{
|
||||
return Mso::ComUtil::AreEqualObjects(pLeft, right.Get());
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool AreEqualObjects(const Mso::TCntPtr<T1>& left, const T2* pRight) noexcept { return Mso::ComUtil::AreEqualObjects(left.Get(), pRight); }
|
||||
bool AreEqualObjects(const Mso::TCntPtr<T1>& left, const T2* pRight) noexcept
|
||||
{
|
||||
return Mso::ComUtil::AreEqualObjects(left.Get(), pRight);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool AreEqualObjects(const Mso::TCntPtr<T1>& left, const Mso::TCntPtr<T2>& right) noexcept { return Mso::ComUtil::AreEqualObjects(left.Get(), right.Get()); }
|
||||
bool AreEqualObjects(const Mso::TCntPtr<T1>& left, const Mso::TCntPtr<T2>& right) noexcept
|
||||
{
|
||||
return Mso::ComUtil::AreEqualObjects(left.Get(), right.Get());
|
||||
}
|
||||
|
||||
} // ComUtil
|
||||
} // Mso
|
||||
}} // namespace Mso::ComUtil
|
||||
|
||||
#endif // LIBLET_COMUTIL_QICASTCORE_H
|
||||
|
|
|
@ -11,23 +11,22 @@
|
|||
|
||||
// Check if Microsoft's Structured Exception Handling (SEH) are supported.
|
||||
#ifndef MSO_HAS_SEH
|
||||
# if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
# define MSO_HAS_SEH 1
|
||||
# else
|
||||
# define MSO_HAS_SEH 0
|
||||
# endif
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
#define MSO_HAS_SEH 1
|
||||
#else
|
||||
#define MSO_HAS_SEH 0
|
||||
#endif
|
||||
#endif // MSO_HAS_SEH
|
||||
|
||||
|
||||
// Check if compiler supports UUID
|
||||
#ifndef COMPILER_SUPPORTS_UUID
|
||||
# if defined(__clang__) || defined(__GNUC__)
|
||||
# define COMPILER_SUPPORTS_UUID 0
|
||||
# elif defined(_MSC_VER)
|
||||
# define COMPILER_SUPPORTS_UUID 1
|
||||
# else
|
||||
# error Unsupported compiler type.
|
||||
# endif
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define COMPILER_SUPPORTS_UUID 0
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_SUPPORTS_UUID 1
|
||||
#else
|
||||
#error Unsupported compiler type.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // COMPILERADAPTERS_COMPILERFEATURES_H
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
This header contains cross-plat macros for suppressing compiler warnings
|
||||
|
||||
Alphabetically add a [BEGIN|END]_DISABLE_WARNING_* pair of macros for the compiler warning.
|
||||
|
||||
clang-only: use BEGIN_DISABLE_COMPILER_WARNING_CLANG with the -W argument
|
||||
msvc-only: use BEGIN_DISABLE_COMPILER_WARNING_MSVC with the warning number
|
||||
both: use BEGIN_DISABLE_COMPILER_WARNING with both values
|
||||
|
||||
FUTURE: auto-generate this from shared file
|
||||
This header contains cross-plat macros for suppressing compiler warnings
|
||||
|
||||
Alphabetically add a [BEGIN|END]_DISABLE_WARNING_* pair of macros for the compiler warning.
|
||||
|
||||
clang-only: use BEGIN_DISABLE_COMPILER_WARNING_CLANG with the -W argument
|
||||
msvc-only: use BEGIN_DISABLE_COMPILER_WARNING_MSVC with the warning number
|
||||
both: use BEGIN_DISABLE_COMPILER_WARNING with both values
|
||||
|
||||
FUTURE: auto-generate this from shared file
|
||||
*/
|
||||
#ifndef LIBLET_COMPILERADAPTERS_COMPILERWARNINGS_H
|
||||
#define LIBLET_COMPILERADAPTERS_COMPILERWARNINGS_H
|
||||
|
@ -25,7 +25,8 @@
|
|||
#define BEGIN_DISABLE_WARNING_HEADER_HYGIENE_VARIABLE() BEGIN_DISABLE_COMPILER_WARNING_CLANG("-Wheader-hygiene")
|
||||
#define END_DISABLE_WARNING_HEADER_HYGIENE_VARIABLE() END_DISABLE_COMPILER_WARNING()
|
||||
|
||||
#define BEGIN_DISABLE_WARNING_INCONSISTENT_MISSING_OVERRIDE() BEGIN_DISABLE_COMPILER_WARNING_CLANG("-Winconsistent-missing-override")
|
||||
#define BEGIN_DISABLE_WARNING_INCONSISTENT_MISSING_OVERRIDE() \
|
||||
BEGIN_DISABLE_COMPILER_WARNING_CLANG("-Winconsistent-missing-override")
|
||||
#define END_DISABLE_WARNING_INCONSISTENT_MISSING_OVERRIDE() END_DISABLE_COMPILER_WARNING()
|
||||
|
||||
#define BEGIN_DISABLE_WARNING_UNUSED_CONST_VARIABLE() BEGIN_DISABLE_COMPILER_WARNING_CLANG("-Wunused-const-variable")
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Implementation details for suppressing compiler warnings in a cross-plat manner.
|
||||
*/
|
||||
Implementation details for suppressing compiler warnings in a cross-plat manner.
|
||||
*/
|
||||
#ifndef LIBLET_COMPILERADAPTERS_COMPILERWARNINGS_IMPL_H
|
||||
#define LIBLET_COMPILERADAPTERS_COMPILERWARNINGS_IMPL_H
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
|||
#define COMPILER_WARNING_POP() __pragma(clang diagnostic pop)
|
||||
|
||||
// See https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html
|
||||
#elif defined (__GNUC__)
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
//TODO: Implement GCC-specific warnings.
|
||||
// TODO: Implement GCC-specific warnings.
|
||||
#define COMPILER_WARNING_PUSH()
|
||||
#define COMPILER_WARNING_DISABLE_ALL(msvcNum, gccWarn)
|
||||
#define COMPILER_WARNING_DISABLE_MSVC(msvcNum)
|
||||
|
@ -29,23 +29,23 @@
|
|||
|
||||
#define COMPILER_WARNING_PUSH() __pragma(warning(push))
|
||||
#define COMPILER_WARNING_DISABLE_ALL(msvcNum, clangWarn) COMPILER_WARNING_DISABLE_MSVC(msvcNum)
|
||||
#define COMPILER_WARNING_DISABLE_MSVC(msvcNum) __pragma(warning(disable:##msvcNum))
|
||||
#define COMPILER_WARNING_DISABLE_MSVC(msvcNum) __pragma(warning(disable :##msvcNum))
|
||||
#define COMPILER_WARNING_DISABLE_CLANG(clangWarn)
|
||||
#define COMPILER_WARNING_POP() __pragma(warning(pop))
|
||||
|
||||
#endif
|
||||
|
||||
#define BEGIN_DISABLE_COMPILER_WARNING_ALL(msvcNum, clangWarn) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_ALL(msvcNum, clangWarn) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_ALL(msvcNum, clangWarn)
|
||||
|
||||
#define BEGIN_DISABLE_COMPILER_WARNING_MSVC(msvcNum) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_MSVC(msvcNum) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_MSVC(msvcNum)
|
||||
|
||||
#define BEGIN_DISABLE_COMPILER_WARNING_CLANG(clangWarn) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_CLANG(clangWarn) \
|
||||
COMPILER_WARNING_PUSH() \
|
||||
COMPILER_WARNING_DISABLE_CLANG(clangWarn)
|
||||
|
||||
#define END_DISABLE_COMPILER_WARNING() COMPILER_WARNING_POP()
|
||||
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Various low level macros that are globally useful
|
||||
Various low level macros that are globally useful
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_CPPMACROS_H
|
||||
#define COMPILERADAPTERS_CPPMACROS_H
|
||||
|
||||
/**
|
||||
Broadly used macros
|
||||
Broadly used macros
|
||||
*/
|
||||
#ifndef FImplies
|
||||
#define FImplies(a, b) (!(a) || (b))
|
||||
#define FImplies(a, b) (!(a) || (b))
|
||||
#endif
|
||||
|
||||
#ifndef FBiImplies
|
||||
#define FBiImplies(a, b) (!(a) == !(b))
|
||||
#define FBiImplies(a, b) (!(a) == !(b))
|
||||
#endif
|
||||
|
||||
#ifndef RgC
|
||||
|
@ -24,75 +24,94 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
Statement - used for control flow macros like Check, IfFailGo
|
||||
Make the macro act like a statement.
|
||||
Statement - used for control flow macros like Check, IfFailGo
|
||||
Make the macro act like a statement.
|
||||
*/
|
||||
#ifndef __GNUC__
|
||||
#define Statement(x) __pragma(warning(push)) __pragma(warning(disable:4127 25037)) do { x; } while(0) __pragma(warning(pop))
|
||||
#define Statement(x) \
|
||||
__pragma(warning(push)) __pragma(warning(disable : 4127 25037)) do \
|
||||
{ \
|
||||
x; \
|
||||
} \
|
||||
while (0) \
|
||||
__pragma(warning(pop))
|
||||
#else
|
||||
#define Statement(x) do { x; } while(0)
|
||||
// error: there are no arguments to '__noop' that depend on a template parameter, so a declaration of '__noop' must be available [-fpermissive]
|
||||
#define Statement(x) \
|
||||
do \
|
||||
{ \
|
||||
x; \
|
||||
} while (0)
|
||||
// error: there are no arguments to '__noop' that depend on a template parameter, so a declaration of '__noop' must be
|
||||
// available [-fpermissive]
|
||||
#define __noop()
|
||||
#endif
|
||||
/**
|
||||
Prevent the compiler from automatically providing implementations of various
|
||||
class features. Use the macro in your class public: section.
|
||||
TODO: probably need to update with new Move functions
|
||||
Prevent the compiler from automatically providing implementations of various
|
||||
class features. Use the macro in your class public: section.
|
||||
TODO: probably need to update with new Move functions
|
||||
*/
|
||||
#ifndef __GNUC__
|
||||
#define MSO_NO_COPYCONSTR( C ) __pragma(warning(suppress:4822)) C(const C&) = delete
|
||||
#define MSO_NO_ASSIGNMENT( C ) __pragma(warning(suppress:4822)) const C& operator=(const C&) = delete
|
||||
#define MSO_NO_DEFAULTCONSTR( C ) __pragma(warning(suppress:4822)) explicit C() = delete
|
||||
#define MSO_NO_COPYCONSTR(C) __pragma(warning(suppress : 4822)) C(const C &) = delete
|
||||
#define MSO_NO_ASSIGNMENT(C) __pragma(warning(suppress : 4822)) const C &operator=(const C &) = delete
|
||||
#define MSO_NO_DEFAULTCONSTR(C) __pragma(warning(suppress : 4822)) explicit C() = delete
|
||||
#else
|
||||
#define MSO_NO_COPYCONSTR( C ) C(const C&) = delete
|
||||
#define MSO_NO_ASSIGNMENT( C ) const C& operator=(const C&) = delete
|
||||
#define MSO_NO_DEFAULTCONSTR( C ) explicit C() = delete
|
||||
#define MSO_NO_COPYCONSTR(C) C(const C &) = delete
|
||||
#define MSO_NO_ASSIGNMENT(C) const C &operator=(const C &) = delete
|
||||
#define MSO_NO_DEFAULTCONSTR(C) explicit C() = delete
|
||||
#endif
|
||||
|
||||
#ifndef DECLARE_COPYCONSTR_AND_ASSIGNMENT
|
||||
#define DECLARE_COPYCONSTR_AND_ASSIGNMENT( C ) MSO_NO_COPYCONSTR( C ); MSO_NO_ASSIGNMENT( C )
|
||||
#define DECLARE_COPYCONSTR_AND_ASSIGNMENT(C) \
|
||||
MSO_NO_COPYCONSTR(C); \
|
||||
MSO_NO_ASSIGNMENT(C)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
When using an enum class to define a set of bitflags, normal bitflag
|
||||
enum operations, such as |, ^, and &, don't work without lots of casts.
|
||||
Use this macro to define |, ^, and & for your enum class, where |, ^ and ~
|
||||
will return an enum class type, and & will evaluate to true or false.
|
||||
The implementation causes error C3281 (global operator cannot have
|
||||
managed type in signature) for managed code.
|
||||
When using an enum class to define a set of bitflags, normal bitflag
|
||||
enum operations, such as |, ^, and &, don't work without lots of casts.
|
||||
Use this macro to define |, ^, and & for your enum class, where |, ^ and ~
|
||||
will return an enum class type, and & will evaluate to true or false.
|
||||
The implementation causes error C3281 (global operator cannot have
|
||||
managed type in signature) for managed code.
|
||||
*/
|
||||
#define ENUM_CLASS_FLAGS_OPERATORS(TEnum) \
|
||||
constexpr TEnum operator~(TEnum a) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(~static_cast<std::underlying_type<TEnum>::type>(a)); \
|
||||
} \
|
||||
constexpr TEnum operator|(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(static_cast<std::underlying_type<TEnum>::type>(a) | static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
constexpr bool operator&(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return !!(static_cast<std::underlying_type<TEnum>::type>(a) & static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
constexpr TEnum operator^(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(static_cast<std::underlying_type<TEnum>::type>(a) ^ static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum& operator|=(TEnum& a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum&>(reinterpret_cast<std::underlying_type<TEnum>::type&>(a) |= static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum& operator&=(TEnum& a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum&>(reinterpret_cast<std::underlying_type<TEnum>::type&>(a) &= static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum& operator^=(TEnum& a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum&>(reinterpret_cast<std::underlying_type<TEnum>::type&>(a) ^= static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
}
|
||||
#define ENUM_CLASS_FLAGS_OPERATORS(TEnum) \
|
||||
constexpr TEnum operator~(TEnum a) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(~static_cast<std::underlying_type<TEnum>::type>(a)); \
|
||||
} \
|
||||
constexpr TEnum operator|(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(static_cast<std::underlying_type<TEnum>::type>(a) | \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
constexpr bool operator&(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return !!(static_cast<std::underlying_type<TEnum>::type>(a) & \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
constexpr TEnum operator^(TEnum a, TEnum b) noexcept \
|
||||
{ \
|
||||
return static_cast<TEnum>(static_cast<std::underlying_type<TEnum>::type>(a) ^ \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum &operator|=(TEnum &a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum &>(reinterpret_cast<std::underlying_type<TEnum>::type &>(a) |= \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum &operator&=(TEnum &a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum &>(reinterpret_cast<std::underlying_type<TEnum>::type &>(a) &= \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
} \
|
||||
inline TEnum &operator^=(TEnum &a, TEnum b) noexcept \
|
||||
{ \
|
||||
return reinterpret_cast<TEnum &>(reinterpret_cast<std::underlying_type<TEnum>::type &>(a) ^= \
|
||||
static_cast<std::underlying_type<TEnum>::type>(b)); \
|
||||
}
|
||||
|
||||
#else // !__cplusplus
|
||||
|
||||
|
@ -101,9 +120,9 @@
|
|||
#endif // !__cplusplus
|
||||
|
||||
/**
|
||||
Macros to help share enums between C++ and CX.
|
||||
Enums must derive from int (the default)
|
||||
Flag enums must derive from unsigned int
|
||||
Macros to help share enums between C++ and CX.
|
||||
Enums must derive from int (the default)
|
||||
Flag enums must derive from unsigned int
|
||||
*/
|
||||
#if defined(_WINRT_COMP) && !defined(__clang__)
|
||||
#define EXPOSE_WINRT_ENUM public
|
||||
|
@ -114,9 +133,9 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
Macros to force placement of symbols into a particular segment.
|
||||
For example on iOS ensure large globals aren't placed in the text
|
||||
segment because there is a size limit enforced by Apple.
|
||||
Macros to force placement of symbols into a particular segment.
|
||||
For example on iOS ensure large globals aren't placed in the text
|
||||
segment because there is a size limit enforced by Apple.
|
||||
*/
|
||||
#if MS_TARGET_APPLE
|
||||
#define DATA_SEGMENT_CONST __attribute__((section("__DATA,__const")))
|
||||
|
@ -125,7 +144,7 @@
|
|||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
#define __forceinline __attribute__ ((always_inline, unused))
|
||||
#define __forceinline __attribute__((always_inline, unused))
|
||||
// Stub __declspec in GCC
|
||||
#define __declspec(s)
|
||||
#endif
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Some globally useful debug-only macros
|
||||
Some globally useful debug-only macros
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_CPPMACROSDEBUG_H
|
||||
|
@ -16,11 +16,11 @@
|
|||
#if DEBUG
|
||||
#define FILELINEPARAMSCOREUNUSED const char*, uint32_t
|
||||
#define FILELINEPARAMSUNUSED , FILELINEPARAMSCOREUNUSED
|
||||
#define FILELINEPARAMSOPTCORE const char* szFile = __FILE__, uint32_t iLine = __LINE__
|
||||
#define FILELINEPARAMSOPTCORE const char *szFile = __FILE__, uint32_t iLine = __LINE__
|
||||
#define FILELINEPARAMSOPT , FILELINEPARAMSOPTCORE
|
||||
#define FILELINEPARAMSOPTUNUSEDCORE const char* = __FILE__, uint32_t = __LINE__
|
||||
#define FILELINEPARAMSOPTUNUSED , FILELINEPARAMSOPTUNUSEDCORE
|
||||
#define FILELINEPARAMSCORE const char* szFile, uint32_t iLine
|
||||
#define FILELINEPARAMSCORE const char *szFile, uint32_t iLine
|
||||
#define FILELINEPARAMS , FILELINEPARAMSCORE
|
||||
#define FILELINEVARSCORE szFile, iLine
|
||||
#define FILELINEVARS , FILELINEVARSCORE
|
||||
|
@ -51,7 +51,7 @@
|
|||
#if DEBUG
|
||||
#define Debug(e) e
|
||||
#define DebugOnly(e) e
|
||||
#define DebugElse(s, t) s
|
||||
#define DebugElse(s, t) s
|
||||
#define DebugList(e) (e),
|
||||
#define DebugParam(e) , e
|
||||
#define DebugFill(pb, cb) (memset((pb), 0xCC, (cb)))
|
||||
|
@ -59,21 +59,24 @@
|
|||
#define Debug(e)
|
||||
#define DebugOnly(e)
|
||||
#define DebugElse(s, t) t
|
||||
#define DebugList(e)
|
||||
#define DebugList(e)
|
||||
#define DebugParam(e)
|
||||
#define DebugFill(pb, cb) (1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
MsoDebugBreak can be used inside expressions.
|
||||
Otherwise code should use __debugbreak() directly.
|
||||
Otherwise code should use __debugbreak() directly.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
__inline int32_t MsoDebugBreak(void)
|
||||
__inline int32_t MsoDebugBreak(void)
|
||||
#ifdef __cplusplus
|
||||
noexcept
|
||||
noexcept
|
||||
#endif // __cplusplus
|
||||
{ __debugbreak(); return 0; }
|
||||
{
|
||||
__debugbreak();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define MsoDebugBreak() (0)
|
||||
#endif
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Cross-compiler support for declspec things
|
||||
FUTURE: reconcile this with compilerFeatures.h
|
||||
Cross-compiler support for declspec things
|
||||
FUTURE: reconcile this with compilerFeatures.h
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_DECLSPECDEFINITIONS_H
|
||||
|
@ -31,14 +31,14 @@
|
|||
|
||||
// Latest versions of clang supports this natively
|
||||
#ifndef DECLSPEC_SELECTANY
|
||||
#define DECLSPEC_SELECTANY __declspec(selectany)
|
||||
#define DECLSPEC_SELECTANY __declspec(selectany)
|
||||
#endif
|
||||
|
||||
// Mark the variable to be the first to be inited in the global init sequence.
|
||||
// GCC/Clang does not have init_seg, rather has a priority 101-65535 where 101 is the highest
|
||||
#ifndef DECLSPEC_INITFIRST
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define DECLSPEC_INITFIRST __attribute__ ((init_priority (101)))
|
||||
#define DECLSPEC_INITFIRST __attribute__((init_priority(101)))
|
||||
#else
|
||||
#define DECLSPEC_INITFIRST
|
||||
#endif
|
||||
|
@ -57,18 +57,18 @@
|
|||
#if defined(__clang__) || !defined(__cplusplus) || defined(__GNUC__)
|
||||
#define DECLSPEC_NOVTABLE
|
||||
#else
|
||||
#define DECLSPEC_NOVTABLE __declspec(novtable)
|
||||
#define DECLSPEC_NOVTABLE __declspec(novtable)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DECLSPEC_DEPRECATED
|
||||
# if defined(__clang__) || defined(__GNUC__)
|
||||
# define DECLSPEC_DEPRECATED __attribute__((deprecated))
|
||||
# elif !defined(MIDL_PASS)
|
||||
# define DECLSPEC_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define DECLSPEC_DEPRECATED
|
||||
# endif
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define DECLSPEC_DEPRECATED __attribute__((deprecated))
|
||||
#elif !defined(MIDL_PASS)
|
||||
#define DECLSPEC_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define DECLSPEC_DEPRECATED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // COMPILERADAPTERS_DECLSPECDEFINITIONS_H
|
||||
|
|
|
@ -2,61 +2,61 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Cross-platform/language macros to decorate APIs
|
||||
Cross-platform/language macros to decorate APIs
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_FUNCTIONDECORATIONS_H
|
||||
#define COMPILERADAPTERS_FUNCTIONDECORATIONS_H
|
||||
|
||||
/**
|
||||
The Liblet::PublicApi attribute is used to mark a function, class or
|
||||
class method as part of your public API. These are automatically
|
||||
added to your export file and shim DLL.
|
||||
|
||||
Note: Your liblet must be configured to process PublicApi.
|
||||
Note: PublicApi replaces all MSOAPI/MSOCPPAPI-style macros.
|
||||
Note: See http://aka.ms/cppdef
|
||||
|
||||
Platform specific APIs are filtered using standard strings:
|
||||
win, win32, win32client, win32server, winrt, android
|
||||
The Liblet::PublicApi attribute is used to mark a function, class or
|
||||
class method as part of your public API. These are automatically
|
||||
added to your export file and shim DLL.
|
||||
|
||||
Usage: an API is exported for all targets
|
||||
LIBLET_PUBLICAPI void MyApi() noexcept;
|
||||
|
||||
Usage: exporting static methods on a class/interface
|
||||
struct MyInterface
|
||||
{
|
||||
LIBLET_PUBLICAPI static MyInterface* Create();
|
||||
...
|
||||
};
|
||||
|
||||
Usage: platform-specific APIs
|
||||
LIBLET_PUBLICAPI_EX("win32") void MyDesktop();
|
||||
LIBLET_PUBLICAPI_EX("winrt") void MyStore();
|
||||
LIBLET_PUBLICAPI_EX("win32", "android") void MyApi();
|
||||
|
||||
Note: LIBLET_PUBLICAPI_EX(...) is not supported on Apple platforms,
|
||||
Use LIBLET_PUBLICAPI_APPLE instead.
|
||||
LIBLET_PUBLICAPI_EX("winrt") LIBLET_PUBLICAPI_APPLE void MyApi();
|
||||
Note: Your liblet must be configured to process PublicApi.
|
||||
Note: PublicApi replaces all MSOAPI/MSOCPPAPI-style macros.
|
||||
Note: See http://aka.ms/cppdef
|
||||
|
||||
Usage: export a whole class (heavily discouraged)
|
||||
struct LIBLET_PUBLICAPI MyExportedClass
|
||||
{
|
||||
// override default class export
|
||||
LIBLET_PUBLICAPI_EX("win32") void MyDesktop();
|
||||
Platform specific APIs are filtered using standard strings:
|
||||
win, win32, win32client, win32server, winrt, android
|
||||
|
||||
void AlsoExported();
|
||||
};
|
||||
|
||||
Usage: export special data types from a class (android + apple only)
|
||||
Values: "typeinfo", "typeinfoname", "vtable", "thunks"
|
||||
struct LIBLET_PUBLICAPI_CLASSDATA("typeinfo") MyClass
|
||||
{
|
||||
};
|
||||
Usage: an API is exported for all targets
|
||||
LIBLET_PUBLICAPI void MyApi() noexcept;
|
||||
|
||||
struct LIBLET_PUBLICAPI_CLASSDATA("typeinfo", "vtable") MyClass
|
||||
{
|
||||
};
|
||||
Usage: exporting static methods on a class/interface
|
||||
struct MyInterface
|
||||
{
|
||||
LIBLET_PUBLICAPI static MyInterface* Create();
|
||||
...
|
||||
};
|
||||
|
||||
Usage: platform-specific APIs
|
||||
LIBLET_PUBLICAPI_EX("win32") void MyDesktop();
|
||||
LIBLET_PUBLICAPI_EX("winrt") void MyStore();
|
||||
LIBLET_PUBLICAPI_EX("win32", "android") void MyApi();
|
||||
|
||||
Note: LIBLET_PUBLICAPI_EX(...) is not supported on Apple platforms,
|
||||
Use LIBLET_PUBLICAPI_APPLE instead.
|
||||
LIBLET_PUBLICAPI_EX("winrt") LIBLET_PUBLICAPI_APPLE void MyApi();
|
||||
|
||||
Usage: export a whole class (heavily discouraged)
|
||||
struct LIBLET_PUBLICAPI MyExportedClass
|
||||
{
|
||||
// override default class export
|
||||
LIBLET_PUBLICAPI_EX("win32") void MyDesktop();
|
||||
|
||||
void AlsoExported();
|
||||
};
|
||||
|
||||
Usage: export special data types from a class (android + apple only)
|
||||
Values: "typeinfo", "typeinfoname", "vtable", "thunks"
|
||||
struct LIBLET_PUBLICAPI_CLASSDATA("typeinfo") MyClass
|
||||
{
|
||||
};
|
||||
|
||||
struct LIBLET_PUBLICAPI_CLASSDATA("typeinfo", "vtable") MyClass
|
||||
{
|
||||
};
|
||||
*/
|
||||
#if defined(__clangpluginrunner__)
|
||||
#define LIBLET_PUBLICAPI [[Liblet::PublicApi]]
|
||||
|
@ -85,19 +85,19 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
The Liblet::MockName macro enables renaming of the generated mock names.
|
||||
This is useful when the function / interface method is overloaded.
|
||||
|
||||
struct IBank
|
||||
{
|
||||
// Generate mock_TryWithdrawlUint32
|
||||
LIBLET_MOCKNAME("TryWithdrawlUint32")
|
||||
virtual bool TryWithdrawl(uint32_t amount) = 0;
|
||||
The Liblet::MockName macro enables renaming of the generated mock names.
|
||||
This is useful when the function / interface method is overloaded.
|
||||
|
||||
// Generate mock_TryWithdrawlFloat
|
||||
LIBLET_MOCKNAME("TryWithdrawlFloat")
|
||||
virtual bool TryWithdrawl(float amount) = 0;
|
||||
};
|
||||
struct IBank
|
||||
{
|
||||
// Generate mock_TryWithdrawlUint32
|
||||
LIBLET_MOCKNAME("TryWithdrawlUint32")
|
||||
virtual bool TryWithdrawl(uint32_t amount) = 0;
|
||||
|
||||
// Generate mock_TryWithdrawlFloat
|
||||
LIBLET_MOCKNAME("TryWithdrawlFloat")
|
||||
virtual bool TryWithdrawl(float amount) = 0;
|
||||
};
|
||||
*/
|
||||
#if defined(__clangpluginrunner__)
|
||||
#define LIBLET_MOCKNAME(name) [[Liblet::MockName(name)]]
|
||||
|
@ -106,21 +106,23 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
Macros for extern "C" that support C & C++
|
||||
Macros for extern "C" that support C & C++
|
||||
*/
|
||||
#if defined(__cplusplus)
|
||||
#define MSOEXTERN_C extern "C"
|
||||
#define MSOEXTERN_C_BEGIN extern "C" {
|
||||
#define MSOEXTERN_C_BEGIN \
|
||||
extern "C" \
|
||||
{
|
||||
#define MSOEXTERN_C_END }
|
||||
#else
|
||||
#define MSOEXTERN_C
|
||||
#define MSOEXTERN_C
|
||||
#define MSOEXTERN_C_BEGIN
|
||||
#define MSOEXTERN_C_END
|
||||
#endif
|
||||
|
||||
/**
|
||||
Macro for constexpr
|
||||
FUTURE: VC14 supports constexpr
|
||||
Macro for constexpr
|
||||
FUTURE: VC14 supports constexpr
|
||||
*/
|
||||
#if defined(__cplusplus) && defined(__clang__)
|
||||
#define MSOCONSTEXPR constexpr
|
||||
|
@ -129,7 +131,7 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
Macros to use nothrow/throw not on clang
|
||||
Macros to use nothrow/throw not on clang
|
||||
*/
|
||||
#if defined(__cplusplus) && !defined(__clang__)
|
||||
/* SSS_WARNINGS_OFF */
|
||||
|
@ -140,7 +142,7 @@
|
|||
#endif // C++
|
||||
|
||||
/**
|
||||
Macro to enable noexcept only for C++
|
||||
Macro to enable noexcept only for C++
|
||||
*/
|
||||
#if defined(__cplusplus)
|
||||
#define MSONOEXCEPT noexcept
|
||||
|
@ -149,25 +151,25 @@
|
|||
#endif // C++
|
||||
|
||||
/**
|
||||
Macro to mark a method as const for C & C++
|
||||
Macro to mark a method as const for C & C++
|
||||
*/
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
#define CONST_METHOD const
|
||||
#define CONST_METHOD const
|
||||
#else
|
||||
#define CONST_METHOD
|
||||
#define CONST_METHOD
|
||||
#endif
|
||||
|
||||
/**
|
||||
Utility macro to make writing NonConst versions of functions easier.
|
||||
In C, you can't overload functions; thus the two flavors of a function will be
|
||||
DoSomething
|
||||
DoSomethingNonConst
|
||||
In C++, you can overload functions, so the two flavors of a function will have
|
||||
the same name (but with different arguments).
|
||||
Utility macro to make writing NonConst versions of functions easier.
|
||||
In C, you can't overload functions; thus the two flavors of a function will be
|
||||
DoSomething
|
||||
DoSomethingNonConst
|
||||
In C++, you can overload functions, so the two flavors of a function will have
|
||||
the same name (but with different arguments).
|
||||
|
||||
To work around this, declare the second function as NON_CONST_VER(DoSomething)
|
||||
This also can be used when you explicitly want to call the nonConst version from
|
||||
an inline function that can be compiled as either C or C++
|
||||
To work around this, declare the second function as NON_CONST_VER(DoSomething)
|
||||
This also can be used when you explicitly want to call the nonConst version from
|
||||
an inline function that can be compiled as either C or C++
|
||||
*/
|
||||
#if defined(__cplusplus)
|
||||
#define NON_CONST_VER(Function) Function
|
||||
|
@ -177,7 +179,7 @@
|
|||
#define NON_CONST_VER_DECL(Function) NON_CONST_VER(Function)
|
||||
|
||||
/**
|
||||
Macro to use __forceinline with cl.exe
|
||||
Macro to use __forceinline with cl.exe
|
||||
*/
|
||||
/* SSS_WARNINGS_OFF */
|
||||
#if defined(__cplusplus) && !defined(__clang__)
|
||||
|
@ -188,14 +190,14 @@
|
|||
/* SSS_WARNINGS_ON */
|
||||
|
||||
#if defined(__clang__)
|
||||
#define MSO_NO_INLINE __attribute__((noinline))
|
||||
#define MSO_NO_INLINE __attribute__((noinline))
|
||||
#else
|
||||
#define MSO_NO_INLINE __declspec(noinline)
|
||||
#define MSO_NO_INLINE __declspec(noinline)
|
||||
#endif
|
||||
|
||||
/**
|
||||
Clang forbids the use of static with friend.
|
||||
msvc issues warning C4211: nonstandard extension used: redefined extern to static without it
|
||||
Clang forbids the use of static with friend.
|
||||
msvc issues warning C4211: nonstandard extension used: redefined extern to static without it
|
||||
*/
|
||||
#if defined(__clang__)
|
||||
#define MSO_STATIC_FRIEND friend
|
||||
|
@ -203,26 +205,25 @@
|
|||
#define MSO_STATIC_FRIEND friend static
|
||||
#endif
|
||||
|
||||
|
||||
// Don't use any of these macros, they are being eliminated
|
||||
// The definitions are a mess to prevent removed macros from sneaking back in
|
||||
|
||||
#if defined(__cplusplus) && !defined(__clang__)
|
||||
|
||||
/* SSS_WARNINGS_OFF */
|
||||
#define MSOCPPAPI_(t) extern "C++" __declspec(nothrow) t __cdecl
|
||||
#define MSOCPPAPI_(t) extern "C++" __declspec(nothrow) t __cdecl
|
||||
#if defined(_M_X64)
|
||||
#define MSOAPI_(t) MSOEXTERN_C __declspec(nothrow) t __fastcall
|
||||
#define MSOAPI_(t) MSOEXTERN_C __declspec(nothrow) t __fastcall
|
||||
#else
|
||||
#define MSOAPI_(t) MSOEXTERN_C __declspec(nothrow) t __stdcall
|
||||
#define MSOAPI_(t) MSOEXTERN_C __declspec(nothrow) t __stdcall
|
||||
#endif
|
||||
/* SSS_WARNINGS_ON */
|
||||
#else
|
||||
#define MSOCPPAPI_(t) extern "C++" t __cdecl
|
||||
#define MSOCPPAPI_(t) extern "C++" t __cdecl
|
||||
#if defined(_M_X64)
|
||||
#define MSOAPI_(t) MSOEXTERN_C t __fastcall
|
||||
#define MSOAPI_(t) MSOEXTERN_C t __fastcall
|
||||
#else
|
||||
#define MSOAPI_(t) MSOEXTERN_C t __stdcall
|
||||
#define MSOAPI_(t) MSOEXTERN_C t __stdcall
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Adapters for various compiler intrinsics
|
||||
Adapters for various compiler intrinsics
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_INTRINSICS_H
|
||||
|
@ -10,12 +10,12 @@
|
|||
|
||||
/** Macro that calls compiler intrinsics to terminate the application and bypass all exception handlers */
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define MSOFASTFAIL(arg) __builtin_trap()
|
||||
#define MSO_FUNC_RETURN_ADDRESS() __builtin_return_address(0)
|
||||
#define MSOFASTFAIL(arg) __builtin_trap()
|
||||
#define MSO_FUNC_RETURN_ADDRESS() __builtin_return_address(0)
|
||||
#else
|
||||
#include <intrin.h>
|
||||
#define MSOFASTFAIL(arg) __fastfail(arg)
|
||||
#define MSO_FUNC_RETURN_ADDRESS() _ReturnAddress()
|
||||
#include <intrin.h>
|
||||
#define MSOFASTFAIL(arg) __fastfail(arg)
|
||||
#define MSO_FUNC_RETURN_ADDRESS() _ReturnAddress()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,29 +2,30 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Things related to linking
|
||||
Things related to linking
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_LINKER_H
|
||||
#define COMPILERADAPTERS_LINKER_H
|
||||
|
||||
/**
|
||||
If a library has no direct entry points, it may be necessary to force
|
||||
the linker to include it. There are two ways to accomplish this:
|
||||
If a library has no direct entry points, it may be necessary to force
|
||||
the linker to include it. There are two ways to accomplish this:
|
||||
|
||||
1) Preferably - link the library as a LOB instead.
|
||||
1) Preferably - link the library as a LOB instead.
|
||||
|
||||
2) Use MSO_DEFINE_SYMBOL to define a symbol in the source file that needs to
|
||||
be included. Then, add the corresponding MSO_LINK_SYMBOL macro to one of the
|
||||
source files that gets compiled directly into your binary (as an *.obj file)
|
||||
2) Use MSO_DEFINE_SYMBOL to define a symbol in the source file that needs to
|
||||
be included. Then, add the corresponding MSO_LINK_SYMBOL macro to one of the
|
||||
source files that gets compiled directly into your binary (as an *.obj file)
|
||||
*/
|
||||
#define MSO_DEFINE_SYMBOL(symbol) extern "C" void __cdecl symbol() {}
|
||||
#define MSO_DEFINE_SYMBOL(symbol) \
|
||||
extern "C" void __cdecl symbol() {}
|
||||
|
||||
// MSO_LINK_INCLUDE and MSO_LINK_SYMBOL macros are only needed for MSVC.
|
||||
#if MS_TARGET_WINDOWS
|
||||
|
||||
/**
|
||||
Machinery for the MSO_LINK_SYMBOL macro
|
||||
Machinery for the MSO_LINK_SYMBOL macro
|
||||
*/
|
||||
#if defined(_M_HYBRID)
|
||||
#define MSO_LINK_INCLUDE(symbol) "/INCLUDE:#" symbol
|
||||
|
@ -37,24 +38,24 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
See MSO_DEFINE_SYMBOL above
|
||||
See MSO_DEFINE_SYMBOL above
|
||||
*/
|
||||
#define MSO_LINK_SYMBOL( symbol ) __pragma( comment( linker, MSO_LINK_INCLUDE( #symbol ) ) )
|
||||
#define MSO_LINK_SYMBOL(symbol) __pragma(comment(linker, MSO_LINK_INCLUDE(#symbol)))
|
||||
|
||||
/**
|
||||
Platform-agnostic macro to define dll export name ("foo" vs "_foo@4")
|
||||
The number is generally [# of params * 4].
|
||||
Platform-agnostic macro to define dll export name ("foo" vs "_foo@4")
|
||||
The number is generally [# of params * 4].
|
||||
|
||||
Note: using GetProcAddress is usually wrong. Prefer linker delay-loading.
|
||||
Note: using GetProcAddress is usually wrong. Prefer linker delay-loading.
|
||||
|
||||
Example:
|
||||
void Foo(int bar);
|
||||
auto pFoo = (decltype(Foo) *) GetProcAddress(hMod, SzDllExport("Foo", 4));
|
||||
Example:
|
||||
void Foo(int bar);
|
||||
auto pFoo = (decltype(Foo) *) GetProcAddress(hMod, SzDllExport("Foo", 4));
|
||||
*/
|
||||
#if defined(_M_X64) || defined(_M_ARM)
|
||||
#define SzDllExport( fn, c ) fn
|
||||
#define SzDllExport(fn, c) fn
|
||||
#elif defined(_M_IX86)
|
||||
#define SzDllExport( fn, c ) "_" fn "@" #c
|
||||
#define SzDllExport(fn, c) "_" fn "@" #c
|
||||
#else
|
||||
#error Unknown Platform!
|
||||
#endif // x64 || _M_ARM
|
||||
|
|
|
@ -2,22 +2,22 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for managed C++ compilation available only in VC++.
|
||||
Support for managed C++ compilation available only in VC++.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef COMPILERADAPTERS_MANAGEDCPP_H
|
||||
#define COMPILERADAPTERS_MANAGEDCPP_H
|
||||
|
||||
/**
|
||||
VC++ specific pragmas to indicate that code must not be compiled as managed.
|
||||
Functions are compiled as managed by default when /clr is used.
|
||||
The pragmas below allow explicitly indicate that code is unmanaged, but these
|
||||
pragmas are not recognized by Clang compiler.
|
||||
VC++ specific pragmas to indicate that code must not be compiled as managed.
|
||||
Functions are compiled as managed by default when /clr is used.
|
||||
The pragmas below allow explicitly indicate that code is unmanaged, but these
|
||||
pragmas are not recognized by Clang compiler.
|
||||
*/
|
||||
#if !defined(__clang__)
|
||||
|
||||
#define MSO_PRAGMA_MANAGED_PUSH_OFF __pragma(managed(push, off))
|
||||
#define MSO_PRAGMA_MANAGED_POP __pragma(managed(pop))
|
||||
#define MSO_PRAGMA_MANAGED_POP __pragma(managed(pop))
|
||||
|
||||
#else // !defined(__clang__)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Helper classes to automatically restore/cleanup/undo an operation.
|
||||
Helper classes to automatically restore/cleanup/undo an operation.
|
||||
*/
|
||||
#ifndef _CPPEXTENSIONS_AUTORESTORE_H_
|
||||
#define _CPPEXTENSIONS_AUTORESTORE_H_
|
||||
|
@ -15,224 +15,244 @@
|
|||
// 4472: 'pointer_safety' is a native enum: add an access specifier (private/public) to declare a managed enum
|
||||
// 4996: 'wmemcpy': This function or variable may be unsafe. Consider using wmemcpy_s instead
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091 4472 4996)
|
||||
#pragma warning(disable : 4091 4472 4996)
|
||||
#include <memory>
|
||||
#pragma warning(pop)
|
||||
|
||||
// 4996: 'wmemcpy': This function or variable may be unsafe. Consider using wmemcpy_s instead
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996)
|
||||
#pragma warning(disable : 4996)
|
||||
#include <utility>
|
||||
#pragma warning(pop)
|
||||
|
||||
namespace Mso {
|
||||
|
||||
/**
|
||||
Restores a variable to its original value in an exception-safe way.
|
||||
For example:
|
||||
{
|
||||
Mso::TRestorer<bool> restoreFlag(m_fFlag, true);
|
||||
... // do something that might throw an exception
|
||||
} // m_fFlag is automatically restored to its original value
|
||||
Restores a variable to its original value in an exception-safe way.
|
||||
For example:
|
||||
{
|
||||
Mso::TRestorer<bool> restoreFlag(m_fFlag, true);
|
||||
... // do something that might throw an exception
|
||||
} // m_fFlag is automatically restored to its original value
|
||||
*/
|
||||
template<typename T> class TRestorer
|
||||
template <typename T>
|
||||
class TRestorer
|
||||
{
|
||||
public:
|
||||
TRestorer(T& value) noexcept : m_pValue(std::addressof(value)), m_previousValue(value) {}
|
||||
TRestorer(T& value, T newValue) noexcept : m_pValue(std::addressof(value)), m_previousValue(value)
|
||||
{ *m_pValue = newValue; }
|
||||
TRestorer(TRestorer&& other) noexcept : m_pValue(other.m_pValue), m_previousValue(other.m_previousValue)
|
||||
{ other.Disable(); }
|
||||
~TRestorer() noexcept { *m_pValue = m_previousValue; }
|
||||
TRestorer(T& value) noexcept : m_pValue(std::addressof(value)), m_previousValue(value) {}
|
||||
TRestorer(T& value, T newValue) noexcept : m_pValue(std::addressof(value)), m_previousValue(value)
|
||||
{
|
||||
*m_pValue = newValue;
|
||||
}
|
||||
TRestorer(TRestorer&& other) noexcept : m_pValue(other.m_pValue), m_previousValue(other.m_previousValue)
|
||||
{
|
||||
other.Disable();
|
||||
}
|
||||
~TRestorer() noexcept
|
||||
{
|
||||
*m_pValue = m_previousValue;
|
||||
}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TRestorer);
|
||||
TRestorer& operator=(TRestorer&& other) noexcept = delete;
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TRestorer);
|
||||
TRestorer& operator=(TRestorer&& other) noexcept = delete;
|
||||
|
||||
bool IsEnabled() noexcept { return (m_pValue != std::addressof(m_previousValue)); }
|
||||
void Disable() noexcept { m_pValue = std::addressof(m_previousValue); }
|
||||
bool IsEnabled() noexcept
|
||||
{
|
||||
return (m_pValue != std::addressof(m_previousValue));
|
||||
}
|
||||
void Disable() noexcept
|
||||
{
|
||||
m_pValue = std::addressof(m_previousValue);
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_pValue;
|
||||
T m_previousValue;
|
||||
T* m_pValue;
|
||||
T m_previousValue;
|
||||
};
|
||||
|
||||
/**
|
||||
AutoRestorer() can be used to avoid being explicit with TRestorer
|
||||
AutoRestorer() can be used to avoid being explicit with TRestorer
|
||||
|
||||
Instead of:
|
||||
Mso::TRestorer<bool> restorer(m_inLayout);
|
||||
Do this:
|
||||
auto restorer = Mso::AutoRestorer(m_inLayout);
|
||||
Instead of:
|
||||
Mso::TRestorer<bool> restorer(m_inLayout);
|
||||
Do this:
|
||||
auto restorer = Mso::AutoRestorer(m_inLayout);
|
||||
*/
|
||||
template <typename T>
|
||||
Mso::TRestorer<T> AutoRestorer(T &value) noexcept
|
||||
Mso::TRestorer<T> AutoRestorer(T& value) noexcept
|
||||
{
|
||||
return Mso::TRestorer<T>(value);
|
||||
return Mso::TRestorer<T>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Mso::TRestorer<T> AutoRestorer(T &value, T newValue) noexcept
|
||||
Mso::TRestorer<T> AutoRestorer(T& value, T newValue) noexcept
|
||||
{
|
||||
return Mso::TRestorer<T>(value, newValue);
|
||||
return Mso::TRestorer<T>(value, newValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Restores a value to its original value (using a getter/setter) in an exception-safe way. For example:
|
||||
Restores a value to its original value (using a getter/setter) in an exception-safe way. For example:
|
||||
|
||||
Foo* GetGlobalFoo()
|
||||
{
|
||||
return s_pFooGlobal;
|
||||
}
|
||||
Foo* GetGlobalFoo()
|
||||
{
|
||||
return s_pFooGlobal;
|
||||
}
|
||||
|
||||
void SetGlobalFoo(Foo* pFoo)
|
||||
{
|
||||
s_pFooGlobal = pFoo;
|
||||
}
|
||||
void SetGlobalFoo(Foo* pFoo)
|
||||
{
|
||||
s_pFooGlobal = pFoo;
|
||||
}
|
||||
|
||||
using GlobalFooRestorer = Mso::TPropertyRestorer<Foo*, GetGlobalFoo, SetGlobalFoo>;
|
||||
using GlobalFooRestorer = Mso::TPropertyRestorer<Foo*, GetGlobalFoo, SetGlobalFoo>;
|
||||
|
||||
void MyMethod(Foo* pFoo)
|
||||
{
|
||||
GlobalFooRestorer rstGlobalFoo(pFoo);
|
||||
... // do something that might throw an exception
|
||||
} // s_pFooGlobal is automatically restored to its original value
|
||||
void MyMethod(Foo* pFoo)
|
||||
{
|
||||
GlobalFooRestorer rstGlobalFoo(pFoo);
|
||||
... // do something that might throw an exception
|
||||
} // s_pFooGlobal is automatically restored to its original value
|
||||
*/
|
||||
template<
|
||||
typename T,
|
||||
T PfnGetter(),
|
||||
void PfnSetter(T value)>
|
||||
template <typename T, T PfnGetter(), void PfnSetter(T value)>
|
||||
struct TPropertyRestorer final
|
||||
{
|
||||
public:
|
||||
TPropertyRestorer() = default;
|
||||
TPropertyRestorer(T newValue) noexcept(noexcept(PfnSetter(PfnGetter()))) // noexcept(any expression with both Get and Set)
|
||||
{
|
||||
PfnSetter(std::forward<T>(newValue));
|
||||
}
|
||||
TPropertyRestorer(TPropertyRestorer&& other) noexcept :
|
||||
m_isDisabled(other.m_isDisabled),
|
||||
m_valueToRestore(std::forward<T>(other.m_valueToRestore))
|
||||
{
|
||||
other.Disable();
|
||||
}
|
||||
TPropertyRestorer() = default;
|
||||
TPropertyRestorer(T newValue) noexcept(
|
||||
noexcept(PfnSetter(PfnGetter()))) // noexcept(any expression with both Get and Set)
|
||||
{
|
||||
PfnSetter(std::forward<T>(newValue));
|
||||
}
|
||||
TPropertyRestorer(TPropertyRestorer&& other) noexcept
|
||||
: m_isDisabled(other.m_isDisabled), m_valueToRestore(std::forward<T>(other.m_valueToRestore))
|
||||
{
|
||||
other.Disable();
|
||||
}
|
||||
|
||||
~TPropertyRestorer()
|
||||
{
|
||||
if (!m_isDisabled)
|
||||
{
|
||||
PfnSetter(std::forward<T>(m_valueToRestore));
|
||||
}
|
||||
}
|
||||
~TPropertyRestorer()
|
||||
{
|
||||
if (!m_isDisabled)
|
||||
{
|
||||
PfnSetter(std::forward<T>(m_valueToRestore));
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TPropertyRestorer); // No copying!
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TPropertyRestorer); // No copying!
|
||||
|
||||
template<typename TSelf = TPropertyRestorer, typename = typename std::enable_if_t<std::is_move_assignable<typename std::remove_reference<T>::type>::value, TSelf>>
|
||||
TSelf& operator=(TSelf&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
m_isDisabled = other.m_isDisabled;
|
||||
m_valueToRestore = std::move(other.m_valueToRestore);
|
||||
template <
|
||||
typename TSelf = TPropertyRestorer,
|
||||
typename =
|
||||
typename std::enable_if_t<std::is_move_assignable<typename std::remove_reference<T>::type>::value, TSelf>>
|
||||
TSelf& operator=(TSelf&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
m_isDisabled = other.m_isDisabled;
|
||||
m_valueToRestore = std::move(other.m_valueToRestore);
|
||||
|
||||
other.Disable();
|
||||
}
|
||||
other.Disable();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IsEnabled() const noexcept { return !m_isDisabled; }
|
||||
void Disable() noexcept { m_isDisabled = true; }
|
||||
bool IsEnabled() const noexcept
|
||||
{
|
||||
return !m_isDisabled;
|
||||
}
|
||||
void Disable() noexcept
|
||||
{
|
||||
m_isDisabled = true;
|
||||
}
|
||||
|
||||
T& ValueToRestore() noexcept { return m_valueToRestore; }
|
||||
T& ValueToRestore() noexcept
|
||||
{
|
||||
return m_valueToRestore;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isDisabled = false;
|
||||
T m_valueToRestore{PfnGetter()};
|
||||
bool m_isDisabled = false;
|
||||
T m_valueToRestore{PfnGetter()};
|
||||
};
|
||||
|
||||
|
||||
namespace TCleanup
|
||||
{
|
||||
namespace TCleanup {
|
||||
/**
|
||||
TCleanup
|
||||
The last resort. If none of the other helper classes work for you,
|
||||
or there is only a single occurrence that you feel doesn't warrant its own
|
||||
helper class, the cleanup lambda option is available.
|
||||
TCleanup
|
||||
The last resort. If none of the other helper classes work for you,
|
||||
or there is only a single occurrence that you feel doesn't warrant its own
|
||||
helper class, the cleanup lambda option is available.
|
||||
|
||||
Runs the passed cleanup function on destruction.
|
||||
This is a handy utility to use previous "goto LError" style cleanup code
|
||||
with the early return pattern.
|
||||
Runs the passed cleanup function on destruction.
|
||||
This is a handy utility to use previous "goto LError" style cleanup code
|
||||
with the early return pattern.
|
||||
|
||||
EXAMPLE:
|
||||
// Specify the code to run on return in a capture-by-reference lambda
|
||||
auto cleanupCloseOLDoc = TCleanup::Make([&m_pOrigFile, &m_pNewFile]
|
||||
{
|
||||
// Close out the local file if appropriate
|
||||
if (m_pOrigFile != m_pNewFile && m_pNewFile->GetOpenCount())
|
||||
{
|
||||
m_pNewFile->BeginCmd(msoiolcmdClose);
|
||||
m_pNewFile->RecordEvent(msoiolevtCmdCompleted);
|
||||
}
|
||||
});
|
||||
EXAMPLE:
|
||||
// Specify the code to run on return in a capture-by-reference lambda
|
||||
auto cleanupCloseOLDoc = TCleanup::Make([&m_pOrigFile, &m_pNewFile]
|
||||
{
|
||||
// Close out the local file if appropriate
|
||||
if (m_pOrigFile != m_pNewFile && m_pNewFile->GetOpenCount())
|
||||
{
|
||||
m_pNewFile->BeginCmd(msoiolcmdClose);
|
||||
m_pNewFile->RecordEvent(msoiolevtCmdCompleted);
|
||||
}
|
||||
});
|
||||
|
||||
hr = /some code that uses and OLDocument/;
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
// Runs the cleanup code above before returning
|
||||
hr = /some code that uses and OLDocument/;
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
// Runs the cleanup code above before returning
|
||||
|
||||
If you want to be able to disable the TCleanup from running on exit,
|
||||
you can do so by:
|
||||
If you want to be able to disable the TCleanup from running on exit,
|
||||
you can do so by:
|
||||
|
||||
if (/some condition/)
|
||||
cleanupCloseOLDoc.disable(); // The TCleanup will now not run on exit
|
||||
if (/some condition/)
|
||||
cleanupCloseOLDoc.disable(); // The TCleanup will now not run on exit
|
||||
*/
|
||||
template<typename Func>
|
||||
template <typename Func>
|
||||
struct TCleanup
|
||||
{
|
||||
public:
|
||||
explicit TCleanup(const Func& fnCleanup) noexcept :
|
||||
m_fnCleanup(fnCleanup)
|
||||
{}
|
||||
explicit TCleanup(const Func& fnCleanup) noexcept : m_fnCleanup(fnCleanup) {}
|
||||
|
||||
explicit TCleanup(Func&& fnCleanup) noexcept :
|
||||
m_fnCleanup(std::move(fnCleanup))
|
||||
{}
|
||||
explicit TCleanup(Func&& fnCleanup) noexcept : m_fnCleanup(std::move(fnCleanup)) {}
|
||||
|
||||
TCleanup(TCleanup&& rVal) noexcept :
|
||||
m_fnCleanup(rVal.m_fnCleanup),
|
||||
m_isEnabled(rVal.m_isEnabled)
|
||||
{
|
||||
// We don't want to run the cleanup twice, so disable the original
|
||||
rVal.disable();
|
||||
}
|
||||
TCleanup(TCleanup&& rVal) noexcept : m_fnCleanup(rVal.m_fnCleanup), m_isEnabled(rVal.m_isEnabled)
|
||||
{
|
||||
// We don't want to run the cleanup twice, so disable the original
|
||||
rVal.disable();
|
||||
}
|
||||
|
||||
~TCleanup() noexcept
|
||||
{
|
||||
if (m_isEnabled)
|
||||
m_fnCleanup();
|
||||
}
|
||||
~TCleanup() noexcept
|
||||
{
|
||||
if (m_isEnabled)
|
||||
m_fnCleanup();
|
||||
}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TCleanup);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TCleanup);
|
||||
|
||||
void enable() noexcept { m_isEnabled = true; }
|
||||
void disable() noexcept { m_isEnabled = false; }
|
||||
void enable() noexcept
|
||||
{
|
||||
m_isEnabled = true;
|
||||
}
|
||||
void disable() noexcept
|
||||
{
|
||||
m_isEnabled = false;
|
||||
}
|
||||
|
||||
private:
|
||||
const Func m_fnCleanup;
|
||||
bool m_isEnabled = true;
|
||||
const Func m_fnCleanup;
|
||||
bool m_isEnabled = true;
|
||||
};
|
||||
|
||||
// Constructs a TCleanup from the passed function or lambda
|
||||
template<typename Func>
|
||||
template <typename Func>
|
||||
inline TCleanup<Func> Make(const Func& pfnCleanup) noexcept
|
||||
{
|
||||
return TCleanup<Func>(pfnCleanup);
|
||||
return TCleanup<Func>(pfnCleanup);
|
||||
}
|
||||
|
||||
} // end TCleanup namespace
|
||||
} // namespace TCleanup
|
||||
|
||||
} // Mso
|
||||
} // namespace Mso
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Use this type to enforce that an actual literal string is provided.
|
||||
|
||||
void DoSomething(const StringLiteral<char>& input) { s_name = input; }
|
||||
|
||||
DoSomething("Hi"_S);
|
||||
|
||||
To use this class and _S, you need to pull in the namespace.
|
||||
using namespace Mso::StringLiterals;
|
||||
Use this type to enforce that an actual literal string is provided.
|
||||
|
||||
void DoSomething(const StringLiteral<char>& input) { s_name = input; }
|
||||
|
||||
DoSomething("Hi"_S);
|
||||
|
||||
To use this class and _S, you need to pull in the namespace.
|
||||
using namespace Mso::StringLiterals;
|
||||
|
||||
*/
|
||||
#ifndef _CPPEXTENSIONS_STRINGLITERAL_H_
|
||||
|
@ -19,92 +19,94 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Mso {
|
||||
namespace StringLiterals {
|
||||
namespace Mso { namespace StringLiterals {
|
||||
|
||||
template<typename char_type>
|
||||
template <typename char_type>
|
||||
class StringLiteral;
|
||||
|
||||
constexpr StringLiteral<char> operator""_S(const char* str, size_t) noexcept;
|
||||
|
||||
/**
|
||||
StringLiteral can only be constructed using the user-defined-literal operators below.
|
||||
StringLiteral can only be constructed using the user-defined-literal operators below.
|
||||
*/
|
||||
template<typename char_type>
|
||||
template <typename char_type>
|
||||
class StringLiteral
|
||||
{
|
||||
public:
|
||||
constexpr operator _Null_terminated_ const char_type*() const noexcept { return m_string; }
|
||||
constexpr operator _Null_terminated_ const char_type *() const noexcept
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
constexpr size_t length() const noexcept
|
||||
{
|
||||
// std::char_traits<>::length() is preferred, but it's not constexpr (even though it optimizes away).
|
||||
// note: using recursion until next version of msvc compiler which supports loops
|
||||
return length_worker(m_string);
|
||||
}
|
||||
constexpr size_t length() const noexcept
|
||||
{
|
||||
// std::char_traits<>::length() is preferred, but it's not constexpr (even though it optimizes away).
|
||||
// note: using recursion until next version of msvc compiler which supports loops
|
||||
return length_worker(m_string);
|
||||
}
|
||||
|
||||
constexpr const char_type* const* operator&() const noexcept { return &m_string; }
|
||||
|
||||
//private:
|
||||
constexpr StringLiteral(_In_z_ const char_type* str) noexcept : m_string(str)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr static size_t length_worker(_In_z_ const char_type* str) noexcept
|
||||
{
|
||||
return *str ? length_worker(str + 1) + 1 : 0;
|
||||
}
|
||||
constexpr const char_type* const* operator&() const noexcept
|
||||
{
|
||||
return &m_string;
|
||||
}
|
||||
|
||||
const char_type* const m_string;
|
||||
// private:
|
||||
constexpr StringLiteral(_In_z_ const char_type* str) noexcept : m_string(str) {}
|
||||
|
||||
//friend constexpr StringLiteral<char> operator""_S(const char*, size_t) noexcept;
|
||||
// friend constexpr StringLiteral<wchar_t> operator""_S(const wchar_t*, size_t);
|
||||
// friend constexpr StringLiteral<char16_t> operator""_S(const char16_t*, size_t);
|
||||
// friend constexpr StringLiteral<char32_t> operator""_S(const char32_t*, size_t);
|
||||
constexpr static size_t length_worker(_In_z_ const char_type* str) noexcept
|
||||
{
|
||||
return *str ? length_worker(str + 1) + 1 : 0;
|
||||
}
|
||||
|
||||
const char_type* const m_string;
|
||||
|
||||
// friend constexpr StringLiteral<char> operator""_S(const char*, size_t) noexcept;
|
||||
// friend constexpr StringLiteral<wchar_t> operator""_S(const wchar_t*, size_t);
|
||||
// friend constexpr StringLiteral<char16_t> operator""_S(const char16_t*, size_t);
|
||||
// friend constexpr StringLiteral<char32_t> operator""_S(const char32_t*, size_t);
|
||||
};
|
||||
|
||||
constexpr StringLiteral<char> operator""_S(const char* str, size_t) noexcept
|
||||
{
|
||||
return { str };
|
||||
return {str};
|
||||
}
|
||||
|
||||
inline constexpr StringLiteral<wchar_t> operator""_S(const wchar_t* str, size_t) noexcept
|
||||
{
|
||||
return { str };
|
||||
return {str};
|
||||
}
|
||||
|
||||
inline constexpr StringLiteral<char16_t> operator""_S(const char16_t* str, size_t) noexcept
|
||||
{
|
||||
return { str };
|
||||
return {str};
|
||||
}
|
||||
|
||||
inline constexpr StringLiteral<char32_t> operator""_S(const char32_t* str, size_t) noexcept
|
||||
{
|
||||
return { str };
|
||||
return {str};
|
||||
}
|
||||
|
||||
/**
|
||||
Special tag used with overloads accepting a raw string pointer.
|
||||
Special tag used with overloads accepting a raw string pointer.
|
||||
|
||||
struct HoldSomething
|
||||
{
|
||||
HoldSomething(const StringLiteral<char>& input) : m_string(input) {}
|
||||
HoldSomething(const char* input, const NonStringLiteral_lifetime_semantics_reviewed&) : m_string(input) {}
|
||||
|
||||
private: const char* m_string = nullptr;
|
||||
};
|
||||
|
||||
void SafeCall()
|
||||
{
|
||||
const char* sz = GetName();
|
||||
HoldSomething h(sz, NonStringLiteral_lifetime_semantics_reviewed);
|
||||
}
|
||||
struct HoldSomething
|
||||
{
|
||||
HoldSomething(const StringLiteral<char>& input) : m_string(input) {}
|
||||
HoldSomething(const char* input, const NonStringLiteral_lifetime_semantics_reviewed&) : m_string(input) {}
|
||||
|
||||
private: const char* m_string = nullptr;
|
||||
};
|
||||
|
||||
void SafeCall()
|
||||
{
|
||||
const char* sz = GetName();
|
||||
HoldSomething h(sz, NonStringLiteral_lifetime_semantics_reviewed);
|
||||
}
|
||||
*/
|
||||
constexpr struct NonStringLiteral_lifetime_semantics_reviewed_t
|
||||
{
|
||||
} NonStringLiteral_lifetime_semantics_reviewed {};
|
||||
} NonStringLiteral_lifetime_semantics_reviewed{};
|
||||
|
||||
} // StringLiterals
|
||||
} // Mso
|
||||
}} // namespace Mso::StringLiterals
|
||||
|
||||
#endif // _CPPEXTENSIONS_STRINGLITERAL_H_
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
FLS-based implementation of TLS
|
||||
FLS-based implementation of TLS
|
||||
*/
|
||||
#ifndef _CPPEXTENSIONS_THREADLOCAL_FLS_H_
|
||||
#define _CPPEXTENSIONS_THREADLOCAL_FLS_H_
|
||||
|
@ -16,67 +16,65 @@ namespace Mso { namespace Details {
|
|||
|
||||
struct ThreadLocalImpl
|
||||
{
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ThreadLocalImpl);
|
||||
static_assert(FLS_OUT_OF_INDEXES == TLS_OUT_OF_INDEXES, "Assumption violation");
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ThreadLocalImpl);
|
||||
static_assert(FLS_OUT_OF_INDEXES == TLS_OUT_OF_INDEXES, "Assumption violation");
|
||||
|
||||
ThreadLocalImpl() noexcept : ThreadLocalImpl(nullptr)
|
||||
{
|
||||
}
|
||||
ThreadLocalImpl() noexcept : ThreadLocalImpl(nullptr) {}
|
||||
|
||||
/**
|
||||
Callback will be invoked with the TLS data on each thread exit
|
||||
*/
|
||||
ThreadLocalImpl(void (__stdcall *pfnCleanup)(void*) noexcept) noexcept
|
||||
{
|
||||
if (pfnCleanup)
|
||||
{
|
||||
m_index = ::FlsAlloc(pfnCleanup);
|
||||
m_isFls = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_index = ::TlsAlloc();
|
||||
}
|
||||
/**
|
||||
Callback will be invoked with the TLS data on each thread exit
|
||||
*/
|
||||
ThreadLocalImpl(void(__stdcall* pfnCleanup)(void*) noexcept) noexcept
|
||||
{
|
||||
if (pfnCleanup)
|
||||
{
|
||||
m_index = ::FlsAlloc(pfnCleanup);
|
||||
m_isFls = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_index = ::TlsAlloc();
|
||||
}
|
||||
|
||||
CheckFatal(m_index != TLS_OUT_OF_INDEXES);
|
||||
}
|
||||
CheckFatal(m_index != TLS_OUT_OF_INDEXES);
|
||||
}
|
||||
|
||||
~ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
::FlsFree(m_index);
|
||||
else
|
||||
::TlsFree(m_index);
|
||||
}
|
||||
~ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
::FlsFree(m_index);
|
||||
else
|
||||
::TlsFree(m_index);
|
||||
}
|
||||
|
||||
void* Get() const noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
return ::FlsGetValue(m_index);
|
||||
else
|
||||
return ::TlsGetValue(m_index);
|
||||
}
|
||||
void* Get() const noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
return ::FlsGetValue(m_index);
|
||||
else
|
||||
return ::TlsGetValue(m_index);
|
||||
}
|
||||
|
||||
void Set(void* t) noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
CheckFatal(!!::FlsSetValue(m_index, t));
|
||||
else
|
||||
CheckFatal(!!::TlsSetValue(m_index, t));
|
||||
}
|
||||
void Set(void* t) noexcept
|
||||
{
|
||||
if (m_isFls)
|
||||
CheckFatal(!!::FlsSetValue(m_index, t));
|
||||
else
|
||||
CheckFatal(!!::TlsSetValue(m_index, t));
|
||||
}
|
||||
|
||||
private:
|
||||
static void CheckFatal(bool result) noexcept
|
||||
{
|
||||
if (!result)
|
||||
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
||||
}
|
||||
static void CheckFatal(bool result) noexcept
|
||||
{
|
||||
if (!result)
|
||||
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
||||
}
|
||||
|
||||
uint32_t m_index = TLS_OUT_OF_INDEXES;
|
||||
bool m_isFls = false;
|
||||
uint32_t m_index = TLS_OUT_OF_INDEXES;
|
||||
bool m_isFls = false;
|
||||
};
|
||||
|
||||
}} // Mso::Details
|
||||
}} // namespace Mso::Details
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // _CPPEXTENSIONS_THREADLOCAL_FLS_H_
|
||||
|
|
|
@ -13,41 +13,41 @@ namespace Mso { namespace Details {
|
|||
|
||||
struct ThreadLocalImpl
|
||||
{
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ThreadLocalImpl);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ThreadLocalImpl);
|
||||
|
||||
ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (pthread_key_create(&m_pthread_key, nullptr) != 0)
|
||||
std::abort();
|
||||
}
|
||||
ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (pthread_key_create(&m_pthread_key, nullptr) != 0)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
ThreadLocalImpl(void (*pfnCleanup)(void*) noexcept) noexcept
|
||||
{
|
||||
if (pthread_key_create(&m_pthread_key, pfnCleanup) != 0)
|
||||
std::abort();
|
||||
}
|
||||
ThreadLocalImpl(void (*pfnCleanup)(void*) noexcept) noexcept
|
||||
{
|
||||
if (pthread_key_create(&m_pthread_key, pfnCleanup) != 0)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
~ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (m_pthread_key)
|
||||
pthread_key_delete(m_pthread_key);
|
||||
}
|
||||
~ThreadLocalImpl() noexcept
|
||||
{
|
||||
if (m_pthread_key)
|
||||
pthread_key_delete(m_pthread_key);
|
||||
}
|
||||
|
||||
void* Get() const noexcept
|
||||
{
|
||||
return pthread_getspecific(m_pthread_key);
|
||||
}
|
||||
|
||||
void Set(const void* t) noexcept
|
||||
{
|
||||
if (pthread_setspecific(m_pthread_key, t) != 0)
|
||||
std::abort();
|
||||
}
|
||||
void* Get() const noexcept
|
||||
{
|
||||
return pthread_getspecific(m_pthread_key);
|
||||
}
|
||||
|
||||
void Set(const void* t) noexcept
|
||||
{
|
||||
if (pthread_setspecific(m_pthread_key, t) != 0)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_key_t m_pthread_key {};
|
||||
pthread_key_t m_pthread_key{};
|
||||
};
|
||||
|
||||
}} // Mso::Details
|
||||
}} // namespace Mso::Details
|
||||
|
||||
#endif // _CPPEXTENSIONS_THREADLOCAL_PTHREAD_H_
|
||||
|
|
|
@ -8,41 +8,40 @@ Basic testing for Mso::TPropertyRestorer<T, PfnGetter, PfnSetter>
|
|||
#include <cppextensions/autoRestore.h>
|
||||
#include <motifcpp/motifcpptest.h>
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
struct TestProperty
|
||||
{
|
||||
static T Get() noexcept
|
||||
{
|
||||
static typename std::decay<T>::type s_value;
|
||||
return std::forward<T>(s_value); // Return any value that compiles.
|
||||
}
|
||||
static T Get() noexcept
|
||||
{
|
||||
static typename std::decay<T>::type s_value;
|
||||
return std::forward<T>(s_value); // Return any value that compiles.
|
||||
}
|
||||
|
||||
static void Set(T /*value*/) noexcept
|
||||
{
|
||||
}
|
||||
static void Set(T /*value*/) noexcept {}
|
||||
|
||||
using Restorer = typename Mso::TPropertyRestorer<T, Get, Set>;
|
||||
using Restorer = typename Mso::TPropertyRestorer<T, Get, Set>;
|
||||
|
||||
template<bool expectingMoveAssignmentToCompile = true>
|
||||
static void VerifyRestorerCompiles() noexcept
|
||||
{
|
||||
Restorer rst(Get()); // Ensure class template instantiation compiles.
|
||||
Restorer rst2(std::move(rst)); // Ensure move semantics compile too.
|
||||
template <bool expectingMoveAssignmentToCompile = true>
|
||||
static void VerifyRestorerCompiles() noexcept
|
||||
{
|
||||
Restorer rst(Get()); // Ensure class template instantiation compiles.
|
||||
Restorer rst2(std::move(rst)); // Ensure move semantics compile too.
|
||||
|
||||
rst = std::move(rst2);
|
||||
}
|
||||
rst = std::move(rst2);
|
||||
}
|
||||
|
||||
template<>
|
||||
static void VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>() noexcept
|
||||
{
|
||||
Restorer rst(Get()); // Ensure class template instantiation compiles.
|
||||
Restorer rst2(std::move(rst)); // Ensure move semantics compile too.
|
||||
template <>
|
||||
static void VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>() noexcept
|
||||
{
|
||||
Restorer rst(Get()); // Ensure class template instantiation compiles.
|
||||
Restorer rst2(std::move(rst)); // Ensure move semantics compile too.
|
||||
|
||||
static_assert(!std::is_move_assignable<T>::value, "If T has a valid move assignment operator, then why can't we move assign it?");
|
||||
}
|
||||
static_assert(
|
||||
!std::is_move_assignable<T>::value,
|
||||
"If T has a valid move assignment operator, then why can't we move assign it?");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const int c_nDefaultValue = 123;
|
||||
static const int c_nNonDefaultValue = 456;
|
||||
static_assert(c_nDefaultValue != c_nNonDefaultValue, "If you break this, then these tests are pretty useless");
|
||||
|
@ -51,199 +50,198 @@ static int s_nGlobal = c_nDefaultValue;
|
|||
|
||||
void SetTestGlobal(int value) noexcept
|
||||
{
|
||||
s_nGlobal = value;
|
||||
s_nGlobal = value;
|
||||
}
|
||||
|
||||
int GetTestGlobal() noexcept
|
||||
{
|
||||
return s_nGlobal;
|
||||
return s_nGlobal;
|
||||
}
|
||||
|
||||
// Hungarian: tr
|
||||
using TestRestorer = Mso::TPropertyRestorer<int, GetTestGlobal, SetTestGlobal>;
|
||||
|
||||
TEST_CLASS(TPropertyRestorerTest)
|
||||
TEST_CLASS (TPropertyRestorerTest)
|
||||
{
|
||||
TEST_METHOD_INITIALIZE(Setup)
|
||||
{
|
||||
SetTestGlobal(c_nDefaultValue); // Ensure every test starts with default global value
|
||||
}
|
||||
TEST_METHOD_INITIALIZE(Setup)
|
||||
{
|
||||
SetTestGlobal(c_nDefaultValue); // Ensure every test starts with default global value
|
||||
}
|
||||
|
||||
TEST_METHOD(DefaultConstructor_GlobalIsReverted)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr;
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should not be updated");
|
||||
TEST_METHOD(DefaultConstructor_GlobalIsReverted)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr;
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should not be updated");
|
||||
|
||||
SetTestGlobal(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
SetTestGlobal(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(ConstructorDestructor_Nested_GlobalIsUpdated)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
TEST_METHOD(ConstructorDestructor_Nested_GlobalIsUpdated)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
{
|
||||
TestRestorer tr2(123456789);
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
{
|
||||
TestRestorer tr2(123456789);
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(Disable_OriginallyEnabled_NotReverted)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TEST_METHOD(Disable_OriginallyEnabled_NotReverted)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
TestAssert::IsTrue(tr.IsEnabled());
|
||||
tr.Disable();
|
||||
TestAssert::IsFalse(tr.IsEnabled());
|
||||
}
|
||||
TestAssert::IsTrue(tr.IsEnabled());
|
||||
tr.Disable();
|
||||
TestAssert::IsFalse(tr.IsEnabled());
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(MoveOperator_OriginallyEnabled_OriginalIsDisabled)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
TEST_METHOD(MoveOperator_OriginallyEnabled_OriginalIsDisabled)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
{
|
||||
TestRestorer tr2(123456789);
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be updated");
|
||||
{
|
||||
TestRestorer tr2(123456789);
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
TestAssert::IsTrue(tr2.IsEnabled());
|
||||
tr = std::move(tr2);
|
||||
TestAssert::IsFalse(tr2.IsEnabled());
|
||||
}
|
||||
TestAssert::IsTrue(tr2.IsEnabled());
|
||||
tr = std::move(tr2);
|
||||
TestAssert::IsFalse(tr2.IsEnabled());
|
||||
}
|
||||
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should not be reverted");
|
||||
}
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should not be reverted");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be reverted to tr2's previous value");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be reverted to tr2's previous value");
|
||||
}
|
||||
|
||||
TEST_METHOD(MoveConstructor_OriginallyEnabled_OriginalIsDisabled)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(std::move(TrSetGlobalToNonDefault()));
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
TEST_METHOD(MoveConstructor_OriginallyEnabled_OriginalIsDisabled)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(std::move(TrSetGlobalToNonDefault()));
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(c_nDefaultValue, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(MoveConstructor_SetRestoreTo123456789_RestoreTo123456789)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(std::move(TrSetGlobalToNonDefaultAndRestoreTo123456789()));
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
TEST_METHOD(MoveConstructor_SetRestoreTo123456789_RestoreTo123456789)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(std::move(TrSetGlobalToNonDefaultAndRestoreTo123456789()));
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(SetValueToRestore_SetRestoreTo123456789_RestoreTo123456789)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
TEST_METHOD(SetValueToRestore_SetRestoreTo123456789_RestoreTo123456789)
|
||||
{
|
||||
// Arrange & Act
|
||||
{
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
TestAssert::AreEqual(c_nDefaultValue, tr.ValueToRestore());
|
||||
tr.ValueToRestore() = 123456789;
|
||||
TestAssert::AreEqual(123456789, tr.ValueToRestore());
|
||||
TestAssert::AreEqual(c_nDefaultValue, tr.ValueToRestore());
|
||||
tr.ValueToRestore() = 123456789;
|
||||
TestAssert::AreEqual(123456789, tr.ValueToRestore());
|
||||
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted yet");
|
||||
}
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted yet");
|
||||
}
|
||||
|
||||
// Assert
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
// Assert
|
||||
TestAssert::AreEqual(123456789, GetTestGlobal(), L"Global should be reverted");
|
||||
}
|
||||
|
||||
TEST_METHOD(ComplexTemplatedTypes_Compiles)
|
||||
{
|
||||
struct Unmovable final
|
||||
{
|
||||
Unmovable() = default;
|
||||
private:
|
||||
Unmovable(Unmovable&&) = delete;
|
||||
Unmovable& operator=(Unmovable&&) = delete;
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(Unmovable); // No copying!
|
||||
};
|
||||
TEST_METHOD(ComplexTemplatedTypes_Compiles)
|
||||
{
|
||||
struct Unmovable final
|
||||
{
|
||||
Unmovable() = default;
|
||||
|
||||
// Ensure PropertyRestorer doesn't cause any extra constructor calls.
|
||||
TestProperty<Unmovable&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<Unmovable&&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<const Unmovable&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
private:
|
||||
Unmovable(Unmovable&&) = delete;
|
||||
Unmovable& operator=(Unmovable&&) = delete;
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(Unmovable); // No copying!
|
||||
};
|
||||
|
||||
TestProperty<int>::VerifyRestorerCompiles();
|
||||
TestProperty<int*>::VerifyRestorerCompiles();
|
||||
TestProperty<int&>::VerifyRestorerCompiles();
|
||||
TestProperty<const int>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<const int*>::VerifyRestorerCompiles();
|
||||
TestProperty<const int&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
// Ensure PropertyRestorer doesn't cause any extra constructor calls.
|
||||
TestProperty<Unmovable&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<Unmovable&&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<const Unmovable&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
|
||||
TestProperty<std::unique_ptr<int>>::VerifyRestorerCompiles();
|
||||
TestProperty<std::unique_ptr<int>&>::VerifyRestorerCompiles();
|
||||
TestProperty<std::unique_ptr<int>&&>::VerifyRestorerCompiles();
|
||||
TestProperty<const std::unique_ptr<int>&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
}
|
||||
TestProperty<int>::VerifyRestorerCompiles();
|
||||
TestProperty<int*>::VerifyRestorerCompiles();
|
||||
TestProperty<int&>::VerifyRestorerCompiles();
|
||||
TestProperty<const int>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
TestProperty<const int*>::VerifyRestorerCompiles();
|
||||
TestProperty<const int&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
|
||||
TestProperty<std::unique_ptr<int>>::VerifyRestorerCompiles();
|
||||
TestProperty<std::unique_ptr<int>&>::VerifyRestorerCompiles();
|
||||
TestProperty<std::unique_ptr<int>&&>::VerifyRestorerCompiles();
|
||||
TestProperty<const std::unique_ptr<int>&>::VerifyRestorerCompiles<false /*expectingMoveAssignmentToCompile*/>();
|
||||
}
|
||||
|
||||
private:
|
||||
// any helper type methods
|
||||
// any helper type methods
|
||||
|
||||
static TestRestorer TrSetGlobalToNonDefault()
|
||||
{
|
||||
TestAssert::AreNotEqual(c_nNonDefaultValue, GetTestGlobal(), L"This test is less useful if the global already is c_nNonDefaultValue");
|
||||
static TestRestorer TrSetGlobalToNonDefault()
|
||||
{
|
||||
TestAssert::AreNotEqual(
|
||||
c_nNonDefaultValue, GetTestGlobal(), L"This test is less useful if the global already is c_nNonDefaultValue");
|
||||
|
||||
auto verifyPostCondition = Mso::TCleanup::Make([]()
|
||||
{
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted");
|
||||
});
|
||||
auto verifyPostCondition = Mso::TCleanup::Make(
|
||||
[]() { TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted"); });
|
||||
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
TestRestorer tr(c_nNonDefaultValue);
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should be updated");
|
||||
|
||||
return tr;
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
|
||||
static TestRestorer TrSetGlobalToNonDefaultAndRestoreTo123456789()
|
||||
{
|
||||
TestAssert::AreNotEqual(c_nNonDefaultValue, GetTestGlobal(), L"This test is less useful if the global already is c_nNonDefaultValue");
|
||||
TestAssert::AreNotEqual(123456789, GetTestGlobal(), L"This test is less useful if the global already is 123456789");
|
||||
static TestRestorer TrSetGlobalToNonDefaultAndRestoreTo123456789()
|
||||
{
|
||||
TestAssert::AreNotEqual(
|
||||
c_nNonDefaultValue, GetTestGlobal(), L"This test is less useful if the global already is c_nNonDefaultValue");
|
||||
TestAssert::AreNotEqual(123456789, GetTestGlobal(), L"This test is less useful if the global already is 123456789");
|
||||
|
||||
SetTestGlobal(123456789);
|
||||
SetTestGlobal(123456789);
|
||||
|
||||
auto verifyPostCondition = Mso::TCleanup::Make([]()
|
||||
{
|
||||
TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted");
|
||||
});
|
||||
auto verifyPostCondition = Mso::TCleanup::Make(
|
||||
[]() { TestAssert::AreEqual(c_nNonDefaultValue, GetTestGlobal(), L"Global should not be reverted"); });
|
||||
|
||||
return TrSetGlobalToNonDefault();
|
||||
}
|
||||
return TrSetGlobalToNonDefault();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Basic testing for StringLiteral classes
|
||||
Basic testing for StringLiteral classes
|
||||
*/
|
||||
#include <cppextensions/stringliteral.h>
|
||||
#include <oacr.h>
|
||||
|
@ -11,66 +11,65 @@
|
|||
#include <string>
|
||||
using namespace Mso::StringLiterals;
|
||||
|
||||
template<typename char_type>
|
||||
template <typename char_type>
|
||||
static void CheckLength(size_t expected, const StringLiteral<char_type>& s)
|
||||
{
|
||||
TestAssert::AreEqual(expected, std::char_traits<char_type>::length(s));
|
||||
TestAssert::AreEqual(expected, std::char_traits<char_type>::length(s));
|
||||
}
|
||||
|
||||
TEST_CLASS(StringLiteralTests)
|
||||
TEST_CLASS (StringLiteralTests)
|
||||
{
|
||||
static void ConstLengths() noexcept
|
||||
{
|
||||
static_assert(""_S.length() == 0, "Invalid compile-time length");
|
||||
static_assert("Test"_S.length() == 4, "Invalid compile-time length");
|
||||
}
|
||||
static void ConstLengths() noexcept
|
||||
{
|
||||
static_assert(""_S.length() == 0, "Invalid compile-time length");
|
||||
static_assert("Test"_S.length() == 4, "Invalid compile-time length");
|
||||
}
|
||||
|
||||
TEST_METHOD(TestEmpty)
|
||||
{
|
||||
CheckLength(0, ""_S);
|
||||
CheckLength(0, L""_S);
|
||||
CheckLength(0, u""_S);
|
||||
CheckLength(0, U""_S);
|
||||
}
|
||||
TEST_METHOD(TestEmpty)
|
||||
{
|
||||
CheckLength(0, ""_S);
|
||||
CheckLength(0, L""_S);
|
||||
CheckLength(0, u""_S);
|
||||
CheckLength(0, U""_S);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestNonEmpty)
|
||||
{
|
||||
CheckLength(3, "Yes"_S);
|
||||
CheckLength(3, L"Yes"_S);
|
||||
CheckLength(3, u"Yes"_S);
|
||||
CheckLength(3, U"Yes"_S);
|
||||
}
|
||||
TEST_METHOD(TestNonEmpty)
|
||||
{
|
||||
CheckLength(3, "Yes"_S);
|
||||
CheckLength(3, L"Yes"_S);
|
||||
CheckLength(3, u"Yes"_S);
|
||||
CheckLength(3, U"Yes"_S);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(StringLiteralArrayTests)
|
||||
TEST_CLASS (StringLiteralArrayTests)
|
||||
{
|
||||
struct HoldString
|
||||
{
|
||||
template<size_t Count>
|
||||
constexpr HoldString(const std::array<StringLiteral<char>, Count>& strings) noexcept :
|
||||
m_strings(&strings[0]),
|
||||
m_count(Count)
|
||||
{
|
||||
static_assert(Count != 0, "Must pass at least 1 string");
|
||||
|
||||
}
|
||||
|
||||
const char* const* m_strings;
|
||||
const size_t m_count;
|
||||
};
|
||||
|
||||
TEST_METHOD(TestArray)
|
||||
{
|
||||
static const std::array<StringLiteral<char>, 3> strings =
|
||||
{
|
||||
"Hi"_S, "This"_S, "Works"_S,
|
||||
};
|
||||
|
||||
const HoldString hold(strings);
|
||||
|
||||
//static_assert(hold.m_count == 3, "Incorrect count"); Look at this later
|
||||
TestAssert::AreEqual("Hi", hold.m_strings[0]);
|
||||
TestAssert::AreEqual("This", hold.m_strings[1]);
|
||||
TestAssert::AreEqual("Works", hold.m_strings[2]);
|
||||
}
|
||||
struct HoldString
|
||||
{
|
||||
template <size_t Count>
|
||||
constexpr HoldString(const std::array<StringLiteral<char>, Count>& strings) noexcept
|
||||
: m_strings(&strings[0]), m_count(Count)
|
||||
{
|
||||
static_assert(Count != 0, "Must pass at least 1 string");
|
||||
}
|
||||
|
||||
const char* const* m_strings;
|
||||
const size_t m_count;
|
||||
};
|
||||
|
||||
TEST_METHOD(TestArray)
|
||||
{
|
||||
static const std::array<StringLiteral<char>, 3> strings = {
|
||||
"Hi"_S,
|
||||
"This"_S,
|
||||
"Works"_S,
|
||||
};
|
||||
|
||||
const HoldString hold(strings);
|
||||
|
||||
// static_assert(hold.m_count == 3, "Incorrect count"); Look at this later
|
||||
TestAssert::AreEqual("Hi", hold.m_strings[0]);
|
||||
TestAssert::AreEqual("This", hold.m_strings[1]);
|
||||
TestAssert::AreEqual("Works", hold.m_strings[2]);
|
||||
}
|
||||
};
|
|
@ -4,4 +4,3 @@
|
|||
#include <motifcpp/motifcpptest.h>
|
||||
|
||||
ImplementTestModule();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Various C++ helpers for working with function types
|
||||
Various C++ helpers for working with function types
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef LIBLET_CPPTYPE_FUNCTIONTYPETRAITS_H
|
||||
|
@ -11,94 +11,91 @@
|
|||
#include <type_traits>
|
||||
#include <tuple>
|
||||
|
||||
namespace Mso
|
||||
{
|
||||
namespace Mso {
|
||||
|
||||
/**
|
||||
Turns a function pointer or method pointer type into a normal function type.
|
||||
Error otherwise.
|
||||
Turns a function pointer or method pointer type into a normal function type.
|
||||
Error otherwise.
|
||||
|
||||
Note: does not strip volatile/const qualifiers on the pointer.
|
||||
Note: does not strip volatile/const qualifiers on the pointer.
|
||||
|
||||
Note: std::remove_reference<T> is used instead of std::common_type<T>
|
||||
because of a bug in the STL that causes common_type to not return the
|
||||
identity with function types.
|
||||
Note: std::remove_reference<T> is used instead of std::common_type<T>
|
||||
because of a bug in the STL that causes common_type to not return the
|
||||
identity with function types.
|
||||
*/
|
||||
template <typename FunctionPointerType>
|
||||
struct fnptr_to_fn;
|
||||
|
||||
// Handle a member function pointer that is non-const
|
||||
template <typename Return, typename Class, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(Class::*)(Arguments ...)>
|
||||
template <typename Return, typename Class, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (Class::*)(Arguments...)>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
// Handle a pointer to a const member function
|
||||
template <typename Return, typename Class, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(Class::*)(Arguments ...) const>
|
||||
template <typename Return, typename Class, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (Class::*)(Arguments...) const>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
// Handle a pointer to a function
|
||||
template <typename Return, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(*)(Arguments ...)>
|
||||
template <typename Return, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (*)(Arguments...)>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
#if defined(__cpp_noexcept_function_type) || (_HAS_NOEXCEPT_FUNCTION_TYPES == 1)
|
||||
|
||||
// Handle a noexcept member function pointer that is non-const
|
||||
template <typename Return, typename Class, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(Class::*)(Arguments ...) noexcept>
|
||||
template <typename Return, typename Class, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (Class::*)(Arguments...) noexcept>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
// Handle a pointer to a const noexcept member function
|
||||
template <typename Return, typename Class, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(Class::*)(Arguments ...) const noexcept>
|
||||
template <typename Return, typename Class, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (Class::*)(Arguments...) const noexcept>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
// Handle a pointer to a noexcept function
|
||||
template <typename Return, typename ... Arguments>
|
||||
struct fnptr_to_fn<Return(*)(Arguments ...) noexcept>
|
||||
template <typename Return, typename... Arguments>
|
||||
struct fnptr_to_fn<Return (*)(Arguments...) noexcept>
|
||||
{
|
||||
typedef typename std::remove_reference<Return(Arguments ...)>::type type;
|
||||
typedef typename std::remove_reference<Return(Arguments...)>::type type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Checks types of two function types to ensure that it is safe to replace the
|
||||
first [From] function's invocation with the second [To].
|
||||
Checks types of two function types to ensure that it is safe to replace the
|
||||
first [From] function's invocation with the second [To].
|
||||
|
||||
Guaranteed to work if both From and To are function types.
|
||||
Error otherwise.
|
||||
Guaranteed to work if both From and To are function types.
|
||||
Error otherwise.
|
||||
|
||||
i.e.: (struct A {}; struct B : A {};)
|
||||
void() -> void() // good
|
||||
void(int) -> void(void*) // bad
|
||||
void(B*) -> void(A*) // good because a B* can always be converted to an A*
|
||||
A*() -> B*() // good because the same reason as the previous
|
||||
void(int, double) -> void(int) // bad because parameter count mismatch
|
||||
i.e.: (struct A {}; struct B : A {};)
|
||||
void() -> void() // good
|
||||
void(int) -> void(void*) // bad
|
||||
void(B*) -> void(A*) // good because a B* can always be converted to an A*
|
||||
A*() -> B*() // good because the same reason as the previous
|
||||
void(int, double) -> void(int) // bad because parameter count mismatch
|
||||
|
||||
The helper class uses a std::tuple to pack lists of arguments to recurse on.
|
||||
This is because recursing using function templates causes and ICE on this version
|
||||
of the compiler. Dev
|
||||
The helper class uses a std::tuple to pack lists of arguments to recurse on.
|
||||
This is because recursing using function templates causes and ICE on this version
|
||||
of the compiler. Dev
|
||||
|
||||
*/
|
||||
template <typename From, typename To>
|
||||
struct are_function_types_convertible;
|
||||
|
||||
namespace details
|
||||
{
|
||||
namespace details {
|
||||
|
||||
template <typename FromTuple, typename ToTuple>
|
||||
struct are_function_types_convertible_arguments_helper;
|
||||
|
@ -107,53 +104,51 @@ struct are_function_types_convertible_arguments_helper;
|
|||
template <>
|
||||
struct are_function_types_convertible_arguments_helper<std::tuple<>, std::tuple<>>
|
||||
{
|
||||
static bool const value = true;
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
// base case - false - too many arguments in From.
|
||||
template <typename ... FromArguments>
|
||||
struct are_function_types_convertible_arguments_helper<
|
||||
std::tuple<FromArguments ...>,
|
||||
std::tuple<>>
|
||||
template <typename... FromArguments>
|
||||
struct are_function_types_convertible_arguments_helper<std::tuple<FromArguments...>, std::tuple<>>
|
||||
{
|
||||
static const bool value = false;
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
// base case - false - too many arguments in To.
|
||||
template <typename ... ToArguments>
|
||||
struct are_function_types_convertible_arguments_helper<
|
||||
std::tuple<>,
|
||||
std::tuple<ToArguments ...>>
|
||||
template <typename... ToArguments>
|
||||
struct are_function_types_convertible_arguments_helper<std::tuple<>, std::tuple<ToArguments...>>
|
||||
{
|
||||
static const bool value = false;
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
// recursive step - valid if the head arguments are compatible and the recursive check on the tails are compatible.
|
||||
template <typename FromArgumentsHead, typename ... FromArgumentsTail, typename ToArgumentsHead, typename ... ToArgumentsTail>
|
||||
template <
|
||||
typename FromArgumentsHead,
|
||||
typename... FromArgumentsTail,
|
||||
typename ToArgumentsHead,
|
||||
typename... ToArgumentsTail>
|
||||
struct are_function_types_convertible_arguments_helper<
|
||||
std::tuple<FromArgumentsHead, FromArgumentsTail ...>,
|
||||
std::tuple<ToArgumentsHead, ToArgumentsTail ...>>
|
||||
std::tuple<FromArgumentsHead, FromArgumentsTail...>,
|
||||
std::tuple<ToArgumentsHead, ToArgumentsTail...>>
|
||||
{
|
||||
static const bool value =
|
||||
std::is_convertible<FromArgumentsHead, ToArgumentsHead>::value &&
|
||||
are_function_types_convertible_arguments_helper<std::tuple<FromArgumentsTail ...>, std::tuple<ToArgumentsTail ...>>::value;
|
||||
static const bool value = std::is_convertible<FromArgumentsHead, ToArgumentsHead>::value
|
||||
&& are_function_types_convertible_arguments_helper<
|
||||
std::tuple<FromArgumentsTail...>,
|
||||
std::tuple<ToArgumentsTail...>>::value;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
// entry point - matches two function types passed in.
|
||||
template <typename FromReturnType, typename ... FromArguments, typename ToReturnType, typename ... ToArguments>
|
||||
struct are_function_types_convertible<FromReturnType(FromArguments ...), ToReturnType(ToArguments ...)>
|
||||
template <typename FromReturnType, typename... FromArguments, typename ToReturnType, typename... ToArguments>
|
||||
struct are_function_types_convertible<FromReturnType(FromArguments...), ToReturnType(ToArguments...)>
|
||||
{
|
||||
static const bool value =
|
||||
std::is_convertible<ToReturnType, FromReturnType>::value &&
|
||||
details::are_function_types_convertible_arguments_helper<
|
||||
std::tuple<FromArguments ...>,
|
||||
std::tuple<ToArguments ...>
|
||||
>::value;
|
||||
static const bool value = std::is_convertible<ToReturnType, FromReturnType>::value
|
||||
&& details::are_function_types_convertible_arguments_helper<
|
||||
std::tuple<FromArguments...>,
|
||||
std::tuple<ToArguments...>>::value;
|
||||
};
|
||||
|
||||
} // namespace Mso
|
||||
|
||||
|
||||
#endif // LIBLET_CPPTYPE_FUNCTIONTYPETRAITS_H
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
A collection of functions to help with restricting the behavior of
|
||||
A collection of functions to help with restricting the behavior of
|
||||
C++ templates based on properties of template parameters. Search the
|
||||
Interweb for "SFINAE" if you want to understand how this works.
|
||||
*/
|
||||
|
@ -28,12 +28,16 @@
|
|||
*
|
||||
* @see MSO_ENABLE_FUNC_IF for enabling a function overload based on a compile-time constraint
|
||||
*/
|
||||
#define MSO_ENABLE_CLASS_IF( ... ) typename std::enable_if< __VA_ARGS__ >::type
|
||||
|
||||
#define MSO_ENABLE_CLASS_IF(...) typename std::enable_if<__VA_ARGS__>::type
|
||||
|
||||
// The Mso::Enabled enumeration is part of the `MSO_ENABLE_FUNC_IF` SFINAE magic.
|
||||
// @see MSO_ENABLE_FUNC_IF
|
||||
namespace Mso { enum class Enabled { _ }; }
|
||||
namespace Mso {
|
||||
enum class Enabled
|
||||
{
|
||||
_
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The `MSO_ENABLE_FUNC_IF` macro allows a function template to apply only when a certain compile-time constraint is
|
||||
|
@ -63,26 +67,23 @@ namespace Mso { enum class Enabled { _ }; }
|
|||
*
|
||||
* @see MSO_ENABLE_CLASS_IF for enabling a partial specialization of a class template based on a compile-time constraint
|
||||
*/
|
||||
#define MSO_ENABLE_FUNC_IF( ... ) typename std::enable_if< __VA_ARGS__, ::Mso::Enabled >::type = ::Mso::Enabled::_
|
||||
|
||||
#define MSO_ENABLE_FUNC_IF(...) typename std::enable_if<__VA_ARGS__, ::Mso::Enabled>::type = ::Mso::Enabled::_
|
||||
|
||||
// Helper macro for constraining function templates to only match certain types via the SFINAE pattern.
|
||||
// Append this macro as the last item in a function template's template parameter list in order to disable
|
||||
// that function template when FROM is not implicitly convertible to TO (note that if what you want is to mimic the
|
||||
// implicit conversion rules for raw pointers, you'll need to specify FROM and TO as pointer types in the macro
|
||||
// invocation, like: MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE( X*, Y* )
|
||||
#define MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE( FROM, TO ) \
|
||||
typename Enable = typename std::enable_if< std::is_convertible< FROM, TO >::value >::type
|
||||
|
||||
#define MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE(FROM, TO) \
|
||||
typename Enable = typename std::enable_if<std::is_convertible<FROM, TO>::value>::type
|
||||
|
||||
// Macro for stamping out embedded typedefs for TYPE.
|
||||
//
|
||||
// This is primarily intended to be invoked within the body of a "smart pointer" class definition, and used in
|
||||
// conjunction with the MSO_IMPLICIT_CONVERSION_[FROM|TO] macros, below.
|
||||
#define MSO_SMART_POINTER_TYPEDEFS( TYPE ) \
|
||||
typedef TYPE ValueType; \
|
||||
typedef ValueType* PointerType;
|
||||
|
||||
#define MSO_SMART_POINTER_TYPEDEFS(TYPE) \
|
||||
typedef TYPE ValueType; \
|
||||
typedef ValueType* PointerType;
|
||||
|
||||
// Helper macros for constraining smart pointer conversions to mimic builtin ptr implicit conversion rules. Use these
|
||||
// with constructor, assignment operator, or cast operators that are implemented as function templates, in order to
|
||||
|
@ -98,9 +99,8 @@ namespace Mso { enum class Enabled { _ }; }
|
|||
//
|
||||
// Use MSO_IMPLICIT_CONVERSION_TO when implementing a cast operator, and MSO_IMPLICIT_CONVERSION_FROM when implementing
|
||||
// a converting constructor or assignment operator.
|
||||
#define MSO_IMPLICIT_CONVERSION_FROM( TYPE ) TYPE, MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE( TYPE*, PointerType )
|
||||
#define MSO_IMPLICIT_CONVERSION_TO( TYPE ) TYPE, MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE( PointerType, TYPE* )
|
||||
|
||||
#define MSO_IMPLICIT_CONVERSION_FROM(TYPE) TYPE, MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE(TYPE*, PointerType)
|
||||
#define MSO_IMPLICIT_CONVERSION_TO(TYPE) TYPE, MSO_ENABLE_IF_IMPLICITLY_CONVERTIBLE(PointerType, TYPE*)
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Various C++ templates to help with compile-time type information.
|
||||
Various C++ templates to help with compile-time type information.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef LIBLET_CPPTYPE_TYPETRAITS_H
|
||||
|
@ -15,179 +15,254 @@
|
|||
namespace Mso {
|
||||
|
||||
/**
|
||||
Empty type used for terminating typelists and other places where
|
||||
a concept of 'no type' is needed.
|
||||
Note that 'no type' is different than 'void'.
|
||||
Empty type used for terminating typelists and other places where
|
||||
a concept of 'no type' is needed.
|
||||
Note that 'no type' is different than 'void'.
|
||||
*/
|
||||
class NilType
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
Type2Type< T >
|
||||
Can be used to overload functions which otherwise would differ only by Return type.
|
||||
e.g.
|
||||
Since following is not allowed,
|
||||
Window* Create();
|
||||
Scrollbar* Create();
|
||||
Type2Type< T >
|
||||
Can be used to overload functions which otherwise would differ only by Return type.
|
||||
e.g.
|
||||
Since following is not allowed,
|
||||
Window* Create();
|
||||
Scrollbar* Create();
|
||||
|
||||
Use:
|
||||
Window* Create( const Type2Type< Window >& );
|
||||
Scrollbar* Create( const Type2Type< Scrollbar >& );
|
||||
Use:
|
||||
Window* Create( const Type2Type< Window >& );
|
||||
Scrollbar* Create( const Type2Type< Scrollbar >& );
|
||||
*/
|
||||
template< typename T >
|
||||
template <typename T>
|
||||
struct Type2Type
|
||||
{
|
||||
typedef T OriginalType;
|
||||
typedef T OriginalType;
|
||||
};
|
||||
|
||||
/**
|
||||
RawTraits can be used in templates that support pointer and non-pointer
|
||||
types to retrieve const and non-const versions.
|
||||
RawTraits can be used in templates that support pointer and non-pointer
|
||||
types to retrieve const and non-const versions.
|
||||
|
||||
T = wchar_t, RawTraits< T >::ArrowType = wchar_t
|
||||
T = wchar_t, RawTraits< T >::AddrType = wchar_t*
|
||||
T = wchar_t, RawTraits< T >::ArrowType = wchar_t
|
||||
T = wchar_t, RawTraits< T >::AddrType = wchar_t*
|
||||
|
||||
T = wchar_t&, RawTraits< T >::ArrowType = wchar_t*
|
||||
T = wchar_t&, RawTraits< T >::AddrType = wchar_t*
|
||||
T = wchar_t&, RawTraits< T >::ArrowType = wchar_t*
|
||||
T = wchar_t&, RawTraits< T >::AddrType = wchar_t*
|
||||
|
||||
T = wchar_t*, RawTraits< T >::ArrowType = wchar_t*
|
||||
T = wchar_t*, RawTraits< T >::AddrType = wchar_t**
|
||||
T = wchar_t*, RawTraits< T >::ArrowType = wchar_t*
|
||||
T = wchar_t*, RawTraits< T >::AddrType = wchar_t**
|
||||
|
||||
T = const wchar_t*, RawTraits< T >::ArrowType = const wchar_t*
|
||||
T = const wchar_t*, RawTraits< T >::AddrType = const wchar_t**
|
||||
T = const wchar_t*, RawTraits< T >::ArrowType = const wchar_t*
|
||||
T = const wchar_t*, RawTraits< T >::AddrType = const wchar_t**
|
||||
*/
|
||||
template< typename T >
|
||||
template <typename T>
|
||||
class RawTraits
|
||||
{
|
||||
{
|
||||
public:
|
||||
using ArrowType = T;
|
||||
using AddrType = T*;
|
||||
using ArrowType = T;
|
||||
using AddrType = T*;
|
||||
|
||||
static T& GetArrowType(T& t) noexcept { return t; }
|
||||
enum R { isPointer = false, isReference = false };
|
||||
};
|
||||
static T& GetArrowType(T& t) noexcept
|
||||
{
|
||||
return t;
|
||||
}
|
||||
enum R
|
||||
{
|
||||
isPointer = false,
|
||||
isReference = false
|
||||
};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
class RawTraits< T& >
|
||||
{
|
||||
template <typename T>
|
||||
class RawTraits<T&>
|
||||
{
|
||||
public:
|
||||
using ArrowType = T*;
|
||||
using AddrType = T*;
|
||||
using ArrowType = T*;
|
||||
using AddrType = T*;
|
||||
|
||||
static T* GetArrowType(T& t) noexcept { return &t; }
|
||||
enum R { isPointer = false, isReference = true };
|
||||
};
|
||||
static T* GetArrowType(T& t) noexcept
|
||||
{
|
||||
return &t;
|
||||
}
|
||||
enum R
|
||||
{
|
||||
isPointer = false,
|
||||
isReference = true
|
||||
};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
class RawTraits< T* >
|
||||
{
|
||||
template <typename T>
|
||||
class RawTraits<T*>
|
||||
{
|
||||
public:
|
||||
using ArrowType = T*;
|
||||
using AddrType = T**;
|
||||
using ArrowType = T*;
|
||||
using AddrType = T**;
|
||||
|
||||
static ArrowType GetArrowType(T* t) noexcept { return t; }
|
||||
enum R { isPointer = true, isReference = false };
|
||||
};
|
||||
static ArrowType GetArrowType(T* t) noexcept
|
||||
{
|
||||
return t;
|
||||
}
|
||||
enum R
|
||||
{
|
||||
isPointer = true,
|
||||
isReference = false
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
EmptyTraits provides methods to deal with whether variables are 'empty'.
|
||||
EmptyTraits provides methods to deal with whether variables are 'empty'.
|
||||
*/
|
||||
|
||||
/**
|
||||
EmptyTraits to use with simple value types
|
||||
EmptyTraits to use with simple value types
|
||||
*/
|
||||
template< typename T >
|
||||
template <typename T>
|
||||
struct EmptyTraits
|
||||
{
|
||||
static T EmptyVal() noexcept { return T(); }
|
||||
static bool IsEmpty(const T& t) noexcept { return (t == EmptyVal()); }
|
||||
static void Empty(T& t) noexcept { t = EmptyVal(); }
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept { Empty(t); }
|
||||
};
|
||||
{
|
||||
static T EmptyVal() noexcept
|
||||
{
|
||||
return T();
|
||||
}
|
||||
static bool IsEmpty(const T& t) noexcept
|
||||
{
|
||||
return (t == EmptyVal());
|
||||
}
|
||||
static void Empty(T& t) noexcept
|
||||
{
|
||||
t = EmptyVal();
|
||||
}
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept
|
||||
{
|
||||
Empty(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
EmptyTraits to use with pointer types
|
||||
EmptyTraits to use with pointer types
|
||||
*/
|
||||
template< typename T >
|
||||
template <typename T>
|
||||
struct EmptyTraits<T*>
|
||||
{
|
||||
static T* EmptyVal() noexcept { return nullptr; }
|
||||
static bool IsEmpty(std::add_const_t<T>* t) noexcept { return (t == EmptyVal()); }
|
||||
static void Empty(T*& t) noexcept { t = EmptyVal(); }
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T*& t) noexcept { Empty(t); }
|
||||
};
|
||||
{
|
||||
static T* EmptyVal() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
static bool IsEmpty(std::add_const_t<T>* t) noexcept
|
||||
{
|
||||
return (t == EmptyVal());
|
||||
}
|
||||
static void Empty(T*& t) noexcept
|
||||
{
|
||||
t = EmptyVal();
|
||||
}
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T*& t) noexcept
|
||||
{
|
||||
Empty(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
EmptyTraits to use with reference types
|
||||
EmptyTraits to use with reference types
|
||||
*/
|
||||
template< typename T >
|
||||
struct EmptyTraits< T& >
|
||||
{
|
||||
// EmptyVal is explicitly not present
|
||||
static bool IsEmpty(const T& /*t*/) noexcept { return false; }
|
||||
// Empty is explicitly not present
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& /*t*/) noexcept {} // Can't empty a reference
|
||||
};
|
||||
template <typename T>
|
||||
struct EmptyTraits<T&>
|
||||
{
|
||||
// EmptyVal is explicitly not present
|
||||
static bool IsEmpty(const T& /*t*/) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Empty is explicitly not present
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& /*t*/) noexcept {} // Can't empty a reference
|
||||
};
|
||||
|
||||
/**
|
||||
Custom EmptyTraits to use with non-zero defaults, TEmptyVal must be a constant
|
||||
Custom EmptyTraits to use with non-zero defaults, TEmptyVal must be a constant
|
||||
*/
|
||||
template< typename T, T TEmptyVal >
|
||||
template <typename T, T TEmptyVal>
|
||||
struct CustomEmptyTraits
|
||||
{
|
||||
static T EmptyVal() noexcept { return TEmptyVal; }
|
||||
static bool IsEmpty(const T& t) noexcept { return (t == EmptyVal()); }
|
||||
static void Empty(T& t) noexcept { t = EmptyVal(); }
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept { Empty(t); }
|
||||
};
|
||||
{
|
||||
static T EmptyVal() noexcept
|
||||
{
|
||||
return TEmptyVal;
|
||||
}
|
||||
static bool IsEmpty(const T& t) noexcept
|
||||
{
|
||||
return (t == EmptyVal());
|
||||
}
|
||||
static void Empty(T& t) noexcept
|
||||
{
|
||||
t = EmptyVal();
|
||||
}
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept
|
||||
{
|
||||
Empty(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Custom EmptyTraits to use with zero-init structures like STATSTG
|
||||
Custom EmptyTraits to use with zero-init structures like STATSTG
|
||||
*/
|
||||
template< typename T >
|
||||
template <typename T>
|
||||
struct PODEmptyTraits
|
||||
{
|
||||
static T EmptyVal() noexcept { T t = {0}; return t; }
|
||||
static bool IsEmpty(const T& t) noexcept { T tEmpty = EmptyVal(); return memcmp(&t, &tEmpty, sizeof(t)) == 0; }
|
||||
static void Empty(T& t) noexcept { t = EmptyVal(); }
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept { Empty(t); }
|
||||
};
|
||||
{
|
||||
static T EmptyVal() noexcept
|
||||
{
|
||||
T t = {0};
|
||||
return t;
|
||||
}
|
||||
static bool IsEmpty(const T& t) noexcept
|
||||
{
|
||||
T tEmpty = EmptyVal();
|
||||
return memcmp(&t, &tEmpty, sizeof(t)) == 0;
|
||||
}
|
||||
static void Empty(T& t) noexcept
|
||||
{
|
||||
t = EmptyVal();
|
||||
}
|
||||
DECLSPEC_DEPRECATED static void UnsafeEmpty(T& t) noexcept
|
||||
{
|
||||
Empty(t);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Helper for use with template arguments to force callers to specify type explicitly.
|
||||
Helper for use with template arguments to force callers to specify type explicitly.
|
||||
|
||||
template <typename T> Bar(const typename Mso::DisableTypeDeduction<T>::type& value)
|
||||
[...]
|
||||
Bar(someInt); // compile fails with C2783
|
||||
Bar<int>(someInt); // compiles successfully
|
||||
template <typename T> Bar(const typename Mso::DisableTypeDeduction<T>::type& value)
|
||||
[...]
|
||||
Bar(someInt); // compile fails with C2783
|
||||
Bar<int>(someInt); // compiles successfully
|
||||
*/
|
||||
template <typename T>
|
||||
struct DisableTypeDeduction
|
||||
{
|
||||
using type = T;
|
||||
using type = T;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
OverloadTag can be used be used to disambiguate two function overloads.
|
||||
OverloadTag instance passed to an overloaded function will give a higher priority to a function accepting
|
||||
OverloadTagP1 versus function that accepts OverloadTagP2.
|
||||
OverloadTag can be used be used to disambiguate two function overloads.
|
||||
OverloadTag instance passed to an overloaded function will give a higher priority to a function accepting
|
||||
OverloadTagP1 versus function that accepts OverloadTagP2.
|
||||
|
||||
For example, we can use it to call a method if it is defined, or do something else if it is not defined.
|
||||
template <typename T> auto Foo(Mso::OverloadTagP1, T* x) -> decltype(x->Bar()) { return x->Bar(); }
|
||||
template <typename T> int Foo(Mso::OverloadTagP2, T* x) { return DefaultBar(x); }
|
||||
For example, we can use it to call a method if it is defined, or do something else if it is not defined.
|
||||
template <typename T> auto Foo(Mso::OverloadTagP1, T* x) -> decltype(x->Bar()) { return x->Bar(); }
|
||||
template <typename T> int Foo(Mso::OverloadTagP2, T* x) { return DefaultBar(x); }
|
||||
|
||||
The right function overload will be chosen depending on whether type T has Bar() method or not:
|
||||
Foo(Mso::OverloadTag(), &x);
|
||||
The right function overload will be chosen depending on whether type T has Bar() method or not:
|
||||
Foo(Mso::OverloadTag(), &x);
|
||||
*/
|
||||
using OverloadTagP1 = int;
|
||||
struct OverloadTagP2 { OverloadTagP2(OverloadTagP1) noexcept {} };
|
||||
struct OverloadTagP2
|
||||
{
|
||||
OverloadTagP2(OverloadTagP1) noexcept {}
|
||||
};
|
||||
using OverloadTag = OverloadTagP1;
|
||||
|
||||
|
||||
/**
|
||||
Identifies the largest type from a variadic set of types.
|
||||
Identifies the largest type from a variadic set of types.
|
||||
*/
|
||||
template <typename... Ts>
|
||||
struct LargestType;
|
||||
|
@ -195,13 +270,13 @@ struct LargestType;
|
|||
template <typename T>
|
||||
struct LargestType<T>
|
||||
{
|
||||
using type = T;
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
struct LargestType<T1, T2, Ts...>
|
||||
{
|
||||
using type = typename LargestType<typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>::type, Ts...>::type;
|
||||
using type = typename LargestType<typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>::type, Ts...>::type;
|
||||
};
|
||||
|
||||
} // namespace Mso
|
||||
|
|
|
@ -18,29 +18,19 @@ DECLSPEC_NORETURN void CrashWithRecovery(uint32_t tag) noexcept;
|
|||
|
||||
// Asserts first so the dev can easily attach. If already attached, it does not assert
|
||||
// because bringing up the assert dialog can cause more code to run (OM:278842).
|
||||
#define VerifyElseCrashSzTag(f, sz, tag) \
|
||||
Statement( \
|
||||
if (!(f)) \
|
||||
{ \
|
||||
AssertSz1Tag(DisableVecAssert(), "Fatal error: %s", (sz), (tag)); \
|
||||
CrashWithRecovery(tag); \
|
||||
} \
|
||||
)
|
||||
#define VerifyElseCrashSzTag(f, sz, tag) \
|
||||
Statement(if (!(f)) { \
|
||||
AssertSz1Tag(DisableVecAssert(), "Fatal error: %s", (sz), (tag)); \
|
||||
CrashWithRecovery(tag); \
|
||||
})
|
||||
|
||||
#define VerifySucceededElseCrashTag(hr, tag) \
|
||||
Statement( \
|
||||
const HRESULT _vec_hr_ = (hr); \
|
||||
if (FAILED(_vec_hr_)) \
|
||||
{ \
|
||||
AssertSzTag(DisableVecAssert(), "Failed: " #hr, tag); \
|
||||
CrashWithRecovery(/*static_cast<int32_t>(_vec_hr_), */tag); \
|
||||
})
|
||||
#define VerifySucceededElseCrashTag(hr, tag) \
|
||||
Statement(const HRESULT _vec_hr_ = (hr); if (FAILED(_vec_hr_)) { \
|
||||
AssertSzTag(DisableVecAssert(), "Failed: " #hr, tag); \
|
||||
CrashWithRecovery(/*static_cast<int32_t>(_vec_hr_), */ tag); \
|
||||
})
|
||||
|
||||
#define VerifyAllocElseCrashTag(ptr, tag) \
|
||||
Statement( \
|
||||
if (!(ptr)) \
|
||||
CrashWithRecovery(tag); \
|
||||
)
|
||||
#define VerifyAllocElseCrashTag(ptr, tag) Statement(if (!(ptr)) CrashWithRecovery(tag);)
|
||||
|
||||
#define CrashWithRecoveryOnOOM() CrashWithRecovery(0)
|
||||
|
||||
|
|
|
@ -11,28 +11,28 @@
|
|||
|
||||
void CrashWithRecovery(uint32_t /*tag*/) noexcept
|
||||
{
|
||||
*((volatile int*)0) = 1;
|
||||
__builtin_trap();
|
||||
*((volatile int*)0) = 1;
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int CrashHandler(const EXCEPTION_POINTERS* excPtr, uint32_t tag) noexcept
|
||||
{
|
||||
excPtr->ExceptionRecord->ExceptionCode = tag;
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
excPtr->ExceptionRecord->ExceptionCode = tag;
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void CrashWithRecovery(uint32_t tag) noexcept
|
||||
{
|
||||
__try
|
||||
{
|
||||
*((int*)0) = 1;
|
||||
}
|
||||
__except (CrashHandler(GetExceptionInformation(), tag))
|
||||
{
|
||||
}
|
||||
__fastfail(tag);
|
||||
__try
|
||||
{
|
||||
*((int*)0) = 1;
|
||||
}
|
||||
__except (CrashHandler(GetExceptionInformation(), tag))
|
||||
{
|
||||
}
|
||||
__fastfail(tag);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -2,13 +2,13 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
API for debug asserts. Must support C callers
|
||||
API for debug asserts. Must support C callers
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef LIBLET_DEBUGASSERTAPI_DEBUGASSERTAPI_H
|
||||
#define LIBLET_DEBUGASSERTAPI_DEBUGASSERTAPI_H
|
||||
#ifndef RC_INVOKED
|
||||
#pragma pack(push,8)
|
||||
#pragma pack(push, 8)
|
||||
|
||||
#include <compilerAdapters/cppMacros.h>
|
||||
#include <compilerAdapters/functionDecorations.h>
|
||||
|
@ -22,22 +22,22 @@ Basic debug assert macros to use.
|
|||
Assert: evaluate in debug, call the debug handler on failure
|
||||
AssertDo: always evaluate, call the debug handler on failure
|
||||
*/
|
||||
#define Assert(f) AssertTag(f, UNTAGGED)
|
||||
#define AssertSz(f, _sz) AssertSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertSz0(f, _sz) AssertSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertSz1(f, _sz, a) AssertSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define AssertSz2(f, _sz, a, b) AssertSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
#define AssertSz3(f, _sz, a, b, c) AssertSz3Tag(f, _sz, a, b, c, UNTAGGED)
|
||||
#define AssertSz4(f, _sz, a, b, c, d) AssertSz4Tag(f, _sz, a, b, c, d, UNTAGGED)
|
||||
#define AssertSz5(f, _sz, a, b, c, d, e) AssertSz5Tag(f, _sz, a, b, c, d, e, UNTAGGED)
|
||||
#define Assert(f) AssertTag(f, UNTAGGED)
|
||||
#define AssertSz(f, _sz) AssertSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertSz0(f, _sz) AssertSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertSz1(f, _sz, a) AssertSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define AssertSz2(f, _sz, a, b) AssertSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
#define AssertSz3(f, _sz, a, b, c) AssertSz3Tag(f, _sz, a, b, c, UNTAGGED)
|
||||
#define AssertSz4(f, _sz, a, b, c, d) AssertSz4Tag(f, _sz, a, b, c, d, UNTAGGED)
|
||||
#define AssertSz5(f, _sz, a, b, c, d, e) AssertSz5Tag(f, _sz, a, b, c, d, e, UNTAGGED)
|
||||
|
||||
#define AssertDo(f) AssertDoTag(f, UNTAGGED)
|
||||
#define AssertDoSz(f, _sz) AssertDoSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertDoSz1(f, _sz, a) AssertDoSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define AssertDoSz2(f, _sz, a, b) AssertDoSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
#define AssertDoSz3(f, _sz, a, b, c) AssertDoSz3Tag(f, _sz, a, b, c, UNTAGGED)
|
||||
#define AssertDoSz4(f, _sz, a, b, c, d) AssertDoSz4Tag(f, _sz, a, b, c, d, UNTAGGED)
|
||||
#define AssertDoSz5(f, _sz, a, b, c, d, e) AssertDoSz5Tag(f, _sz, a, b, c, d, e, UNTAGGED)
|
||||
#define AssertDo(f) AssertDoTag(f, UNTAGGED)
|
||||
#define AssertDoSz(f, _sz) AssertDoSzTag(f, _sz, UNTAGGED)
|
||||
#define AssertDoSz1(f, _sz, a) AssertDoSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define AssertDoSz2(f, _sz, a, b) AssertDoSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
#define AssertDoSz3(f, _sz, a, b, c) AssertDoSz3Tag(f, _sz, a, b, c, UNTAGGED)
|
||||
#define AssertDoSz4(f, _sz, a, b, c, d) AssertDoSz4Tag(f, _sz, a, b, c, d, UNTAGGED)
|
||||
#define AssertDoSz5(f, _sz, a, b, c, d, e) AssertDoSz5Tag(f, _sz, a, b, c, d, e, UNTAGGED)
|
||||
|
||||
/**
|
||||
The following macros can be used to assert inside an expression.
|
||||
|
@ -46,39 +46,39 @@ LATER: add more if needed or ShipAssert versions.
|
|||
|
||||
if (FAssertDo(f)) { ... } // same as "if (f) { ... } else Assert(false);"
|
||||
*/
|
||||
#define FAssertDo(f) FAssertDoTag(f, UNTAGGED)
|
||||
#define FAssertDoSz(f, _sz) FAssertDoSzTag(f, _sz, UNTAGGED)
|
||||
#define FAssertDoSz1(f, _sz, a) FAssertDoSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define FAssertDoSz2(f, _sz, a, b) FAssertDoSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
#define FAssertDo(f) FAssertDoTag(f, UNTAGGED)
|
||||
#define FAssertDoSz(f, _sz) FAssertDoSzTag(f, _sz, UNTAGGED)
|
||||
#define FAssertDoSz1(f, _sz, a) FAssertDoSz1Tag(f, _sz, a, UNTAGGED)
|
||||
#define FAssertDoSz2(f, _sz, a, b) FAssertDoSz2Tag(f, _sz, a, b, UNTAGGED)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AssertDetails_SzCast(sz) static_cast<const char*>(sz)
|
||||
#else
|
||||
#define AssertDetails_SzCast(sz) (const char*) (sz)
|
||||
#define AssertDetails_SzCast(sz) (const char*)(sz)
|
||||
#endif
|
||||
|
||||
/**
|
||||
Suppressed warnings in Assert macros:
|
||||
C4127 - if/while loop condition is a constant
|
||||
C4018 - signed/unsigned compare was converted to unsigned/unsigned compare
|
||||
C4389 - operation involved signed/unsigned variables
|
||||
6239 - OACR left expression is always false
|
||||
25011 - OACR missing 'break' or '__fallthrough' statement
|
||||
25037 - OACR expression is always false
|
||||
25038 - OACR expression is always false
|
||||
25039 - OACR expression is always true
|
||||
25041 - OACR if/while loop condition is true
|
||||
25042 - OACR if/while loop condition is false
|
||||
25064 - OACR function called twice in macro
|
||||
C4127 - if/while loop condition is a constant
|
||||
C4018 - signed/unsigned compare was converted to unsigned/unsigned compare
|
||||
C4389 - operation involved signed/unsigned variables
|
||||
6239 - OACR left expression is always false
|
||||
25011 - OACR missing 'break' or '__fallthrough' statement
|
||||
25037 - OACR expression is always false
|
||||
25038 - OACR expression is always false
|
||||
25039 - OACR expression is always true
|
||||
25041 - OACR if/while loop condition is true
|
||||
25042 - OACR if/while loop condition is false
|
||||
25064 - OACR function called twice in macro
|
||||
*/
|
||||
#define AssertDetails_Statement_Begin \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable:4127 4018 4389 6239 25037 25038 25039 25041 25042 25064 25306)) \
|
||||
do {
|
||||
#define AssertDetails_Statement_Begin \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable : 4127 4018 4389 6239 25037 25038 25039 25041 25042 25064 25306)) do \
|
||||
{
|
||||
#define AssertDetails_Statement_End \
|
||||
} while(0) \
|
||||
__pragma(warning(suppress:25011)) \
|
||||
__pragma(warning(pop)) \
|
||||
} \
|
||||
while (0) \
|
||||
__pragma(warning(suppress : 25011)) __pragma(warning(pop))
|
||||
|
||||
// NOTE: OACR_ASSUME uses the Assert macro, so oacr.h must be included after Assert is defined
|
||||
#include <oacr.h>
|
||||
|
@ -89,7 +89,7 @@ FUTURE: get rid of this and just use void*
|
|||
*/
|
||||
typedef struct _MSORADDR
|
||||
{
|
||||
void* pfnCaller;
|
||||
void* pfnCaller;
|
||||
} MSORADDR;
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ using MsoAssertParamsType = const struct _MsoAssertParams&;
|
|||
#else // __cplusplus
|
||||
typedef struct _MsoAssertParams* MsoAssertParamsType;
|
||||
#ifdef DEBUG
|
||||
#define DeclareMsoAssertParams(...) (__pragma(warning(suppress:4204)) MsoAssertParams params = { __VA_ARGS__ })
|
||||
#define DeclareMsoAssertParams(...) (__pragma(warning(suppress : 4204)) MsoAssertParams params = {__VA_ARGS__})
|
||||
#else
|
||||
#define DeclareMsoAssertParams(...)
|
||||
#endif // DEBUG
|
||||
|
@ -129,50 +129,69 @@ This structure is duplicated in MotifTest project.
|
|||
*/
|
||||
typedef struct _MsoAssertParams
|
||||
{
|
||||
uint32_t dwTag;
|
||||
const char* szFile;
|
||||
uint32_t iLine;
|
||||
const char* szTitle;
|
||||
const MSORADDR* rgCallstack;
|
||||
uint32_t cCallstack;
|
||||
const char* szCondition;
|
||||
uint32_t dwTag;
|
||||
const char* szFile;
|
||||
uint32_t iLine;
|
||||
const char* szTitle;
|
||||
const MSORADDR* rgCallstack;
|
||||
uint32_t cCallstack;
|
||||
const char* szCondition;
|
||||
|
||||
// Skip this many frames if a callstack is generated.
|
||||
// Skip this many frames if a callstack is generated.
|
||||
#ifdef __cplusplus
|
||||
mutable
|
||||
mutable
|
||||
#endif // __cplusplus
|
||||
uint32_t framesToSkip;
|
||||
uint32_t framesToSkip;
|
||||
|
||||
#ifdef __cplusplus
|
||||
explicit _MsoAssertParams(uint32_t dwTagLocal = UNTAGGED,
|
||||
_In_z_ const char* szFileLocal = __FILE__, uint32_t iLineLocal = __LINE__,
|
||||
_In_opt_z_ const char* szTitleLocal = nullptr,
|
||||
_In_opt_count_(cCallstackLocal) const MSORADDR* rgCallstackLocal = nullptr, uint32_t cCallstackLocal = 0,
|
||||
_In_opt_z_ const char* szConditionLocal = "false") MSONOEXCEPT :
|
||||
dwTag(dwTagLocal), szFile(szFileLocal), iLine(iLineLocal), szTitle(szTitleLocal),
|
||||
rgCallstack(rgCallstackLocal), cCallstack(cCallstackLocal),
|
||||
szCondition(szConditionLocal), framesToSkip(0)
|
||||
{
|
||||
}
|
||||
explicit _MsoAssertParams(
|
||||
uint32_t dwTagLocal = UNTAGGED,
|
||||
_In_z_ const char* szFileLocal = __FILE__,
|
||||
uint32_t iLineLocal = __LINE__,
|
||||
_In_opt_z_ const char* szTitleLocal = nullptr,
|
||||
_In_opt_count_(cCallstackLocal) const MSORADDR* rgCallstackLocal = nullptr,
|
||||
uint32_t cCallstackLocal = 0,
|
||||
_In_opt_z_ const char* szConditionLocal = "false") MSONOEXCEPT
|
||||
: dwTag(dwTagLocal)
|
||||
, szFile(szFileLocal)
|
||||
, iLine(iLineLocal)
|
||||
, szTitle(szTitleLocal)
|
||||
, rgCallstack(rgCallstackLocal)
|
||||
, cCallstack(cCallstackLocal)
|
||||
, szCondition(szConditionLocal)
|
||||
, framesToSkip(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit _MsoAssertParams(_In_z_ const char* szConditionLocal, uint32_t dwTagLocal, _In_z_ const char* szFileLocal, int32_t iLineLocal) MSONOEXCEPT :
|
||||
dwTag(dwTagLocal), szFile(szFileLocal), iLine(static_cast<uint32_t>(iLineLocal)), szTitle(nullptr),
|
||||
rgCallstack(nullptr), cCallstack(0), szCondition(szConditionLocal), framesToSkip(0)
|
||||
{
|
||||
}
|
||||
explicit _MsoAssertParams(
|
||||
_In_z_ const char* szConditionLocal,
|
||||
uint32_t dwTagLocal,
|
||||
_In_z_ const char* szFileLocal,
|
||||
int32_t iLineLocal) MSONOEXCEPT
|
||||
: dwTag(dwTagLocal)
|
||||
, szFile(szFileLocal)
|
||||
, iLine(static_cast<uint32_t>(iLineLocal))
|
||||
, szTitle(nullptr)
|
||||
, rgCallstack(nullptr)
|
||||
, cCallstack(0)
|
||||
, szCondition(szConditionLocal)
|
||||
, framesToSkip(0)
|
||||
{
|
||||
}
|
||||
#endif // __cplusplus
|
||||
} MsoAssertParams;
|
||||
|
||||
/**
|
||||
Function for handling asserts with strings. Return value is AssertResult.
|
||||
Note: this function has to be callable from C code
|
||||
Function for handling asserts with strings. Return value is AssertResult.
|
||||
Note: this function has to be callable from C code
|
||||
*/
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif // C++
|
||||
LIBLET_PUBLICAPI int32_t MsoAssertSzTagProc(MsoAssertParamsType params, _Printf_format_string_ const char* szFmt, va_list argList) MSONOEXCEPT;
|
||||
LIBLET_PUBLICAPI int32_t
|
||||
MsoAssertSzTagProc(MsoAssertParamsType params, _Printf_format_string_ const char* szFmt, va_list argList) MSONOEXCEPT;
|
||||
#if defined(__cplusplus)
|
||||
|
||||
}
|
||||
#endif // C++
|
||||
|
||||
|
@ -182,99 +201,95 @@ LIBLET_PUBLICAPI int32_t MsoAssertSzTagProc(MsoAssertParamsType params, _Printf_
|
|||
|
||||
#ifdef DEBUG
|
||||
#ifdef __cplusplus
|
||||
namespace Mso {
|
||||
namespace DebugAsserts {
|
||||
namespace Mso { namespace DebugAsserts {
|
||||
|
||||
using AssertIgnorer = bool(*)(const MsoAssertParams& params, const char* szMsg);
|
||||
using AssertIgnorer = bool (*)(const MsoAssertParams& params, const char* szMsg);
|
||||
|
||||
/**
|
||||
Add an assert ignorer for this process.
|
||||
Note: this API is not thread-safe.
|
||||
Add an assert ignorer for this process.
|
||||
Note: this API is not thread-safe.
|
||||
*/
|
||||
LIBLET_PUBLICAPI void AddAssertIgnorer(AssertIgnorer ignorer) noexcept;
|
||||
|
||||
/**
|
||||
Remove an assert ignorer for this process.
|
||||
Note: this API is not thread-safe.
|
||||
Remove an assert ignorer for this process.
|
||||
Note: this API is not thread-safe.
|
||||
*/
|
||||
LIBLET_PUBLICAPI void RemoveAssertIgnorer(AssertIgnorer ignorer) noexcept;
|
||||
|
||||
struct AutoRegisterAssertIgnorer
|
||||
{
|
||||
AutoRegisterAssertIgnorer(AssertIgnorer ignorer) noexcept : m_ignorer(ignorer)
|
||||
{
|
||||
AddAssertIgnorer(m_ignorer);
|
||||
}
|
||||
AutoRegisterAssertIgnorer(AssertIgnorer ignorer) noexcept : m_ignorer(ignorer)
|
||||
{
|
||||
AddAssertIgnorer(m_ignorer);
|
||||
}
|
||||
|
||||
~AutoRegisterAssertIgnorer() noexcept
|
||||
{
|
||||
RemoveAssertIgnorer(m_ignorer);
|
||||
}
|
||||
~AutoRegisterAssertIgnorer() noexcept
|
||||
{
|
||||
RemoveAssertIgnorer(m_ignorer);
|
||||
}
|
||||
|
||||
private:
|
||||
AssertIgnorer m_ignorer;
|
||||
AssertIgnorer m_ignorer;
|
||||
};
|
||||
|
||||
using AssertListener = void(*)(const MsoAssertParams& params, const char* szMsg);
|
||||
using AssertListener = void (*)(const MsoAssertParams& params, const char* szMsg);
|
||||
|
||||
/**
|
||||
Add an assert listener for this process.
|
||||
Note: this API is not thread-safe.
|
||||
Add an assert listener for this process.
|
||||
Note: this API is not thread-safe.
|
||||
*/
|
||||
LIBLET_PUBLICAPI void AddAssertListener(AssertListener listener) noexcept;
|
||||
|
||||
/**
|
||||
Remove a previously registered assert listener for this process.
|
||||
Note: this API is not thread-safe.
|
||||
Remove a previously registered assert listener for this process.
|
||||
Note: this API is not thread-safe.
|
||||
*/
|
||||
LIBLET_PUBLICAPI void RemoveAssertListener(AssertListener listener) noexcept;
|
||||
|
||||
struct AutoRegisterAssertListener
|
||||
{
|
||||
AutoRegisterAssertListener(AssertListener listener) noexcept : m_listener(listener)
|
||||
{
|
||||
AddAssertListener(m_listener);
|
||||
}
|
||||
AutoRegisterAssertListener(AssertListener listener) noexcept : m_listener(listener)
|
||||
{
|
||||
AddAssertListener(m_listener);
|
||||
}
|
||||
|
||||
~AutoRegisterAssertListener() noexcept
|
||||
{
|
||||
RemoveAssertListener(m_listener);
|
||||
}
|
||||
~AutoRegisterAssertListener() noexcept
|
||||
{
|
||||
RemoveAssertListener(m_listener);
|
||||
}
|
||||
|
||||
private:
|
||||
AssertListener m_listener;
|
||||
AssertListener m_listener;
|
||||
};
|
||||
|
||||
using AssertHandler = int32_t(*)(const MsoAssertParams& params, const char* szMsg);
|
||||
using AssertHandler = int32_t (*)(const MsoAssertParams& params, const char* szMsg);
|
||||
|
||||
/**
|
||||
Set the assert handler for this process. The previous handler is returned.
|
||||
Note: this API is not thread-safe.
|
||||
Set the assert handler for this process. The previous handler is returned.
|
||||
Note: this API is not thread-safe.
|
||||
*/
|
||||
LIBLET_PUBLICAPI AssertHandler SetAssertHandler(AssertHandler handler) noexcept;
|
||||
|
||||
/**
|
||||
Get the current assert handler for this process.
|
||||
Get the current assert handler for this process.
|
||||
*/
|
||||
LIBLET_PUBLICAPI AssertHandler GetAssertHandler() noexcept;
|
||||
|
||||
struct AutoRegisterAssertHandler
|
||||
{
|
||||
AutoRegisterAssertHandler(AssertHandler handler) noexcept : m_previous(SetAssertHandler(handler))
|
||||
{
|
||||
}
|
||||
AutoRegisterAssertHandler(AssertHandler handler) noexcept : m_previous(SetAssertHandler(handler)) {}
|
||||
|
||||
~AutoRegisterAssertHandler() noexcept
|
||||
{
|
||||
SetAssertHandler(m_previous);
|
||||
}
|
||||
~AutoRegisterAssertHandler() noexcept
|
||||
{
|
||||
SetAssertHandler(m_previous);
|
||||
}
|
||||
|
||||
private:
|
||||
AssertHandler m_previous;
|
||||
AssertHandler m_previous;
|
||||
};
|
||||
|
||||
} // DebugAsserts
|
||||
} // Mso
|
||||
}} // namespace Mso::DebugAsserts
|
||||
#endif // __cplusplus
|
||||
#endif // DEBUG
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Private implementation details for debug assert macros
|
||||
This header should not be included directly - only through debugAssertApi.h
|
||||
Private implementation details for debug assert macros
|
||||
This header should not be included directly - only through debugAssertApi.h
|
||||
|
||||
Note that C4706 is purposely left enabled. Assignments in inside Assert
|
||||
statements are almost always a bug (e.g. Assert(m_foo = fooBar))
|
||||
Note that C4706 is purposely left enabled. Assignments in inside Assert
|
||||
statements are almost always a bug (e.g. Assert(m_foo = fooBar))
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef LIBLET_DEBUGASSERTAPI_DEBUGASSERTDETAILS_H
|
||||
|
@ -18,19 +18,25 @@
|
|||
/**
|
||||
Mappings for core Debug asserts.
|
||||
*/
|
||||
#pragma warning(suppress:4005) // macro redefinition :(
|
||||
#define AssertTag(f, tag) AssertAnnotatedSzNTagImpl(f, L## #f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), "%s", #f)
|
||||
#define AssertSzTag(f, sz, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), "%s", AssertDetails_SzCast(sz))
|
||||
#define AssertSz1Tag(f, sz, a, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a)
|
||||
#define AssertSz2Tag(f, sz, a, b, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b)
|
||||
#define AssertSz3Tag(f, sz, a, b, c, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c)
|
||||
#define AssertSz4Tag(f, sz, a, b, c, d, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c, d)
|
||||
#define AssertSz5Tag(f, sz, a, b, c, d, e, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c, d, e)
|
||||
#pragma warning(suppress : 4005) // macro redefinition :(
|
||||
#define AssertTag(f, tag) \
|
||||
AssertAnnotatedSzNTagImpl(f, L## #f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), "%s", #f)
|
||||
#define AssertSzTag(f, sz, tag) \
|
||||
AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), "%s", AssertDetails_SzCast(sz))
|
||||
#define AssertSz1Tag(f, sz, a, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a)
|
||||
#define AssertSz2Tag(f, sz, a, b, tag) AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b)
|
||||
#define AssertSz3Tag(f, sz, a, b, c, tag) \
|
||||
AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c)
|
||||
#define AssertSz4Tag(f, sz, a, b, c, d, tag) \
|
||||
AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c, d)
|
||||
#define AssertSz5Tag(f, sz, a, b, c, d, e, tag) \
|
||||
AssertSzNTagImpl(f, InlineMsoAssertParams(#f, tag, __FILE__, __LINE__), sz, a, b, c, d, e)
|
||||
|
||||
#define FAssertDoTag(f, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L## #f, "%s", #f)
|
||||
#define FAssertDoSzTag(f, sz, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, "%s", AssertDetails_SzCast(sz))
|
||||
#define FAssertDoSz1Tag(f, sz, a, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, sz, a)
|
||||
#define FAssertDoSz2Tag(f, sz, a, b, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, sz, a, b)
|
||||
#define FAssertDoTag(f, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L## #f, "%s", #f)
|
||||
#define FAssertDoSzTag(f, sz, tag) \
|
||||
FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, "%s", AssertDetails_SzCast(sz))
|
||||
#define FAssertDoSz1Tag(f, sz, a, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, sz, a)
|
||||
#define FAssertDoSz2Tag(f, sz, a, b, tag) FAssertDoSzNTagImpl(f, tag, __FILE__, __LINE__, L##sz, sz, a, b)
|
||||
|
||||
/**
|
||||
Return values from MsoAssertSzTagProc(Inline)(2)
|
||||
|
@ -41,21 +47,18 @@ static const uint32_t c_assertAlwaysIgnore = 2;
|
|||
|
||||
#ifdef __cplusplus
|
||||
#ifdef DEBUG
|
||||
namespace Mso {
|
||||
namespace DebugAsserts {
|
||||
namespace Mso { namespace DebugAsserts {
|
||||
|
||||
/**
|
||||
Return values from MsoAssertSzTagProc(Inline)(2)
|
||||
Return values from MsoAssertSzTagProc(Inline)(2)
|
||||
*/
|
||||
__pragma(warning(suppress:4472)) enum class AssertResult : uint32_t
|
||||
{
|
||||
Ignore = c_assertIgnore,
|
||||
Break = c_assertDebugBreak,
|
||||
AlwaysIgnore = c_assertAlwaysIgnore,
|
||||
__pragma(warning(suppress : 4472)) enum class AssertResult : uint32_t {
|
||||
Ignore = c_assertIgnore,
|
||||
Break = c_assertDebugBreak,
|
||||
AlwaysIgnore = c_assertAlwaysIgnore,
|
||||
};
|
||||
|
||||
} // DebugAsserts
|
||||
} // Mso
|
||||
}} // namespace Mso::DebugAsserts
|
||||
#endif // DEBUG
|
||||
#endif // C++
|
||||
|
||||
|
@ -63,58 +66,57 @@ __pragma(warning(suppress:4472)) enum class AssertResult : uint32_t
|
|||
#if defined(__clang__) || defined(__cplusplus_cli) || defined(__INTELLISENSE__)
|
||||
#define AssertBreak(wzMsg) __debugbreak()
|
||||
#elif defined(_DBGRAISEASSERTIONFAILURE_)
|
||||
#define AssertBreak(wzMsg) \
|
||||
(__annotation(L"Debug", L"AssertFail", wzMsg), DbgRaiseAssertionFailure())
|
||||
#define AssertBreak(wzMsg) (__annotation(L"Debug", L"AssertFail", wzMsg), DbgRaiseAssertionFailure())
|
||||
#else
|
||||
#define AssertBreak(wzMsg) \
|
||||
(__annotation(L"Debug", L"AssertFail", wzMsg), __debugbreak())
|
||||
#define AssertBreak(wzMsg) (__annotation(L"Debug", L"AssertFail", wzMsg), __debugbreak())
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
/**
|
||||
MsoAssertSzTagProcInline
|
||||
MsoAssertSzTagProcInline
|
||||
|
||||
Converts variable arguments into the arg list format
|
||||
Converts variable arguments into the arg list format
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
static
|
||||
#endif
|
||||
__inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_format_string_ const char* szFmt, ...) MSONOEXCEPT
|
||||
{
|
||||
va_list argList;
|
||||
va_start(argList, szFmt);
|
||||
AccessMsoAssertParams(params)->framesToSkip++;
|
||||
return MsoAssertSzTagProc(params, szFmt, argList);
|
||||
__inline int32_t
|
||||
MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_format_string_ const char* szFmt, ...) MSONOEXCEPT
|
||||
{
|
||||
va_list argList;
|
||||
va_start(argList, szFmt);
|
||||
AccessMsoAssertParams(params)->framesToSkip++;
|
||||
return MsoAssertSzTagProc(params, szFmt, argList);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
MsoAssertSzTagProcInline2
|
||||
MsoAssertSzTagProcInline2
|
||||
|
||||
Converts variable arguments into the arg list format
|
||||
Converts variable arguments into the arg list format
|
||||
|
||||
Use a lamba function to generate a unique function providing storage of
|
||||
_fIgnore_ (for in-proc based assert management) and unique break address.
|
||||
Use a lamba function to generate a unique function providing storage of
|
||||
_fIgnore_ (for in-proc based assert management) and unique break address.
|
||||
*/
|
||||
#define MsoAssertSzTagProcInline2(dwTag, szFile, iLine, wzAnnotation, szFmt, ...) \
|
||||
[&]() -> int32_t { \
|
||||
__pragma(warning(suppress:4456)) /* declaration of '_fIgnore_' hides previous local declaration */ \
|
||||
static int32_t _fIgnore_ = false; \
|
||||
if (!_fIgnore_) \
|
||||
{ \
|
||||
__pragma(warning(suppress:4456)) /* declaration of 'params' hides previous local declaration */ \
|
||||
DeclareMsoAssertParams(dwTag, szFile, iLine); \
|
||||
params.framesToSkip++; \
|
||||
const int32_t _assertResult_ = MsoAssertSzTagProcInline(PassMsoAssertParams(params), szFmt, __VA_ARGS__); \
|
||||
__pragma(warning(suppress:4700)) /* MSVC is unhappy with this used in a loop conditional */ \
|
||||
if(_assertResult_ == c_assertDebugBreak) \
|
||||
{ \
|
||||
AssertBreak(wzAnnotation); \
|
||||
} \
|
||||
_fIgnore_ = (_assertResult_ == c_assertAlwaysIgnore); \
|
||||
} \
|
||||
return FALSE; \
|
||||
}()
|
||||
#define MsoAssertSzTagProcInline2(dwTag, szFile, iLine, wzAnnotation, szFmt, ...) \
|
||||
[&]() -> int32_t { \
|
||||
__pragma(warning(suppress : 4456)) /* declaration of '_fIgnore_' hides previous local declaration */ \
|
||||
static int32_t _fIgnore_ = false; \
|
||||
if (!_fIgnore_) \
|
||||
{ \
|
||||
__pragma(warning(suppress : 4456)) /* declaration of 'params' hides previous local declaration */ \
|
||||
DeclareMsoAssertParams(dwTag, szFile, iLine); \
|
||||
params.framesToSkip++; \
|
||||
const int32_t _assertResult_ = MsoAssertSzTagProcInline(PassMsoAssertParams(params), szFmt, __VA_ARGS__); \
|
||||
__pragma(warning(suppress : 4700)) /* MSVC is unhappy with this used in a loop conditional */ \
|
||||
if (_assertResult_ == c_assertDebugBreak) \
|
||||
{ \
|
||||
AssertBreak(wzAnnotation); \
|
||||
} \
|
||||
_fIgnore_ = (_assertResult_ == c_assertAlwaysIgnore); \
|
||||
} \
|
||||
return FALSE; \
|
||||
}()
|
||||
#endif // __cplusplus
|
||||
|
||||
#else
|
||||
|
@ -122,7 +124,7 @@ __inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_fo
|
|||
#endif // DEBUG
|
||||
|
||||
/**
|
||||
Hide the debug assert 'f' condition from OACR so it acts more like a ship build
|
||||
Hide the debug assert 'f' condition from OACR so it acts more like a ship build
|
||||
*/
|
||||
#if OACR
|
||||
#define AssertDetails_ShouldRaiseDebugAssert(f, fIgnore) 0
|
||||
|
@ -133,27 +135,25 @@ __inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_fo
|
|||
#if DEBUG
|
||||
|
||||
/**
|
||||
AssertSzNTagImpl - all debug asserts funnel into here
|
||||
TODO: need to move the Ignore logic, should it be on params?
|
||||
Params is MsoAssertParams, only C callers need to do something weird.
|
||||
AssertSzNTagImpl - all debug asserts funnel into here
|
||||
TODO: need to move the Ignore logic, should it be on params?
|
||||
Params is MsoAssertParams, only C callers need to do something weird.
|
||||
|
||||
Note: be aware that ... requires at least one argument
|
||||
Note: be aware that ... requires at least one argument
|
||||
*/
|
||||
#define AssertSzNTagImpl(f, params, sz, ...) \
|
||||
AssertAnnotatedSzNTagImpl(f, L## #f, params, sz, __VA_ARGS__)
|
||||
#define AssertSzNTagImpl(f, params, sz, ...) AssertAnnotatedSzNTagImpl(f, L## #f, params, sz, __VA_ARGS__)
|
||||
|
||||
#define AssertAnnotatedSzNTagImpl(f, wzAnnotation, params, sz, ...) \
|
||||
AssertDetails_Statement_Begin \
|
||||
static int32_t _fIgnore_ = 0; \
|
||||
if (AssertDetails_ShouldRaiseDebugAssert(f, _fIgnore_)) \
|
||||
{ \
|
||||
DeclareInlineMsoAssertParams(params); \
|
||||
const int32_t _assertResult_ = MsoAssertSzTagProcInline(PassMsoAssertParams(params), sz, __VA_ARGS__); \
|
||||
if (_assertResult_ == c_assertDebugBreak) \
|
||||
AssertBreak(wzAnnotation); \
|
||||
_fIgnore_ = (_assertResult_ == c_assertAlwaysIgnore); \
|
||||
} \
|
||||
AssertDetails_Statement_End
|
||||
#define AssertAnnotatedSzNTagImpl(f, wzAnnotation, params, sz, ...) \
|
||||
AssertDetails_Statement_Begin static int32_t _fIgnore_ = 0; \
|
||||
if (AssertDetails_ShouldRaiseDebugAssert(f, _fIgnore_)) \
|
||||
{ \
|
||||
DeclareInlineMsoAssertParams(params); \
|
||||
const int32_t _assertResult_ = MsoAssertSzTagProcInline(PassMsoAssertParams(params), sz, __VA_ARGS__); \
|
||||
if (_assertResult_ == c_assertDebugBreak) \
|
||||
AssertBreak(wzAnnotation); \
|
||||
_fIgnore_ = (_assertResult_ == c_assertAlwaysIgnore); \
|
||||
} \
|
||||
AssertDetails_Statement_End
|
||||
|
||||
#else
|
||||
|
||||
|
@ -165,15 +165,15 @@ __inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_fo
|
|||
#if DEBUG && !OACR // OACR can't understand FAssertDoSzNTagImpl
|
||||
|
||||
/**
|
||||
FAssertDoSzNTagImpl - all debug FAsserts funnel into here
|
||||
<condition> || <assertResult && false>
|
||||
FAssertDoSzNTagImpl - all debug FAsserts funnel into here
|
||||
<condition> || <assertResult && false>
|
||||
|
||||
TODO: need to move the Ignore logic, handle these differently?
|
||||
Note: be aware that ... requires at least one argument
|
||||
TODO: need to move the Ignore logic, handle these differently?
|
||||
Note: be aware that ... requires at least one argument
|
||||
*/
|
||||
|
||||
#define FAssertDoSzNTagImpl(f, dwTag, szFile, iLine, wzAnnotation, sz, ...) \
|
||||
((f) || (MsoAssertSzTagProcInline2(dwTag, szFile, iLine, wzAnnotation, sz, __VA_ARGS__)))
|
||||
((f) || (MsoAssertSzTagProcInline2(dwTag, szFile, iLine, wzAnnotation, sz, __VA_ARGS__)))
|
||||
|
||||
#else
|
||||
|
||||
|
@ -183,40 +183,50 @@ __inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_fo
|
|||
#endif // DEBUG && !OACR
|
||||
|
||||
// Note: AssertDo may contain assignments so I'm using the != 0 syntax which allows that.
|
||||
#define AssertDoTag(f, tag) Statement(if ((f) == 0) { AssertSzTag(0, #f, tag); })
|
||||
#define AssertDoSzTag(f, sz, tag) Statement(if ((f) == 0) { AssertSzTag(0, sz, tag); })
|
||||
#define AssertDoSz1Tag(f, sz, a, tag) Statement(if ((f) == 0) { AssertSz1Tag(0, sz, a, tag); })
|
||||
#define AssertDoSz2Tag(f, sz, a, b, tag) Statement(if ((f) == 0) { AssertSz2Tag(0, sz, a, b, tag); })
|
||||
#define AssertDoSz3Tag(f, sz, a, b, c, tag) Statement(if ((f) == 0) { AssertSz3Tag(0, sz, a, b, c, tag); })
|
||||
#define AssertDoSz4Tag(f, sz, a, b, c, d, tag) Statement(if ((f) == 0) { AssertSz4Tag(0, sz, a, b, c, d, tag); })
|
||||
#define AssertDoSz5Tag(f, sz, a, b, c, d, e, tag) Statement(if ((f) == 0) { AssertSz5Tag(0, sz, a, b, c, d, e, tag); })
|
||||
#define AssertDoTag(f, tag) Statement(if ((f) == 0) { AssertSzTag(0, #f, tag); })
|
||||
#define AssertDoSzTag(f, sz, tag) Statement(if ((f) == 0) { AssertSzTag(0, sz, tag); })
|
||||
#define AssertDoSz1Tag(f, sz, a, tag) Statement(if ((f) == 0) { AssertSz1Tag(0, sz, a, tag); })
|
||||
#define AssertDoSz2Tag(f, sz, a, b, tag) Statement(if ((f) == 0) { AssertSz2Tag(0, sz, a, b, tag); })
|
||||
#define AssertDoSz3Tag(f, sz, a, b, c, tag) Statement(if ((f) == 0) { AssertSz3Tag(0, sz, a, b, c, tag); })
|
||||
#define AssertDoSz4Tag(f, sz, a, b, c, d, tag) Statement(if ((f) == 0) { AssertSz4Tag(0, sz, a, b, c, d, tag); })
|
||||
#define AssertDoSz5Tag(f, sz, a, b, c, d, e, tag) Statement(if ((f) == 0) { AssertSz5Tag(0, sz, a, b, c, d, e, tag); })
|
||||
|
||||
/**
|
||||
Helpers for debug HRESULT asserts.
|
||||
|
||||
Ship versions are not supported; consider VerifySucceededElseCrash
|
||||
Helpers for debug HRESULT asserts.
|
||||
|
||||
Ship versions are not supported; consider VerifySucceededElseCrash
|
||||
*/
|
||||
#if DEBUG
|
||||
|
||||
#define AssertDoSucceededTag(expr, tag) \
|
||||
AssertDetails_Statement_Begin \
|
||||
HRESULT _hr_ = (expr); \
|
||||
AssertAnnotatedSzNTagImpl(SUCCEEDED(_hr_), L#expr L" success. Check local variable _hr_.", InlineMsoAssertParams("SUCCEEDED(" #expr ")", tag, __FILE__, __LINE__), "%s failed with 0x%08x", #expr, _hr_); \
|
||||
AssertDetails_Statement_End
|
||||
#define AssertDoSucceededTag(expr, tag) \
|
||||
AssertDetails_Statement_Begin HRESULT _hr_ = (expr); \
|
||||
AssertAnnotatedSzNTagImpl( \
|
||||
SUCCEEDED(_hr_), \
|
||||
L#expr L" success. Check local variable _hr_.", \
|
||||
InlineMsoAssertParams("SUCCEEDED(" #expr ")", tag, __FILE__, __LINE__), \
|
||||
"%s failed with 0x%08x", \
|
||||
#expr, \
|
||||
_hr_); \
|
||||
AssertDetails_Statement_End
|
||||
|
||||
#define FAssertDoSucceededTag(expr, tag) \
|
||||
[&]() noexcept -> bool { \
|
||||
HRESULT _hr_ = (expr); \
|
||||
if (FAILED(_hr_)) \
|
||||
{ \
|
||||
if (MsoAssertSzTagProcInline(InlineMsoAssertParams("SUCCEEDED(" #expr ")", tag, __FILE__, __LINE__), "%s failed with 0x%08x", #expr, _hr_) == c_assertDebugBreak) \
|
||||
{ \
|
||||
AssertBreak(L#expr L" success. Check local variable _hr_."); \
|
||||
} \
|
||||
} \
|
||||
return SUCCEEDED(_hr_); \
|
||||
OACR_WARNING_SUPPRESS( NOEXCEPT_FUNC_THROWS, "Ignore whether expr throws." ); \
|
||||
}() \
|
||||
#define FAssertDoSucceededTag(expr, tag) \
|
||||
[&]() noexcept -> bool { \
|
||||
HRESULT _hr_ = (expr); \
|
||||
if (FAILED(_hr_)) \
|
||||
{ \
|
||||
if (MsoAssertSzTagProcInline( \
|
||||
InlineMsoAssertParams("SUCCEEDED(" #expr ")", tag, __FILE__, __LINE__), \
|
||||
"%s failed with 0x%08x", \
|
||||
#expr, \
|
||||
_hr_) \
|
||||
== c_assertDebugBreak) \
|
||||
{ \
|
||||
AssertBreak(L#expr L" success. Check local variable _hr_."); \
|
||||
} \
|
||||
} \
|
||||
return SUCCEEDED(_hr_); \
|
||||
OACR_WARNING_SUPPRESS(NOEXCEPT_FUNC_THROWS, "Ignore whether expr throws."); \
|
||||
}()
|
||||
|
||||
#else
|
||||
|
||||
|
@ -226,9 +236,9 @@ __inline int32_t MsoAssertSzTagProcInline(MsoAssertParamsType params, _Printf_fo
|
|||
#endif // DEBUG
|
||||
|
||||
/**
|
||||
Random other deprecated assert macros
|
||||
Random other deprecated assert macros
|
||||
*/
|
||||
#define AssertImpliesTag(a, b, tag) AssertSzTag(FImplies(a, b), #a " => " #b, tag)
|
||||
#define AssertBiImpliesTag(a, b, tag) AssertSzTag(FBiImplies(a, b), #a " <==> " #b, tag)
|
||||
#define AssertImpliesTag(a, b, tag) AssertSzTag(FImplies(a, b), #a " => " #b, tag)
|
||||
#define AssertBiImpliesTag(a, b, tag) AssertSzTag(FBiImplies(a, b), #a " <==> " #b, tag)
|
||||
|
||||
#endif // LIBLET_DEBUGASSERTAPI_DEBUGASSERTDETAILS_H
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
#pragma once
|
||||
#include <debugAssertApi/debugAssertApi.h>
|
||||
|
||||
namespace Mso {
|
||||
namespace DebugAsserts {
|
||||
namespace Mso { namespace DebugAsserts {
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/**
|
||||
Legacy APIs for disabling asserts
|
||||
Legacy APIs for disabling asserts
|
||||
*/
|
||||
LIBLET_PUBLICAPI void DisableAssertTag(uint32_t tag) noexcept;
|
||||
LIBLET_PUBLICAPI void EnableAssertTag(uint32_t tag) noexcept;
|
||||
|
@ -20,7 +19,10 @@ LIBLET_PUBLICAPI bool IsAssertTagDisabled(uint32_t tag) noexcept;
|
|||
|
||||
inline void DisableAssertTag(uint32_t) noexcept {}
|
||||
inline void EnableAssertTag(uint32_t) noexcept {}
|
||||
inline bool IsAssertTagDisabled(uint32_t) noexcept { return false; }
|
||||
inline bool IsAssertTagDisabled(uint32_t) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -85,5 +87,4 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
} // DebugAsserts
|
||||
} // Mso
|
||||
}} // namespace Mso::DebugAsserts
|
||||
|
|
|
@ -12,130 +12,129 @@
|
|||
|
||||
#ifdef DEBUG
|
||||
|
||||
namespace Mso {
|
||||
namespace DebugAsserts {
|
||||
namespace Mso { namespace DebugAsserts {
|
||||
|
||||
struct Data
|
||||
{
|
||||
std::set<uint32_t> DisabledTags;
|
||||
std::set<AssertIgnorer> Ignorers;
|
||||
std::set<AssertListener> Listeners;
|
||||
AssertHandler Handler = nullptr;
|
||||
std::set<uint32_t> DisabledTags;
|
||||
std::set<AssertIgnorer> Ignorers;
|
||||
std::set<AssertListener> Listeners;
|
||||
AssertHandler Handler = nullptr;
|
||||
|
||||
static Data& Get() noexcept
|
||||
{
|
||||
// To avoid static initializer ordering issues, this object is created on demand and purposely leaked
|
||||
static auto s_data = Construct();
|
||||
return *s_data;
|
||||
}
|
||||
static Data& Get() noexcept
|
||||
{
|
||||
// To avoid static initializer ordering issues, this object is created on demand and purposely leaked
|
||||
static auto s_data = Construct();
|
||||
return *s_data;
|
||||
}
|
||||
|
||||
private:
|
||||
static Data* Construct() noexcept
|
||||
{
|
||||
// xlsrv doesn't allow calling new from static initializers. Using std::allocator instead, which has been modified to call malloc.
|
||||
std::allocator<Data> alloc;
|
||||
auto p = alloc.allocate(1);
|
||||
alloc.construct(p);
|
||||
return p;
|
||||
}
|
||||
static Data* Construct() noexcept
|
||||
{
|
||||
// xlsrv doesn't allow calling new from static initializers. Using std::allocator instead, which has been modified
|
||||
// to call malloc.
|
||||
std::allocator<Data> alloc;
|
||||
auto p = alloc.allocate(1);
|
||||
alloc.construct(p);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
void DisableAssertTag(uint32_t tag) noexcept
|
||||
{
|
||||
Data::Get().DisabledTags.insert(tag);
|
||||
Data::Get().DisabledTags.insert(tag);
|
||||
}
|
||||
|
||||
void EnableAssertTag(uint32_t tag) noexcept
|
||||
{
|
||||
Data::Get().DisabledTags.erase(tag);
|
||||
Data::Get().DisabledTags.erase(tag);
|
||||
}
|
||||
|
||||
bool IsAssertTagDisabled(uint32_t tag) noexcept
|
||||
{
|
||||
const auto& disabledTags = Data::Get().DisabledTags;
|
||||
return (disabledTags.find(tag) != std::end(disabledTags));
|
||||
const auto& disabledTags = Data::Get().DisabledTags;
|
||||
return (disabledTags.find(tag) != std::end(disabledTags));
|
||||
}
|
||||
|
||||
void AddAssertIgnorer(AssertIgnorer ignorer) noexcept
|
||||
{
|
||||
Data::Get().Ignorers.insert(ignorer);
|
||||
Data::Get().Ignorers.insert(ignorer);
|
||||
}
|
||||
|
||||
void RemoveAssertIgnorer(AssertIgnorer ignorer) noexcept
|
||||
{
|
||||
Data::Get().Ignorers.erase(ignorer);
|
||||
Data::Get().Ignorers.erase(ignorer);
|
||||
}
|
||||
|
||||
void AddAssertListener(AssertListener listener) noexcept
|
||||
{
|
||||
Data::Get().Listeners.insert(listener);
|
||||
Data::Get().Listeners.insert(listener);
|
||||
}
|
||||
|
||||
void RemoveAssertListener(AssertListener listener) noexcept
|
||||
{
|
||||
Data::Get().Listeners.erase(listener);
|
||||
Data::Get().Listeners.erase(listener);
|
||||
}
|
||||
|
||||
AssertHandler GetAssertHandler() noexcept
|
||||
{
|
||||
return Data::Get().Handler;
|
||||
return Data::Get().Handler;
|
||||
}
|
||||
|
||||
AssertHandler SetAssertHandler(AssertHandler handler) noexcept
|
||||
{
|
||||
std::swap(Data::Get().Handler, handler);
|
||||
return handler;
|
||||
std::swap(Data::Get().Handler, handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
static bool v_isInAssertHandler;
|
||||
|
||||
extern "C" int32_t MsoAssertSzTagProc(const MsoAssertParams& params, _Printf_format_string_ const char* szFormat, va_list argList) noexcept
|
||||
extern "C" int32_t
|
||||
MsoAssertSzTagProc(const MsoAssertParams& params, _Printf_format_string_ const char* szFormat, va_list argList) noexcept
|
||||
{
|
||||
if (IsAssertTagDisabled(params.dwTag))
|
||||
return c_assertIgnore;
|
||||
if (IsAssertTagDisabled(params.dwTag))
|
||||
return c_assertIgnore;
|
||||
|
||||
char assertMessage[4096];
|
||||
(void) vsnprintf(assertMessage, sizeof(assertMessage), szFormat, argList);
|
||||
params.framesToSkip++;
|
||||
char assertMessage[4096];
|
||||
(void)vsnprintf(assertMessage, sizeof(assertMessage), szFormat, argList);
|
||||
params.framesToSkip++;
|
||||
|
||||
const auto& data = Data::Get();
|
||||
for (const auto& ignorer : data.Ignorers)
|
||||
{
|
||||
if (ignorer(params, assertMessage))
|
||||
return c_assertIgnore;
|
||||
}
|
||||
|
||||
for (const auto& listener : data.Listeners)
|
||||
{
|
||||
listener(params, assertMessage);
|
||||
}
|
||||
const auto& data = Data::Get();
|
||||
for (const auto& ignorer : data.Ignorers)
|
||||
{
|
||||
if (ignorer(params, assertMessage))
|
||||
return c_assertIgnore;
|
||||
}
|
||||
|
||||
if (data.Handler)
|
||||
{
|
||||
Mso::TRestorer<bool> restoreInAssertHandler(v_isInAssertHandler, true);
|
||||
return static_cast<int32_t>(data.Handler(params, assertMessage));
|
||||
}
|
||||
for (const auto& listener : data.Listeners)
|
||||
{
|
||||
listener(params, assertMessage);
|
||||
}
|
||||
|
||||
return c_assertIgnore;
|
||||
if (data.Handler)
|
||||
{
|
||||
Mso::TRestorer<bool> restoreInAssertHandler(v_isInAssertHandler, true);
|
||||
return static_cast<int32_t>(data.Handler(params, assertMessage));
|
||||
}
|
||||
|
||||
return c_assertIgnore;
|
||||
}
|
||||
|
||||
bool IsInAssertHandler() noexcept
|
||||
{
|
||||
return v_isInAssertHandler;
|
||||
return v_isInAssertHandler;
|
||||
}
|
||||
|
||||
} // DebugAsserts
|
||||
} // Mso
|
||||
|
||||
}} // namespace Mso::DebugAsserts
|
||||
|
||||
LIBLET_PUBLICAPI_APPLE MSOAPI_(void) MsoFAddIgnoredAssertTag(uint32_t tag) noexcept
|
||||
{
|
||||
Mso::DebugAsserts::DisableAssertTag(tag);
|
||||
Mso::DebugAsserts::DisableAssertTag(tag);
|
||||
}
|
||||
|
||||
LIBLET_PUBLICAPI_APPLE MSOAPI_(void) MsoFRemoveIgnoredAssertTag(uint32_t tag) noexcept
|
||||
{
|
||||
Mso::DebugAsserts::EnableAssertTag(tag);
|
||||
Mso::DebugAsserts::EnableAssertTag(tag);
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -9,28 +9,27 @@
|
|||
#include <compilerAdapters/functionDecorations.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace Mso {
|
||||
namespace Async {
|
||||
namespace Mso { namespace Async {
|
||||
|
||||
//! Shared event wait handle interface used by ManualResetEvent and AutoResetEvent.
|
||||
//! Since, ManualResetEvent and AutoResetEvent are most commonly used between different threads, we used
|
||||
//! shared ownership based on ref counting to ensure proper lifetime of the synchronization events.
|
||||
struct IEventWaitHandle : Mso::IRefCounted
|
||||
{
|
||||
virtual void Set() const noexcept = 0;
|
||||
virtual void Reset() const noexcept = 0;
|
||||
virtual void Wait() const noexcept = 0;
|
||||
virtual bool WaitFor(const std::chrono::milliseconds& waitDuration) const noexcept = 0;
|
||||
virtual void Set() const noexcept = 0;
|
||||
virtual void Reset() const noexcept = 0;
|
||||
virtual void Wait() const noexcept = 0;
|
||||
virtual bool WaitFor(const std::chrono::milliseconds& waitDuration) const noexcept = 0;
|
||||
};
|
||||
|
||||
//! State of the IEventWaitHandle.
|
||||
enum class EventWaitHandleState : int32_t
|
||||
{
|
||||
//! Threads are blocked when calling Wait() or WaitFor() methods.
|
||||
NotSet,
|
||||
//! Threads are blocked when calling Wait() or WaitFor() methods.
|
||||
NotSet,
|
||||
|
||||
//! Threads are allowed to proceed or getting unblocked when calling Wait() or WaitFor() methods.
|
||||
IsSet,
|
||||
//! Threads are allowed to proceed or getting unblocked when calling Wait() or WaitFor() methods.
|
||||
IsSet,
|
||||
};
|
||||
|
||||
//! Notifies one or more waiting threads that an event has occurred. This class cannot be inherited.
|
||||
|
@ -56,59 +55,55 @@ enum class EventWaitHandleState : int32_t
|
|||
class ManualResetEvent final
|
||||
{
|
||||
public:
|
||||
//! Creates new ManualResetEvent with the non-signaling state.
|
||||
ManualResetEvent() noexcept : ManualResetEvent { EventWaitHandleState::NotSet }
|
||||
{
|
||||
}
|
||||
//! Creates new ManualResetEvent with the non-signaling state.
|
||||
ManualResetEvent() noexcept : ManualResetEvent{EventWaitHandleState::NotSet} {}
|
||||
|
||||
//! Creates new ManualResetEvent with the requested signaling state.
|
||||
LIBLET_PUBLICAPI explicit ManualResetEvent(EventWaitHandleState state) noexcept;
|
||||
//! Creates new ManualResetEvent with the requested signaling state.
|
||||
LIBLET_PUBLICAPI explicit ManualResetEvent(EventWaitHandleState state) noexcept;
|
||||
|
||||
//! Creates new ManualResetEvent with the provided handle.
|
||||
ManualResetEvent(IEventWaitHandle& handle) noexcept : m_handle { &handle }
|
||||
{
|
||||
}
|
||||
//! Creates new ManualResetEvent with the provided handle.
|
||||
ManualResetEvent(IEventWaitHandle& handle) noexcept : m_handle{&handle} {}
|
||||
|
||||
ManualResetEvent(const ManualResetEvent&) = default;
|
||||
ManualResetEvent& operator=(const ManualResetEvent&) = default;
|
||||
ManualResetEvent(const ManualResetEvent&) = default;
|
||||
ManualResetEvent& operator=(const ManualResetEvent&) = default;
|
||||
|
||||
//! No move semantic
|
||||
ManualResetEvent(ManualResetEvent&&) = delete;
|
||||
ManualResetEvent& operator=(ManualResetEvent&&) = delete;
|
||||
//! No move semantic
|
||||
ManualResetEvent(ManualResetEvent&&) = delete;
|
||||
ManualResetEvent& operator=(ManualResetEvent&&) = delete;
|
||||
|
||||
//! Sets the state of the event to signaled, which allows one or more waiting threads to proceed.
|
||||
void Set() const noexcept
|
||||
{
|
||||
m_handle->Set();
|
||||
}
|
||||
//! Sets the state of the event to signaled, which allows one or more waiting threads to proceed.
|
||||
void Set() const noexcept
|
||||
{
|
||||
m_handle->Set();
|
||||
}
|
||||
|
||||
//! Sets the state of the event to non-signaled state, which causes waiting threads to block.
|
||||
void Reset() const noexcept
|
||||
{
|
||||
m_handle->Reset();
|
||||
}
|
||||
//! Sets the state of the event to non-signaled state, which causes waiting threads to block.
|
||||
void Reset() const noexcept
|
||||
{
|
||||
m_handle->Reset();
|
||||
}
|
||||
|
||||
//! Blocks thread indefinitely and waits for the event signaling state.
|
||||
void Wait() const noexcept
|
||||
{
|
||||
m_handle->Wait();
|
||||
}
|
||||
//! Blocks thread indefinitely and waits for the event signaling state.
|
||||
void Wait() const noexcept
|
||||
{
|
||||
m_handle->Wait();
|
||||
}
|
||||
|
||||
//! Blocks thread for waitDuration time and waits for the event signaling state.
|
||||
template <typename TRep, typename TPeriod>
|
||||
bool WaitFor(const std::chrono::duration<TRep, TPeriod>& waitDuration) const noexcept
|
||||
{
|
||||
return m_handle->WaitFor(std::chrono::duration_cast<std::chrono::milliseconds>(waitDuration));
|
||||
}
|
||||
//! Blocks thread for waitDuration time and waits for the event signaling state.
|
||||
template <typename TRep, typename TPeriod>
|
||||
bool WaitFor(const std::chrono::duration<TRep, TPeriod>& waitDuration) const noexcept
|
||||
{
|
||||
return m_handle->WaitFor(std::chrono::duration_cast<std::chrono::milliseconds>(waitDuration));
|
||||
}
|
||||
|
||||
//! Gets the internal event handle.
|
||||
IEventWaitHandle& GetHandle() const noexcept
|
||||
{
|
||||
return *m_handle;
|
||||
}
|
||||
//! Gets the internal event handle.
|
||||
IEventWaitHandle& GetHandle() const noexcept
|
||||
{
|
||||
return *m_handle;
|
||||
}
|
||||
|
||||
private:
|
||||
Mso::TCntPtr<IEventWaitHandle> m_handle;
|
||||
Mso::TCntPtr<IEventWaitHandle> m_handle;
|
||||
};
|
||||
|
||||
//! Notifies a waiting thread that an event has occurred. This class cannot be inherited.
|
||||
|
@ -130,62 +125,57 @@ private:
|
|||
class AutoResetEvent final
|
||||
{
|
||||
public:
|
||||
//! Creates new AutoResetEvent with the non-signaling state.
|
||||
AutoResetEvent() noexcept : AutoResetEvent { EventWaitHandleState::NotSet }
|
||||
{
|
||||
}
|
||||
//! Creates new AutoResetEvent with the non-signaling state.
|
||||
AutoResetEvent() noexcept : AutoResetEvent{EventWaitHandleState::NotSet} {}
|
||||
|
||||
//! Creates new AutoResetEvent with the requested signaling state.
|
||||
LIBLET_PUBLICAPI explicit AutoResetEvent(EventWaitHandleState state) noexcept;
|
||||
//! Creates new AutoResetEvent with the requested signaling state.
|
||||
LIBLET_PUBLICAPI explicit AutoResetEvent(EventWaitHandleState state) noexcept;
|
||||
|
||||
//! Creates new AutoResetEvent with the provided handle. Crash if handle is null.
|
||||
AutoResetEvent(IEventWaitHandle& handle) noexcept : m_handle { &handle }
|
||||
{
|
||||
}
|
||||
//! Creates new AutoResetEvent with the provided handle. Crash if handle is null.
|
||||
AutoResetEvent(IEventWaitHandle& handle) noexcept : m_handle{&handle} {}
|
||||
|
||||
AutoResetEvent(const AutoResetEvent&) = default;
|
||||
AutoResetEvent& operator=(const AutoResetEvent&) = default;
|
||||
AutoResetEvent(const AutoResetEvent&) = default;
|
||||
AutoResetEvent& operator=(const AutoResetEvent&) = default;
|
||||
|
||||
// No move semantic
|
||||
AutoResetEvent(AutoResetEvent&&) = delete;
|
||||
AutoResetEvent& operator=(AutoResetEvent&&) = delete;
|
||||
// No move semantic
|
||||
AutoResetEvent(AutoResetEvent&&) = delete;
|
||||
AutoResetEvent& operator=(AutoResetEvent&&) = delete;
|
||||
|
||||
//! Sets the state of the event to signaled, which allows at most one waiting thread to proceed.
|
||||
void Set() const noexcept
|
||||
{
|
||||
m_handle->Set();
|
||||
}
|
||||
//! Sets the state of the event to signaled, which allows at most one waiting thread to proceed.
|
||||
void Set() const noexcept
|
||||
{
|
||||
m_handle->Set();
|
||||
}
|
||||
|
||||
//! Sets the state of the event to non-signaled state, which causes waiting threads to block.
|
||||
void Reset() const noexcept
|
||||
{
|
||||
m_handle->Reset();
|
||||
}
|
||||
//! Sets the state of the event to non-signaled state, which causes waiting threads to block.
|
||||
void Reset() const noexcept
|
||||
{
|
||||
m_handle->Reset();
|
||||
}
|
||||
|
||||
//! Blocks thread indefinitely and waits for the event signaling state.
|
||||
void Wait() const noexcept
|
||||
{
|
||||
m_handle->Wait();
|
||||
}
|
||||
//! Blocks thread indefinitely and waits for the event signaling state.
|
||||
void Wait() const noexcept
|
||||
{
|
||||
m_handle->Wait();
|
||||
}
|
||||
|
||||
//! Blocks thread for waitDuration time and waits for the event signaling state.
|
||||
template <typename TRep, typename TPeriod>
|
||||
bool WaitFor(const std::chrono::duration<TRep, TPeriod>& waitDuration) const noexcept
|
||||
{
|
||||
return m_handle->WaitFor(std::chrono::duration_cast<std::chrono::milliseconds>(waitDuration));
|
||||
}
|
||||
//! Blocks thread for waitDuration time and waits for the event signaling state.
|
||||
template <typename TRep, typename TPeriod>
|
||||
bool WaitFor(const std::chrono::duration<TRep, TPeriod>& waitDuration) const noexcept
|
||||
{
|
||||
return m_handle->WaitFor(std::chrono::duration_cast<std::chrono::milliseconds>(waitDuration));
|
||||
}
|
||||
|
||||
//! Gets the internal event handle.
|
||||
IEventWaitHandle& GetHandle() const noexcept
|
||||
{
|
||||
return *m_handle;
|
||||
}
|
||||
//! Gets the internal event handle.
|
||||
IEventWaitHandle& GetHandle() const noexcept
|
||||
{
|
||||
return *m_handle;
|
||||
}
|
||||
|
||||
private:
|
||||
Mso::TCntPtr<IEventWaitHandle> m_handle;
|
||||
Mso::TCntPtr<IEventWaitHandle> m_handle;
|
||||
};
|
||||
|
||||
} // namespace Async
|
||||
} // namespace Mso
|
||||
}} // namespace Mso::Async
|
||||
|
||||
#endif // LIBLET_DISPATCHQUEUE_EVENTWAITHANDLE_H
|
||||
|
|
|
@ -7,17 +7,16 @@
|
|||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
namespace Mso {
|
||||
namespace Async {
|
||||
namespace Mso { namespace Async {
|
||||
|
||||
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
struct WaitTimePoint
|
||||
{
|
||||
bool IsInfinite { false };
|
||||
bool ShouldUpdateWaitDuration { false };
|
||||
std::chrono::milliseconds WaitDuration {};
|
||||
TimePoint WaitUntil {};
|
||||
bool IsInfinite{false};
|
||||
bool ShouldUpdateWaitDuration{false};
|
||||
std::chrono::milliseconds WaitDuration{};
|
||||
TimePoint WaitUntil{};
|
||||
};
|
||||
|
||||
// Implementation of the IEventWaitHandle interface
|
||||
|
@ -25,84 +24,82 @@ template <class TMutex, class TConditionVariable>
|
|||
class EventWaitHandle final : public Mso::RefCountedObject<IEventWaitHandle>
|
||||
{
|
||||
public:
|
||||
EventWaitHandle(bool isAutoReset, EventWaitHandleState state) noexcept
|
||||
: m_isAutoReset { isAutoReset }
|
||||
, m_state { state }
|
||||
{
|
||||
}
|
||||
EventWaitHandle(bool isAutoReset, EventWaitHandleState state) noexcept : m_isAutoReset{isAutoReset}, m_state{state} {}
|
||||
|
||||
public: // IEventWaitHandle
|
||||
void Set() const noexcept override
|
||||
{
|
||||
std::lock_guard<TMutex> lock { m_mutex };
|
||||
m_state = EventWaitHandleState::IsSet;
|
||||
void Set() const noexcept override
|
||||
{
|
||||
std::lock_guard<TMutex> lock{m_mutex};
|
||||
m_state = EventWaitHandleState::IsSet;
|
||||
|
||||
// Notify under the lock to avoid missed signals in case if Wait loop is between
|
||||
// checking variable and calling wait for signal API.
|
||||
if (m_isAutoReset)
|
||||
{
|
||||
m_cond.NotifyOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cond.NotifyAll();
|
||||
}
|
||||
}
|
||||
// Notify under the lock to avoid missed signals in case if Wait loop is between
|
||||
// checking variable and calling wait for signal API.
|
||||
if (m_isAutoReset)
|
||||
{
|
||||
m_cond.NotifyOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cond.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() const noexcept override
|
||||
{
|
||||
std::lock_guard<TMutex> lock { m_mutex };
|
||||
m_state = EventWaitHandleState::NotSet;
|
||||
}
|
||||
void Reset() const noexcept override
|
||||
{
|
||||
std::lock_guard<TMutex> lock{m_mutex};
|
||||
m_state = EventWaitHandleState::NotSet;
|
||||
}
|
||||
|
||||
void Wait() const noexcept override
|
||||
{
|
||||
WaitTimePoint waitTimePoint {};
|
||||
waitTimePoint.IsInfinite = true;
|
||||
VerifyElseCrashSzTag(WaitUntil(waitTimePoint), "Must not timeout", 0x026e348b /* tag_c19sl */);
|
||||
}
|
||||
void Wait() const noexcept override
|
||||
{
|
||||
WaitTimePoint waitTimePoint{};
|
||||
waitTimePoint.IsInfinite = true;
|
||||
VerifyElseCrashSzTag(WaitUntil(waitTimePoint), "Must not timeout", 0x026e348b /* tag_c19sl */);
|
||||
}
|
||||
|
||||
bool WaitFor(const std::chrono::milliseconds& waitDuration) const noexcept override
|
||||
{
|
||||
VerifyElseCrashSzTag(waitDuration.count() < std::numeric_limits<uint32_t>::max(),
|
||||
"waitDuration must not exceed uint32_t size for milliseconds.", 0x026e348c /* tag_c19sm */);
|
||||
bool WaitFor(const std::chrono::milliseconds& waitDuration) const noexcept override
|
||||
{
|
||||
VerifyElseCrashSzTag(
|
||||
waitDuration.count() < std::numeric_limits<uint32_t>::max(),
|
||||
"waitDuration must not exceed uint32_t size for milliseconds.",
|
||||
0x026e348c /* tag_c19sm */);
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
WaitTimePoint waitTimePoint {};
|
||||
waitTimePoint.WaitDuration = waitDuration;
|
||||
waitTimePoint.WaitUntil = now + waitDuration;
|
||||
VerifyElseCrashSzTag(waitTimePoint.WaitUntil >= now, "waitDuration causes clock overflow", 0x026e348d /* tag_c19sn */);
|
||||
WaitTimePoint waitTimePoint{};
|
||||
waitTimePoint.WaitDuration = waitDuration;
|
||||
waitTimePoint.WaitUntil = now + waitDuration;
|
||||
VerifyElseCrashSzTag(
|
||||
waitTimePoint.WaitUntil >= now, "waitDuration causes clock overflow", 0x026e348d /* tag_c19sn */);
|
||||
|
||||
return WaitUntil(waitTimePoint);
|
||||
}
|
||||
return WaitUntil(waitTimePoint);
|
||||
}
|
||||
|
||||
private:
|
||||
bool WaitUntil(WaitTimePoint& timePoint) const noexcept
|
||||
{
|
||||
std::lock_guard<TMutex> lock { m_mutex };
|
||||
while (m_state != EventWaitHandleState::IsSet)
|
||||
{
|
||||
if (!m_cond.WaitUntil(m_mutex, timePoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool WaitUntil(WaitTimePoint& timePoint) const noexcept
|
||||
{
|
||||
std::lock_guard<TMutex> lock{m_mutex};
|
||||
while (m_state != EventWaitHandleState::IsSet)
|
||||
{
|
||||
if (!m_cond.WaitUntil(m_mutex, timePoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isAutoReset)
|
||||
{
|
||||
m_state = EventWaitHandleState::NotSet;
|
||||
}
|
||||
if (m_isAutoReset)
|
||||
{
|
||||
m_state = EventWaitHandleState::NotSet;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable TMutex m_mutex;
|
||||
mutable TConditionVariable m_cond;
|
||||
const bool m_isAutoReset;
|
||||
mutable EventWaitHandleState m_state;
|
||||
mutable TMutex m_mutex;
|
||||
mutable TConditionVariable m_cond;
|
||||
const bool m_isAutoReset;
|
||||
mutable EventWaitHandleState m_state;
|
||||
};
|
||||
|
||||
} // namespace Async
|
||||
} // namespace Mso
|
||||
}} // namespace Mso::Async
|
||||
|
|
|
@ -5,93 +5,92 @@
|
|||
#include "eventWaitHandle.h"
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Mso {
|
||||
namespace Async {
|
||||
namespace Mso { namespace Async {
|
||||
|
||||
namespace {
|
||||
|
||||
// Mutex class based on pthread_mutex_t that meets BasicLockable standard requirements.
|
||||
struct PThreadMutex
|
||||
{
|
||||
~PThreadMutex() noexcept
|
||||
{
|
||||
pthread_mutex_destroy(&Handle);
|
||||
}
|
||||
~PThreadMutex() noexcept
|
||||
{
|
||||
pthread_mutex_destroy(&Handle);
|
||||
}
|
||||
|
||||
void lock() noexcept
|
||||
{
|
||||
pthread_mutex_lock(&Handle);
|
||||
}
|
||||
void lock() noexcept
|
||||
{
|
||||
pthread_mutex_lock(&Handle);
|
||||
}
|
||||
|
||||
void unlock() noexcept
|
||||
{
|
||||
pthread_mutex_unlock(&Handle);
|
||||
}
|
||||
void unlock() noexcept
|
||||
{
|
||||
pthread_mutex_unlock(&Handle);
|
||||
}
|
||||
|
||||
pthread_mutex_t Handle = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t Handle = PTHREAD_MUTEX_INITIALIZER;
|
||||
};
|
||||
|
||||
// pthread_cond_t wrapper
|
||||
struct ConditionVariable
|
||||
{
|
||||
~ConditionVariable() noexcept
|
||||
{
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
~ConditionVariable() noexcept
|
||||
{
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
|
||||
void NotifyOne() noexcept
|
||||
{
|
||||
pthread_cond_signal(&m_cond);
|
||||
}
|
||||
void NotifyOne() noexcept
|
||||
{
|
||||
pthread_cond_signal(&m_cond);
|
||||
}
|
||||
|
||||
void NotifyAll() noexcept
|
||||
{
|
||||
pthread_cond_broadcast(&m_cond);
|
||||
}
|
||||
void NotifyAll() noexcept
|
||||
{
|
||||
pthread_cond_broadcast(&m_cond);
|
||||
}
|
||||
|
||||
bool WaitUntil(PThreadMutex& mutex, const WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (waitTimePoint.IsInfinite)
|
||||
{
|
||||
VerifyElseCrashSzTag(!pthread_cond_wait(&m_cond, &mutex.Handle), "pthread_cond_wait failed", 0x026e348e /* tag_c19so */);
|
||||
return true;
|
||||
}
|
||||
bool WaitUntil(PThreadMutex& mutex, const WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (waitTimePoint.IsInfinite)
|
||||
{
|
||||
VerifyElseCrashSzTag(
|
||||
!pthread_cond_wait(&m_cond, &mutex.Handle), "pthread_cond_wait failed", 0x026e348e /* tag_c19so */);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto tsWaitUntil = DurationToTimeSpec(waitTimePoint.WaitUntil.time_since_epoch());
|
||||
if (int error = pthread_cond_timedwait(&m_cond, &mutex.Handle, &tsWaitUntil))
|
||||
{
|
||||
VerifyElseCrashSzTag(error == ETIMEDOUT, "pthread_cond_wait failed", 0x026e348f /* tag_c19sp */);
|
||||
return false;
|
||||
}
|
||||
auto tsWaitUntil = DurationToTimeSpec(waitTimePoint.WaitUntil.time_since_epoch());
|
||||
if (int error = pthread_cond_timedwait(&m_cond, &mutex.Handle, &tsWaitUntil))
|
||||
{
|
||||
VerifyElseCrashSzTag(error == ETIMEDOUT, "pthread_cond_wait failed", 0x026e348f /* tag_c19sp */);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static timespec DurationToTimeSpec(const TimePoint::duration& duration) noexcept
|
||||
{
|
||||
using namespace std::chrono;
|
||||
timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(duration_cast<seconds>(duration).count());
|
||||
ts.tv_nsec = static_cast<long>(duration_cast<nanoseconds>(duration % seconds { 1 }).count());
|
||||
return ts;
|
||||
}
|
||||
static timespec DurationToTimeSpec(const TimePoint::duration& duration) noexcept
|
||||
{
|
||||
using namespace std::chrono;
|
||||
timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(duration_cast<seconds>(duration).count());
|
||||
ts.tv_nsec = static_cast<long>(duration_cast<nanoseconds>(duration % seconds{1}).count());
|
||||
return ts;
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_cond_t m_cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_cond_t m_cond = PTHREAD_COND_INITIALIZER;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
LIBLET_PUBLICAPI ManualResetEvent::ManualResetEvent(EventWaitHandleState state) noexcept
|
||||
: m_handle { Mso::Make<EventWaitHandle<PThreadMutex, ConditionVariable>>(/*isAutoReset:*/false, state) }
|
||||
: m_handle{Mso::Make<EventWaitHandle<PThreadMutex, ConditionVariable>>(/*isAutoReset:*/ false, state)}
|
||||
{
|
||||
}
|
||||
|
||||
LIBLET_PUBLICAPI AutoResetEvent::AutoResetEvent(EventWaitHandleState state) noexcept
|
||||
: m_handle { Mso::Make<EventWaitHandle<PThreadMutex, ConditionVariable>>(/*isAutoReset:*/true, state) }
|
||||
: m_handle{Mso::Make<EventWaitHandle<PThreadMutex, ConditionVariable>>(/*isAutoReset:*/ true, state)}
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Async
|
||||
} // namespace Mso
|
||||
}} // namespace Mso::Async
|
||||
|
|
|
@ -10,95 +10,90 @@
|
|||
//! These APIs were added in Windows Vista and considered to be more
|
||||
//! lightweight when events created with CreateEventEx Windows API.
|
||||
|
||||
namespace Mso {
|
||||
namespace Async {
|
||||
namespace Mso { namespace Async {
|
||||
|
||||
namespace {
|
||||
|
||||
// Mutex class based on SRWLOCK that meets BasicLockable standard requirements.
|
||||
struct SRWMutex
|
||||
{
|
||||
_Requires_lock_not_held_(this->Handle)
|
||||
_Acquires_lock_(this->Handle)
|
||||
void lock() noexcept
|
||||
{
|
||||
AcquireSRWLockExclusive(&Handle);
|
||||
}
|
||||
_Requires_lock_not_held_(this->Handle) _Acquires_lock_(this->Handle) void lock() noexcept
|
||||
{
|
||||
AcquireSRWLockExclusive(&Handle);
|
||||
}
|
||||
|
||||
_Requires_lock_held_(this->Handle)
|
||||
_Releases_lock_(this->Handle)
|
||||
void unlock() noexcept
|
||||
{
|
||||
ReleaseSRWLockExclusive(&Handle);
|
||||
}
|
||||
_Requires_lock_held_(this->Handle) _Releases_lock_(this->Handle) void unlock() noexcept
|
||||
{
|
||||
ReleaseSRWLockExclusive(&Handle);
|
||||
}
|
||||
|
||||
SRWLOCK Handle{ SRWLOCK_INIT };
|
||||
SRWLOCK Handle{SRWLOCK_INIT};
|
||||
};
|
||||
|
||||
// Windows CONDITION_VARIABLE wrapper
|
||||
struct ConditionVariable
|
||||
{
|
||||
void NotifyOne() noexcept
|
||||
{
|
||||
WakeConditionVariable(&m_cond);
|
||||
}
|
||||
void NotifyOne() noexcept
|
||||
{
|
||||
WakeConditionVariable(&m_cond);
|
||||
}
|
||||
|
||||
void NotifyAll() noexcept
|
||||
{
|
||||
WakeAllConditionVariable(&m_cond);
|
||||
}
|
||||
void NotifyAll() noexcept
|
||||
{
|
||||
WakeAllConditionVariable(&m_cond);
|
||||
}
|
||||
|
||||
bool WaitUntil(SRWMutex& mutex, WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (!SleepConditionVariableSRW(&m_cond, &mutex.Handle, GetWaitTimeInMs(waitTimePoint), 0))
|
||||
{
|
||||
VerifyElseCrashSzTag(GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW failed.", 0x026e3490 /* tag_c19sq */);
|
||||
return false;
|
||||
}
|
||||
bool WaitUntil(SRWMutex& mutex, WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (!SleepConditionVariableSRW(&m_cond, &mutex.Handle, GetWaitTimeInMs(waitTimePoint), 0))
|
||||
{
|
||||
VerifyElseCrashSzTag(
|
||||
GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW failed.", 0x026e3490 /* tag_c19sq */);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t GetWaitTimeInMs(WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (waitTimePoint.IsInfinite)
|
||||
{
|
||||
return INFINITE;
|
||||
}
|
||||
static uint32_t GetWaitTimeInMs(WaitTimePoint& waitTimePoint) noexcept
|
||||
{
|
||||
if (waitTimePoint.IsInfinite)
|
||||
{
|
||||
return INFINITE;
|
||||
}
|
||||
|
||||
// For the very first time here we use duration provided by user.
|
||||
// If we get here again, then it means that the condition_variable was woken up
|
||||
// due to sporatic wakeups, and we have to recalculate the wait duration.
|
||||
if (waitTimePoint.ShouldUpdateWaitDuration)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto timeLeft = duration_cast<milliseconds>(waitTimePoint.WaitUntil - system_clock::now());
|
||||
// Make sure that we do not have negative duration because we waited beyond the target time point,
|
||||
// or because system_clock::now() gave inacccurate time due to internal implementation.
|
||||
waitTimePoint.WaitDuration = std::max(timeLeft, milliseconds { 0 });
|
||||
}
|
||||
// For the very first time here we use duration provided by user.
|
||||
// If we get here again, then it means that the condition_variable was woken up
|
||||
// due to sporatic wakeups, and we have to recalculate the wait duration.
|
||||
if (waitTimePoint.ShouldUpdateWaitDuration)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto timeLeft = duration_cast<milliseconds>(waitTimePoint.WaitUntil - system_clock::now());
|
||||
// Make sure that we do not have negative duration because we waited beyond the target time point,
|
||||
// or because system_clock::now() gave inacccurate time due to internal implementation.
|
||||
waitTimePoint.WaitDuration = std::max(timeLeft, milliseconds{0});
|
||||
}
|
||||
|
||||
waitTimePoint.ShouldUpdateWaitDuration = true;
|
||||
waitTimePoint.ShouldUpdateWaitDuration = true;
|
||||
|
||||
return static_cast<uint32_t>(waitTimePoint.WaitDuration.count());
|
||||
}
|
||||
return static_cast<uint32_t>(waitTimePoint.WaitDuration.count());
|
||||
}
|
||||
|
||||
private:
|
||||
CONDITION_VARIABLE m_cond { CONDITION_VARIABLE_INIT };
|
||||
CONDITION_VARIABLE m_cond{CONDITION_VARIABLE_INIT};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
LIBLET_PUBLICAPI ManualResetEvent::ManualResetEvent(EventWaitHandleState state) noexcept
|
||||
: m_handle { Mso::Make<EventWaitHandle<SRWMutex, ConditionVariable>>(/*isAutoReset:*/false, state) }
|
||||
: m_handle{Mso::Make<EventWaitHandle<SRWMutex, ConditionVariable>>(/*isAutoReset:*/ false, state)}
|
||||
{
|
||||
}
|
||||
|
||||
LIBLET_PUBLICAPI AutoResetEvent::AutoResetEvent(EventWaitHandleState state) noexcept
|
||||
: m_handle { Mso::Make<EventWaitHandle<SRWMutex, ConditionVariable>>(/*isAutoReset:*/true, state) }
|
||||
: m_handle{Mso::Make<EventWaitHandle<SRWMutex, ConditionVariable>>(/*isAutoReset:*/ true, state)}
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Async
|
||||
} // namespace Mso
|
||||
}} // namespace Mso::Async
|
||||
|
|
|
@ -11,327 +11,323 @@
|
|||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace Mso {
|
||||
namespace Async {
|
||||
namespace Test {
|
||||
namespace Mso { namespace Async { namespace Test {
|
||||
|
||||
TEST_CLASS(EventWaitHandleTest)
|
||||
TEST_CLASS (EventWaitHandleTest)
|
||||
{
|
||||
//TODO: enable MemoryLeakDetection
|
||||
//MemoryLeakDetectionHook::TrackPerTest m_trackLeakPerTest;
|
||||
// TODO: enable MemoryLeakDetection
|
||||
// MemoryLeakDetectionHook::TrackPerTest m_trackLeakPerTest;
|
||||
|
||||
TEST_METHOD(ManualResetEvent_ctor_default)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
TestCheck(&ev.GetHandle());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_ctor_default)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
TestCheck(&ev.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_ctor_Signaling)
|
||||
{
|
||||
ManualResetEvent ev{ EventWaitHandleState::IsSet };
|
||||
TestCheck(&ev.GetHandle());
|
||||
ev.Wait();
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_ctor_Signaling)
|
||||
{
|
||||
ManualResetEvent ev{EventWaitHandleState::IsSet};
|
||||
TestCheck(&ev.GetHandle());
|
||||
ev.Wait();
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_ctor_NotSignaling)
|
||||
{
|
||||
ManualResetEvent ev { EventWaitHandleState::NotSet };
|
||||
TestCheck(&ev.GetHandle());
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must timeout
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_ctor_NotSignaling)
|
||||
{
|
||||
ManualResetEvent ev{EventWaitHandleState::NotSet};
|
||||
TestCheck(&ev.GetHandle());
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must timeout
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_ctor_handle)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2 { ev1.GetHandle() };
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_ctor_handle)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2{ev1.GetHandle()};
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_ctor_copy)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2{ ev1 }; // Copy
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_ctor_copy)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2{ev1}; // Copy
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_assignment_copy)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2;
|
||||
ev2 = ev1; // Copy
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_assignment_copy)
|
||||
{
|
||||
ManualResetEvent ev1;
|
||||
ManualResetEvent ev2;
|
||||
ev2 = ev1; // Copy
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_Set)
|
||||
{
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
ManualResetEvent ev;
|
||||
// Note that we capture by value to make a copy of the pointer.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_Set)
|
||||
{
|
||||
std::atomic<int32_t> value{0};
|
||||
ManualResetEvent ev;
|
||||
// Note that we capture by value to make a copy of the pointer.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_NoAutoReset)
|
||||
{
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
ManualResetEvent ev;
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
ev.Wait(); // Second Wait succeeds because it is not auto-reset.
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_NoAutoReset)
|
||||
{
|
||||
std::atomic<int32_t> value{0};
|
||||
ManualResetEvent ev;
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
ev.Wait(); // Second Wait succeeds because it is not auto-reset.
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_TwoWaitThreadsWakeup)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
std::thread th2([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_TwoWaitThreadsWakeup)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
std::thread th2([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_Reset)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
ev.Reset();
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
std::thread th2([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
TestCheckEqual(1, value.load()); // Make sure that th2 waits
|
||||
ev.Set();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_Reset)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
ev.Reset();
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
std::thread th2([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
TestCheckEqual(1, value.load()); // Make sure that th2 waits
|
||||
ev.Set();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_WaitFor0)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(0ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_WaitFor0)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(0ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_WaitFor1)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_WaitFor1)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_WaitFor_Succeed)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(ev.WaitFor(1000s)); // Must succeed because we call Set.
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_WaitFor_Succeed)
|
||||
{
|
||||
ManualResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(ev.WaitFor(1000s)); // Must succeed because we call Set.
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TESTMETHOD_REQUIRES_SEH(ManualResetEvent_WaitFor_CrashForOverflow)
|
||||
{
|
||||
//TODO: enable MemoryLeakDetection
|
||||
//TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
ManualResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::seconds::max()));
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(ManualResetEvent_WaitFor_CrashForOverflow)
|
||||
{
|
||||
// TODO: enable MemoryLeakDetection
|
||||
// TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
ManualResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::seconds::max()));
|
||||
}
|
||||
|
||||
TEST_METHOD(ManualResetEvent_WaitFor_CrashForInfinite)
|
||||
{
|
||||
//TODO: enable MemoryLeakDetection
|
||||
//TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
ManualResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::milliseconds(std::numeric_limits<uint32_t>::max())));
|
||||
}
|
||||
TEST_METHOD(ManualResetEvent_WaitFor_CrashForInfinite)
|
||||
{
|
||||
// TODO: enable MemoryLeakDetection
|
||||
// TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
ManualResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::milliseconds(std::numeric_limits<uint32_t>::max())));
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_ctor_default)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
TestCheck(&ev.GetHandle());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_ctor_default)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
TestCheck(&ev.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_ctor_Signaling)
|
||||
{
|
||||
AutoResetEvent ev { EventWaitHandleState::IsSet };
|
||||
TestCheck(&ev.GetHandle());
|
||||
ev.Wait();
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_ctor_Signaling)
|
||||
{
|
||||
AutoResetEvent ev{EventWaitHandleState::IsSet};
|
||||
TestCheck(&ev.GetHandle());
|
||||
ev.Wait();
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_ctor_NotSignaling)
|
||||
{
|
||||
AutoResetEvent ev { EventWaitHandleState::NotSet };
|
||||
TestCheck(&ev.GetHandle());
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must timeout
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_ctor_NotSignaling)
|
||||
{
|
||||
AutoResetEvent ev{EventWaitHandleState::NotSet};
|
||||
TestCheck(&ev.GetHandle());
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must timeout
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_ctor_handle)
|
||||
{
|
||||
AutoResetEvent ev1;
|
||||
AutoResetEvent ev2 { ev1.GetHandle() };
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_ctor_handle)
|
||||
{
|
||||
AutoResetEvent ev1;
|
||||
AutoResetEvent ev2{ev1.GetHandle()};
|
||||
TestCheck(&ev1.GetHandle() == &ev2.GetHandle());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_Set)
|
||||
{
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
AutoResetEvent ev;
|
||||
// Note that we capture by value to make a copy of the pointer.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_Set)
|
||||
{
|
||||
std::atomic<int32_t> value{0};
|
||||
AutoResetEvent ev;
|
||||
// Note that we capture by value to make a copy of the pointer.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_AutoReset)
|
||||
{
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
AutoResetEvent ev;
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
TestCheck(!ev.WaitFor(1ms)); // Second Wait fails because it is auto-reset.
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_AutoReset)
|
||||
{
|
||||
std::atomic<int32_t> value{0};
|
||||
AutoResetEvent ev;
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Set();
|
||||
++value;
|
||||
});
|
||||
ev.Wait();
|
||||
TestCheck(!ev.WaitFor(1ms)); // Second Wait fails because it is auto-reset.
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_TwoWaitThreadsWakeup)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
ManualResetEvent valueChanged;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value, valueChanged]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
valueChanged.Set();
|
||||
});
|
||||
std::thread th2([ev, &value, valueChanged]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
valueChanged.Set();
|
||||
});
|
||||
ev.Set();
|
||||
valueChanged.Wait();
|
||||
TestCheckEqual(1, value.load());
|
||||
ev.Set();
|
||||
th1.join();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_TwoWaitThreadsWakeup)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
ManualResetEvent valueChanged;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value, valueChanged]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
valueChanged.Set();
|
||||
});
|
||||
std::thread th2([ev, &value, valueChanged]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
valueChanged.Set();
|
||||
});
|
||||
ev.Set();
|
||||
valueChanged.Wait();
|
||||
TestCheckEqual(1, value.load());
|
||||
ev.Set();
|
||||
th1.join();
|
||||
th2.join();
|
||||
TestCheckEqual(2, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_Reset)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
ev.Set();
|
||||
ev.Reset();
|
||||
TestCheck(!ev.WaitFor(1ms)); // Make sure that ev is not signaling.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_Reset)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
ev.Set();
|
||||
ev.Reset();
|
||||
TestCheck(!ev.WaitFor(1ms)); // Make sure that ev is not signaling.
|
||||
std::thread th([ev, &value]() noexcept {
|
||||
ev.Wait();
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_WaitFor0)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(0ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_WaitFor0)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(0ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_WaitFor1)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_WaitFor1)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(!ev.WaitFor(1ms)); // Must always timeout in our case.
|
||||
++value;
|
||||
});
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(AutoResetEvent_WaitFor_Succeed)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{ 0 };
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(ev.WaitFor(1000s)); // Must succeed because we call Set.
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
TEST_METHOD(AutoResetEvent_WaitFor_Succeed)
|
||||
{
|
||||
AutoResetEvent ev;
|
||||
std::atomic<int32_t> value{0};
|
||||
std::thread th1([ev, &value]() noexcept {
|
||||
TestCheck(ev.WaitFor(1000s)); // Must succeed because we call Set.
|
||||
++value;
|
||||
});
|
||||
ev.Set();
|
||||
th1.join();
|
||||
TestCheckEqual(1, value.load());
|
||||
}
|
||||
|
||||
TESTMETHOD_REQUIRES_SEH(AutoResetEvent_WaitFor_CrashForOverflow)
|
||||
{
|
||||
//TODO: enable MemoryLeakDetection
|
||||
//TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
AutoResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::seconds::max()));
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(AutoResetEvent_WaitFor_CrashForOverflow)
|
||||
{
|
||||
// TODO: enable MemoryLeakDetection
|
||||
// TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
AutoResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::seconds::max()));
|
||||
}
|
||||
|
||||
TESTMETHOD_REQUIRES_SEH(AutoResetEvent_WaitFor_CrashForInfinite)
|
||||
{
|
||||
//TODO: enable MemoryLeakDetection
|
||||
//TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
AutoResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::milliseconds(std::numeric_limits<uint32_t>::max())));
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(AutoResetEvent_WaitFor_CrashForInfinite)
|
||||
{
|
||||
// TODO: enable MemoryLeakDetection
|
||||
// TEST_DISABLE_MEMORY_LEAK_DETECTION();
|
||||
AutoResetEvent ev;
|
||||
TestCheckCrash(ev.WaitFor(std::chrono::milliseconds(std::numeric_limits<uint32_t>::max())));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Testing
|
||||
} // namespace Async
|
||||
} // namespace Mso
|
||||
}}} // namespace Mso::Async::Test
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -17,7 +17,7 @@
|
|||
//! stored as a field in an object.
|
||||
//!
|
||||
//! Mso::FunctorRef has the following semantics:
|
||||
//!
|
||||
//!
|
||||
//! - Only keeps a reference to the function object.
|
||||
//! - Never copies or moves the provided function object.
|
||||
//! - Does not do a heap allocation and has size of two pointers (one is
|
||||
|
@ -29,10 +29,10 @@
|
|||
//! unlike std::function<>.
|
||||
//! - Supports optional semantic by allowing to be initialized with nullptr, and
|
||||
//! then to be checked for non-null by the explicit bool operator.
|
||||
//!
|
||||
//!
|
||||
//! The Mso::FunctorRef only supports noexcept function objects.
|
||||
//! Use Mso::FunctorRefThrow for throwing function objects.
|
||||
//!
|
||||
//!
|
||||
//! In case if you want to store function object or transfer its ownership, you can use:
|
||||
//! - Mso::Functor that is an Mso::TCntPtr to a ref counted heap-allocated object.
|
||||
//! - Mso::SmallFunctor that uses in-place storage.
|
||||
|
@ -45,7 +45,7 @@
|
|||
//! virtual MyObject FindInCache(const Mso::FunctorRef<bool(MyObject&)>& predicate) = 0;
|
||||
//!
|
||||
//! This method can be called as following:
|
||||
//!
|
||||
//!
|
||||
//! myCache->FindInCache([this](MyObject& obj) noexcept { return this->CheckObj(obj); });
|
||||
//!
|
||||
//! Note that we can simply pass lambda as a parameter because we allow use of
|
||||
|
@ -115,91 +115,91 @@ using FunctorRefStorage = std::aligned_storage<sizeof(uintptr_t) * 2, 8>::type;
|
|||
|
||||
} // namespace Details
|
||||
|
||||
//! Reference to a non-throwing function object.
|
||||
//! Reference to a non-throwing function object.
|
||||
template <typename TResult, typename... TArgs>
|
||||
class FunctorRef<TResult(TArgs...)>
|
||||
#if !defined(__cpp_noexcept_function_type) && (_HAS_NOEXCEPT_FUNCTION_TYPES != 1)
|
||||
final
|
||||
final
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
//! Creates an empty FunctorRef
|
||||
_Allow_implicit_ctor_ FunctorRef(std::nullptr_t) noexcept
|
||||
{
|
||||
}
|
||||
//! Creates an empty FunctorRef
|
||||
_Allow_implicit_ctor_ FunctorRef(std::nullptr_t) noexcept {}
|
||||
|
||||
//! Creates a non-empty FunctorRef
|
||||
template <typename T>
|
||||
_Allow_implicit_ctor_ FunctorRef(T&& func) noexcept
|
||||
{
|
||||
using WrapperType = FunctorRefWrapper<std::remove_reference_t<T>>;
|
||||
//! Creates a non-empty FunctorRef
|
||||
template <typename T>
|
||||
_Allow_implicit_ctor_ FunctorRef(T&& func) noexcept
|
||||
{
|
||||
using WrapperType = FunctorRefWrapper<std::remove_reference_t<T>>;
|
||||
|
||||
// Make sure that WrapperType storage requirements match the FunctorRefStorage.
|
||||
static_assert(sizeof(WrapperType) <= sizeof(Details::FunctorRefStorage),
|
||||
"WrapperType is too big to fit in FunctorRefStorage.");
|
||||
static_assert(std::alignment_of<WrapperType>::value <= std::alignment_of<Details::FunctorRefStorage>::value,
|
||||
"WrapperType alignment does not match to FunctorRefStorage.");
|
||||
// Make sure that WrapperType storage requirements match the FunctorRefStorage.
|
||||
static_assert(
|
||||
sizeof(WrapperType) <= sizeof(Details::FunctorRefStorage),
|
||||
"WrapperType is too big to fit in FunctorRefStorage.");
|
||||
static_assert(
|
||||
std::alignment_of<WrapperType>::value <= std::alignment_of<Details::FunctorRefStorage>::value,
|
||||
"WrapperType alignment does not match to FunctorRefStorage.");
|
||||
|
||||
::new (std::addressof(m_storage)) WrapperType{&func};
|
||||
}
|
||||
::new (std::addressof(m_storage)) WrapperType{&func};
|
||||
}
|
||||
|
||||
//! Delete copy and move constructors and assignment operators.
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRef);
|
||||
//! Delete copy and move constructors and assignment operators.
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRef);
|
||||
|
||||
//! Calls referenced function object.
|
||||
//! Crash if referenced function object is nullptr.
|
||||
TResult operator()(TArgs... args) const noexcept
|
||||
{
|
||||
// It's important that we don't use '&&' on TArgs here. Since TArgs is a class
|
||||
// template parameter, the '&&' would force each argument to become an rvalue
|
||||
// (or, subject to ref-collapsing, an lvalue ref) which could prevent us from
|
||||
// being able to correctly invoke the underlying function object. With
|
||||
// that said, it is important that the wrapper's 'Invoke' uses '&&' to prevent
|
||||
// a second copy of any non-ref-qualified parameter since a copy would already
|
||||
// have been made here.
|
||||
//! Calls referenced function object.
|
||||
//! Crash if referenced function object is nullptr.
|
||||
TResult operator()(TArgs... args) const noexcept
|
||||
{
|
||||
// It's important that we don't use '&&' on TArgs here. Since TArgs is a class
|
||||
// template parameter, the '&&' would force each argument to become an rvalue
|
||||
// (or, subject to ref-collapsing, an lvalue ref) which could prevent us from
|
||||
// being able to correctly invoke the underlying function object. With
|
||||
// that said, it is important that the wrapper's 'Invoke' uses '&&' to prevent
|
||||
// a second copy of any non-ref-qualified parameter since a copy would already
|
||||
// have been made here.
|
||||
|
||||
VerifyElseCrashSzTag(*this, "FunctorRef must not be empty", 0x025d9804 /* tag_cxz6e */);
|
||||
return reinterpret_cast<const IFunctorRef*>(std::addressof(m_storage))->Invoke(std::forward<TArgs>(args)...);
|
||||
}
|
||||
VerifyElseCrashSzTag(*this, "FunctorRef must not be empty", 0x025d9804 /* tag_cxz6e */);
|
||||
return reinterpret_cast<const IFunctorRef*>(std::addressof(m_storage))->Invoke(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
//! True if wrapped function object is not nullptr.
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return *reinterpret_cast<const uintptr_t*>(std::addressof(m_storage)) != 0;
|
||||
}
|
||||
//! True if wrapped function object is not nullptr.
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return *reinterpret_cast<const uintptr_t*>(std::addressof(m_storage)) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//! The interface to be implemented by FunctorRefWrapper<T>.
|
||||
struct IFunctorRef
|
||||
{
|
||||
virtual TResult Invoke(TArgs&&...) const noexcept = 0;
|
||||
};
|
||||
//! The interface to be implemented by FunctorRefWrapper<T>.
|
||||
struct IFunctorRef
|
||||
{
|
||||
virtual TResult Invoke(TArgs&&...) const noexcept = 0;
|
||||
};
|
||||
|
||||
//! A wrapper for a function object reference.
|
||||
//! We do not call destructor for this class because it does not need to free any resources.
|
||||
template <typename T>
|
||||
struct FunctorRefWrapper final : IFunctorRef
|
||||
{
|
||||
FunctorRefWrapper(T* func) noexcept : m_func(func) {}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefWrapper);
|
||||
~FunctorRefWrapper() = delete;
|
||||
//! A wrapper for a function object reference.
|
||||
//! We do not call destructor for this class because it does not need to free any resources.
|
||||
template <typename T>
|
||||
struct FunctorRefWrapper final : IFunctorRef
|
||||
{
|
||||
FunctorRefWrapper(T* func) noexcept : m_func(func) {}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefWrapper);
|
||||
~FunctorRefWrapper() = delete;
|
||||
|
||||
TResult Invoke(TArgs&&... args) const noexcept override
|
||||
{
|
||||
// If you see OACR warning "Nothrow Func Throws" here then it means that the
|
||||
// provided lambda or function object's operator() are not marked as noexcept.
|
||||
TResult Invoke(TArgs&&... args) const noexcept override
|
||||
{
|
||||
// If you see OACR warning "Nothrow Func Throws" here then it means that the
|
||||
// provided lambda or function object's operator() are not marked as noexcept.
|
||||
|
||||
// We use const_cast to enable support for mutable lambdas
|
||||
return (*const_cast<FunctorRefWrapper*>(this)->m_func)(std::forward<TArgs>(args)...);
|
||||
}
|
||||
// We use const_cast to enable support for mutable lambdas
|
||||
return (*const_cast<FunctorRefWrapper*>(this)->m_func)(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_func;
|
||||
};
|
||||
private:
|
||||
T* m_func;
|
||||
};
|
||||
|
||||
private:
|
||||
// Storage for 2 pointers: v_table + reference
|
||||
Details::FunctorRefStorage m_storage { 0 };
|
||||
// Storage for 2 pointers: v_table + reference
|
||||
Details::FunctorRefStorage m_storage{0};
|
||||
};
|
||||
|
||||
#if defined(__cpp_noexcept_function_type) || (_HAS_NOEXCEPT_FUNCTION_TYPES == 1)
|
||||
|
@ -210,7 +210,7 @@ template <typename TResult, typename... TArgs>
|
|||
class FunctorRef<TResult(TArgs...) noexcept> : public FunctorRef<TResult(TArgs...)>
|
||||
{
|
||||
public:
|
||||
using FunctorRef<TResult(TArgs...)>::FunctorRef;
|
||||
using FunctorRef<TResult(TArgs...)>::FunctorRef;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -220,88 +220,88 @@ template <typename TResult, typename... TArgs>
|
|||
class FunctorRefThrow<TResult(TArgs...)> final
|
||||
{
|
||||
public:
|
||||
//! Creates an empty FunctorRefThrow
|
||||
_Allow_implicit_ctor_ FunctorRefThrow(std::nullptr_t) noexcept
|
||||
{
|
||||
}
|
||||
//! Creates an empty FunctorRefThrow
|
||||
_Allow_implicit_ctor_ FunctorRefThrow(std::nullptr_t) noexcept {}
|
||||
|
||||
//! Creates an non-empty FunctorRefThrow
|
||||
template <typename T>
|
||||
_Allow_implicit_ctor_ FunctorRefThrow(T&& func) noexcept
|
||||
{
|
||||
using WrapperType = FunctorRefThrowWrapper<std::remove_reference_t<T>>;
|
||||
//! Creates an non-empty FunctorRefThrow
|
||||
template <typename T>
|
||||
_Allow_implicit_ctor_ FunctorRefThrow(T&& func) noexcept
|
||||
{
|
||||
using WrapperType = FunctorRefThrowWrapper<std::remove_reference_t<T>>;
|
||||
|
||||
// Make sure that WrapperType storage requirements match the FunctorRefStorage.
|
||||
static_assert(sizeof(WrapperType) <= sizeof(Details::FunctorRefStorage),
|
||||
"WrapperType is too big to fit in FunctorRefStorage.");
|
||||
static_assert(std::alignment_of<WrapperType>::value <= std::alignment_of<Details::FunctorRefStorage>::value,
|
||||
"WrapperType alignment does not match to FunctorRefStorage.");
|
||||
// Make sure that WrapperType storage requirements match the FunctorRefStorage.
|
||||
static_assert(
|
||||
sizeof(WrapperType) <= sizeof(Details::FunctorRefStorage),
|
||||
"WrapperType is too big to fit in FunctorRefStorage.");
|
||||
static_assert(
|
||||
std::alignment_of<WrapperType>::value <= std::alignment_of<Details::FunctorRefStorage>::value,
|
||||
"WrapperType alignment does not match to FunctorRefStorage.");
|
||||
|
||||
::new (std::addressof(m_storage)) WrapperType{&func};
|
||||
}
|
||||
::new (std::addressof(m_storage)) WrapperType{&func};
|
||||
}
|
||||
|
||||
//! Delete copy and move constructors and assignment operators.
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefThrow);
|
||||
//! Delete copy and move constructors and assignment operators.
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefThrow);
|
||||
|
||||
//! Calls referenced function object.
|
||||
//! Crash if referenced function object is nullptr.
|
||||
TResult operator()(TArgs... args) const
|
||||
{
|
||||
// It's important that we don't use '&&' on TArgs here. Since TArgs is a class
|
||||
// template parameter, the '&&' would force each argument to become an rvalue
|
||||
// (or, subject to ref-collapsing, an lvalue ref) which could prevent us from
|
||||
// being able to correctly invoke the underlying function object. With
|
||||
// that said, it is important that the wrapper's 'Invoke' uses '&&' to prevent
|
||||
// a second copy of any non-ref-qualified parameter since a copy would already
|
||||
// have been made here.
|
||||
//! Calls referenced function object.
|
||||
//! Crash if referenced function object is nullptr.
|
||||
TResult operator()(TArgs... args) const
|
||||
{
|
||||
// It's important that we don't use '&&' on TArgs here. Since TArgs is a class
|
||||
// template parameter, the '&&' would force each argument to become an rvalue
|
||||
// (or, subject to ref-collapsing, an lvalue ref) which could prevent us from
|
||||
// being able to correctly invoke the underlying function object. With
|
||||
// that said, it is important that the wrapper's 'Invoke' uses '&&' to prevent
|
||||
// a second copy of any non-ref-qualified parameter since a copy would already
|
||||
// have been made here.
|
||||
|
||||
VerifyElseCrashSzTag(*this, "FunctorRefThrow must not be empty", 0x025d9805 /* tag_cxz6f */);
|
||||
return reinterpret_cast<const IFunctorRefThrow*>(std::addressof(m_storage))->Invoke(std::forward<TArgs>(args)...);
|
||||
}
|
||||
VerifyElseCrashSzTag(*this, "FunctorRefThrow must not be empty", 0x025d9805 /* tag_cxz6f */);
|
||||
return reinterpret_cast<const IFunctorRefThrow*>(std::addressof(m_storage))->Invoke(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
//! True if wrapped function object is not nullptr.
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return *reinterpret_cast<const uintptr_t*>(std::addressof(m_storage)) != 0;
|
||||
}
|
||||
//! True if wrapped function object is not nullptr.
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return *reinterpret_cast<const uintptr_t*>(std::addressof(m_storage)) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//! The interface to be implemented by FunctorRefThrowWrapper<T>.
|
||||
struct IFunctorRefThrow
|
||||
{
|
||||
virtual TResult Invoke(TArgs&&...) const = 0;
|
||||
};
|
||||
//! The interface to be implemented by FunctorRefThrowWrapper<T>.
|
||||
struct IFunctorRefThrow
|
||||
{
|
||||
virtual TResult Invoke(TArgs&&...) const = 0;
|
||||
};
|
||||
|
||||
//! A wrapper for a function object reference.
|
||||
//! We do not call destructor for this class because it does not need to free any resources.
|
||||
template <typename T>
|
||||
struct FunctorRefThrowWrapper final : IFunctorRefThrow
|
||||
{
|
||||
FunctorRefThrowWrapper(T* func) noexcept : m_func(func) {}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefThrowWrapper);
|
||||
~FunctorRefThrowWrapper() = delete;
|
||||
//! A wrapper for a function object reference.
|
||||
//! We do not call destructor for this class because it does not need to free any resources.
|
||||
template <typename T>
|
||||
struct FunctorRefThrowWrapper final : IFunctorRefThrow
|
||||
{
|
||||
FunctorRefThrowWrapper(T* func) noexcept : m_func(func) {}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FunctorRefThrowWrapper);
|
||||
~FunctorRefThrowWrapper() = delete;
|
||||
|
||||
TResult Invoke(TArgs&&... args) const override
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
// We use const_cast to enable support for mutable lambdas
|
||||
return (*const_cast<FunctorRefThrowWrapper*>(this)->m_func)(std::forward<TArgs>(args)...);
|
||||
}
|
||||
TResult Invoke(TArgs&&... args) const override
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
// We use const_cast to enable support for mutable lambdas
|
||||
return (*const_cast<FunctorRefThrowWrapper*>(this)->m_func)(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_func;
|
||||
};
|
||||
private:
|
||||
T* m_func;
|
||||
};
|
||||
|
||||
private:
|
||||
// Storage for 2 pointers: v_table + reference
|
||||
Details::FunctorRefStorage m_storage { 0 };
|
||||
// Storage for 2 pointers: v_table + reference
|
||||
Details::FunctorRefStorage m_storage{0};
|
||||
};
|
||||
|
||||
/// Aliases for the most common function object type "void()".
|
||||
using VoidFunctorRef = FunctorRef<void()>;
|
||||
using VoidFunctorRefThrow = FunctorRefThrow<void()>;
|
||||
|
||||
}
|
||||
} // namespace Mso
|
||||
|
||||
#pragma pop_macro("new")
|
||||
|
||||
|
|
|
@ -16,353 +16,370 @@ Unit tests for classes in the Functor.h
|
|||
namespace {
|
||||
struct MyParam
|
||||
{
|
||||
void AddRef() const { ++AddRefCount; }
|
||||
void Release() const { UNREFERENCED_OACR(this); /* Do not decrement */ }
|
||||
mutable int AddRefCount = 0; // AddRef() call count
|
||||
int Value;
|
||||
void AddRef() const
|
||||
{
|
||||
++AddRefCount;
|
||||
}
|
||||
void Release() const
|
||||
{
|
||||
UNREFERENCED_OACR(this); /* Do not decrement */
|
||||
}
|
||||
mutable int AddRefCount = 0; // AddRef() call count
|
||||
int Value;
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct TestClass
|
||||
{
|
||||
static void Execute(const Mso::VoidFunctorRef& func) noexcept
|
||||
{
|
||||
func();
|
||||
}
|
||||
static void Execute(const Mso::VoidFunctorRef& func) noexcept
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
static void ExecuteThrow(const Mso::VoidFunctorRefThrow& func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
static void ExecuteThrow(const Mso::VoidFunctorRefThrow& func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
static void ExecuteNullable(const Mso::VoidFunctorRef& func) noexcept
|
||||
{
|
||||
if (func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
static void ExecuteNullable(const Mso::VoidFunctorRef& func) noexcept
|
||||
{
|
||||
if (func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
static void ExecuteNullableThrow(const Mso::VoidFunctorRefThrow& func)
|
||||
{
|
||||
if (func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
static void ExecuteNullableThrow(const Mso::VoidFunctorRefThrow& func)
|
||||
{
|
||||
if (func)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
static int Aggregate(int x, int y, const Mso::FunctorRef<int(int, int)>& func) noexcept
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
static int Aggregate(int x, int y, const Mso::FunctorRef<int(int, int)>& func) noexcept
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
|
||||
static int AggregateThrow(int x, int y, const Mso::FunctorRefThrow<int(int, int)>& func)
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
static int AggregateThrow(int x, int y, const Mso::FunctorRefThrow<int(int, int)>& func)
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
|
||||
static int PassByRef(const Mso::TCntPtr<MyParam>& p, const Mso::FunctorRef<int(const Mso::TCntPtr<MyParam>&)>& func) noexcept
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
static int PassByRef(
|
||||
const Mso::TCntPtr<MyParam>& p,
|
||||
const Mso::FunctorRef<int(const Mso::TCntPtr<MyParam>&)>& func) noexcept
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
|
||||
static int PassByRefThrow(const Mso::TCntPtr<MyParam>& p, const Mso::FunctorRefThrow<int(const Mso::TCntPtr<MyParam>&)>& func)
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
static int PassByRefThrow(
|
||||
const Mso::TCntPtr<MyParam>& p,
|
||||
const Mso::FunctorRefThrow<int(const Mso::TCntPtr<MyParam>&)>& func)
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
|
||||
static Mso::TCntPtr<MyParam> PassAsRValue(Mso::TCntPtr<MyParam>&& p, const Mso::FunctorRef<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&)>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
static Mso::TCntPtr<MyParam> PassAsRValue(
|
||||
Mso::TCntPtr<MyParam>&& p,
|
||||
const Mso::FunctorRef<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&)>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
|
||||
static Mso::TCntPtr<MyParam> PassAsRValueThrow(Mso::TCntPtr<MyParam>&& p, const Mso::FunctorRefThrow<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&)>& func)
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
static Mso::TCntPtr<MyParam> PassAsRValueThrow(
|
||||
Mso::TCntPtr<MyParam>&& p,
|
||||
const Mso::FunctorRefThrow<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&)>& func)
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(BY_VALUE_TEMPLATEFORMAL_WITH_DTOR, "We want to test that we can pass by value")
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(BY_VALUE_TEMPLATEFORMAL_WITH_DTOR, "We want to test that we can pass by value")
|
||||
|
||||
static std::unique_ptr<int> PassByValue(std::unique_ptr<int> p, const Mso::FunctorRef<std::unique_ptr<int>(std::unique_ptr<int>)>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
static std::unique_ptr<int> PassByValue(
|
||||
std::unique_ptr<int> p,
|
||||
const Mso::FunctorRef<std::unique_ptr<int>(std::unique_ptr<int>)>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
|
||||
static std::unique_ptr<int> PassByValueThrow(std::unique_ptr<int> p, const Mso::FunctorRefThrow<std::unique_ptr<int>(std::unique_ptr<int>)>& func)
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
static std::unique_ptr<int> PassByValueThrow(
|
||||
std::unique_ptr<int> p,
|
||||
const Mso::FunctorRefThrow<std::unique_ptr<int>(std::unique_ptr<int>)>& func)
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
|
||||
OACR_WARNING_POP
|
||||
OACR_WARNING_POP
|
||||
};
|
||||
|
||||
struct TestClassNoexcept
|
||||
{
|
||||
static void Execute(const Mso::FunctorRef<void() noexcept>& func) noexcept
|
||||
{
|
||||
func();
|
||||
}
|
||||
static void Execute(const Mso::FunctorRef<void() noexcept>& func) noexcept
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
static int Aggregate(int x, int y, const Mso::FunctorRef<int(int, int) noexcept>& func) noexcept
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
static int Aggregate(int x, int y, const Mso::FunctorRef<int(int, int) noexcept>& func) noexcept
|
||||
{
|
||||
return func(x, y);
|
||||
}
|
||||
|
||||
static int PassByRef(const Mso::TCntPtr<MyParam>& p, const Mso::FunctorRef<int(const Mso::TCntPtr<MyParam>&) noexcept>& func) noexcept
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
static int PassByRef(
|
||||
const Mso::TCntPtr<MyParam>& p,
|
||||
const Mso::FunctorRef<int(const Mso::TCntPtr<MyParam>&) noexcept>& func) noexcept
|
||||
{
|
||||
return func(p);
|
||||
}
|
||||
|
||||
static Mso::TCntPtr<MyParam> PassAsRValue(Mso::TCntPtr<MyParam>&& p, const Mso::FunctorRef<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&) noexcept>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
static Mso::TCntPtr<MyParam> PassAsRValue(
|
||||
Mso::TCntPtr<MyParam>&& p,
|
||||
const Mso::FunctorRef<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&) noexcept>& func) noexcept
|
||||
{
|
||||
return func(std::move(p));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(FunctorRefTest)
|
||||
TEST_CLASS (FunctorRefTest)
|
||||
{
|
||||
TEST_METHOD(FunctorRef_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::Execute([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::Execute([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteThrow([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRefThrow_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteThrow([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_Lambda2)
|
||||
{
|
||||
bool isCalled = false;
|
||||
auto lambda = [&isCalled]() noexcept { isCalled = true; };
|
||||
TestClass::Execute(lambda);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_Lambda2)
|
||||
{
|
||||
bool isCalled = false;
|
||||
auto lambda = [&isCalled]() noexcept { isCalled = true; };
|
||||
TestClass::Execute(lambda);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_Lambda2)
|
||||
{
|
||||
bool isCalled = false;
|
||||
auto lambda = [&isCalled]() noexcept { isCalled = true; };
|
||||
TestClass::ExecuteThrow(lambda);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRefThrow_Lambda2)
|
||||
{
|
||||
bool isCalled = false;
|
||||
auto lambda = [&isCalled]() noexcept { isCalled = true; };
|
||||
TestClass::ExecuteThrow(lambda);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_Nullable)
|
||||
{
|
||||
TestClass::ExecuteNullable(nullptr);
|
||||
TEST_METHOD(FunctorRef_Nullable)
|
||||
{
|
||||
TestClass::ExecuteNullable(nullptr);
|
||||
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteNullable([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteNullable([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_Nullable)
|
||||
{
|
||||
TestClass::ExecuteNullableThrow(nullptr);
|
||||
TEST_METHOD(FunctorRefThrow_Nullable)
|
||||
{
|
||||
TestClass::ExecuteNullableThrow(nullptr);
|
||||
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteNullableThrow([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteNullableThrow([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TESTMETHOD_REQUIRES_SEH(FunctorRef_Nullable_Crash)
|
||||
{
|
||||
TestCheckCrash(TestClass::Execute(nullptr));
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(FunctorRef_Nullable_Crash)
|
||||
{
|
||||
TestCheckCrash(TestClass::Execute(nullptr));
|
||||
}
|
||||
|
||||
TESTMETHOD_REQUIRES_SEH(FunctorRefThrow_Nullable_Crash)
|
||||
{
|
||||
TestCheckCrash(TestClass::ExecuteThrow(nullptr));
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(FunctorRefThrow_Nullable_Crash)
|
||||
{
|
||||
TestCheckCrash(TestClass::ExecuteThrow(nullptr));
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_MsoFunctor)
|
||||
{
|
||||
// It can be useful in case when we have an std::function available.
|
||||
bool isCalled = false;
|
||||
auto func = Mso::VoidFunctor([&isCalled]() noexcept { isCalled = true; });
|
||||
TestClass::Execute(func);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_MsoFunctor)
|
||||
{
|
||||
// It can be useful in case when we have an std::function available.
|
||||
bool isCalled = false;
|
||||
auto func = Mso::VoidFunctor([&isCalled]() noexcept { isCalled = true; });
|
||||
TestClass::Execute(func);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_LambdaReturnValue)
|
||||
{
|
||||
int addResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x + y; });
|
||||
TestCheckEqual(8, addResult);
|
||||
TEST_METHOD(FunctorRef_LambdaReturnValue)
|
||||
{
|
||||
int addResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x + y; });
|
||||
TestCheckEqual(8, addResult);
|
||||
|
||||
int multResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x * y; });
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
int multResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x * y; });
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
|
||||
int result = TestClass::PassByRef(spParam, [](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; });
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
int result = TestClass::PassByRef(spParam, [](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; });
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
|
||||
auto spParam2 = TestClass::PassAsRValue(std::move(spParam), [](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); });
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
|
||||
TEST_METHOD(FunctorRef_noexcept_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::Execute([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
auto spParam2 =
|
||||
TestClass::PassAsRValue(std::move(spParam), [](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); });
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_noexcept_MsoFunctor)
|
||||
{
|
||||
// It can be useful in case when we have an std::function available.
|
||||
bool isCalled = false;
|
||||
auto func = Mso::VoidFunctor([&isCalled]() noexcept { isCalled = true; });
|
||||
TestClass::Execute(func);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_noexcept_Lambda)
|
||||
{
|
||||
bool isCalled = false;
|
||||
TestClass::Execute([&isCalled]() noexcept { isCalled = true; });
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaReturnValue)
|
||||
{
|
||||
int addResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x + y; });
|
||||
TestCheckEqual(8, addResult);
|
||||
TEST_METHOD(FunctorRef_noexcept_MsoFunctor)
|
||||
{
|
||||
// It can be useful in case when we have an std::function available.
|
||||
bool isCalled = false;
|
||||
auto func = Mso::VoidFunctor([&isCalled]() noexcept { isCalled = true; });
|
||||
TestClass::Execute(func);
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
int multResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x * y; });
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaReturnValue)
|
||||
{
|
||||
int addResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x + y; });
|
||||
TestCheckEqual(8, addResult);
|
||||
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
int multResult = TestClass::Aggregate(3, 5, [](int x, int y) noexcept { return x * y; });
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
|
||||
int result = TestClass::PassByRef(spParam, [](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; });
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
int result = TestClass::PassByRef(spParam, [](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; });
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
auto spParam2 = TestClass::PassAsRValue(std::move(spParam), [](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); });
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_noexcept_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_StdFunction)
|
||||
{
|
||||
// We use the std::function as an example of throwing function object.
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteThrow(std::function<void()>([&isCalled]() noexcept { isCalled = true; }));
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
auto spParam2 =
|
||||
TestClass::PassAsRValue(std::move(spParam), [](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); });
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_LambdaReturnValue)
|
||||
{
|
||||
int addResult = TestClass::AggregateThrow(3, 5, std::function<int(int, int)>([](int x, int y) noexcept { return x + y; }));
|
||||
TestCheckEqual(8, addResult);
|
||||
TEST_METHOD(FunctorRefThrow_StdFunction)
|
||||
{
|
||||
// We use the std::function as an example of throwing function object.
|
||||
bool isCalled = false;
|
||||
TestClass::ExecuteThrow(std::function<void()>([&isCalled]() noexcept { isCalled = true; }));
|
||||
TestCheck(isCalled);
|
||||
}
|
||||
|
||||
int multResult = TestClass::AggregateThrow(3, 5, std::function<int(int, int)>([](int x, int y) noexcept{ return x * y; }));
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
TEST_METHOD(FunctorRefThrow_LambdaReturnValue)
|
||||
{
|
||||
int addResult =
|
||||
TestClass::AggregateThrow(3, 5, std::function<int(int, int)>([](int x, int y) noexcept { return x + y; }));
|
||||
TestCheckEqual(8, addResult);
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
int multResult =
|
||||
TestClass::AggregateThrow(3, 5, std::function<int(int, int)>([](int x, int y) noexcept { return x * y; }));
|
||||
TestCheckEqual(15, multResult);
|
||||
}
|
||||
|
||||
using Function = std::function<int(const Mso::TCntPtr<MyParam>&)>;
|
||||
int result = TestClass::PassByRefThrow(spParam, Function([](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; }));
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamByRef)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an const reference and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
param.Value = 10;
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
using Function = std::function<int(const Mso::TCntPtr<MyParam>&)>;
|
||||
int result =
|
||||
TestClass::PassByRefThrow(spParam, Function([](const Mso::TCntPtr<MyParam>& p) noexcept { return p->Value; }));
|
||||
TestCheckEqual(10, result);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
using Function = std::function<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam>&&)>;
|
||||
auto spParam2 = TestClass::PassAsRValueThrow(std::move(spParam), Function([](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); }));
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamAsRValue)
|
||||
{
|
||||
// Pass an Mso::TCntPtr as an r-value and see that it was never called AddRef()
|
||||
MyParam param;
|
||||
auto spParam = Mso::TCntPtr<MyParam>(¶m);
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
|
||||
TEST_METHOD(FunctorRef_MutableLambda)
|
||||
{
|
||||
std::unique_ptr<bool> isCalled { nullptr };
|
||||
auto mutableValue = std::make_unique<bool>(true);
|
||||
using Function = std::function<Mso::TCntPtr<MyParam>(Mso::TCntPtr<MyParam> &&)>;
|
||||
auto spParam2 = TestClass::PassAsRValueThrow(
|
||||
std::move(spParam), Function([](Mso::TCntPtr<MyParam>&& p) noexcept { return std::move(p); }));
|
||||
TestCheckEqual(1, param.AddRefCount);
|
||||
}
|
||||
|
||||
TestClass::Execute([&isCalled, mutableValue = std::move(mutableValue)]() mutable noexcept
|
||||
{
|
||||
isCalled = std::move(mutableValue);
|
||||
});
|
||||
TEST_METHOD(FunctorRef_MutableLambda)
|
||||
{
|
||||
std::unique_ptr<bool> isCalled{nullptr};
|
||||
auto mutableValue = std::make_unique<bool>(true);
|
||||
|
||||
TestCheck(*isCalled);
|
||||
}
|
||||
TestClass::Execute(
|
||||
[&isCalled, mutableValue = std::move(mutableValue)]() mutable noexcept { isCalled = std::move(mutableValue); });
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_MutableLambda)
|
||||
{
|
||||
std::unique_ptr<bool> isCalled { nullptr };
|
||||
auto mutableValue = std::make_unique<bool>(true);
|
||||
TestCheck(*isCalled);
|
||||
}
|
||||
|
||||
TestClass::ExecuteThrow([&isCalled, mutableValue = std::move(mutableValue)]() mutable noexcept
|
||||
{
|
||||
isCalled = std::move(mutableValue);
|
||||
});
|
||||
TEST_METHOD(FunctorRefThrow_MutableLambda)
|
||||
{
|
||||
std::unique_ptr<bool> isCalled{nullptr};
|
||||
auto mutableValue = std::make_unique<bool>(true);
|
||||
|
||||
TestCheck(*isCalled);
|
||||
}
|
||||
TestClass::ExecuteThrow(
|
||||
[&isCalled, mutableValue = std::move(mutableValue)]() mutable noexcept { isCalled = std::move(mutableValue); });
|
||||
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(BY_VALUE_TEMPLATEFORMAL_WITH_DTOR, "We want to test that we can pass by value")
|
||||
TestCheck(*isCalled);
|
||||
}
|
||||
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamByValue)
|
||||
{
|
||||
auto inValue = std::make_unique<int>(5);
|
||||
auto result = TestClass::PassByValue(std::move(inValue), [](std::unique_ptr<int> p) noexcept
|
||||
{
|
||||
return p;
|
||||
});
|
||||
TestCheckEqual(5, *result);
|
||||
}
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(BY_VALUE_TEMPLATEFORMAL_WITH_DTOR, "We want to test that we can pass by value")
|
||||
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamByValue)
|
||||
{
|
||||
auto inValue = std::make_unique<int>(5);
|
||||
auto result = TestClass::PassByValueThrow(std::move(inValue), [](std::unique_ptr<int> p) noexcept
|
||||
{
|
||||
return p;
|
||||
});
|
||||
TestCheckEqual(5, *result);
|
||||
}
|
||||
TEST_METHOD(FunctorRef_LambdaPassParamByValue)
|
||||
{
|
||||
auto inValue = std::make_unique<int>(5);
|
||||
auto result = TestClass::PassByValue(std::move(inValue), [](std::unique_ptr<int> p) noexcept { return p; });
|
||||
TestCheckEqual(5, *result);
|
||||
}
|
||||
|
||||
OACR_WARNING_POP
|
||||
TEST_METHOD(FunctorRefThrow_LambdaPassParamByValue)
|
||||
{
|
||||
auto inValue = std::make_unique<int>(5);
|
||||
auto result = TestClass::PassByValueThrow(std::move(inValue), [](std::unique_ptr<int> p) noexcept { return p; });
|
||||
TestCheckEqual(5, *result);
|
||||
}
|
||||
|
||||
OACR_WARNING_POP
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,79 +9,87 @@ Common code for functor (Functor, SmallFunctor) unit tests.
|
|||
|
||||
namespace FunctorTestsCommon {
|
||||
|
||||
class TestData
|
||||
: public Mso::RefCountedObjectNoVTable<TestData>
|
||||
class TestData : public Mso::RefCountedObjectNoVTable<TestData>
|
||||
{
|
||||
using Super = Mso::RefCountedObjectNoVTable<TestData>;
|
||||
using Super = Mso::RefCountedObjectNoVTable<TestData>;
|
||||
|
||||
public:
|
||||
TestData(int& addRefCalls, int& releaseCalls) noexcept
|
||||
: m_addRefCalls(addRefCalls)
|
||||
, m_releaseCalls(releaseCalls)
|
||||
, Value(0)
|
||||
{
|
||||
}
|
||||
TestData(int& addRefCalls, int& releaseCalls) noexcept
|
||||
: m_addRefCalls(addRefCalls), m_releaseCalls(releaseCalls), Value(0)
|
||||
{
|
||||
}
|
||||
|
||||
void AddRef() const
|
||||
{
|
||||
++m_addRefCalls;
|
||||
Super::AddRef();
|
||||
}
|
||||
void AddRef() const
|
||||
{
|
||||
++m_addRefCalls;
|
||||
Super::AddRef();
|
||||
}
|
||||
|
||||
void Release() const
|
||||
{
|
||||
++m_releaseCalls;
|
||||
Super::Release();
|
||||
}
|
||||
void Release() const
|
||||
{
|
||||
++m_releaseCalls;
|
||||
Super::Release();
|
||||
}
|
||||
|
||||
int Value;
|
||||
int Value;
|
||||
|
||||
private:
|
||||
int& m_addRefCalls;
|
||||
int& m_releaseCalls;
|
||||
int& m_addRefCalls;
|
||||
int& m_releaseCalls;
|
||||
};
|
||||
|
||||
struct StaticMethod
|
||||
{
|
||||
static int Add(int x, int y) noexcept
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
static int Add(int x, int y) noexcept
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(FUNC_COULD_BE_NOEXCEPT, "Simple non-nonexcept function for use in unit tests")
|
||||
static int AddThrow(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
OACR_WARNING_POP
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(FUNC_COULD_BE_NOEXCEPT, "Simple non-nonexcept function for use in unit tests")
|
||||
static int AddThrow(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
OACR_WARNING_POP
|
||||
};
|
||||
|
||||
struct StructParam
|
||||
{
|
||||
void AddRef() const { UNREFERENCED_OACR(this); }
|
||||
void Release() const { UNREFERENCED_OACR(this); }
|
||||
void AddRef() const
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
}
|
||||
void Release() const
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
}
|
||||
};
|
||||
|
||||
struct BaseClass
|
||||
{
|
||||
virtual int GetInt() noexcept { return 0; }
|
||||
virtual int GetInt() noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Derived final
|
||||
: public BaseClass
|
||||
struct Derived final : public BaseClass
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
int GetInt() noexcept override { return i; }
|
||||
int GetInt() noexcept override
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
static Derived& DerivedFromBaseClass(BaseClass& baseClass) noexcept
|
||||
{
|
||||
static Derived derived;
|
||||
static Derived derived;
|
||||
|
||||
derived.i = baseClass.GetInt();
|
||||
return derived;
|
||||
derived.i = baseClass.GetInt();
|
||||
return derived;
|
||||
}
|
||||
|
||||
} // namespace FunctorTestsCommon
|
||||
|
|
|
@ -21,7 +21,7 @@ See MsoMemory.h for information about operator new.
|
|||
#ifndef MsoSetLazyLeakDetection
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOAPI_(void) MsoSetLazyLeakDetection(const void* pv) noexcept;
|
||||
#else
|
||||
#else
|
||||
#define MsoSetLazyLeakDetection(pv) 0
|
||||
#endif // DEBUG
|
||||
#endif // !MsoSetLazyLeakDetection
|
||||
|
@ -29,89 +29,87 @@ LIBLET_PUBLICAPI_EX("android", "win") MSOAPI_(void) MsoSetLazyLeakDetection(cons
|
|||
#ifndef MsoSetShutdownLeakDetection
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOAPI_(void) MsoSetShutdownLeakDetection(const void* pv) noexcept;
|
||||
#else
|
||||
#else
|
||||
#define MsoSetShutdownLeakDetection(pv) 0
|
||||
#endif // DEBUG
|
||||
#endif // !MsoSetShutdownLeakDetection
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace Mso {
|
||||
namespace LibletAPI {
|
||||
struct ILibletMemoryMarking
|
||||
{
|
||||
virtual void MarkLiblets(intptr_t lParam) noexcept = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace Mso { namespace LibletAPI {
|
||||
struct ILibletMemoryMarking
|
||||
{
|
||||
virtual void MarkLiblets(intptr_t lParam) noexcept = 0;
|
||||
};
|
||||
}} // namespace Mso::LibletAPI
|
||||
|
||||
#ifdef DEBUG
|
||||
struct IMsoMemHeap;
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOAPI_(BOOL) FMemHeapMsoSaveBeHost(void* pinst, LPARAM lParam, const void* pvBlock, LONG_PTR cb, IMsoMemHeap* pmmh) MSONOEXCEPT;
|
||||
LIBLET_PUBLICAPI_EX("android", "win")
|
||||
MSOAPI_(BOOL)
|
||||
FMemHeapMsoSaveBeHost(void* pinst, LPARAM lParam, const void* pvBlock, LONG_PTR cb, IMsoMemHeap* pmmh) MSONOEXCEPT;
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOAPI_(void) MsoCheckShutdownLeaks() noexcept;
|
||||
LIBLET_PUBLICAPI_EX("win") MSOAPI_(void) HeapEnableLeakTracking(bool isEnabled);
|
||||
LIBLET_PUBLICAPI_EX("win") MSOAPI_(void) MsoBeforeThreadTerminatesThreaded(DWORD mainThreadId) noexcept;
|
||||
#endif
|
||||
LIBLET_PUBLICAPI_EX("android", "win") __declspec(noreturn) void ThrowOOM();
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace Mso { namespace Memory {
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOCPPAPI_(void) RegisterCallback(Mso::LibletAPI::ILibletMemoryMarking& libletCallback) noexcept;
|
||||
LIBLET_PUBLICAPI_EX("android", "win") MSOCPPAPI_(void) UnregisterCallback(Mso::LibletAPI::ILibletMemoryMarking& libletCallback) noexcept;
|
||||
LIBLET_PUBLICAPI_EX("android", "win")
|
||||
MSOCPPAPI_(void) RegisterCallback(Mso::LibletAPI::ILibletMemoryMarking& libletCallback) noexcept;
|
||||
LIBLET_PUBLICAPI_EX("android", "win")
|
||||
MSOCPPAPI_(void) UnregisterCallback(Mso::LibletAPI::ILibletMemoryMarking& libletCallback) noexcept;
|
||||
|
||||
/**
|
||||
Helper class to register arbitrary callback for memory marking
|
||||
*/
|
||||
struct RegisterMarkMemoryCallback : public Mso::LibletAPI::ILibletMemoryMarking
|
||||
{
|
||||
using MarkMemHandler = std::add_pointer_t<void(intptr_t) noexcept>;
|
||||
/**
|
||||
Helper class to register arbitrary callback for memory marking
|
||||
*/
|
||||
struct RegisterMarkMemoryCallback : public Mso::LibletAPI::ILibletMemoryMarking
|
||||
{
|
||||
using MarkMemHandler = std::add_pointer_t<void(intptr_t) noexcept>;
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RegisterMarkMemoryCallback);
|
||||
RegisterMarkMemoryCallback(MarkMemHandler handler) noexcept
|
||||
: m_handler(handler)
|
||||
{
|
||||
RegisterCallback(*this);
|
||||
}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RegisterMarkMemoryCallback);
|
||||
RegisterMarkMemoryCallback(MarkMemHandler handler) noexcept : m_handler(handler)
|
||||
{
|
||||
RegisterCallback(*this);
|
||||
}
|
||||
|
||||
virtual ~RegisterMarkMemoryCallback() noexcept
|
||||
{
|
||||
UnregisterCallback(*this);
|
||||
}
|
||||
virtual ~RegisterMarkMemoryCallback() noexcept
|
||||
{
|
||||
UnregisterCallback(*this);
|
||||
}
|
||||
|
||||
void MarkLiblets(intptr_t lParam) noexcept override
|
||||
{
|
||||
m_handler(lParam);
|
||||
}
|
||||
void MarkLiblets(intptr_t lParam) noexcept override
|
||||
{
|
||||
m_handler(lParam);
|
||||
}
|
||||
|
||||
private:
|
||||
MarkMemHandler m_handler;
|
||||
};
|
||||
private:
|
||||
MarkMemHandler m_handler;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
#ifdef DEBUG
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
// Legacy API
|
||||
inline _Ret_notnull_ _Post_writable_byte_size_(cb) void* Allocate(size_t cb FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return AllocateEx(cb, 0);
|
||||
}
|
||||
|
||||
// Legacy API
|
||||
inline _Ret_maybenull_ _Post_writable_byte_size_(cb) void* AllocateEx(size_t cb, DWORD allocFlags FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return AllocateEx(cb, allocFlags);
|
||||
}
|
||||
|
||||
// Legacy API
|
||||
inline _Ret_maybenull_ void* Reallocate(_Inout_ void** ppv, size_t cb FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return Reallocate(ppv, cb);
|
||||
}
|
||||
}
|
||||
namespace Mso { namespace Memory {
|
||||
// Legacy API
|
||||
inline _Ret_notnull_ _Post_writable_byte_size_(cb) void* Allocate(size_t cb FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return AllocateEx(cb, 0);
|
||||
}
|
||||
|
||||
// Legacy API
|
||||
inline _Ret_maybenull_ _Post_writable_byte_size_(
|
||||
cb) void* AllocateEx(size_t cb, DWORD allocFlags FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return AllocateEx(cb, allocFlags);
|
||||
}
|
||||
|
||||
// Legacy API
|
||||
inline _Ret_maybenull_ void* Reallocate(_Inout_ void** ppv, size_t cb FILELINEPARAMSUNUSED) noexcept
|
||||
{
|
||||
return Reallocate(ppv, cb);
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Deprecated APIs related to leak detection
|
||||
Deprecated APIs related to leak detection
|
||||
*/
|
||||
#pragma once
|
||||
#include <memoryLeakScope/memoryLeakScope.h>
|
||||
|
||||
/**
|
||||
When this object is active, allocations on this thread are marked as
|
||||
needing to be cleaned up by shutdown.
|
||||
When this object is active, allocations on this thread are marked as
|
||||
needing to be cleaned up by shutdown.
|
||||
*/
|
||||
using MsoDebugShutdownLazyScope = Mso::Memory::AutoShutdownLeakScope;
|
||||
|
||||
/**
|
||||
When this object is active, allocations on this thread are not tracked
|
||||
for leak detection. Typically used when the memory lifetime is the life of
|
||||
the DLL or is managed by an external dependency.
|
||||
When this object is active, allocations on this thread are not tracked
|
||||
for leak detection. Typically used when the memory lifetime is the life of
|
||||
the DLL or is managed by an external dependency.
|
||||
*/
|
||||
using MsoDebugLazyScope = Mso::Memory::AutoIgnoreLeakScope;
|
||||
|
|
|
@ -28,421 +28,421 @@ See MsoMemory.h for information about operator new.
|
|||
#undef New
|
||||
#include <memory>
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace Details {
|
||||
template <typename T>
|
||||
struct Emplacer
|
||||
{
|
||||
template <typename... Args>
|
||||
static void Place(_Inout_updates_bytes_all_(sizeof(T)) void* mem, Args&&... args) noexcept
|
||||
{
|
||||
new (mem) T(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
namespace Mso { namespace Memory {
|
||||
namespace Details {
|
||||
template <typename T>
|
||||
struct Emplacer
|
||||
{
|
||||
template <typename... Args>
|
||||
static void Place(_Inout_updates_bytes_all_(sizeof(T)) void* mem, Args&&... args) noexcept
|
||||
{
|
||||
new (mem) T(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
struct Emplacer < T[N] >
|
||||
{
|
||||
template <typename... Args>
|
||||
static void Place(void* mem, Args&&... args)
|
||||
{
|
||||
new (mem) T[N]
|
||||
{
|
||||
std::forward<Args>(args)...
|
||||
};
|
||||
}
|
||||
};
|
||||
template <typename T, size_t N>
|
||||
struct Emplacer<T[N]>
|
||||
{
|
||||
template <typename... Args>
|
||||
static void Place(void* mem, Args&&... args)
|
||||
{
|
||||
new (mem) T[N]{std::forward<Args>(args)...};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct Destructor
|
||||
{
|
||||
static void Destruct(T&) noexcept { /* noop */ }
|
||||
};
|
||||
template <typename T, typename Enable = void>
|
||||
struct Destructor
|
||||
{
|
||||
static void Destruct(T&) noexcept
|
||||
{ /* noop */
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Destructor < T,
|
||||
typename std::enable_if<std::is_destructible<T>::value && !std::is_pod<T>::value>::type >
|
||||
{
|
||||
static void Destruct(T& obj) noexcept
|
||||
{
|
||||
UNREFERENCED_PARAMETER(obj);
|
||||
obj.~T();
|
||||
}
|
||||
};
|
||||
} // Details
|
||||
|
||||
/**
|
||||
Mso::Memory::AllocFlags
|
||||
*/
|
||||
namespace AllocFlags {
|
||||
enum Enum : unsigned int
|
||||
{
|
||||
// check this memory during shutdown leak detection
|
||||
ShutdownLeak = 0x0001,
|
||||
|
||||
// ignore this memory during leak detection
|
||||
IgnoreLeak = 0x0002,
|
||||
|
||||
// track this memory using memory marking / idle time leak detection
|
||||
MarkingLeak = 0x0004,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Returns nullptr on failure
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") _Ret_maybenull_ _Post_writable_byte_size_(cb) void* AllocateEx(size_t cb, DWORD allocFlags) noexcept;
|
||||
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Returns nullptr on failure
|
||||
*/
|
||||
inline _Ret_maybenull_ _Post_writable_byte_size_(cb) void* Allocate(size_t cb) noexcept
|
||||
{
|
||||
return Mso::Memory::AllocateEx(cb, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
Reallocate an existing allocation to a new size
|
||||
Returns nullptr on failure
|
||||
TODO: Do we need ReallocateEx? Only if allocFlags grows.
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") _Ret_maybenull_ void* Reallocate(_Inout_ void** ppv, size_t cb) noexcept;
|
||||
|
||||
template<typename T>
|
||||
_Ret_maybenull_ T* Reallocate(T* pT, size_t cb) noexcept
|
||||
{
|
||||
return reinterpret_cast<T*>(Reallocate(reinterpret_cast<void**>(&pT), cb));
|
||||
}
|
||||
|
||||
/**
|
||||
Return the allocation size of a given pointer
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") size_t AllocationSize(_In_opt_ const void* pv) noexcept;
|
||||
|
||||
/**
|
||||
Release a previously allocated block of memory
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") void Free(_Pre_maybenull_ _Post_invalid_ void* pv) noexcept;
|
||||
|
||||
/**
|
||||
Disambiguator used to ensure a throwing new
|
||||
new (Mso::Memory::throwNew) Zoo();
|
||||
*/
|
||||
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
||||
static const struct throwNew_t
|
||||
{
|
||||
throwNew_t() noexcept = default;
|
||||
} throwNew;
|
||||
|
||||
/**
|
||||
Disambiguator used to ensure a crashing new
|
||||
new (Mso::Memory::failFast) Zoo();
|
||||
*/
|
||||
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
||||
static const struct failFast_t
|
||||
{
|
||||
failFast_t() noexcept = default;
|
||||
} failFast;
|
||||
|
||||
/**
|
||||
Construct a object of type `T` stored at `mem`.
|
||||
|
||||
Arguments are forwarded to constructor of `T`.
|
||||
*/
|
||||
template <typename T, typename... Args>
|
||||
static void Place(__inout_bcount(sizeof(T)) void* mem, Args&&... args)
|
||||
{
|
||||
Details::Emplacer<T>::Place(mem, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
Manually destruct an object of type `T`.
|
||||
*/
|
||||
template <typename T,
|
||||
typename = typename std::enable_if<!std::is_array<T>::value>::type>
|
||||
static void Destruct(T& obj) noexcept
|
||||
{
|
||||
Details::Destructor<T>::Destruct(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
Manually destruct a `T[N]`.
|
||||
|
||||
Destructs contents of the array in reverse order.
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
static void Destruct(T(&obj)[N]) noexcept
|
||||
{
|
||||
size_t i = N;
|
||||
while (i--)
|
||||
Destruct(obj[i]);
|
||||
}
|
||||
} // Memory
|
||||
} // Mso
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace FailFast {
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Never returns nullptr
|
||||
*/
|
||||
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void* AllocateEx(size_t cb, DWORD allocFlags) noexcept
|
||||
{
|
||||
auto pv = Mso::Memory::AllocateEx(cb, allocFlags);
|
||||
if (pv == nullptr)
|
||||
CrashWithRecoveryOnOOM();
|
||||
return pv;
|
||||
}
|
||||
|
||||
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void* Allocate(size_t cb) noexcept
|
||||
{
|
||||
return AllocateEx(cb, 0);
|
||||
}
|
||||
} // FailFast
|
||||
} // Memory
|
||||
} // Mso
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace NoThrow {
|
||||
template <typename T>
|
||||
struct Destructor<T, typename std::enable_if<std::is_destructible<T>::value && !std::is_pod<T>::value>::type>
|
||||
{
|
||||
static void Destruct(T& obj) noexcept
|
||||
{
|
||||
UNREFERENCED_PARAMETER(obj);
|
||||
obj.~T();
|
||||
}
|
||||
};
|
||||
} // namespace Details
|
||||
|
||||
/**
|
||||
non-throwing operator new using memory-marking leak detection
|
||||
new (Mso::Memory::NoThrow::MarkingLeak) Zoo();
|
||||
Mso::Memory::AllocFlags
|
||||
*/
|
||||
namespace AllocFlags {
|
||||
enum Enum : unsigned int
|
||||
{
|
||||
// check this memory during shutdown leak detection
|
||||
ShutdownLeak = 0x0001,
|
||||
|
||||
// ignore this memory during leak detection
|
||||
IgnoreLeak = 0x0002,
|
||||
|
||||
// track this memory using memory marking / idle time leak detection
|
||||
MarkingLeak = 0x0004,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Returns nullptr on failure
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android")
|
||||
_Ret_maybenull_ _Post_writable_byte_size_(cb) void* AllocateEx(size_t cb, DWORD allocFlags) noexcept;
|
||||
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Returns nullptr on failure
|
||||
*/
|
||||
inline _Ret_maybenull_ _Post_writable_byte_size_(cb) void* Allocate(size_t cb) noexcept
|
||||
{
|
||||
return Mso::Memory::AllocateEx(cb, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
Reallocate an existing allocation to a new size
|
||||
Returns nullptr on failure
|
||||
TODO: Do we need ReallocateEx? Only if allocFlags grows.
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") _Ret_maybenull_ void* Reallocate(_Inout_ void** ppv, size_t cb) noexcept;
|
||||
|
||||
template <typename T>
|
||||
_Ret_maybenull_ T* Reallocate(T* pT, size_t cb) noexcept
|
||||
{
|
||||
return reinterpret_cast<T*>(Reallocate(reinterpret_cast<void**>(&pT), cb));
|
||||
}
|
||||
|
||||
/**
|
||||
Return the allocation size of a given pointer
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") size_t AllocationSize(_In_opt_ const void* pv) noexcept;
|
||||
|
||||
/**
|
||||
Release a previously allocated block of memory
|
||||
*/
|
||||
LIBLET_PUBLICAPI_EX("win", "android") void Free(_Pre_maybenull_ _Post_invalid_ void* pv) noexcept;
|
||||
|
||||
/**
|
||||
Disambiguator used to ensure a throwing new
|
||||
new (Mso::Memory::throwNew) Zoo();
|
||||
*/
|
||||
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
||||
static const struct throwNew_t
|
||||
{
|
||||
throwNew_t() noexcept = default;
|
||||
} throwNew;
|
||||
|
||||
/**
|
||||
Disambiguator used to ensure a crashing new
|
||||
new (Mso::Memory::failFast) Zoo();
|
||||
*/
|
||||
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
||||
static const struct failFast_t
|
||||
{
|
||||
failFast_t() noexcept = default;
|
||||
} failFast;
|
||||
|
||||
/**
|
||||
Construct a object of type `T` stored at `mem`.
|
||||
|
||||
Arguments are forwarded to constructor of `T`.
|
||||
*/
|
||||
template <typename T, typename... Args>
|
||||
static void Place(__inout_bcount(sizeof(T)) void* mem, Args&&... args)
|
||||
{
|
||||
Details::Emplacer<T>::Place(mem, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
Manually destruct an object of type `T`.
|
||||
*/
|
||||
template <typename T, typename = typename std::enable_if<!std::is_array<T>::value>::type>
|
||||
static void Destruct(T& obj) noexcept
|
||||
{
|
||||
Details::Destructor<T>::Destruct(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
Manually destruct a `T[N]`.
|
||||
|
||||
Destructs contents of the array in reverse order.
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
static void Destruct(T (&obj)[N]) noexcept
|
||||
{
|
||||
size_t i = N;
|
||||
while (i--)
|
||||
Destruct(obj[i]);
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
namespace Mso { namespace Memory { namespace FailFast {
|
||||
/**
|
||||
Return a new allocation of the requested size (cb)
|
||||
Never returns nullptr
|
||||
*/
|
||||
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void* AllocateEx(size_t cb, DWORD allocFlags) noexcept
|
||||
{
|
||||
auto pv = Mso::Memory::AllocateEx(cb, allocFlags);
|
||||
if (pv == nullptr)
|
||||
CrashWithRecoveryOnOOM();
|
||||
return pv;
|
||||
}
|
||||
|
||||
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void* Allocate(size_t cb) noexcept
|
||||
{
|
||||
return AllocateEx(cb, 0);
|
||||
}
|
||||
}}} // namespace Mso::Memory::FailFast
|
||||
|
||||
namespace Mso { namespace Memory { namespace NoThrow {
|
||||
|
||||
/**
|
||||
non-throwing operator new using memory-marking leak detection
|
||||
new (Mso::Memory::NoThrow::MarkingLeak) Zoo();
|
||||
*/
|
||||
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
||||
static const struct MarkingLeak_t
|
||||
{
|
||||
MarkingLeak_t() noexcept = default;
|
||||
MarkingLeak_t() noexcept = default;
|
||||
} MarkingLeak;
|
||||
|
||||
} // NoThrow
|
||||
} // Memory
|
||||
} // Mso
|
||||
}}} // namespace Mso::Memory::NoThrow
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace Throw {
|
||||
namespace Mso { namespace Memory { namespace Throw {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4100)
|
||||
/**
|
||||
Mso::Memory::Throw::New<T>(args)
|
||||
#pragma warning(disable : 4100)
|
||||
/**
|
||||
Mso::Memory::Throw::New<T>(args)
|
||||
|
||||
Allocates object T by passing args to its constructor.
|
||||
*/
|
||||
template <typename T, typename ...TArgs>
|
||||
OACR_WARNING_SUPPRESS( NULL_ON_NON_POINTER, "false positive" )
|
||||
_Ret_notnull_ T* New(TArgs&&... t)
|
||||
{
|
||||
Debug(Mso::Memory::AutoShutdownLeakScope scope);
|
||||
T* pT = new(std::nothrow) T(std::forward<TArgs>(t)...);
|
||||
if (pT == nullptr)
|
||||
throw std::bad_alloc();
|
||||
return pT;
|
||||
}
|
||||
Allocates object T by passing args to its constructor.
|
||||
*/
|
||||
template <typename T, typename... TArgs>
|
||||
OACR_WARNING_SUPPRESS(NULL_ON_NON_POINTER, "false positive")
|
||||
_Ret_notnull_ T* New(TArgs&&... t)
|
||||
{
|
||||
Debug(Mso::Memory::AutoShutdownLeakScope scope);
|
||||
T* pT = new (std::nothrow) T(std::forward<TArgs>(t)...);
|
||||
if (pT == nullptr)
|
||||
throw std::bad_alloc();
|
||||
return pT;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
} // Throw
|
||||
} // Memory
|
||||
} // Mso
|
||||
}}} // namespace Mso::Memory::Throw
|
||||
|
||||
namespace Mso {
|
||||
/**
|
||||
Frees memory allocated by Mso::Memory
|
||||
*/
|
||||
template< typename T > struct MemoryPtrHelper
|
||||
{
|
||||
static void Free(T* pT) noexcept { Mso::Memory::Free(pT); }
|
||||
};
|
||||
/**
|
||||
Frees memory allocated by Mso::Memory
|
||||
*/
|
||||
template <typename T>
|
||||
struct MemoryPtrHelper
|
||||
{
|
||||
static void Free(T* pT) noexcept
|
||||
{
|
||||
Mso::Memory::Free(pT);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Mso::MemoryPtr
|
||||
/**
|
||||
Mso::MemoryPtr
|
||||
|
||||
Smart pointer for memory allocated by Mso::Memory.
|
||||
Suitable for raw memory or vanilla structs.
|
||||
Does not run copy constructors or destructors.
|
||||
Smart pointer for memory allocated by Mso::Memory.
|
||||
Suitable for raw memory or vanilla structs.
|
||||
Does not run copy constructors or destructors.
|
||||
|
||||
The equals operator is purposely left out. Use the c'tor or Attach().
|
||||
The equals operator is purposely left out. Use the c'tor or Attach().
|
||||
|
||||
Mso::MemoryPtr<BYTE> pFoo; // equivalent to BYTE* pFoo;
|
||||
*/
|
||||
template< typename T, DWORD allocFlags = 0 > class MemoryPtr : public Mso::THolder< T*, MemoryPtrHelper< T > >
|
||||
{
|
||||
typedef Mso::THolder< T*, MemoryPtrHelper< T > > Super;
|
||||
Mso::MemoryPtr<BYTE> pFoo; // equivalent to BYTE* pFoo;
|
||||
*/
|
||||
template <typename T, DWORD allocFlags = 0>
|
||||
class MemoryPtr : public Mso::THolder<T*, MemoryPtrHelper<T>>
|
||||
{
|
||||
typedef Mso::THolder<T*, MemoryPtrHelper<T>> Super;
|
||||
|
||||
public:
|
||||
MemoryPtr() noexcept {}
|
||||
explicit MemoryPtr(_In_opt_ T* pT) noexcept { this->Attach(pT); } // Takes ownership
|
||||
IMPLEMENT_THOLDER_RVALUE_REFS_(MemoryPtr, Super);
|
||||
public:
|
||||
MemoryPtr() noexcept {}
|
||||
explicit MemoryPtr(_In_opt_ T* pT) noexcept
|
||||
{
|
||||
this->Attach(pT);
|
||||
} // Takes ownership
|
||||
IMPLEMENT_THOLDER_RVALUE_REFS_(MemoryPtr, Super);
|
||||
|
||||
/**
|
||||
AllocBytes
|
||||
/**
|
||||
AllocBytes
|
||||
|
||||
Allocate space for the given number of bytes.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
Allocate space for the given number of bytes.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
AllocElem-variants are strongly preferred to avoid integer overflows.
|
||||
AllocElem-variants are strongly preferred to avoid integer overflows.
|
||||
|
||||
Mso::MemoryPtr<BYTE> pbFoo;
|
||||
if (pbFoo.AllocBytes(cbData)) { ... }
|
||||
*/
|
||||
bool AllocBytes(size_t cb) noexcept
|
||||
{
|
||||
T* pT = static_cast<T*>(Mso::Memory::AllocateEx(cb, allocFlags));
|
||||
if (pT != nullptr)
|
||||
this->Attach(pT);
|
||||
return (pT != nullptr);
|
||||
}
|
||||
Mso::MemoryPtr<BYTE> pbFoo;
|
||||
if (pbFoo.AllocBytes(cbData)) { ... }
|
||||
*/
|
||||
bool AllocBytes(size_t cb) noexcept
|
||||
{
|
||||
T* pT = static_cast<T*>(Mso::Memory::AllocateEx(cb, allocFlags));
|
||||
if (pT != nullptr)
|
||||
this->Attach(pT);
|
||||
return (pT != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
AllocElem
|
||||
/**
|
||||
AllocElem
|
||||
|
||||
Allocate space for cElements + cExtra instances.
|
||||
On success, any previously held data is replaced.
|
||||
Optional out parameters to receive the number of elements/bytes allocated
|
||||
Returns false on OOM / overflow.
|
||||
Allocate space for cElements + cExtra instances.
|
||||
On success, any previously held data is replaced.
|
||||
Optional out parameters to receive the number of elements/bytes allocated
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
Mso::MemoryPtr<wchar_t> wzFoo;
|
||||
size_t cchFoo = 0;
|
||||
if (wzFoo.AllocElemCb(cch, 1, &cchFoo)) { ... }
|
||||
*/
|
||||
bool AllocElem(size_t cElements, size_t cExtra = 0, _Out_opt_ size_t* pcElements = nullptr, _Out_opt_ size_t* pcbAlloc = nullptr) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
||||
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
||||
bool fRet = AllocBytes(cbAlloc);
|
||||
if (pcElements != nullptr)
|
||||
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
||||
if (pcbAlloc != nullptr)
|
||||
*pcbAlloc = (fRet ? cbAlloc : 0);
|
||||
return fRet;
|
||||
}
|
||||
Mso::MemoryPtr<wchar_t> wzFoo;
|
||||
size_t cchFoo = 0;
|
||||
if (wzFoo.AllocElemCb(cch, 1, &cchFoo)) { ... }
|
||||
*/
|
||||
bool AllocElem(
|
||||
size_t cElements,
|
||||
size_t cExtra = 0,
|
||||
_Out_opt_ size_t* pcElements = nullptr,
|
||||
_Out_opt_ size_t* pcbAlloc = nullptr) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
||||
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
||||
bool fRet = AllocBytes(cbAlloc);
|
||||
if (pcElements != nullptr)
|
||||
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
||||
if (pcbAlloc != nullptr)
|
||||
*pcbAlloc = (fRet ? cbAlloc : 0);
|
||||
return fRet;
|
||||
}
|
||||
|
||||
/**
|
||||
AllocOne
|
||||
/**
|
||||
AllocOne
|
||||
|
||||
Allocate space for 1 instance.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
Allocate space for 1 instance.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
Mso::MemoryPtr<Foo> pFoo;
|
||||
if (pFoo.AllocOne()) { ... }
|
||||
*/
|
||||
bool AllocOne() noexcept
|
||||
{
|
||||
return AllocElem(1);
|
||||
}
|
||||
Mso::MemoryPtr<Foo> pFoo;
|
||||
if (pFoo.AllocOne()) { ... }
|
||||
*/
|
||||
bool AllocOne() noexcept
|
||||
{
|
||||
return AllocElem(1);
|
||||
}
|
||||
|
||||
// TODO: Does it make sense for CallocElemCb? See if there is need.
|
||||
// TODO: Does it make sense for CallocElemCb? See if there is need.
|
||||
|
||||
/**
|
||||
ReallocBytes
|
||||
/**
|
||||
ReallocBytes
|
||||
|
||||
Reallocate the exact number of bytes.
|
||||
Returns false on OOM / overflow.
|
||||
Reallocate the exact number of bytes.
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
ReallocElem is strongly preferred.
|
||||
*/
|
||||
bool ReallocBytes(size_t cb) noexcept
|
||||
{
|
||||
const T* pT = static_cast<const T*>(Mso::Memory::Reallocate(reinterpret_cast<void**>(&this->m_pT), cb));
|
||||
return (pT != nullptr);
|
||||
}
|
||||
ReallocElem is strongly preferred.
|
||||
*/
|
||||
bool ReallocBytes(size_t cb) noexcept
|
||||
{
|
||||
const T* pT = static_cast<const T*>(Mso::Memory::Reallocate(reinterpret_cast<void**>(&this->m_pT), cb));
|
||||
return (pT != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
ReallocElem
|
||||
/**
|
||||
ReallocElem
|
||||
|
||||
Reallocate space for cElements + cExtra instances.
|
||||
Optional out parameters to receive the number of elements/bytes allocated
|
||||
Returns false on OOM / overflow.
|
||||
*/
|
||||
bool ReallocElem(size_t cElements, size_t cExtra = 0, _Out_opt_ size_t* pcElements = nullptr, _Out_opt_ size_t* pcbAlloc = nullptr) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
||||
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
||||
bool fRet = ReallocBytes(cbAlloc);
|
||||
if (pcElements != nullptr)
|
||||
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
||||
if (pcbAlloc != nullptr)
|
||||
*pcbAlloc = (fRet ? cbAlloc : 0);
|
||||
return fRet;
|
||||
}
|
||||
Reallocate space for cElements + cExtra instances.
|
||||
Optional out parameters to receive the number of elements/bytes allocated
|
||||
Returns false on OOM / overflow.
|
||||
*/
|
||||
bool ReallocElem(
|
||||
size_t cElements,
|
||||
size_t cExtra = 0,
|
||||
_Out_opt_ size_t* pcElements = nullptr,
|
||||
_Out_opt_ size_t* pcbAlloc = nullptr) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
||||
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
||||
bool fRet = ReallocBytes(cbAlloc);
|
||||
if (pcElements != nullptr)
|
||||
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
||||
if (pcbAlloc != nullptr)
|
||||
*pcbAlloc = (fRet ? cbAlloc : 0);
|
||||
return fRet;
|
||||
}
|
||||
|
||||
/**
|
||||
CloneBytes
|
||||
/**
|
||||
CloneBytes
|
||||
|
||||
Allocates a new buffer and copies the data into it.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
Allocates a new buffer and copies the data into it.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
CloneElem-variants are strongly preferred to avoid integer overflow.
|
||||
FUTURE: I could add cbExtra parameter if it makes sense.
|
||||
CloneElem-variants are strongly preferred to avoid integer overflow.
|
||||
FUTURE: I could add cbExtra parameter if it makes sense.
|
||||
|
||||
Mso::MemoryPtr<BYTE> pbFoo;
|
||||
if (pbFoo.CloneBytes(pbSrc, cbSrc)) { ... }
|
||||
*/
|
||||
bool CloneBytes(_In_opt_bytecount_(cb) const T* pT, size_t cb) noexcept
|
||||
{
|
||||
if (pT != nullptr)
|
||||
{
|
||||
Mso::MemoryPtr<T, allocFlags> pNew;
|
||||
if (pNew.AllocBytes(cb))
|
||||
{
|
||||
memcpy_s(pNew.Get(), cb, pT, cb);
|
||||
this->Swap(pNew);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Mso::MemoryPtr<BYTE> pbFoo;
|
||||
if (pbFoo.CloneBytes(pbSrc, cbSrc)) { ... }
|
||||
*/
|
||||
bool CloneBytes(_In_opt_bytecount_(cb) const T* pT, size_t cb) noexcept
|
||||
{
|
||||
if (pT != nullptr)
|
||||
{
|
||||
Mso::MemoryPtr<T, allocFlags> pNew;
|
||||
if (pNew.AllocBytes(cb))
|
||||
{
|
||||
memcpy_s(pNew.Get(), cb, pT, cb);
|
||||
this->Swap(pNew);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
CloneElem
|
||||
/**
|
||||
CloneElem
|
||||
|
||||
Allocate space and copies cElements instances from the source.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
Allocate space and copies cElements instances from the source.
|
||||
On success, any previously held data is replaced.
|
||||
Returns false on OOM / overflow.
|
||||
|
||||
Mso::MemoryPtr<FooData> rgFooData;
|
||||
if (rgFooData.CloneElem(rgFooDataSrc, cFooDataSrc) { ... }
|
||||
*/
|
||||
bool CloneElem(_In_opt_count_(cElements) const T* pT, size_t cElements) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = msl::utilities::SafeInt<size_t>(cElements) * sizeof(T);
|
||||
size_t cbAlloc = (cElements)* sizeof(T);
|
||||
return CloneBytes(pT, cbAlloc);
|
||||
}
|
||||
Mso::MemoryPtr<FooData> rgFooData;
|
||||
if (rgFooData.CloneElem(rgFooDataSrc, cFooDataSrc) { ... }
|
||||
*/
|
||||
bool CloneElem(_In_opt_count_(cElements) const T* pT, size_t cElements) noexcept
|
||||
{
|
||||
// TODO: size_t cbAlloc = msl::utilities::SafeInt<size_t>(cElements) * sizeof(T);
|
||||
size_t cbAlloc = (cElements) * sizeof(T);
|
||||
return CloneBytes(pT, cbAlloc);
|
||||
}
|
||||
|
||||
/**
|
||||
CloneOne
|
||||
/**
|
||||
CloneOne
|
||||
|
||||
Allocates space and copies 1 instance from the source.
|
||||
Any previously held data is replaced on success.
|
||||
Allocates space and copies 1 instance from the source.
|
||||
Any previously held data is replaced on success.
|
||||
|
||||
Mso::MemoryPtr<Foo> pFoo;
|
||||
if (pFoo.AllocOne(pFooSrc)) { ... }
|
||||
*/
|
||||
bool CloneOne(_In_opt_count_(1) const T* pT) noexcept
|
||||
{
|
||||
return CloneElem(pT, 1);
|
||||
}
|
||||
Mso::MemoryPtr<Foo> pFoo;
|
||||
if (pFoo.AllocOne(pFooSrc)) { ... }
|
||||
*/
|
||||
bool CloneOne(_In_opt_count_(1) const T* pT) noexcept
|
||||
{
|
||||
return CloneElem(pT, 1);
|
||||
}
|
||||
|
||||
#ifdef MSO_THOLDER_EXPLICIT_GET_ONLY
|
||||
T& operator*() const noexcept
|
||||
{
|
||||
return *this->Get();
|
||||
}
|
||||
T& operator*() const noexcept
|
||||
{
|
||||
return *this->Get();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(MemoryPtr);
|
||||
};
|
||||
} // Mso
|
||||
private:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(MemoryPtr);
|
||||
};
|
||||
} // namespace Mso
|
||||
|
||||
#pragma pop_macro("New")
|
||||
#pragma pop_macro("new")
|
||||
|
|
|
@ -13,68 +13,65 @@ namespace Mso {
|
|||
Acts like unique_ptr for a thread-local variable, automatically deletes on thread-exit.
|
||||
Use as static variable since this object lifetime controls the backing thread-storage.
|
||||
*/
|
||||
template<typename T, typename Deleter = std::default_delete<T>>
|
||||
template <typename T, typename Deleter = std::default_delete<T>>
|
||||
class threadlocal_unique_ptr : private Mso::ThreadLocal<T*>
|
||||
{
|
||||
using Super = Mso::ThreadLocal<T*>;
|
||||
using Super = Mso::ThreadLocal<T*>;
|
||||
|
||||
public:
|
||||
threadlocal_unique_ptr() noexcept : Super(Delete)
|
||||
{
|
||||
}
|
||||
threadlocal_unique_ptr() noexcept : Super(Delete) {}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return get() != nullptr;
|
||||
}
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return get() != nullptr;
|
||||
}
|
||||
|
||||
T* operator ->() const noexcept
|
||||
{
|
||||
return get();
|
||||
}
|
||||
T* operator->() const noexcept
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
T* get() const noexcept
|
||||
{
|
||||
return Super::GetValue();
|
||||
}
|
||||
T* get() const noexcept
|
||||
{
|
||||
return Super::GetValue();
|
||||
}
|
||||
|
||||
T& getOrCreate() noexcept
|
||||
{
|
||||
auto t = get();
|
||||
if (!t)
|
||||
{
|
||||
T& getOrCreate() noexcept
|
||||
{
|
||||
auto t = get();
|
||||
if (!t)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Mso::Memory::AutoIgnoreLeakScope ignoreTlsVar;
|
||||
Mso::Memory::AutoIgnoreLeakScope ignoreTlsVar;
|
||||
#endif
|
||||
t = new T();
|
||||
reset(t);
|
||||
}
|
||||
t = new T();
|
||||
reset(t);
|
||||
}
|
||||
|
||||
return *t;
|
||||
}
|
||||
return *t;
|
||||
}
|
||||
|
||||
void reset(T* t) noexcept
|
||||
{
|
||||
Delete(release());
|
||||
Super::SetValue(t);
|
||||
}
|
||||
void reset(T* t) noexcept
|
||||
{
|
||||
Delete(release());
|
||||
Super::SetValue(t);
|
||||
}
|
||||
|
||||
T* release() noexcept
|
||||
{
|
||||
auto t = get();
|
||||
Super::SetValue(nullptr);
|
||||
return t;
|
||||
}
|
||||
T* release() noexcept
|
||||
{
|
||||
auto t = get();
|
||||
Super::SetValue(nullptr);
|
||||
return t;
|
||||
}
|
||||
|
||||
private:
|
||||
static void __stdcall Delete(T* t) noexcept
|
||||
{
|
||||
Deleter()(t);
|
||||
}
|
||||
static void __stdcall Delete(T* t) noexcept
|
||||
{
|
||||
Deleter()(t);
|
||||
}
|
||||
};
|
||||
|
||||
} // Mso
|
||||
} // namespace Mso
|
||||
|
||||
#endif // C++
|
||||
#endif // _CPPEXTENSIONS_THREADLOCALUNIQUEPTR_H_
|
||||
|
||||
|
|
|
@ -7,22 +7,24 @@
|
|||
#ifdef __cplusplus
|
||||
#include <compilerAdapters/functionDecorations.h>
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace Mso { namespace Memory {
|
||||
|
||||
/**
|
||||
Is the ShutdownLeakScope currently active?
|
||||
Is the ShutdownLeakScope currently active?
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI bool IsInShutdownLeakScope() noexcept;
|
||||
#else
|
||||
inline bool IsInShutdownLeakScope() noexcept { return false; }
|
||||
inline bool IsInShutdownLeakScope() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Enter the scope so allocations on this thread should be marked for shutdown leak detection.
|
||||
Scope can be entered a limited number of times.
|
||||
Frames to skip is used to track which caller address is associated with this Enter.
|
||||
Enter the scope so allocations on this thread should be marked for shutdown leak detection.
|
||||
Scope can be entered a limited number of times.
|
||||
Frames to skip is used to track which caller address is associated with this Enter.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI void EnterShutdownLeakScope(unsigned int framesToSkip = 0) noexcept;
|
||||
|
@ -31,7 +33,7 @@ inline void EnterShutdownLeakScope(unsigned int /*framesToSkip*/ = 0) noexcept {
|
|||
#endif
|
||||
|
||||
/**
|
||||
Leave
|
||||
Leave
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI void LeaveShutdownLeakScope() noexcept;
|
||||
|
@ -40,18 +42,21 @@ inline void LeaveShutdownLeakScope() noexcept {}
|
|||
#endif
|
||||
|
||||
/**
|
||||
Is the IgnoreLeakScope currently active?
|
||||
Is the IgnoreLeakScope currently active?
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI bool IsInIgnoreLeakScope() noexcept;
|
||||
#else
|
||||
inline bool IsInIgnoreLeakScope() noexcept { return false; }
|
||||
inline bool IsInIgnoreLeakScope() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Enter the scope so allocations on this thread should be ignored for leak detection.
|
||||
Scope can be entered a limited number of times.
|
||||
Frames to skip is used to track which caller address is associated with this Enter.
|
||||
Enter the scope so allocations on this thread should be ignored for leak detection.
|
||||
Scope can be entered a limited number of times.
|
||||
Frames to skip is used to track which caller address is associated with this Enter.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI void EnterIgnoreLeakScope(unsigned int framesToSkip = 0) noexcept;
|
||||
|
@ -60,7 +65,7 @@ inline void EnterIgnoreLeakScope(unsigned int /*framesToSkip*/ = 0) noexcept {}
|
|||
#endif
|
||||
|
||||
/**
|
||||
Leave
|
||||
Leave
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
LIBLET_PUBLICAPI void LeaveIgnoreLeakScope() noexcept;
|
||||
|
@ -70,38 +75,37 @@ inline void LeaveIgnoreLeakScope() noexcept {}
|
|||
|
||||
struct AutoShutdownLeakScope
|
||||
{
|
||||
AutoShutdownLeakScope(unsigned int framesToSkip = 0) noexcept
|
||||
{
|
||||
EnterShutdownLeakScope(++framesToSkip);
|
||||
}
|
||||
|
||||
AutoShutdownLeakScope(const AutoShutdownLeakScope& /*other*/) noexcept : AutoShutdownLeakScope(1) {}
|
||||
AutoShutdownLeakScope(AutoShutdownLeakScope&& /*other*/) noexcept : AutoShutdownLeakScope(1) {}
|
||||
AutoShutdownLeakScope(unsigned int framesToSkip = 0) noexcept
|
||||
{
|
||||
EnterShutdownLeakScope(++framesToSkip);
|
||||
}
|
||||
|
||||
~AutoShutdownLeakScope() noexcept
|
||||
{
|
||||
LeaveShutdownLeakScope();
|
||||
}
|
||||
AutoShutdownLeakScope(const AutoShutdownLeakScope& /*other*/) noexcept : AutoShutdownLeakScope(1) {}
|
||||
AutoShutdownLeakScope(AutoShutdownLeakScope&& /*other*/) noexcept : AutoShutdownLeakScope(1) {}
|
||||
|
||||
~AutoShutdownLeakScope() noexcept
|
||||
{
|
||||
LeaveShutdownLeakScope();
|
||||
}
|
||||
};
|
||||
|
||||
struct AutoIgnoreLeakScope
|
||||
{
|
||||
AutoIgnoreLeakScope(unsigned int framesToSkip = 0) noexcept
|
||||
{
|
||||
EnterIgnoreLeakScope(++framesToSkip);
|
||||
}
|
||||
AutoIgnoreLeakScope(unsigned int framesToSkip = 0) noexcept
|
||||
{
|
||||
EnterIgnoreLeakScope(++framesToSkip);
|
||||
}
|
||||
|
||||
AutoIgnoreLeakScope(const AutoIgnoreLeakScope& /*other*/) noexcept : AutoIgnoreLeakScope(1) {}
|
||||
AutoIgnoreLeakScope(AutoIgnoreLeakScope&& /*other*/) noexcept : AutoIgnoreLeakScope(1) {}
|
||||
AutoIgnoreLeakScope(const AutoIgnoreLeakScope& /*other*/) noexcept : AutoIgnoreLeakScope(1) {}
|
||||
AutoIgnoreLeakScope(AutoIgnoreLeakScope&& /*other*/) noexcept : AutoIgnoreLeakScope(1) {}
|
||||
|
||||
~AutoIgnoreLeakScope() noexcept
|
||||
{
|
||||
LeaveIgnoreLeakScope();
|
||||
}
|
||||
~AutoIgnoreLeakScope() noexcept
|
||||
{
|
||||
LeaveIgnoreLeakScope();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // Mso::Memory
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
#endif // C++
|
||||
#endif // LIBLET_MEMORYAPI_SCOPE
|
||||
|
|
|
@ -8,98 +8,101 @@
|
|||
#include <sal.h>
|
||||
//#include <intsafe.h>
|
||||
/*
|
||||
* MsoCbBufSizeT
|
||||
* This function returns the bytes needed to allocate a buffer for (cElements + cExtra)
|
||||
* instances of type T. This is a low level function, and typical usage would be to call
|
||||
* one of the wrappers that deal with (for example) WCHAR arrays.
|
||||
*
|
||||
* Note: Strictly speaking, this isn't an exported MSO function, as it's instantiated by
|
||||
* the caller, and simply picked up from the header.
|
||||
*
|
||||
* Arguments:
|
||||
* cElements - the number of elements needed
|
||||
* cExtra - any additional elements needed
|
||||
* cbMaxBufSize - optional argument for the maximum bytes allowed. Default is the maximum
|
||||
* possible value for the architecture.
|
||||
*
|
||||
* Return:
|
||||
* Either a mathematically correct number of bytes required, or in the case of an error,
|
||||
* INVALID_BUFFER_SIZE
|
||||
*
|
||||
* Revision history:
|
||||
* 10/22/04 - dleblanc - created
|
||||
*/
|
||||
* MsoCbBufSizeT
|
||||
* This function returns the bytes needed to allocate a buffer for (cElements + cExtra)
|
||||
* instances of type T. This is a low level function, and typical usage would be to call
|
||||
* one of the wrappers that deal with (for example) WCHAR arrays.
|
||||
*
|
||||
* Note: Strictly speaking, this isn't an exported MSO function, as it's instantiated by
|
||||
* the caller, and simply picked up from the header.
|
||||
*
|
||||
* Arguments:
|
||||
* cElements - the number of elements needed
|
||||
* cExtra - any additional elements needed
|
||||
* cbMaxBufSize - optional argument for the maximum bytes allowed. Default is the maximum
|
||||
* possible value for the architecture.
|
||||
*
|
||||
* Return:
|
||||
* Either a mathematically correct number of bytes required, or in the case of an error,
|
||||
* INVALID_BUFFER_SIZE
|
||||
*
|
||||
* Revision history:
|
||||
* 10/22/04 - dleblanc - created
|
||||
*/
|
||||
|
||||
const size_t INVALID_BUFFER_SIZE = ((size_t)~0);
|
||||
const size_t MAXIMUM_BUFFER_SIZE = ((size_t)INT_MAX);
|
||||
|
||||
template <typename T>
|
||||
_Ret_range_(==, (cElements + cExtra) * sizeof(T))
|
||||
size_t MsoCbBufSizeT(size_t cElements, size_t cExtra = 0, size_t cbMaxBufSize = MAXIMUM_BUFFER_SIZE) noexcept
|
||||
_Ret_range_(==, (cElements + cExtra) * sizeof(T)) size_t
|
||||
MsoCbBufSizeT(size_t cElements, size_t cExtra = 0, size_t cbMaxBufSize = MAXIMUM_BUFFER_SIZE) noexcept
|
||||
{
|
||||
//first calculate maximum allowed
|
||||
//typical case is a compile time constant
|
||||
const size_t cMax = cbMaxBufSize/sizeof(T);
|
||||
const size_t cMaxAllowed = cMax - cExtra;
|
||||
// first calculate maximum allowed
|
||||
// typical case is a compile time constant
|
||||
const size_t cMax = cbMaxBufSize / sizeof(T);
|
||||
const size_t cMaxAllowed = cMax - cExtra;
|
||||
|
||||
if(cExtra > cMax || //cExtra is too large
|
||||
cElements > cMaxAllowed) //real check here
|
||||
{
|
||||
if (cExtra > cMax || // cExtra is too large
|
||||
cElements > cMaxAllowed) // real check here
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: This should be changed to a tagged function
|
||||
//__asm int 3
|
||||
// TODO: This should be changed to a tagged function
|
||||
//__asm int 3
|
||||
#endif
|
||||
//we have a problem
|
||||
return INVALID_BUFFER_SIZE;
|
||||
}
|
||||
// we have a problem
|
||||
return INVALID_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
return (cElements + cExtra) * sizeof(T);
|
||||
return (cElements + cExtra) * sizeof(T);
|
||||
}
|
||||
|
||||
/*
|
||||
* MsoCbBufSizeExT
|
||||
*
|
||||
* Variant of MsoCbBufSizeT in which you may specify an additional "cbExtra" size
|
||||
* (for array headers, etc.)
|
||||
*
|
||||
* Arguments:
|
||||
* cElements - the number of elements needed
|
||||
* cbExtra - any additional bytes needed
|
||||
* cExtra - any additional elements needed
|
||||
* cbMaxBufSize - optional argument for the maximum bytes allowed. Default is the maximum
|
||||
* possible value for the architecture.
|
||||
*
|
||||
* Return:
|
||||
* Either a mathematically correct number of bytes required, or in the case of an error,
|
||||
* INVALID_BUFFER_SIZE
|
||||
*
|
||||
* Revision history:
|
||||
* 8/14/05 - LeeHu - created
|
||||
*/
|
||||
* MsoCbBufSizeExT
|
||||
*
|
||||
* Variant of MsoCbBufSizeT in which you may specify an additional "cbExtra" size
|
||||
* (for array headers, etc.)
|
||||
*
|
||||
* Arguments:
|
||||
* cElements - the number of elements needed
|
||||
* cbExtra - any additional bytes needed
|
||||
* cExtra - any additional elements needed
|
||||
* cbMaxBufSize - optional argument for the maximum bytes allowed. Default is the maximum
|
||||
* possible value for the architecture.
|
||||
*
|
||||
* Return:
|
||||
* Either a mathematically correct number of bytes required, or in the case of an error,
|
||||
* INVALID_BUFFER_SIZE
|
||||
*
|
||||
* Revision history:
|
||||
* 8/14/05 - LeeHu - created
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
_Ret_range_(==, (cElements + cExtra) * sizeof(T) + cbExtra)
|
||||
size_t MsoCbBufSizeExT(size_t cElements, size_t cbExtra, size_t cExtra = 0, size_t cbMaxBufSize = MAXIMUM_BUFFER_SIZE) noexcept
|
||||
_Ret_range_(==, (cElements + cExtra) * sizeof(T) + cbExtra) size_t MsoCbBufSizeExT(
|
||||
size_t cElements,
|
||||
size_t cbExtra,
|
||||
size_t cExtra = 0,
|
||||
size_t cbMaxBufSize = MAXIMUM_BUFFER_SIZE) noexcept
|
||||
{
|
||||
//first calculate maximum allowed
|
||||
const size_t cbMax = cbMaxBufSize - cbExtra;
|
||||
const size_t cMax = cbMax/sizeof(T);
|
||||
const size_t cMaxAllowed = cMax - cExtra;
|
||||
// first calculate maximum allowed
|
||||
const size_t cbMax = cbMaxBufSize - cbExtra;
|
||||
const size_t cMax = cbMax / sizeof(T);
|
||||
const size_t cMaxAllowed = cMax - cExtra;
|
||||
|
||||
if(cbExtra > cbMaxBufSize || //cbExtra is too large
|
||||
cExtra > cMax || //cExtra is too large
|
||||
cElements > cMaxAllowed //"real" buf size check here
|
||||
)
|
||||
{
|
||||
if (cbExtra > cbMaxBufSize || // cbExtra is too large
|
||||
cExtra > cMax || // cExtra is too large
|
||||
cElements > cMaxAllowed //"real" buf size check here
|
||||
)
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: This should be changed to a tagged function
|
||||
//__asm int 3
|
||||
// TODO: This should be changed to a tagged function
|
||||
//__asm int 3
|
||||
#endif
|
||||
//we have a problem
|
||||
return INVALID_BUFFER_SIZE;
|
||||
}
|
||||
// we have a problem
|
||||
return INVALID_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
return (cElements + cExtra) * sizeof(T) + cbExtra;
|
||||
return (cElements + cExtra) * sizeof(T) + cbExtra;
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
|
|
@ -2,21 +2,17 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
||||
namespace Mso
|
||||
namespace Mso { namespace Memory {
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
namespace Memory
|
||||
{
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
if (pv == nullptr)
|
||||
return 0;
|
||||
if (pv == nullptr)
|
||||
return 0;
|
||||
|
||||
return ::_msize(const_cast<void *>(pv));
|
||||
}
|
||||
}
|
||||
return ::_msize(const_cast<void*>(pv));
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
|
|
@ -2,19 +2,15 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
||||
namespace Mso
|
||||
namespace Mso { namespace Memory {
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
namespace Memory
|
||||
{
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
AssertSzTag(false, "AllocationSize is not implemented for this platform", 0x006951de /* tag_a0vh4 */);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
AssertSzTag(false, "AllocationSize is not implemented for this platform", 0x006951de /* tag_a0vh4 */);
|
||||
return 0;
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
|
|
@ -2,18 +2,14 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
||||
namespace Mso
|
||||
namespace Mso { namespace Memory {
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
namespace Memory
|
||||
{
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
return malloc_usable_size(pv);
|
||||
}
|
||||
}
|
||||
return malloc_usable_size(pv);
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
|
|
@ -7,16 +7,12 @@ CRT based implementation for Mso::Memory on Apple platform
|
|||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
||||
namespace Mso
|
||||
namespace Mso { namespace Memory {
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
namespace Memory
|
||||
{
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void* pv) noexcept
|
||||
{
|
||||
if (pv == nullptr)
|
||||
return 0;
|
||||
if (pv == nullptr)
|
||||
return 0;
|
||||
|
||||
return ::malloc_size(const_cast<void *>(pv));
|
||||
}
|
||||
}
|
||||
return ::malloc_size(const_cast<void*>(pv));
|
||||
}
|
||||
}} // namespace Mso::Memory
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <platformadapters/windowsfirst.h>
|
||||
|
@ -12,8 +12,7 @@
|
|||
|
||||
MSOAPI_(BOOL) MsoCheckHeap(void) noexcept
|
||||
{
|
||||
return ::_heapchk() == _HEAPOK;
|
||||
return ::_heapchk() == _HEAPOK;
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
MSOAPI_(BOOL) MsoCheckHeap(void) noexcept
|
||||
{
|
||||
AssertSzTag(false, "MsoCheckHeap is not implemented for this platform", 0x0125310c /* tag_bjtem */);
|
||||
return true;
|
||||
AssertSzTag(false, "MsoCheckHeap is not implemented for this platform", 0x0125310c /* tag_bjtem */);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
MSOAPI_(BOOL) MsoCheckHeap(void) noexcept
|
||||
{
|
||||
AssertSzTag(false, "MsoCheckHeap is not implemented for this platform", 0x027c404d /* tag_c5ebn */);
|
||||
return true;
|
||||
AssertSzTag(false, "MsoCheckHeap is not implemented for this platform", 0x027c404d /* tag_c5ebn */);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
MSOAPI_(BOOL) MsoCheckHeap(void) noexcept
|
||||
{
|
||||
// passing nullptr checks all zones
|
||||
return malloc_zone_check(nullptr);
|
||||
// passing nullptr checks all zones
|
||||
return malloc_zone_check(nullptr);
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -2,87 +2,89 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#if !__clang__ && !__GNUC__
|
||||
#pragma detect_mismatch("Allocator", "Crt")
|
||||
#pragma detect_mismatch("Allocator", "Crt")
|
||||
#endif
|
||||
|
||||
__declspec(noreturn) void ThrowOOM() { throw std::bad_alloc(); }
|
||||
|
||||
namespace Mso { namespace Memory {
|
||||
|
||||
_Use_decl_annotations_
|
||||
void* AllocateEx(size_t cb, DWORD /*allocFlags*/) noexcept
|
||||
__declspec(noreturn) void ThrowOOM()
|
||||
{
|
||||
return ::malloc(cb);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
void* Reallocate(void** ppv, size_t cb) noexcept
|
||||
namespace Mso { namespace Memory {
|
||||
|
||||
_Use_decl_annotations_ void* AllocateEx(size_t cb, DWORD /*allocFlags*/) noexcept
|
||||
{
|
||||
if (ppv == nullptr)
|
||||
return Mso::Memory::Allocate(cb);
|
||||
|
||||
if (*ppv == nullptr)
|
||||
{
|
||||
*ppv = Mso::Memory::Allocate(cb);
|
||||
return *ppv;
|
||||
}
|
||||
|
||||
void* pv = ::realloc(*ppv, cb);
|
||||
if (pv != nullptr)
|
||||
{
|
||||
*ppv = pv;
|
||||
}
|
||||
else if (cb == 0)
|
||||
{
|
||||
// HeapReAlloc with 0 size returns valid pointer and we want all implementations do the same
|
||||
// realloc(ptr, 0) on Windows or Mac/iOS with ASAN frees the original pointer and returns null
|
||||
// std lib on Mac/iOS returns a valid 0-sized pointer
|
||||
// We want to standardize to have only one behavior in shared code
|
||||
// so let's allocate a new 0-sized block if resize(ptr, 0) returns nullptr
|
||||
pv = ::malloc(0);
|
||||
*ppv = pv;
|
||||
}
|
||||
//else pv = nullptr, cb != 0: if realloc truly failed, the original ptr is untouched
|
||||
|
||||
return pv;
|
||||
return ::malloc(cb);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
void Free(void* pv) noexcept
|
||||
_Use_decl_annotations_ void* Reallocate(void** ppv, size_t cb) noexcept
|
||||
{
|
||||
::free(pv);
|
||||
if (ppv == nullptr)
|
||||
return Mso::Memory::Allocate(cb);
|
||||
|
||||
if (*ppv == nullptr)
|
||||
{
|
||||
*ppv = Mso::Memory::Allocate(cb);
|
||||
return *ppv;
|
||||
}
|
||||
|
||||
void* pv = ::realloc(*ppv, cb);
|
||||
if (pv != nullptr)
|
||||
{
|
||||
*ppv = pv;
|
||||
}
|
||||
else if (cb == 0)
|
||||
{
|
||||
// HeapReAlloc with 0 size returns valid pointer and we want all implementations do the same
|
||||
// realloc(ptr, 0) on Windows or Mac/iOS with ASAN frees the original pointer and returns null
|
||||
// std lib on Mac/iOS returns a valid 0-sized pointer
|
||||
// We want to standardize to have only one behavior in shared code
|
||||
// so let's allocate a new 0-sized block if resize(ptr, 0) returns nullptr
|
||||
pv = ::malloc(0);
|
||||
*ppv = pv;
|
||||
}
|
||||
// else pv = nullptr, cb != 0: if realloc truly failed, the original ptr is untouched
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
_Use_decl_annotations_ void Free(void* pv) noexcept
|
||||
{
|
||||
::free(pv);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void RegisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept {}
|
||||
|
||||
void UnregisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept {}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}} // Mso::Memory
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
#ifdef DEBUG
|
||||
MSOAPI_(void) MsoSetLazyLeakDetection(const void *) noexcept
|
||||
MSOAPI_(void) MsoSetLazyLeakDetection(const void*) noexcept {}
|
||||
|
||||
MSOAPI_(void) MsoSetShutdownLeakDetection(const void*) noexcept {}
|
||||
|
||||
MSOAPI_(BOOL)
|
||||
FMemHeapMsoSaveBeHost(
|
||||
void* /*pinst*/,
|
||||
LPARAM /*lParam*/,
|
||||
const void* /*pvBlock*/,
|
||||
LONG_PTR /*cb*/,
|
||||
struct IMsoMemHeap* /*pmmh*/) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MSOAPI_(void) MsoSetShutdownLeakDetection(const void *) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
MSOAPI_(BOOL) FMemHeapMsoSaveBeHost(void* /*pinst*/, LPARAM /*lParam*/, const void* /*pvBlock*/, LONG_PTR /*cb*/, struct IMsoMemHeap* /*pmmh*/) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//MSOAPI_(VOID) MsoDebugRegisterLazyObject(IMsoDebugLazyObject* /*pidlo*/) noexcept {}
|
||||
//MSOAPI_(VOID) MsoDebugUnregisterLazyObjectThreaded(IMsoDebugLazyObject* /*pidlo*/, bool /*fMainThread*/) noexcept {}
|
||||
// MSOAPI_(VOID) MsoDebugRegisterLazyObject(IMsoDebugLazyObject* /*pidlo*/) noexcept {}
|
||||
// MSOAPI_(VOID) MsoDebugUnregisterLazyObjectThreaded(IMsoDebugLazyObject* /*pidlo*/, bool /*fMainThread*/) noexcept {}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
CRT based implementation for Mso::Memory
|
||||
CRT based implementation for Mso::Memory
|
||||
*/
|
||||
#include <core/memoryApi.h>
|
||||
#include <malloc.h>
|
||||
|
@ -10,58 +10,73 @@
|
|||
|
||||
#pragma detect_mismatch("Allocator", "EmptyImpl")
|
||||
|
||||
__declspec(noreturn) void ThrowOOM() { throw std::bad_alloc(); }
|
||||
__declspec(noreturn) void ThrowOOM()
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
namespace Mso { namespace Memory {
|
||||
namespace Mso { namespace Memory {
|
||||
|
||||
MSOCPPAPI_(size_t) AllocationSize(_In_opt_ const void*) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x0115e605 /* tag_bf4yf */);
|
||||
VerifyElseCrashTag(false, 0x0115e605 /* tag_bf4yf */);
|
||||
}
|
||||
|
||||
MSOCPPAPI_(void*) AllocateEx(size_t /*cb*/, DWORD /*allocFlags*/) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc64b /* tag_a1mzl */);
|
||||
VerifyElseCrashTag(false, 0x006cc64b /* tag_a1mzl */);
|
||||
}
|
||||
|
||||
MSOCPPAPI_(void*) Reallocate(_Inout_ void** /*ppv*/, size_t /*cb*/) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc64c /* tag_a1mzm */);
|
||||
VerifyElseCrashTag(false, 0x006cc64c /* tag_a1mzm */);
|
||||
}
|
||||
|
||||
MSOCPPAPI_(void) Free(_In_opt_ void* /*pv*/) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc64d /* tag_a1mzn */);
|
||||
VerifyElseCrashTag(false, 0x006cc64d /* tag_a1mzn */);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MSOCPPAPI_(void) RegisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept { VerifyElseCrashTag(false, 0x006cc64e /* tag_a1mzo */); }
|
||||
MSOCPPAPI_(void) UnregisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept { VerifyElseCrashTag(false, 0x006cc64f /* tag_a1mzp */); }
|
||||
#endif
|
||||
MSOCPPAPI_(void) RegisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc64e /* tag_a1mzo */);
|
||||
}
|
||||
MSOCPPAPI_(void) UnregisterCallback(Mso::LibletAPI::ILibletMemoryMarking&) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc64f /* tag_a1mzp */);
|
||||
}
|
||||
#endif
|
||||
|
||||
}} // Mso::Memory
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
MSOAPI_(BOOL) MsoCheckHeap(void) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x0125310d /* tag_bjten */);
|
||||
return true;
|
||||
VerifyElseCrashTag(false, 0x0125310d /* tag_bjten */);
|
||||
return true;
|
||||
}
|
||||
|
||||
MSOAPI_(void) MsoSetLazyLeakDetection(const void *) noexcept
|
||||
MSOAPI_(void) MsoSetLazyLeakDetection(const void*) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc650 /* tag_a1mzq */);
|
||||
VerifyElseCrashTag(false, 0x006cc650 /* tag_a1mzq */);
|
||||
}
|
||||
|
||||
MSOAPI_(void) MsoSetShutdownLeakDetection(const void *) noexcept
|
||||
MSOAPI_(void) MsoSetShutdownLeakDetection(const void*) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc651 /* tag_a1mzr */);
|
||||
VerifyElseCrashTag(false, 0x006cc651 /* tag_a1mzr */);
|
||||
}
|
||||
|
||||
MSOAPI_(BOOL) FMemHeapMsoSaveBeHost(void* /*pinst*/, LPARAM /*lParam*/, const void* /*pvBlock*/, LONG_PTR /*cb*/, struct IMsoMemHeap* /*pmmh*/) noexcept
|
||||
MSOAPI_(BOOL)
|
||||
FMemHeapMsoSaveBeHost(
|
||||
void* /*pinst*/,
|
||||
LPARAM /*lParam*/,
|
||||
const void* /*pvBlock*/,
|
||||
LONG_PTR /*cb*/,
|
||||
struct IMsoMemHeap* /*pmmh*/) noexcept
|
||||
{
|
||||
VerifyElseCrashTag(false, 0x006cc652 /* tag_a1mzs */);
|
||||
VerifyElseCrashTag(false, 0x006cc652 /* tag_a1mzs */);
|
||||
}
|
||||
|
||||
MSOAPI_(VOID) MsoDebugRegisterLazyObject(IMsoDebugLazyObject* /*pidlo*/) noexcept {}
|
||||
|
|
|
@ -2,46 +2,35 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Empty implementation for memoryleakscope
|
||||
Empty implementation for memoryleakscope
|
||||
|
||||
DO NOT INCLUDE THIS HEADER - LINK WITH THE LOB
|
||||
(unless you need to link in pass0 e.g. msoprep\legacy)
|
||||
DO NOT INCLUDE THIS HEADER - LINK WITH THE LOB
|
||||
(unless you need to link in pass0 e.g. msoprep\legacy)
|
||||
*/
|
||||
#include <memoryleakscope/memoryLeakScope.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
namespace Mso {
|
||||
namespace Memory {
|
||||
namespace Mso { namespace Memory {
|
||||
|
||||
bool IsInShutdownLeakScope() noexcept
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnterShutdownLeakScope(unsigned int /*framesToSkip*/) noexcept
|
||||
{
|
||||
}
|
||||
void EnterShutdownLeakScope(unsigned int /*framesToSkip*/) noexcept {}
|
||||
|
||||
void LeaveShutdownLeakScope() noexcept
|
||||
{
|
||||
}
|
||||
void LeaveShutdownLeakScope() noexcept {}
|
||||
|
||||
bool IsInIgnoreLeakScope() noexcept
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnterIgnoreLeakScope(unsigned int /*framesToSkip*/) noexcept
|
||||
{
|
||||
}
|
||||
void EnterIgnoreLeakScope(unsigned int /*framesToSkip*/) noexcept {}
|
||||
|
||||
void LeaveIgnoreLeakScope() noexcept
|
||||
{
|
||||
}
|
||||
void LeaveIgnoreLeakScope() noexcept {}
|
||||
|
||||
} // Memory
|
||||
} // Mso
|
||||
}} // namespace Mso::Memory
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
|
|
|
@ -18,72 +18,71 @@
|
|||
// UNTAGGED that can conflict with tagutils.h.
|
||||
extern "C"
|
||||
{
|
||||
typedef UInt32 nl_assert_tag_t;
|
||||
|
||||
Boolean NLFAssertsEnabled();
|
||||
void NLEnableAllAsserts(Boolean inEnabled);
|
||||
typedef UInt32 nl_assert_tag_t;
|
||||
|
||||
Boolean NLIsAssertEnabledByTag(nl_assert_tag_t inTag);
|
||||
void NLDisableAssertByTag(nl_assert_tag_t inTag);
|
||||
void NLEnableAssertByTag(nl_assert_tag_t inTag);
|
||||
Boolean NLFAssertsEnabled();
|
||||
void NLEnableAllAsserts(Boolean inEnabled);
|
||||
|
||||
Boolean NLIsAssertEnabledByTag(nl_assert_tag_t inTag);
|
||||
void NLDisableAssertByTag(nl_assert_tag_t inTag);
|
||||
void NLEnableAssertByTag(nl_assert_tag_t inTag);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Mso {
|
||||
|
||||
#if DEBUG
|
||||
class IgnoreAllAssertsPlatformImpl
|
||||
class IgnoreAllAssertsPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAllAssertsPlatformImpl() : m_nlEnabled(NLFAssertsEnabled())
|
||||
{
|
||||
NLEnableAllAsserts(false);
|
||||
}
|
||||
|
||||
~IgnoreAllAssertsPlatformImpl()
|
||||
{
|
||||
NLEnableAllAsserts(m_nlEnabled);
|
||||
}
|
||||
|
||||
private:
|
||||
const bool m_nlEnabled;
|
||||
};
|
||||
|
||||
class IgnoreAssertPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(DWORD tag) : m_tag(tag), m_tagEnabled(NLIsAssertEnabledByTag(tag))
|
||||
{
|
||||
if (m_tagEnabled)
|
||||
{
|
||||
public:
|
||||
IgnoreAllAssertsPlatformImpl() :
|
||||
m_nlEnabled(NLFAssertsEnabled())
|
||||
{
|
||||
NLEnableAllAsserts(false);
|
||||
}
|
||||
|
||||
~IgnoreAllAssertsPlatformImpl()
|
||||
{
|
||||
NLEnableAllAsserts(m_nlEnabled);
|
||||
}
|
||||
|
||||
private:
|
||||
const bool m_nlEnabled;
|
||||
};
|
||||
|
||||
class IgnoreAssertPlatformImpl
|
||||
NLDisableAssertByTag(m_tag);
|
||||
}
|
||||
}
|
||||
|
||||
~IgnoreAssertPlatformImpl()
|
||||
{
|
||||
if (m_tagEnabled)
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(DWORD tag) :
|
||||
m_tag(tag),
|
||||
m_tagEnabled(NLIsAssertEnabledByTag(tag))
|
||||
{
|
||||
if (m_tagEnabled)
|
||||
{
|
||||
NLDisableAssertByTag(m_tag);
|
||||
}
|
||||
}
|
||||
|
||||
~IgnoreAssertPlatformImpl()
|
||||
{
|
||||
if (m_tagEnabled)
|
||||
{
|
||||
NLEnableAssertByTag(m_tag);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool m_tagEnabled;
|
||||
const nl_assert_tag_t m_tag;
|
||||
};
|
||||
NLEnableAssertByTag(m_tag);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool m_tagEnabled;
|
||||
const nl_assert_tag_t m_tag;
|
||||
};
|
||||
#else // DEBUG
|
||||
class IgnoreAllAssertsPlatformImpl {};
|
||||
class IgnoreAssertPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(uint32_t) {}
|
||||
};
|
||||
class IgnoreAllAssertsPlatformImpl
|
||||
{
|
||||
};
|
||||
class IgnoreAssertPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(uint32_t) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // Mso
|
||||
|
||||
} // namespace Mso
|
||||
|
||||
#endif // MOTIFCPP_ASSERT_IGNOREPLAT_APPLE_H
|
||||
|
|
|
@ -12,14 +12,16 @@
|
|||
|
||||
namespace Mso {
|
||||
|
||||
class IgnoreAllAssertsPlatformImpl {};
|
||||
class IgnoreAllAssertsPlatformImpl
|
||||
{
|
||||
};
|
||||
|
||||
class IgnoreAssertPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(uint32_t) {}
|
||||
};
|
||||
class IgnoreAssertPlatformImpl
|
||||
{
|
||||
public:
|
||||
IgnoreAssertPlatformImpl(uint32_t) {}
|
||||
};
|
||||
|
||||
} // Mso
|
||||
} // namespace Mso
|
||||
|
||||
#endif // MOTIFCPP_ASSERT_IGNOREPLAT_EMPTYIMPL_H
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#define MOTIFCPP_ASSERT_MOTIFAPI_H
|
||||
#include "motifCppTest.h"
|
||||
|
||||
#define BeginSupportFileMap() \
|
||||
#define BeginSupportFileMap()
|
||||
|
||||
#define Dependency(filepath) \
|
||||
#define Dependency(filepath)
|
||||
|
||||
#define EndSupportFileMap() \
|
||||
#define EndSupportFileMap()
|
||||
|
||||
#endif // MOTIFCPP_ASSERT_MOTIFAPI_H
|
|
@ -14,8 +14,7 @@ namespace Mso { namespace UnitTests { namespace GTest {
|
|||
struct GTestFixture : ::testing::Test
|
||||
{
|
||||
explicit GTestFixture(const TestClassInfo& classInfo, const TestMethodInfo& methodInfo)
|
||||
: m_methodInfo { methodInfo }
|
||||
, m_test { classInfo.CreateTest() }
|
||||
: m_methodInfo{methodInfo}, m_test{classInfo.CreateTest()}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -24,26 +23,18 @@ struct GTestFixture : ::testing::Test
|
|||
m_methodInfo.Invoke(*m_test);
|
||||
}
|
||||
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
}
|
||||
static void SetUpTestSuite() {}
|
||||
|
||||
static void TearDownTestSuite()
|
||||
{
|
||||
}
|
||||
static void TearDownTestSuite() {}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
}
|
||||
void SetUp() override {}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
}
|
||||
void TearDown() override {}
|
||||
|
||||
private:
|
||||
const TestMethodInfo& m_methodInfo;
|
||||
std::unique_ptr<TestClass> m_test;
|
||||
};
|
||||
private:
|
||||
const TestMethodInfo& m_methodInfo;
|
||||
std::unique_ptr<TestClass> m_test;
|
||||
};
|
||||
|
||||
inline void RegisterUnitTests()
|
||||
{
|
||||
|
@ -52,13 +43,15 @@ inline void RegisterUnitTests()
|
|||
for (const TestMethodInfo* methodInfo : classInfo->MethodInfos())
|
||||
{
|
||||
::testing::RegisterTest(
|
||||
/*test_suite_name:*/classInfo->ClassName(),
|
||||
/*test_name:*/methodInfo->MethodName(),
|
||||
/*type_param:*/nullptr,
|
||||
/*value_param:*/nullptr,
|
||||
/*file:*/classInfo->FileName(),
|
||||
/*line:*/methodInfo->SourceLine(),
|
||||
/*factory:*/[classInfo, methodInfo]() { return new GTestFixture { *classInfo, *methodInfo }; });
|
||||
/*test_suite_name:*/ classInfo->ClassName(),
|
||||
/*test_name:*/ methodInfo->MethodName(),
|
||||
/*type_param:*/ nullptr,
|
||||
/*value_param:*/ nullptr,
|
||||
/*file:*/ classInfo->FileName(),
|
||||
/*line:*/ methodInfo->SourceLine(),
|
||||
/*factory:*/ [classInfo, methodInfo]() {
|
||||
return new GTestFixture{*classInfo, *methodInfo};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
#include <csignal>
|
||||
|
||||
#if defined(MSO_USE_GTEST)
|
||||
# include <motifCpp/gTestAdapter.h>
|
||||
#include <motifCpp/gTestAdapter.h>
|
||||
#elif defined(MSO_USE_XCTEST)
|
||||
# include <motifCpp/xcTestAdapter.h>
|
||||
#include <motifCpp/xcTestAdapter.h>
|
||||
#else
|
||||
# error "Undefined unit test framework"
|
||||
#error "Undefined unit test framework"
|
||||
#endif
|
||||
|
||||
typedef wchar_t WCHAR;
|
||||
|
@ -25,175 +25,186 @@ namespace TestAssert {
|
|||
// Asserts that the specified condition is true, if it is false the unit test will fail
|
||||
inline void IsTrue(bool condition, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
ASSERT_TRUE(condition) << message;
|
||||
ASSERT_TRUE(condition) << message;
|
||||
}
|
||||
|
||||
// Verify that a condition is true:
|
||||
template<typename T, typename TEnable = typename std::enable_if<!std::is_same<bool, T>::value>::type>
|
||||
template <typename T, typename TEnable = typename std::enable_if<!std::is_same<bool, T>::value>::type>
|
||||
inline void IsTrue(const T& condition, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
return IsTrue(!!condition, message);
|
||||
return IsTrue(!!condition, message);
|
||||
}
|
||||
|
||||
// Asserts that the specified condition is false, if it is true the unit test will fail
|
||||
inline void IsFalse(bool condition, const WCHAR* message = L"")
|
||||
{
|
||||
ASSERT_FALSE(condition) << message;
|
||||
ASSERT_FALSE(condition) << message;
|
||||
}
|
||||
|
||||
template<typename T, typename TEnable = typename std::enable_if<!std::is_same<bool, T>::value>::type>
|
||||
template <typename T, typename TEnable = typename std::enable_if<!std::is_same<bool, T>::value>::type>
|
||||
inline void IsFalse(const T& condition, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
return IsFalse(!!condition, message);
|
||||
return IsFalse(!!condition, message);
|
||||
}
|
||||
|
||||
template <typename ExpectedType, typename ActualType>
|
||||
inline void AreEqual(_In_ const ExpectedType& expected, _In_ const ActualType& actual, _In_z_ const WCHAR* message = L"")
|
||||
inline void
|
||||
AreEqual(_In_ const ExpectedType& expected, _In_ const ActualType& actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_EQ(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_EQ(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
template <typename ExpectedType, typename ActualType,
|
||||
typename TEnable = typename std::enable_if<std::is_same<ExpectedType, ActualType>::value
|
||||
&& !std::is_same<ExpectedType, wchar_t>::value && !std::is_same<ExpectedType, char>::value>::type>
|
||||
template <
|
||||
typename ExpectedType,
|
||||
typename ActualType,
|
||||
typename TEnable = typename std::enable_if<
|
||||
std::is_same<ExpectedType, ActualType>::value && !std::is_same<ExpectedType, wchar_t>::value
|
||||
&& !std::is_same<ExpectedType, char>::value>::type>
|
||||
void AreEqual(_In_ const ExpectedType* expected, _In_ const ActualType* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
AreEqual(*expected, *actual, message);
|
||||
AreEqual(*expected, *actual, message);
|
||||
}
|
||||
|
||||
inline void AreEqual(_In_ const wchar_t* expected, _In_ const wchar_t* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreEqual(_In_ const char* expected, _In_ const char* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreEqual(_In_ wchar_t* expected, _In_ const wchar_t* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreEqual(_In_ char* expected, _In_ const char* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STREQ(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
template <typename ExpectedType, typename ActualType>
|
||||
inline void AreNotEqual(_In_ const ExpectedType& expected, _In_ const ActualType& actual, _In_z_ const WCHAR* message = L"")
|
||||
inline void
|
||||
AreNotEqual(_In_ const ExpectedType& expected, _In_ const ActualType& actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_NE(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_NE(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
template <typename ExpectedType, typename ActualType,
|
||||
typename TEnable = typename std::enable_if<std::is_same<ExpectedType, ActualType>::value
|
||||
&& !std::is_same<ExpectedType, wchar_t>::value && !std::is_same<ExpectedType, char>::value>::type>
|
||||
template <
|
||||
typename ExpectedType,
|
||||
typename ActualType,
|
||||
typename TEnable = typename std::enable_if<
|
||||
std::is_same<ExpectedType, ActualType>::value && !std::is_same<ExpectedType, wchar_t>::value
|
||||
&& !std::is_same<ExpectedType, char>::value>::type>
|
||||
void AreNotEqual(_In_ const ExpectedType* expected, _In_ const ActualType* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
AreNotEqual(*expected, *actual, message);
|
||||
AreNotEqual(*expected, *actual, message);
|
||||
}
|
||||
|
||||
inline void AreNotEqual(_In_ const wchar_t* expected, _In_ const wchar_t* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreNotEqual(_In_ const char* expected, _In_ const char* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreNotEqual(_In_ wchar_t* expected, _In_ const wchar_t* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void AreNotEqual(_In_ char* expected, _In_ const char* actual, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
std::wstring wstrMessage(message);
|
||||
ASSERT_STRNE(expected, actual) << wstrMessage.c_str();
|
||||
}
|
||||
|
||||
inline void Comment(_In_ const WCHAR* message)
|
||||
{
|
||||
std::wcerr << L"[ INFO ] " << message << std::endl;
|
||||
std::wcerr << L"[ INFO ] " << message << std::endl;
|
||||
}
|
||||
|
||||
inline void CommentEx(_In_z_ _Printf_format_string_ const WCHAR* format, va_list args) noexcept
|
||||
{
|
||||
fwprintf(stderr, L"[ INFO ] ");
|
||||
vfwprintf(stderr, format, args);
|
||||
fwprintf(stderr, L"\n");
|
||||
fwprintf(stderr, L"[ INFO ] ");
|
||||
vfwprintf(stderr, format, args);
|
||||
fwprintf(stderr, L"\n");
|
||||
}
|
||||
|
||||
inline void CommentEx(_In_z_ _Printf_format_string_ const WCHAR* format, ...) noexcept
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fwprintf(stderr, L"[ INFO ] ");
|
||||
vfwprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fwprintf(stderr, L"\n");
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fwprintf(stderr, L"[ INFO ] ");
|
||||
vfwprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fwprintf(stderr, L"\n");
|
||||
}
|
||||
|
||||
inline void Pass(_In_z_ const WCHAR* message = L"") noexcept
|
||||
{
|
||||
ASSERT_TRUE(true) << message;
|
||||
ASSERT_TRUE(true) << message;
|
||||
}
|
||||
|
||||
inline void Fail(_In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
ASSERT_TRUE(false) << message;
|
||||
ASSERT_TRUE(false) << message;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
template <typename ValueType>
|
||||
inline void IsNull(const ValueType& ptr, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
AreEqual(ptr, nullptr, message);
|
||||
AreEqual(ptr, nullptr, message);
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
template <typename ValueType>
|
||||
inline void IsNotNull(const ValueType& ptr, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
AreNotEqual(ptr, nullptr, message);
|
||||
AreNotEqual(ptr, nullptr, message);
|
||||
}
|
||||
|
||||
template<typename ExceptionType>
|
||||
inline void ExpectException(const std::function<void ()>& statement, const WCHAR* message = L"")
|
||||
template <typename ExceptionType>
|
||||
inline void ExpectException(const std::function<void()>& statement, const WCHAR* message = L"")
|
||||
{
|
||||
EXPECT_THROW(statement(), ExceptionType) << message;
|
||||
EXPECT_THROW(statement(), ExceptionType) << message;
|
||||
}
|
||||
|
||||
template<typename ExceptionType>
|
||||
inline void ExpectException(const std::function<void ()>& statement, const std::function<void ()>& onException, const WCHAR* message = L"")
|
||||
template <typename ExceptionType>
|
||||
inline void ExpectException(
|
||||
const std::function<void()>& statement,
|
||||
const std::function<void()>& onException,
|
||||
const WCHAR* message = L"")
|
||||
{
|
||||
EXPECT_THROW(statement(), ExceptionType) << message;
|
||||
EXPECT_THROW(statement(), ExceptionType) << message;
|
||||
}
|
||||
|
||||
inline void ExpectNoThrow(const std::function<void ()>& statement, const WCHAR* message = L"")
|
||||
inline void ExpectNoThrow(const std::function<void()>& statement, const WCHAR* message = L"")
|
||||
{
|
||||
EXPECT_NO_THROW(statement()) << message;
|
||||
EXPECT_NO_THROW(statement()) << message;
|
||||
}
|
||||
|
||||
inline void HrSucceeded(HRESULT hr, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
ASSERT_TRUE(SUCCEEDED(hr)) << message;
|
||||
ASSERT_TRUE(SUCCEEDED(hr)) << message;
|
||||
}
|
||||
|
||||
inline void HrFailed(HRESULT hr, _In_z_ const WCHAR* message = L"")
|
||||
{
|
||||
ASSERT_FALSE(SUCCEEDED(hr)) << message;
|
||||
ASSERT_FALSE(SUCCEEDED(hr)) << message;
|
||||
}
|
||||
|
||||
#ifdef MS_TARGET_WINDOWS
|
||||
|
@ -202,35 +213,35 @@ static const DWORD EXCEPTION_CPLUSPLUS = static_cast<DWORD>(0xE06D7363);
|
|||
|
||||
inline DWORD FilterCrashExceptions(DWORD exceptionCode) noexcept
|
||||
{
|
||||
if ((exceptionCode == EXCEPTION_BREAKPOINT) // allow exceptions to get to the debugger
|
||||
|| (exceptionCode == EXCEPTION_SINGLE_STEP) // allow exceptions to get to the debugger
|
||||
|| (exceptionCode == EXCEPTION_GUARD_PAGE) // allow to crash on memory page access violation
|
||||
|| (exceptionCode == EXCEPTION_STACK_OVERFLOW)) // allow to crash on stack overflow
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
if (exceptionCode == EXCEPTION_CPLUSPLUS) // log C++ exception and pass it through
|
||||
{
|
||||
TestAssert::Fail(L"Test function did not crash, but exception is thrown!");
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
if ((exceptionCode == EXCEPTION_BREAKPOINT) // allow exceptions to get to the debugger
|
||||
|| (exceptionCode == EXCEPTION_SINGLE_STEP) // allow exceptions to get to the debugger
|
||||
|| (exceptionCode == EXCEPTION_GUARD_PAGE) // allow to crash on memory page access violation
|
||||
|| (exceptionCode == EXCEPTION_STACK_OVERFLOW)) // allow to crash on stack overflow
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
if (exceptionCode == EXCEPTION_CPLUSPLUS) // log C++ exception and pass it through
|
||||
{
|
||||
TestAssert::Fail(L"Test function did not crash, but exception is thrown!");
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
template <typename Fn>
|
||||
inline bool ExpectCrashCore(const Fn& fn, const WCHAR* message)
|
||||
{
|
||||
__try
|
||||
{
|
||||
fn();
|
||||
}
|
||||
__except (FilterCrashExceptions(GetExceptionCode()))
|
||||
{
|
||||
//Pass(message == nullptr || message[0] == L'\0' ? L"Crash occurred as expected." : message);
|
||||
return true;
|
||||
}
|
||||
//Fail(message == nullptr || message[0] == L'\0' ? L"Test function did not crash!" : message);
|
||||
return false;
|
||||
__try
|
||||
{
|
||||
fn();
|
||||
}
|
||||
__except (FilterCrashExceptions(GetExceptionCode()))
|
||||
{
|
||||
// Pass(message == nullptr || message[0] == L'\0' ? L"Crash occurred as expected." : message);
|
||||
return true;
|
||||
}
|
||||
// Fail(message == nullptr || message[0] == L'\0' ? L"Test function did not crash!" : message);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
|
@ -240,49 +251,48 @@ using SigAction = void (*)(int, siginfo_t*, void*);
|
|||
// restores the old one in the destructor.
|
||||
struct CrashState
|
||||
{
|
||||
CrashState(SigAction action) noexcept
|
||||
{
|
||||
sigemptyset(&m_action.sa_mask);
|
||||
m_action.sa_sigaction = action;
|
||||
m_action.sa_flags = SA_NODEFER;
|
||||
sigaction(SIGSEGV, &m_action, &m_oldAction);
|
||||
}
|
||||
CrashState(SigAction action) noexcept
|
||||
{
|
||||
sigemptyset(&m_action.sa_mask);
|
||||
m_action.sa_sigaction = action;
|
||||
m_action.sa_flags = SA_NODEFER;
|
||||
sigaction(SIGSEGV, &m_action, &m_oldAction);
|
||||
}
|
||||
|
||||
~CrashState() noexcept
|
||||
{
|
||||
sigaction(SIGSEGV, &m_oldAction, nullptr);
|
||||
}
|
||||
~CrashState() noexcept
|
||||
{
|
||||
sigaction(SIGSEGV, &m_oldAction, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
struct sigaction m_action{};
|
||||
struct sigaction m_oldAction{};
|
||||
struct sigaction m_action
|
||||
{
|
||||
};
|
||||
struct sigaction m_oldAction
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
// Returns true if crash (segmentation fault happened)
|
||||
template <class Fn>
|
||||
inline bool ExpectCrashCore(const Fn& fn, const WCHAR* message)
|
||||
{
|
||||
static sigjmp_buf buf{};
|
||||
static sigjmp_buf buf{};
|
||||
|
||||
// Set sigaction and save the previous action to be restored in the end of
|
||||
// function.
|
||||
CrashState crashState{
|
||||
[](int signal, siginfo_t *si, void *arg)
|
||||
{
|
||||
longjmp(buf, 1);
|
||||
}
|
||||
};
|
||||
// Set sigaction and save the previous action to be restored in the end of
|
||||
// function.
|
||||
CrashState crashState{[](int signal, siginfo_t* si, void* arg) { longjmp(buf, 1); }};
|
||||
|
||||
// setjmp originally returns 0, and when longjmp is called it returns 1.
|
||||
if (!setjmp(buf))
|
||||
{
|
||||
fn();
|
||||
return true; // must not be executed if fn() caused crash and the longjmp is executed.
|
||||
}
|
||||
else
|
||||
{
|
||||
return true; // executed if longjmp is executed in the SigAction handler.
|
||||
}
|
||||
// setjmp originally returns 0, and when longjmp is called it returns 1.
|
||||
if (!setjmp(buf))
|
||||
{
|
||||
fn();
|
||||
return true; // must not be executed if fn() caused crash and the longjmp is executed.
|
||||
}
|
||||
else
|
||||
{
|
||||
return true; // executed if longjmp is executed in the SigAction handler.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -290,10 +300,10 @@ inline bool ExpectCrashCore(const Fn& fn, const WCHAR* message)
|
|||
template <typename Fn>
|
||||
inline void ExpectVEC(const Fn& fn, const WCHAR* message = L"")
|
||||
{
|
||||
if (!ExpectCrashCore(fn, message))
|
||||
{
|
||||
Fail(message);
|
||||
}
|
||||
if (!ExpectCrashCore(fn, message))
|
||||
{
|
||||
Fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestAssert
|
||||
} // namespace TestAssert
|
|
@ -8,19 +8,18 @@ typedef wchar_t WCHAR;
|
|||
class MotifCppTestBase
|
||||
{
|
||||
public:
|
||||
MotifCppTestBase()
|
||||
{}
|
||||
MotifCppTestBase() {}
|
||||
|
||||
~MotifCppTestBase()
|
||||
{
|
||||
TestClassTeardown();
|
||||
}
|
||||
~MotifCppTestBase()
|
||||
{
|
||||
TestClassTeardown();
|
||||
}
|
||||
|
||||
virtual void TestClassSetup() {}
|
||||
virtual void TestClassSetup() {}
|
||||
|
||||
virtual void TestClassTeardown() {}
|
||||
virtual void TestClassTeardown() {}
|
||||
|
||||
virtual void Setup() {}
|
||||
virtual void Setup() {}
|
||||
|
||||
virtual void Teardown() {}
|
||||
virtual void Teardown() {}
|
||||
};
|
|
@ -10,37 +10,33 @@
|
|||
#include <vector>
|
||||
#include <motifCpp/motifCppTestBase.h>
|
||||
|
||||
#define TEST_CLASS(className) \
|
||||
struct className; \
|
||||
\
|
||||
struct TestClassInfo_##className final \
|
||||
: Mso::UnitTests::Internal::TestClassInfoReg<TestClassInfo_##className, className> \
|
||||
{ \
|
||||
TestClassInfo_##className() noexcept \
|
||||
: TestClassInfoRegType{ #className, __FILE__, __LINE__ } {} \
|
||||
};\
|
||||
\
|
||||
struct className : Mso::UnitTests::Internal::TestClassBase<className, TestClassInfo_##className>
|
||||
#define TEST_CLASS(className) \
|
||||
struct className; \
|
||||
\
|
||||
struct TestClassInfo_##className final \
|
||||
: Mso::UnitTests::Internal::TestClassInfoReg<TestClassInfo_##className, className> \
|
||||
{ \
|
||||
TestClassInfo_##className() noexcept : TestClassInfoRegType{#className, __FILE__, __LINE__} {} \
|
||||
}; \
|
||||
\
|
||||
struct className : Mso::UnitTests::Internal::TestClassBase<className, TestClassInfo_##className>
|
||||
|
||||
#define TEST_METHOD(methodName) \
|
||||
struct TestMethodInfo_##methodName final \
|
||||
: Mso::UnitTests::Internal::TestMethodInfoReg<TestMethodInfo_##methodName> \
|
||||
{ \
|
||||
TestMethodInfo_##methodName() \
|
||||
: TestMethodInfoRegType{ TestClassInfoType::Instance, #methodName, __LINE__ } {} \
|
||||
\
|
||||
void Invoke(Mso::UnitTests::TestClass& test) const override \
|
||||
{ \
|
||||
static_cast<TestClassType&>(test).methodName(); \
|
||||
} \
|
||||
}; \
|
||||
virtual void methodName()
|
||||
#define TEST_METHOD(methodName) \
|
||||
struct TestMethodInfo_##methodName final : Mso::UnitTests::Internal::TestMethodInfoReg<TestMethodInfo_##methodName> \
|
||||
{ \
|
||||
TestMethodInfo_##methodName() : TestMethodInfoRegType{TestClassInfoType::Instance, #methodName, __LINE__} {} \
|
||||
\
|
||||
void Invoke(Mso::UnitTests::TestClass& test) const override \
|
||||
{ \
|
||||
static_cast<TestClassType&>(test).methodName(); \
|
||||
} \
|
||||
}; \
|
||||
virtual void methodName()
|
||||
|
||||
#define TestClassComponent(x1, x2)
|
||||
|
||||
// Allows a test to be compiled, but not executed
|
||||
#define SKIPTESTMETHOD(methodName) \
|
||||
virtual void skipped_##methodName()
|
||||
#define SKIPTESTMETHOD(methodName) virtual void skipped_##methodName()
|
||||
|
||||
namespace Mso { namespace UnitTests {
|
||||
|
||||
|
@ -51,7 +47,7 @@ struct TestMethodInfo;
|
|||
template <class T>
|
||||
struct TestClassInfos
|
||||
{
|
||||
static std::vector<TestClassInfo*> s_classInfos;
|
||||
static std::vector<TestClassInfo*> s_classInfos;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
@ -59,56 +55,76 @@ std::vector<TestClassInfo*> TestClassInfos<T>::s_classInfos;
|
|||
|
||||
struct TestClassInfo
|
||||
{
|
||||
TestClassInfo(const char* className, const char* fileName, int sourceLine) noexcept
|
||||
: m_className { className }
|
||||
, m_fileName { fileName }
|
||||
, m_sourceLine { sourceLine }
|
||||
{
|
||||
TestClassInfos<int>::s_classInfos.push_back(this);
|
||||
}
|
||||
TestClassInfo(const char* className, const char* fileName, int sourceLine) noexcept
|
||||
: m_className{className}, m_fileName{fileName}, m_sourceLine{sourceLine}
|
||||
{
|
||||
TestClassInfos<int>::s_classInfos.push_back(this);
|
||||
}
|
||||
|
||||
const char* ClassName() const noexcept { return m_className; }
|
||||
const char* FileName() const noexcept { return m_fileName; }
|
||||
int SourceLine() const noexcept { return m_sourceLine; }
|
||||
const std::vector<TestMethodInfo*>& MethodInfos() const noexcept { return m_methodInfos; }
|
||||
static const std::vector<TestClassInfo*>& ClassInfos() noexcept { return TestClassInfos<int>::s_classInfos; }
|
||||
const char* ClassName() const noexcept
|
||||
{
|
||||
return m_className;
|
||||
}
|
||||
const char* FileName() const noexcept
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
int SourceLine() const noexcept
|
||||
{
|
||||
return m_sourceLine;
|
||||
}
|
||||
const std::vector<TestMethodInfo*>& MethodInfos() const noexcept
|
||||
{
|
||||
return m_methodInfos;
|
||||
}
|
||||
static const std::vector<TestClassInfo*>& ClassInfos() noexcept
|
||||
{
|
||||
return TestClassInfos<int>::s_classInfos;
|
||||
}
|
||||
|
||||
void AddMethodInfo(TestMethodInfo& methodInfo) noexcept
|
||||
{
|
||||
m_methodInfos.push_back(&methodInfo);
|
||||
}
|
||||
void AddMethodInfo(TestMethodInfo& methodInfo) noexcept
|
||||
{
|
||||
m_methodInfos.push_back(&methodInfo);
|
||||
}
|
||||
|
||||
public: // To implement in derived classes
|
||||
virtual std::unique_ptr<TestClass> CreateTest() const = 0;
|
||||
virtual std::unique_ptr<TestClass> CreateTest() const = 0;
|
||||
|
||||
private:
|
||||
const char* m_className { nullptr };
|
||||
const char* m_fileName { nullptr };
|
||||
int m_sourceLine { 0 };
|
||||
std::vector<TestMethodInfo*> m_methodInfos;
|
||||
const char* m_className{nullptr};
|
||||
const char* m_fileName{nullptr};
|
||||
int m_sourceLine{0};
|
||||
std::vector<TestMethodInfo*> m_methodInfos;
|
||||
};
|
||||
|
||||
struct TestMethodInfo
|
||||
{
|
||||
TestMethodInfo(TestClassInfo& classInfo, const char* methodName, int sourceLine) noexcept
|
||||
: m_classInfo { classInfo }
|
||||
, m_methodName { methodName }
|
||||
, m_sourceLine { sourceLine }
|
||||
{
|
||||
classInfo.AddMethodInfo(*this);
|
||||
}
|
||||
TestMethodInfo(TestClassInfo& classInfo, const char* methodName, int sourceLine) noexcept
|
||||
: m_classInfo{classInfo}, m_methodName{methodName}, m_sourceLine{sourceLine}
|
||||
{
|
||||
classInfo.AddMethodInfo(*this);
|
||||
}
|
||||
|
||||
const TestClassInfo& ClassInfo() const noexcept { return m_classInfo; }
|
||||
const char* MethodName() const noexcept { return m_methodName; }
|
||||
int SourceLine() const noexcept { return m_sourceLine; }
|
||||
const TestClassInfo& ClassInfo() const noexcept
|
||||
{
|
||||
return m_classInfo;
|
||||
}
|
||||
const char* MethodName() const noexcept
|
||||
{
|
||||
return m_methodName;
|
||||
}
|
||||
int SourceLine() const noexcept
|
||||
{
|
||||
return m_sourceLine;
|
||||
}
|
||||
|
||||
public: // To implement in derived classes
|
||||
virtual void Invoke(TestClass& test) const = 0;
|
||||
virtual void Invoke(TestClass& test) const = 0;
|
||||
|
||||
private:
|
||||
const TestClassInfo& m_classInfo;
|
||||
const char* m_methodName { nullptr };
|
||||
int m_sourceLine { 0 };
|
||||
const TestClassInfo& m_classInfo;
|
||||
const char* m_methodName{nullptr};
|
||||
int m_sourceLine{0};
|
||||
};
|
||||
|
||||
}} // namespace Mso::UnitTests
|
||||
|
@ -125,15 +141,15 @@ struct TestClassBase : TestClass
|
|||
template <class TClassInfo, class TTestClass>
|
||||
struct TestClassInfoReg : TestClassInfo
|
||||
{
|
||||
using TestClassInfoRegType = TestClassInfoReg;
|
||||
using TestClassInfo::TestClassInfo;
|
||||
using TestClassInfoRegType = TestClassInfoReg;
|
||||
using TestClassInfo::TestClassInfo;
|
||||
|
||||
static TClassInfo Instance;
|
||||
static TClassInfo Instance;
|
||||
|
||||
std::unique_ptr<TestClass> CreateTest() const override
|
||||
{
|
||||
return std::unique_ptr<TestClass> { new TTestClass() };
|
||||
}
|
||||
std::unique_ptr<TestClass> CreateTest() const override
|
||||
{
|
||||
return std::unique_ptr<TestClass>{new TTestClass()};
|
||||
}
|
||||
};
|
||||
|
||||
template <class TClassInfo, class TTestClass>
|
||||
|
@ -142,15 +158,15 @@ TClassInfo TestClassInfoReg<TClassInfo, TTestClass>::Instance;
|
|||
template <class TMethodInfo>
|
||||
struct TestMethodInfoReg : TestMethodInfo
|
||||
{
|
||||
using TestMethodInfoRegType = TestMethodInfoReg;
|
||||
using TestMethodInfoRegType = TestMethodInfoReg;
|
||||
|
||||
TestMethodInfoReg(TestClassInfo& classInfo, const char* methodName, int sourceLine) noexcept
|
||||
: TestMethodInfo { classInfo, methodName, sourceLine }
|
||||
{
|
||||
(void)&Instance; // To ensure that we create static instance
|
||||
}
|
||||
TestMethodInfoReg(TestClassInfo& classInfo, const char* methodName, int sourceLine) noexcept
|
||||
: TestMethodInfo{classInfo, methodName, sourceLine}
|
||||
{
|
||||
(void)&Instance; // To ensure that we create static instance
|
||||
}
|
||||
|
||||
static TMethodInfo Instance;
|
||||
static TMethodInfo Instance;
|
||||
};
|
||||
|
||||
template <class TMethodInfo>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
class LibletAwareMemLeakDetection : public MotifCppTestBase
|
||||
{
|
||||
protected:
|
||||
void InitLiblets() noexcept
|
||||
{}
|
||||
void InitLiblets() noexcept {}
|
||||
|
||||
void UninitLiblets() noexcept
|
||||
{}
|
||||
void UninitLiblets() noexcept {}
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
// This file is added temporary for the purpose of excluding the liblet UT's that used SEH. //
|
||||
// This file is added temporary for the purpose of excluding the liblet UT's that used SEH. //
|
||||
// Currently there is no support for filtering in Android. Bug # OM: 1706117 Need Filter support for Android Unit tests.
|
||||
//
|
||||
//
|
||||
// Action item post this bug will be fixed ...
|
||||
// we need to remove this file and all the occurrences of the macro TESTMETHOD_REQUIRES_SEH should be
|
||||
// we need to remove this file and all the occurrences of the macro TESTMETHOD_REQUIRES_SEH should be
|
||||
// replaced with TESTMETHODEX(${1}, TestCategory(RequriesSEH)).
|
||||
|
||||
#pragma once
|
||||
|
|
|
@ -24,46 +24,41 @@
|
|||
// A helper macro to provide current line number as a wide char string.
|
||||
//=============================================================================
|
||||
#ifndef MSO_TO_STR
|
||||
# define MSO_INTERNAL_TO_STR(value) #value
|
||||
# define MSO_TO_STR(value) MSO_INTERNAL_TO_STR(value)
|
||||
#define MSO_INTERNAL_TO_STR(value) #value
|
||||
#define MSO_TO_STR(value) MSO_INTERNAL_TO_STR(value)
|
||||
#endif
|
||||
|
||||
#ifndef MSO_WIDE_STR
|
||||
# define MSO_INTERNAL_WIDE_STR(str) L ## str
|
||||
# define MSO_WIDE_STR(str) MSO_INTERNAL_WIDE_STR(str)
|
||||
#define MSO_INTERNAL_WIDE_STR(str) L##str
|
||||
#define MSO_WIDE_STR(str) MSO_INTERNAL_WIDE_STR(str)
|
||||
#endif
|
||||
|
||||
#define MSO_LINE_STR MSO_TO_STR(__LINE__)
|
||||
#define MSO_LINE_WIDE_STR MSO_WIDE_STR(MSO_LINE_STR)
|
||||
#define MSO_LINE_STR MSO_TO_STR(__LINE__)
|
||||
#define MSO_LINE_WIDE_STR MSO_WIDE_STR(MSO_LINE_STR)
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckFail fails the test unconditionally.
|
||||
//=============================================================================
|
||||
#define TestCheckFailL(message, line) \
|
||||
TestAssert::Fail(MSO_WIDE_STR("Line: " line " " message))
|
||||
#define TestCheckFailL(message, line) TestAssert::Fail(MSO_WIDE_STR("Line: " line " " message))
|
||||
#define TestCheckFail(message) TestCheckFailL(message, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheck checks if provided expression evaluates to true.
|
||||
// If check fails then it reports the line number and the failed expression.
|
||||
//=============================================================================
|
||||
#define TestCheckL(expr, line) \
|
||||
TestAssert::IsTrue(expr, MSO_WIDE_STR("Line: " line " [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheckL(expr, line) TestAssert::IsTrue(expr, MSO_WIDE_STR("Line: " line " [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheck(expr) TestCheckL(expr, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckEqual checks if two provided values are equal.
|
||||
// If check fails then it reports the line number and the failed expression.
|
||||
// In addition the TestAssert::AreEqual reports expected and actual values.
|
||||
//=============================================================================
|
||||
#define TestCheckEqualL(expected, actual, line) \
|
||||
TestAssert::AreEqual(expected, actual, \
|
||||
MSO_WIDE_STR("Line: " line " [ " MSO_TO_STR(expected) " == " MSO_TO_STR(actual) " ]"))
|
||||
TestAssert::AreEqual( \
|
||||
expected, actual, MSO_WIDE_STR("Line: " line " [ " MSO_TO_STR(expected) " == " MSO_TO_STR(actual) " ]"))
|
||||
#define TestCheckEqual(expected, actual) TestCheckEqualL(expected, actual, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckIgnore ignores the provided expression.
|
||||
// It can be used to avoid compilation errors related to unused variables.
|
||||
|
@ -71,17 +66,19 @@
|
|||
//=============================================================================
|
||||
#define TestCheckIgnore(expr) (void)expr
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckCrash expects that the provided expression causes a crash.
|
||||
//=============================================================================
|
||||
// Mso::IgnoreAllAsserts ignore;
|
||||
#define TestCheckCrashL(expr, line) \
|
||||
TestAssert::ExpectVEC([&]() { OACR_POSSIBLE_THROW; expr; }, \
|
||||
MSO_WIDE_STR("Line: " line " Must crash: [ " MSO_TO_STR(expr) " ]"))
|
||||
TestAssert::ExpectVEC( \
|
||||
[&]() { \
|
||||
OACR_POSSIBLE_THROW; \
|
||||
expr; \
|
||||
}, \
|
||||
MSO_WIDE_STR("Line: " line " Must crash: [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheckCrash(expr) TestCheckCrashL(expr, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckTerminate expects that the provided expression causes process termination
|
||||
// with a call to std::terminate().
|
||||
|
@ -92,38 +89,28 @@
|
|||
// You should disable memory leak detection in tests that use TestCheckTerminate.
|
||||
//=============================================================================
|
||||
#define TestCheckTerminateL(expr, line) \
|
||||
TestAssert::ExpectTerminate([&]() { expr; }, \
|
||||
MSO_WIDE_STR("Line: " line " Must terminate: [ " MSO_TO_STR(expr) " ]"))
|
||||
TestAssert::ExpectTerminate([&]() { expr; }, MSO_WIDE_STR("Line: " line " Must terminate: [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheckTerminate(expr) TestCheckTerminateL(expr, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckException expects that the provided expression throws an exception.
|
||||
//=============================================================================
|
||||
#define TestCheckExceptionL(ex, expr, line) \
|
||||
TestAssert::ExpectException<ex>([&]() { expr; }, \
|
||||
MSO_WIDE_STR("Line: " line " Must throw: " MSO_TO_STR(ex) " [ " MSO_TO_STR(expr) " ]"))
|
||||
TestAssert::ExpectException<ex>( \
|
||||
[&]() { expr; }, MSO_WIDE_STR("Line: " line " Must throw: " MSO_TO_STR(ex) " [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheckException(ex, expr) TestCheckExceptionL(ex, expr, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckNoThrow expects that the provided expression does not throw an exception.
|
||||
//=============================================================================
|
||||
#define TestCheckNoThrowL(expr, line) \
|
||||
TestAssert::ExpectNoThrow([&]() { expr; }, \
|
||||
MSO_WIDE_STR("Line: " line " Must not throw: [ " MSO_TO_STR(expr) " ]"))
|
||||
TestAssert::ExpectNoThrow([&]() { expr; }, MSO_WIDE_STR("Line: " line " Must not throw: [ " MSO_TO_STR(expr) " ]"))
|
||||
#define TestCheckNoThrow(expr) TestCheckNoThrowL(expr, MSO_LINE_STR)
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TestCheckAssert checks for the code to produce assert with specified tag.
|
||||
//=============================================================================
|
||||
#define TestCheckShipAssert(tag, expr) \
|
||||
Statement( \
|
||||
Mso::ExpectShipAssert expectAssert(tag); \
|
||||
expr; \
|
||||
);
|
||||
|
||||
#define TestCheckShipAssert(tag, expr) Statement(Mso::ExpectShipAssert expectAssert(tag); expr;);
|
||||
|
||||
//=============================================================================
|
||||
// A macro to disable memory leak detection in unit tests where we expect crash
|
||||
|
@ -134,12 +121,8 @@
|
|||
// MemoryLeakDetectionHook::TrackPerTest m_trackLeakPerTest;
|
||||
//=============================================================================
|
||||
#define TEST_DISABLE_MEMORY_LEAK_DETECTION() \
|
||||
StopTrackingMemoryAllocations(); \
|
||||
auto restartTrackingMemoryAllocations = Mso::TCleanup::Make([&]() noexcept \
|
||||
{ \
|
||||
StartTrackingMemoryAllocations(); \
|
||||
});
|
||||
|
||||
StopTrackingMemoryAllocations(); \
|
||||
auto restartTrackingMemoryAllocations = Mso::TCleanup::Make([&]() noexcept { StartTrackingMemoryAllocations(); });
|
||||
|
||||
//=============================================================================
|
||||
// Helper functions to implement TestCheckTerminate.
|
||||
|
@ -148,49 +131,44 @@ namespace TestAssert {
|
|||
|
||||
struct TerminateHandlerRestorer
|
||||
{
|
||||
~TerminateHandlerRestorer() noexcept
|
||||
{
|
||||
std::set_terminate(Handler);
|
||||
}
|
||||
~TerminateHandlerRestorer() noexcept
|
||||
{
|
||||
std::set_terminate(Handler);
|
||||
}
|
||||
|
||||
std::terminate_handler Handler;
|
||||
std::terminate_handler Handler;
|
||||
};
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4611) // interaction between '_setjmp' and C++ object destruction is non-portable
|
||||
#pragma warning(disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
|
||||
template <class Fn>
|
||||
inline bool ExpectTerminateCore(const Fn& fn)
|
||||
{
|
||||
static jmp_buf buf;
|
||||
static jmp_buf buf;
|
||||
|
||||
// Set a terminate handler and save the previous terminate handler to be restored in the end of function.
|
||||
TerminateHandlerRestorer terminateRestore = {
|
||||
std::set_terminate([]()
|
||||
{
|
||||
longjmp(buf, 1);
|
||||
})
|
||||
};
|
||||
// Set a terminate handler and save the previous terminate handler to be restored in the end of function.
|
||||
TerminateHandlerRestorer terminateRestore = {std::set_terminate([]() { longjmp(buf, 1); })};
|
||||
|
||||
// setjmp originally returns 0, and when longjmp is called it returns 1.
|
||||
if (!setjmp(buf))
|
||||
{
|
||||
fn();
|
||||
return false; // must not be executed if fn() caused termination and the longjmp is executed.
|
||||
}
|
||||
else
|
||||
{
|
||||
return true; // executed if longjmp is executed in the terminate handler.
|
||||
}
|
||||
// setjmp originally returns 0, and when longjmp is called it returns 1.
|
||||
if (!setjmp(buf))
|
||||
{
|
||||
fn();
|
||||
return false; // must not be executed if fn() caused termination and the longjmp is executed.
|
||||
}
|
||||
else
|
||||
{
|
||||
return true; // executed if longjmp is executed in the terminate handler.
|
||||
}
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
template <class Fn>
|
||||
inline void ExpectTerminate(const Fn& fn, const WCHAR* message = L"")
|
||||
{
|
||||
if (!ExpectTerminateCore(fn))
|
||||
{
|
||||
Fail(message == nullptr || message[0] == L'\0' ? L"Test function did not terminate!" : message);
|
||||
}
|
||||
if (!ExpectTerminateCore(fn))
|
||||
{
|
||||
Fail(message == nullptr || message[0] == L'\0' ? L"Test function did not terminate!" : message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestAssert
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <motifCpp/gTestAdapter.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Mso::UnitTests::GTest::RegisterUnitTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define __extern_c extern "C"
|
||||
#define __extern_c extern "C"
|
||||
#define __extern_cplus extern "C++"
|
||||
#else
|
||||
#define __extern_c
|
||||
|
@ -37,7 +37,7 @@
|
|||
|
||||
#ifndef __noreturn
|
||||
#ifndef __clang__
|
||||
#define __noreturn __declspec( noreturn )
|
||||
#define __noreturn __declspec(noreturn)
|
||||
#else
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
#endif
|
||||
|
@ -51,21 +51,18 @@
|
|||
#define _Sealed_method_ __oacr_sealed_method
|
||||
#endif // _Sealed_method_
|
||||
|
||||
|
||||
#if !defined(_Sealed_class_)
|
||||
// use _Sealed_class_ to specify C# style 'sealed' behavior for classes
|
||||
#define _Sealed_class_ __oacr_sealed_class
|
||||
#endif // _Sealed_class_
|
||||
|
||||
|
||||
#if !defined(_SA_deprecated_)
|
||||
// use _SA_deprecated_ to mark functions or classes that should not be called any more
|
||||
// pass the replacement function or class as an argument to the macro
|
||||
#define _SA_deprecated_(NewFunctionName) __oacr_sa_deprecated(NewFunctionName)
|
||||
#endif // _SA_deprecated_
|
||||
|
||||
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
// use OACR_MARK_CLASS_DEPRECATED to mark classes that should not be used any more
|
||||
// this macro is a workaround for template classes. Vanilla classes should use _SA_deprecated_
|
||||
// pass the replacement class as argument to the macro
|
||||
|
@ -74,7 +71,6 @@
|
|||
#define OACR_MARK_CLASS_DEPRECATED(NewFunctionName)
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(_SA_deprecated_staging_)
|
||||
// use _SA_deprecated_staging_ to mark functions or classes that should not be called any more
|
||||
// pass the replacement function or class as an argument to the macro
|
||||
|
@ -83,17 +79,16 @@
|
|||
#define _SA_deprecated_staging_(NewFunctionName) __oacr_sa_deprecated_staging(NewFunctionName)
|
||||
#endif // _SA_deprecated_staging_
|
||||
|
||||
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
// use OACR_MARK_CLASS_DEPRECATED_STAGING to mark classes that should not be used any more
|
||||
// this macro is a workaround for template classes. Vanilla classes should use _SA_deprecated_
|
||||
// pass the replacement function as argument to the macro
|
||||
#define OACR_MARK_CLASS_DEPRECATED_STAGING(NewFunctionName) _SA_deprecated_staging_(NewFunctionName) void OACRMarkClass();
|
||||
#define OACR_MARK_CLASS_DEPRECATED_STAGING(NewFunctionName) \
|
||||
_SA_deprecated_staging_(NewFunctionName) void OACRMarkClass();
|
||||
#else
|
||||
#define OACR_MARK_CLASS_DEPRECATED_STAGING(NewFunctionName)
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(_Intl_deprecated_)
|
||||
// annotation used by warning UNMARKED_INTL_DEPRECATED_FUNCTION (25114)
|
||||
// to mark deprecated functions using LCID parameters
|
||||
|
@ -101,13 +96,11 @@
|
|||
#define _Intl_deprecated_ __oacr_intl_deprecated
|
||||
#endif // _Intl_deprecated_
|
||||
|
||||
|
||||
#if !defined(_Oleo_deprecated_)
|
||||
// Office specifix annotation used by the OLEO effort
|
||||
#define _Oleo_deprecated_( NewFunctionName ) __oacr_oleo_deprecated(NewFunctionName)
|
||||
#define _Oleo_deprecated_(NewFunctionName) __oacr_oleo_deprecated(NewFunctionName)
|
||||
#endif // _Oleo_deprecated_
|
||||
|
||||
|
||||
#if !defined(_Norefcapture_asynclambda_)
|
||||
//
|
||||
// use _Norefcapture_asynclambda_ on async function. This warns user of possible unsafe variable capture.
|
||||
|
@ -146,7 +139,7 @@
|
|||
//
|
||||
// Example:
|
||||
// _Noexcept_mayterminate_ignore_("what") void Foo() noexcept;
|
||||
#define _Noexcept_mayterminate_ignore_(FunctionName) __oacr_noexcept_mayterminate_ignore(FunctionName)
|
||||
#define _Noexcept_mayterminate_ignore_(FunctionName) __oacr_noexcept_mayterminate_ignore(FunctionName)
|
||||
#endif // _Noexcept_mayterminate_ignore_
|
||||
|
||||
#if !defined(_Dont_swap_)
|
||||
|
@ -163,33 +156,29 @@
|
|||
#define _Rpc_ __oacr_rpc
|
||||
#endif // _Rpc_
|
||||
|
||||
|
||||
#if !defined(_RequireNoThrow_)
|
||||
// use _RequireNoThrow_ inside a compound statement to indicate that the rest of the block
|
||||
// should not emit exceptions.
|
||||
#define _RequireNoThrow_ __oacr_requireNoThrow
|
||||
#endif // _RequireNoThrow_
|
||||
|
||||
|
||||
#if !defined(_Canthrow_)
|
||||
// use _Canthrow_ for method declarations of template classes that have throwing and non-throwing
|
||||
// specializations to suppress warning FUNC_COULD_BE_NOTHROW (25307).
|
||||
#define _Canthrow_ __oacr_canthrow
|
||||
#endif // __requireNoThrow
|
||||
|
||||
|
||||
#if !defined(_Genericfunctype_)
|
||||
// use _Genericfunctype_ for function typedefs used for arrays of functions of different function types.
|
||||
// if the typedef is marked as _Genericfunctype_, OACR will not generate DIFFERENT_CALLING_CONVENTION (25018) warnings
|
||||
// e.g. typedef _Genericfunctype_ void (*FUNCPTR)();
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define _Genericfunctype_ __oacr_genericfunctype
|
||||
#else
|
||||
#define _Genericfunctype_
|
||||
#endif
|
||||
#endif // _Genericfunctype_
|
||||
|
||||
|
||||
#if !defined(_Nothrowfunctype_)
|
||||
// use _Nothrowfunctype_ for function typedefs of non exception throwing function pointers
|
||||
// e.g. typedef _Nothrowfunctype_ void (*FUNCPTR)();
|
||||
|
@ -197,7 +186,6 @@
|
|||
#define _Nothrowfunctype_ __oacr_nothrowfunctype
|
||||
#endif // _Nothrowfunctype_
|
||||
|
||||
|
||||
#if !defined(_BindReturn_)
|
||||
// stronger than __checkReturn, typically used by functions that return
|
||||
// a pointer to an allocated object
|
||||
|
@ -211,7 +199,6 @@
|
|||
#define _BindReturn_ __oacr_bindReturn
|
||||
#endif // _BindReturn_
|
||||
|
||||
|
||||
#if !defined(_Memberinitializer_)
|
||||
// use _Memberinitializer_ for init functions that initialize all members of a class
|
||||
// e.g.:
|
||||
|
@ -226,7 +213,6 @@
|
|||
#define _Memberinitializer_ __oacr_memberinitializer
|
||||
#endif // _Memberinitializer_
|
||||
|
||||
|
||||
#if !defined(_Noheap_)
|
||||
// use _Noheap_ classes that should not be instantiated on the heap
|
||||
// e.g.:
|
||||
|
@ -239,8 +225,7 @@
|
|||
#define _Noheap_ __oacr_noheap
|
||||
#endif // _Noheap_
|
||||
|
||||
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
// use OACR_MARK_CLASS_NOHEAP to mark classes that should not be instantiated on the heap
|
||||
// this macro is a workaround for template classes. Vanilla classes should use _Noheap_
|
||||
#define OACR_MARK_CLASS_NOHEAP _Noheap_ void OACRMarkClass();
|
||||
|
@ -248,7 +233,6 @@
|
|||
#define OACR_MARK_CLASS_NOHEAP
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(_Unsafe_string_api_)
|
||||
// use _Unsafe_string_api_ to phase out functions that pass unbound writable buffers
|
||||
// e.g.
|
||||
|
@ -256,12 +240,12 @@
|
|||
#define _Unsafe_string_api_ __oacr_unsafe_string_api
|
||||
#endif // _Unsafe_string_api_
|
||||
|
||||
|
||||
#if !defined(_Needsreview_)
|
||||
// use _Needsreview_ to mark functions whose calls need to be reviewed for a
|
||||
// special reason.
|
||||
// e.g. a wrapper function to another function that needs to be reviewed
|
||||
//_Needsreview_ __inline BOOL MsoGetStringTypeExW(LCID Locale, DWORD dwInfoType, LPCWSTR lpSrcStr, int cchSrc, LPWORD lpCharType)
|
||||
//_Needsreview_ __inline BOOL MsoGetStringTypeExW(LCID Locale, DWORD dwInfoType, LPCWSTR lpSrcStr, int cchSrc, LPWORD
|
||||
//lpCharType)
|
||||
//{
|
||||
// return OACR_REVIEWED_CALL("hannesr", GetStringTypeExW(Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType));
|
||||
//}
|
||||
|
@ -269,7 +253,6 @@
|
|||
#define _Needsreview_ __oacr_needsreview
|
||||
#endif // _Needsreview_
|
||||
|
||||
|
||||
#if !defined(_Notrunccast_)
|
||||
// use _Notrunccast_ to find unsafe truncating cast on allocating functions taking
|
||||
// a 16 bit size parameter
|
||||
|
@ -278,96 +261,95 @@
|
|||
#define _Notrunccast_ __oacr_notrunccast
|
||||
#endif // _Notrunccast_
|
||||
|
||||
|
||||
#if !defined(_Noinference_)
|
||||
// keyword to disable (wrong) Hungarian inference of __count annotations
|
||||
// inference is disabled for the all formals of the function
|
||||
// e.g. _Noinference_ void FreePv( void* pv, size_t cb );
|
||||
#define _Noinference_ __oacr_noinference
|
||||
#define _Noinference_ __oacr_noinference
|
||||
#endif // _Noinference_
|
||||
|
||||
#if !defined(_Allow_implicit_ctor_)
|
||||
// use _Allow_implicit_ctor_ when a constructors is expected to be implicit.
|
||||
//
|
||||
// e.g.
|
||||
//
|
||||
//
|
||||
// _Allow_implicit_ctor_ CPoint( const POINT& );
|
||||
//
|
||||
#define _Allow_implicit_ctor_ __oacr_allow_implicit_ctor
|
||||
#endif // _Allow_implicit_ctor_
|
||||
|
||||
// Pre Orcas style annotations are deprecated
|
||||
#if( OACR_DEPRECATED )
|
||||
#if (OACR_DEPRECATED)
|
||||
|
||||
#if !defined(__sealed_method)
|
||||
#define __sealed_method _Sealed_method_
|
||||
#define __sealed_method _Sealed_method_
|
||||
#endif
|
||||
#if !defined(__sealed_class)
|
||||
#define __sealed_class _Sealed_class_
|
||||
#define __sealed_class _Sealed_class_
|
||||
#endif
|
||||
#if !defined(__sa_deprecated)
|
||||
#define __sa_deprecated(NewFunctionName) _SA_deprecated_(NewFunctionName)
|
||||
#define __sa_deprecated(NewFunctionName) _SA_deprecated_(NewFunctionName)
|
||||
#endif
|
||||
#if !defined(__intl_deprecated)
|
||||
#define __intl_deprecated _Intl_deprecated_
|
||||
#define __intl_deprecated _Intl_deprecated_
|
||||
#endif
|
||||
#if !defined(__oleo_deprecated)
|
||||
#define __oleo_deprecated( NewFunctionName ) _Oleo_deprecated_(NewFunctionName)
|
||||
#define __oleo_deprecated(NewFunctionName) _Oleo_deprecated_(NewFunctionName)
|
||||
#endif
|
||||
#if !defined(__rpc)
|
||||
#define __rpc _Rpc_
|
||||
#define __rpc _Rpc_
|
||||
#endif
|
||||
#if !defined(__requireNoThrow)
|
||||
#define __requireNoThrow _RequireNoThrow_
|
||||
#define __requireNoThrow _RequireNoThrow_
|
||||
#endif
|
||||
#if !defined(__canthrow)
|
||||
#define __canthrow _Canthrow_
|
||||
#define __canthrow _Canthrow_
|
||||
#endif
|
||||
#if !defined(__genericfunctype)
|
||||
#define __genericfunctype _Genericfunctype_
|
||||
#define __genericfunctype _Genericfunctype_
|
||||
#endif
|
||||
#if !defined(__nothrowfunctype)
|
||||
#define __nothrowfunctype _Nothrowfunctype_
|
||||
#define __nothrowfunctype _Nothrowfunctype_
|
||||
#endif
|
||||
#if !defined(__bindReturn)
|
||||
#define __bindReturn _BindReturn_
|
||||
#define __bindReturn _BindReturn_
|
||||
#endif
|
||||
#if !defined(__memberinitializer)
|
||||
#define __memberinitializer _Memberinitializer_
|
||||
#define __memberinitializer _Memberinitializer_
|
||||
#endif
|
||||
#if !defined(__noheap)
|
||||
#define __noheap _Noheap_
|
||||
#define __noheap _Noheap_
|
||||
#endif
|
||||
#if !defined(__nostack)
|
||||
#define __nostack // not supported
|
||||
#define __nostack // not supported
|
||||
#endif
|
||||
#if !defined(__unsafe_string_api)
|
||||
#define __unsafe_string_api _Unsafe_string_api_
|
||||
#define __unsafe_string_api _Unsafe_string_api_
|
||||
#endif
|
||||
#if !defined(__needsreview)
|
||||
#define __needsreview _Needsreview_
|
||||
#define __needsreview _Needsreview_
|
||||
#endif
|
||||
#if !defined(__notrunccast)
|
||||
#define __notrunccast _Notrunccast_
|
||||
#define __notrunccast _Notrunccast_
|
||||
#endif
|
||||
#if !defined(__noinference)
|
||||
#define __noinference _Noinference_
|
||||
#define __noinference _Noinference_
|
||||
#endif
|
||||
#if !defined(__min_function)
|
||||
#define __min_function // deprecated, use range annotations
|
||||
#define __min_function // deprecated, use range annotations
|
||||
#endif
|
||||
#if !defined(__max_function)
|
||||
#define __max_function // deprecated, use range annotations
|
||||
#define __max_function // deprecated, use range annotations
|
||||
#endif
|
||||
#if !defined(__printf_format_string)
|
||||
#define __printf_format_string _Printf_format_string_
|
||||
#define __printf_format_string _Printf_format_string_
|
||||
#endif
|
||||
|
||||
#undef __callback
|
||||
#define __callback _Callback_
|
||||
#define __callback _Callback_
|
||||
|
||||
#if !defined(__sa_sealed) // use __sealed_method and __sealed_class instead
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define __sa_sealed __declspec("_Sealed_")
|
||||
#else
|
||||
#define __sa_sealed
|
||||
|
@ -382,13 +364,13 @@
|
|||
|
||||
#if !defined(OACR_USE_PTR)
|
||||
// use to suppress constness and related warnings:
|
||||
// NONCONST_LOCAL (25003), NONCONST_PARAM( 25004), NONCONST_FUNCTION (25005),
|
||||
// NONCONST_LOCAL (25003), NONCONST_PARAM( 25004), NONCONST_FUNCTION (25005),
|
||||
// NONCONST_LOCAL_BUFFERPTR (25032), NONCONST_BUFFER_PARAM (25033)
|
||||
#if( defined(OACR) && OACR )
|
||||
__extern_c void OACRUsePtr( void* p );
|
||||
#define OACR_USE_PTR( p ) OACRUsePtr( p )
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c void OACRUsePtr(void* p);
|
||||
#define OACR_USE_PTR(p) OACRUsePtr(p)
|
||||
#else
|
||||
#define OACR_USE_PTR( p ) __oacr_noop()
|
||||
#define OACR_USE_PTR(p) __oacr_noop()
|
||||
#endif
|
||||
#endif // OACR_USE_PTR
|
||||
|
||||
|
@ -400,7 +382,7 @@ __extern_c void OACRUsePtr( void* p );
|
|||
// is spread across both ctor initializer list and a _Memberinitializer_ method).
|
||||
// NOTES: This is functionally to OACR_USE_PTR(&m).
|
||||
// Contrast with OACR_MEMBER_IS_INITIALIZED_IN_MEMBERINITIALIZER.
|
||||
#define OACR_MEMBER_IS_INITIALIZED_IN_CTOR( m ) OACR_USE_PTR( &m )
|
||||
#define OACR_MEMBER_IS_INITIALIZED_IN_CTOR(m) OACR_USE_PTR(&m)
|
||||
#endif // OACR_MEMBER_IS_INITIALIZED_IN_CTOR
|
||||
|
||||
#if !defined(OACR_MEMBER_IS_INITIALIZED_IN_MEMBERINITIALIZER)
|
||||
|
@ -410,13 +392,13 @@ __extern_c void OACRUsePtr( void* p );
|
|||
// is spread across both ctor initializer list and a _Memberinitializer_ method).
|
||||
// NOTES: This is functionally equivalent to OACR_USE_PTR(&m).
|
||||
// Contrast with OACR_MEMBER_IS_INITIALIZED_IN_CTOR.
|
||||
#define OACR_MEMBER_IS_INITIALIZED_IN_MEMBERINITIALIZER( m ) OACR_USE_PTR( &m )
|
||||
#define OACR_MEMBER_IS_INITIALIZED_IN_MEMBERINITIALIZER(m) OACR_USE_PTR(&m)
|
||||
#endif // OACR_MEMBER_IS_INITIALIZED_IN_MEMBERINITIALIZER
|
||||
#endif // !RC_INVOKED
|
||||
|
||||
#if !defined(OACR_POSSIBLE_THROW)
|
||||
// use to suppress __nothrow related warnings NOTHROW_FUNC_THROWS (25306) and FUNC_COULD_BE_NOTHROW (25307)
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_cplus void OACRPossibleThrow();
|
||||
#define OACR_POSSIBLE_THROW OACRPossibleThrow()
|
||||
#else
|
||||
|
@ -425,12 +407,19 @@ __extern_cplus void OACRPossibleThrow();
|
|||
#endif // OACR_POSSIBLE_THROW
|
||||
|
||||
#if !defined(OACR_ASSUME_NOTHROW_BEGIN)
|
||||
// use with OACR_ASSUME_NOTHROW_END to suppress NOTHROW_FUNC_THROWS warnings around functions that don't throw in this context
|
||||
// macro pair needs to be on the same scope
|
||||
#if( defined(OACR) && OACR )
|
||||
// use with OACR_ASSUME_NOTHROW_END to suppress NOTHROW_FUNC_THROWS warnings around functions that don't throw in this
|
||||
// context macro pair needs to be on the same scope
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c __noreturn void OACRNoReturn();
|
||||
#define OACR_ASSUME_NOTHROW_BEGIN try{
|
||||
#define OACR_ASSUME_NOTHROW_END }catch(...){ OACRNoReturn(); }
|
||||
#define OACR_ASSUME_NOTHROW_BEGIN \
|
||||
try \
|
||||
{
|
||||
#define OACR_ASSUME_NOTHROW_END \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
OACRNoReturn(); \
|
||||
}
|
||||
#else
|
||||
#define OACR_ASSUME_NOTHROW_BEGIN
|
||||
#define OACR_ASSUME_NOTHROW_END
|
||||
|
@ -439,86 +428,81 @@ __extern_c __noreturn void OACRNoReturn();
|
|||
|
||||
#if !defined(UNREFERENCED_OACR)
|
||||
// use to function staticness and related warnings: STATIC_FUNCTION (25007)
|
||||
#if( defined(OACR) && OACR )
|
||||
#define UNREFERENCED_OACR( p ) __assume( p == p )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define UNREFERENCED_OACR(p) __assume(p == p)
|
||||
#else
|
||||
#define UNREFERENCED_OACR( p )
|
||||
#define UNREFERENCED_OACR(p)
|
||||
#endif
|
||||
#endif // UNREFERENCED_OACR
|
||||
|
||||
|
||||
#if !defined(OACR_OWN_PTR)
|
||||
// can be used for objects that attach themselves to an owner
|
||||
// in their constructors
|
||||
#if( defined(OACR) && OACR )
|
||||
__extern_c void OACROwnPtr( const void* p );
|
||||
#define OACR_OWN_PTR( p ) OACROwnPtr( p )
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c void OACROwnPtr(const void* p);
|
||||
#define OACR_OWN_PTR(p) OACROwnPtr(p)
|
||||
#else
|
||||
#define OACR_OWN_PTR( p ) __oacr_noop()
|
||||
#define OACR_OWN_PTR(p) __oacr_noop()
|
||||
#endif
|
||||
#endif // OACR_OWN_PTR
|
||||
|
||||
|
||||
#if !defined(OACR_PTR_NOT_NULL)
|
||||
// tells OACR that a pointer is not null at this point
|
||||
#if( defined(OACR) && OACR )
|
||||
#define OACR_PTR_NOT_NULL( p ) OACR_ASSUME( 0 != p )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define OACR_PTR_NOT_NULL(p) OACR_ASSUME(0 != p)
|
||||
#else
|
||||
#define OACR_PTR_NOT_NULL( p ) __oacr_noop()
|
||||
#define OACR_PTR_NOT_NULL(p) __oacr_noop()
|
||||
#endif
|
||||
#endif // OACR_PTR_NOT_NULL
|
||||
|
||||
|
||||
#if !defined(OACR_NOT_IMPLEMENTED_MEMBER)
|
||||
#if( defined(OACR) && OACR )
|
||||
#define OACR_NOT_IMPLEMENTED_MEMBER OACR_USE_PTR( (void*)this )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define OACR_NOT_IMPLEMENTED_MEMBER OACR_USE_PTR((void*)this)
|
||||
#else
|
||||
#define OACR_NOT_IMPLEMENTED_MEMBER
|
||||
#endif
|
||||
#endif // OACR_NOT_IMPLEMENTED_MEMBER
|
||||
|
||||
|
||||
#if !defined(OACR_DECLARE_FILLER)
|
||||
#if( defined(OACR) && OACR )
|
||||
#define OACR_DECLARE_FILLER( type, inst ) type __filler##inst;
|
||||
#if (defined(OACR) && OACR)
|
||||
#define OACR_DECLARE_FILLER(type, inst) type __filler##inst;
|
||||
#else
|
||||
#define OACR_DECLARE_FILLER( type, inst )
|
||||
#define OACR_DECLARE_FILLER(type, inst)
|
||||
#endif
|
||||
#endif // OACR_DECLARE_FILLER
|
||||
|
||||
|
||||
// use this macro once you have inspected warnings FUNCTION_NEEDS_REVIEW (25028)
|
||||
#if !defined(OACR_REVIEWED_CALL)
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c void __OACRReviewedCall();
|
||||
#define OACR_REVIEWED_CALL( reviewer, functionCall ) ( __OACRReviewedCall(), functionCall )
|
||||
#define OACR_REVIEWED_CALL(reviewer, functionCall) (__OACRReviewedCall(), functionCall)
|
||||
#else
|
||||
#define OACR_REVIEWED_CALL( reviewer, functionCall ) functionCall
|
||||
#define OACR_REVIEWED_CALL(reviewer, functionCall) functionCall
|
||||
#endif
|
||||
#endif // OACR_REVIEWED_CALL
|
||||
|
||||
|
||||
// use this macro once you have inspected warnings URL_NEEDS_TO_BE_REVIEWED (25085)
|
||||
#if !defined(OACR_REVIEWED_URL)
|
||||
#if( defined(OACR) && OACR )
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c void __OACRReviewedUrl();
|
||||
#define OACR_REVIEWED_URL( reviewer, reviewedUrl ) ( __OACRReviewedUrl(), reviewedUrl )
|
||||
#define OACR_REVIEWED_URL(reviewer, reviewedUrl) (__OACRReviewedUrl(), reviewedUrl)
|
||||
#else
|
||||
#define OACR_REVIEWED_URL( reviewer, reviewedUrl ) reviewedUrl
|
||||
#define OACR_REVIEWED_URL(reviewer, reviewedUrl) reviewedUrl
|
||||
#endif
|
||||
#endif // OACR_REVIEWED_URL
|
||||
|
||||
#if !defined(OACR_DONT_SWAP)
|
||||
// use to suppress warnings MISSING_MEMBER_SWAP (25146) for nonswappable data members like refcounts, critical sections, etc.
|
||||
#if( defined(OACR) && OACR )
|
||||
__extern_c void OACRDontSwap( void* p );
|
||||
#define OACR_DONT_SWAP( m ) OACRDontSwap( &( m ) )
|
||||
// use to suppress warnings MISSING_MEMBER_SWAP (25146) for nonswappable data members like refcounts, critical sections,
|
||||
// etc.
|
||||
#if (defined(OACR) && OACR)
|
||||
__extern_c void OACRDontSwap(void* p);
|
||||
#define OACR_DONT_SWAP(m) OACRDontSwap(&(m))
|
||||
#else
|
||||
#define OACR_DONT_SWAP( m ) __oacr_noop()
|
||||
#define OACR_DONT_SWAP(m) __oacr_noop()
|
||||
#endif
|
||||
#endif // OACR_DONT_SWAP
|
||||
|
||||
#if( defined(OACR) && OACR && defined(_WINDEF_) && 0 )
|
||||
#if (defined(OACR) && OACR && defined(_WINDEF_) && 0)
|
||||
|
||||
// redefine FALSE & TRUE for better HRESULT<->BOOL conversion detection
|
||||
#if defined(FALSE)
|
||||
|
@ -543,11 +527,11 @@ __extern_c void OACRDontSwap( void* p );
|
|||
// p->Foo();
|
||||
|
||||
#if !defined(OACR_WARNING_PUSH)
|
||||
#if( defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS) )
|
||||
#if( 1400 <=_MSC_VER )
|
||||
#define OACR_WARNING_PUSH __pragma ( warning( push ) )
|
||||
#if (defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS))
|
||||
#if (1400 <= _MSC_VER)
|
||||
#define OACR_WARNING_PUSH __pragma(warning(push))
|
||||
#else
|
||||
#define OACR_WARNING_PUSH __pragma ( prefast( push ) )
|
||||
#define OACR_WARNING_PUSH __pragma(prefast(push))
|
||||
#endif
|
||||
#else
|
||||
#define OACR_WARNING_PUSH
|
||||
|
@ -555,11 +539,11 @@ __extern_c void OACRDontSwap( void* p );
|
|||
#endif
|
||||
|
||||
#if !defined(OACR_WARNING_POP)
|
||||
#if( defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS) )
|
||||
#if( 1400 <=_MSC_VER )
|
||||
#define OACR_WARNING_POP __pragma ( warning( pop ) )
|
||||
#if (defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS))
|
||||
#if (1400 <= _MSC_VER)
|
||||
#define OACR_WARNING_POP __pragma(warning(pop))
|
||||
#else
|
||||
#define OACR_WARNING_POP __pragma ( prefast( pop ) )
|
||||
#define OACR_WARNING_POP __pragma(prefast(pop))
|
||||
#endif
|
||||
#else
|
||||
#define OACR_WARNING_POP
|
||||
|
@ -567,38 +551,38 @@ __extern_c void OACRDontSwap( void* p );
|
|||
#endif
|
||||
|
||||
#if !defined(OACR_WARNING_ENABLE)
|
||||
#if( defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS) )
|
||||
#if( 1400 <=_MSC_VER )
|
||||
#define OACR_WARNING_ENABLE( cWarning, comment ) __pragma ( warning( default: __WARNING_##cWarning ) )
|
||||
#if (defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS))
|
||||
#if (1400 <= _MSC_VER)
|
||||
#define OACR_WARNING_ENABLE(cWarning, comment) __pragma(warning(default : __WARNING_##cWarning))
|
||||
#else
|
||||
#define OACR_WARNING_ENABLE( cWarning, comment )
|
||||
#define OACR_WARNING_ENABLE(cWarning, comment)
|
||||
#endif
|
||||
#else
|
||||
#define OACR_WARNING_ENABLE( cWarning, comment )
|
||||
#define OACR_WARNING_ENABLE(cWarning, comment)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(OACR_WARNING_DISABLE)
|
||||
#if( defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS) )
|
||||
#if( 1400 <=_MSC_VER )
|
||||
#define OACR_WARNING_DISABLE( cWarning, comment ) __pragma ( warning( disable: __WARNING_##cWarning ) )
|
||||
#if (defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS))
|
||||
#if (1400 <= _MSC_VER)
|
||||
#define OACR_WARNING_DISABLE(cWarning, comment) __pragma(warning(disable : __WARNING_##cWarning))
|
||||
#else
|
||||
#define OACR_WARNING_DISABLE( cWarning, comment ) __pragma ( prefast( disable: __WARNING_##cWarning, comment ) )
|
||||
#define OACR_WARNING_DISABLE(cWarning, comment) __pragma(prefast(disable : __WARNING_##cWarning, comment))
|
||||
#endif
|
||||
#else
|
||||
#define OACR_WARNING_DISABLE( cWarning, comment )
|
||||
#define OACR_WARNING_DISABLE(cWarning, comment)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(OACR_WARNING_SUPPRESS)
|
||||
#if( defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS) )
|
||||
#if( 1400 <=_MSC_VER )
|
||||
#define OACR_WARNING_SUPPRESS( cWarning, comment ) __pragma ( warning( suppress: __WARNING_##cWarning ) )
|
||||
#if (defined(OACR) && OACR && !defined(OACR_NO_WARNING_MACROS))
|
||||
#if (1400 <= _MSC_VER)
|
||||
#define OACR_WARNING_SUPPRESS(cWarning, comment) __pragma(warning(suppress : __WARNING_##cWarning))
|
||||
#else
|
||||
#define OACR_WARNING_SUPPRESS( cWarning, comment ) __pragma ( prefast( suppress: __WARNING_##cWarning, comment) )
|
||||
#define OACR_WARNING_SUPPRESS(cWarning, comment) __pragma(prefast(suppress : __WARNING_##cWarning, comment))
|
||||
#endif
|
||||
#else
|
||||
#define OACR_WARNING_SUPPRESS( cWarning, comment )
|
||||
#define OACR_WARNING_SUPPRESS(cWarning, comment)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -608,54 +592,53 @@ __extern_c void OACRDontSwap( void* p );
|
|||
// if( g_pRoot == NULL ) // global variable g_pRoot
|
||||
// {
|
||||
// InitRoot();
|
||||
// OACR_ASSUME( NULL != g_pRoot );
|
||||
// OACR_ASSUME( NULL != g_pRoot );
|
||||
// }
|
||||
// g_pRoot->Traverse(); // without OACR_ASSUME this would cause warning 11
|
||||
#if( defined(OACR) && OACR )
|
||||
#define OACR_ASSUME( fCondition ) __assume( fCondition )
|
||||
#if (defined(OACR) && OACR)
|
||||
#define OACR_ASSUME(fCondition) __assume(fCondition)
|
||||
|
||||
#elif defined(Assert)
|
||||
#define OACR_ASSUME( fCondition ) Assert( fCondition )
|
||||
#define OACR_ASSUME(fCondition) Assert(fCondition)
|
||||
|
||||
#else
|
||||
#define OACR_ASSUME( fCondition ) __oacr_noop()
|
||||
#define OACR_ASSUME(fCondition) __oacr_noop()
|
||||
|
||||
#endif
|
||||
|
||||
// macro to tell OACR that a string is null terminated at this point of execution
|
||||
#if( defined(OACR) && OACR && defined(_Post_z_) )
|
||||
__extern_c void __OACRAssumeNullterminated( _Post_z_ const char* sz );
|
||||
#define OACR_ASSUME_NULLTERMINATED( string ) __OACRAssumeNullterminated( (const char*)string )
|
||||
#if (defined(OACR) && OACR && defined(_Post_z_))
|
||||
__extern_c void __OACRAssumeNullterminated(_Post_z_ const char* sz);
|
||||
#define OACR_ASSUME_NULLTERMINATED(string) __OACRAssumeNullterminated((const char*)string)
|
||||
|
||||
#else
|
||||
#define OACR_ASSUME_NULLTERMINATED( string ) __oacr_noop()
|
||||
#define OACR_ASSUME_NULLTERMINATED(string) __oacr_noop()
|
||||
|
||||
#endif
|
||||
|
||||
// macro to tell OACR that a pointer is null valid at this point of execution
|
||||
#if( defined(OACR) && OACR && defined(_Post_valid_) )
|
||||
__extern_c void __OACRAssumeValid( _Post_valid_ const void* pv );
|
||||
#define OACR_ASSUME_VALID( ptr ) __OACRAssumeValid( ptr )
|
||||
#if (defined(OACR) && OACR && defined(_Post_valid_))
|
||||
__extern_c void __OACRAssumeValid(_Post_valid_ const void* pv);
|
||||
#define OACR_ASSUME_VALID(ptr) __OACRAssumeValid(ptr)
|
||||
|
||||
#else
|
||||
#define OACR_ASSUME_VALID( ptr ) __oacr_noop()
|
||||
#define OACR_ASSUME_VALID(ptr) __oacr_noop()
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// macro to tell OACR that a buffer has a certain readable extent at this point of execution
|
||||
// it can be used to silent noisy espX INCORRECT_ANNOTATION warnings
|
||||
#if( defined(OACR) && OACR && defined(_Post_bytecount_) )
|
||||
__extern_c void __OACRAssumeByteCount( _Post_bytecount_(cb) const void* pv, size_t cb );
|
||||
#define OACR_ASSUME_BYTECOUNT( pv, cb ) __OACRAssumeByteCount( pv, cb )
|
||||
#if (defined(OACR) && OACR && defined(_Post_bytecount_))
|
||||
__extern_c void __OACRAssumeByteCount(_Post_bytecount_(cb) const void* pv, size_t cb);
|
||||
#define OACR_ASSUME_BYTECOUNT(pv, cb) __OACRAssumeByteCount(pv, cb)
|
||||
|
||||
#else
|
||||
#define OACR_ASSUME_BYTECOUNT( pv, cb ) __oacr_noop()
|
||||
#define OACR_ASSUME_BYTECOUNT(pv, cb) __oacr_noop()
|
||||
|
||||
#endif
|
||||
|
||||
// macro indicate lambda mayterminate
|
||||
#if( defined(OACR) && OACR && defined(__cplusplus))
|
||||
#if (defined(OACR) && OACR && defined(__cplusplus))
|
||||
void _lambda_noexcept_mayterminate_() noexcept {};
|
||||
#define OACR_LAMBDA_NOEXCEPT_MAYTERMINATE _lambda_noexcept_mayterminate_()
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE _lambda_noexcept_mayterminate_()
|
||||
|
@ -665,7 +648,7 @@ void _lambda_noexcept_mayterminate_() noexcept {};
|
|||
#endif
|
||||
|
||||
// macro indicate mayterminate ignore stl
|
||||
#if( defined(OACR) && OACR && defined(__cplusplus))
|
||||
#if (defined(OACR) && OACR && defined(__cplusplus))
|
||||
void _noexcept_mayterminate_ignore_stl_() noexcept {};
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE_IGNORE_STL _noexcept_mayterminate_ignore_stl_()
|
||||
#else
|
||||
|
@ -673,28 +656,28 @@ void _noexcept_mayterminate_ignore_stl_() noexcept {};
|
|||
#endif
|
||||
|
||||
// macro indicate mayterminate ignore
|
||||
#if( defined(OACR) && OACR && defined(__cplusplus))
|
||||
#if (defined(OACR) && OACR && defined(__cplusplus))
|
||||
__extern_c void _noexcept_mayterminate_ignore_(const char*) noexcept;
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE_IGNORE( funcs ) _noexcept_mayterminate_ignore_(funcs)
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE_IGNORE(funcs) _noexcept_mayterminate_ignore_(funcs)
|
||||
#else
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE_IGNORE( funcs ) __oacr_noop()
|
||||
#define OACR_NOEXCEPT_MAYTERMINATE_IGNORE(funcs) __oacr_noop()
|
||||
#endif
|
||||
|
||||
// OACR custom plugin specific extensions
|
||||
|
||||
//=======================================================================
|
||||
|
||||
#define _Callback_ __oacr_callback
|
||||
#define _Callback_ __oacr_callback
|
||||
|
||||
//======================================================================
|
||||
// OACR custom attributes
|
||||
|
||||
// OACR custom plugin specific extensions
|
||||
|
||||
#if( _USE_DECLSPECS_FOR_SAL )
|
||||
#if (_USE_DECLSPECS_FOR_SAL)
|
||||
|
||||
#ifndef _SA_SPECSTRIZE
|
||||
#define _SA_SPECSTRIZE( x ) #x
|
||||
#define _SA_SPECSTRIZE(x) #x
|
||||
#endif
|
||||
|
||||
__ANNOTATION(_Callback_(void));
|
||||
|
@ -707,8 +690,8 @@ __ANNOTATION(_Intl_deprecated_(void));
|
|||
__ANNOTATION(_Noinference_(void));
|
||||
__ANNOTATION(_Canthrow_(void));
|
||||
__ANNOTATION(_BindReturn_(void));
|
||||
__ANNOTATION(_SA_deprecated_(__AuToQuOtE char *));
|
||||
__ANNOTATION(_Oleo_deprecated_(__AuToQuOtE char *));
|
||||
__ANNOTATION(_SA_deprecated_(__AuToQuOtE char*));
|
||||
__ANNOTATION(_Oleo_deprecated_(__AuToQuOtE char*));
|
||||
__ANNOTATION(_Genericfunctype_(void));
|
||||
__ANNOTATION(__nothrowfunctype(void));
|
||||
__ANNOTATION(_Noheap_(void));
|
||||
|
@ -717,36 +700,38 @@ __ANNOTATION(_Sealed_(void));
|
|||
__ANNOTATION(_Allow_implicit_ctor_(void));
|
||||
__ANNOTATION(_Norefcapture_asynclambda_(void));
|
||||
__ANNOTATION(_Nodecl_lambda_(void));
|
||||
__ANNOTATION(_Noexcept_maythrow_( void ) );
|
||||
__ANNOTATION(_Noexcept_mayterminate_( void ));
|
||||
__ANNOTATION(_Noexcept_mayterminate_ignore_( __AuToQuOtE char * ));
|
||||
__ANNOTATION(_Dont_swap_( void ));
|
||||
__ANNOTATION(_Noexcept_maythrow_(void));
|
||||
__ANNOTATION(_Noexcept_mayterminate_(void));
|
||||
__ANNOTATION(_Noexcept_mayterminate_ignore_(__AuToQuOtE char*));
|
||||
__ANNOTATION(_Dont_swap_(void));
|
||||
|
||||
#define __oacr_callback __declspec("_Callback_")
|
||||
#define __oacr_rpc __declspec("_Rpc_")
|
||||
#define __oacr_sealed_method __declspec("_Sealed_")
|
||||
#define __oacr_memberinitializer __declspec("_Memberinitializer_")
|
||||
#define __oacr_unsafe_string_api __declspec("_Unsafe_string_api_")
|
||||
#define __oacr_needsreview __declspec("_Needsreview_")
|
||||
#define __oacr_intl_deprecated __declspec("_Intl_deprecated_")
|
||||
#define __oacr_noinference __declspec("_Noinference_")
|
||||
#define __oacr_canthrow __declspec("_Canthrow_")
|
||||
#define __oacr_bindReturn __declspec("_BindReturn_")
|
||||
#define __oacr_sa_deprecated(NewFunctionName) __declspec("_SA_deprecated_("_SA_SPECSTRIZE(NewFunctionName)")")
|
||||
#define __oacr_sa_deprecated_staging(NewFunctionName) __declspec("_SA_deprecated_staging_("_SA_SPECSTRIZE(NewFunctionName)")")
|
||||
#define __oacr_oleo_deprecated(NewFunctionName) __declspec("_Oleo_deprecated_("_SA_SPECSTRIZE(NewFunctionName)")")
|
||||
#define __oacr_genericfunctype __declspec("_Genericfunctype_")
|
||||
#define __oacr_nothrowfunctype __declspec("__nothrowfunctype")
|
||||
#define __oacr_noheap __declspec("_Noheap_")
|
||||
#define __oacr_notrunccast __declspec("_Notrunccast_")
|
||||
#define __oacr_sealed_class __declspec("_Sealed_")
|
||||
#define __oacr_allow_implicit_ctor __declspec("_Allow_implicit_ctor_")
|
||||
#define __oacr_norefcapture_asynclambda __declspec("_Norefcapture_asynclambda_")
|
||||
#define __oacr_nodecl_lambda __declspec("_Nodecl_lambda_")
|
||||
#define __oacr_noexcept_maythrow __declspec("_Noexcept_maythrow_")
|
||||
#define __oacr_noexcept_mayterminate __declspec("_Noexcept_mayterminate_")
|
||||
#define __oacr_noexcept_mayterminate_ignore(FunctionName) __declspec("_Noexcept_mayterminate_ignore_("_SA_SPECSTRIZE(FunctionName)")")
|
||||
#define __oacr_dont_swap __declspec("_Dont_swap_")
|
||||
#define __oacr_callback __declspec("_Callback_")
|
||||
#define __oacr_rpc __declspec("_Rpc_")
|
||||
#define __oacr_sealed_method __declspec("_Sealed_")
|
||||
#define __oacr_memberinitializer __declspec("_Memberinitializer_")
|
||||
#define __oacr_unsafe_string_api __declspec("_Unsafe_string_api_")
|
||||
#define __oacr_needsreview __declspec("_Needsreview_")
|
||||
#define __oacr_intl_deprecated __declspec("_Intl_deprecated_")
|
||||
#define __oacr_noinference __declspec("_Noinference_")
|
||||
#define __oacr_canthrow __declspec("_Canthrow_")
|
||||
#define __oacr_bindReturn __declspec("_BindReturn_")
|
||||
#define __oacr_sa_deprecated(NewFunctionName) __declspec("_SA_deprecated_("_SA_SPECSTRIZE(NewFunctionName) ")")
|
||||
#define __oacr_sa_deprecated_staging(NewFunctionName) \
|
||||
__declspec("_SA_deprecated_staging_("_SA_SPECSTRIZE(NewFunctionName) ")")
|
||||
#define __oacr_oleo_deprecated(NewFunctionName) __declspec("_Oleo_deprecated_("_SA_SPECSTRIZE(NewFunctionName) ")")
|
||||
#define __oacr_genericfunctype __declspec("_Genericfunctype_")
|
||||
#define __oacr_nothrowfunctype __declspec("__nothrowfunctype")
|
||||
#define __oacr_noheap __declspec("_Noheap_")
|
||||
#define __oacr_notrunccast __declspec("_Notrunccast_")
|
||||
#define __oacr_sealed_class __declspec("_Sealed_")
|
||||
#define __oacr_allow_implicit_ctor __declspec("_Allow_implicit_ctor_")
|
||||
#define __oacr_norefcapture_asynclambda __declspec("_Norefcapture_asynclambda_")
|
||||
#define __oacr_nodecl_lambda __declspec("_Nodecl_lambda_")
|
||||
#define __oacr_noexcept_maythrow __declspec("_Noexcept_maythrow_")
|
||||
#define __oacr_noexcept_mayterminate __declspec("_Noexcept_mayterminate_")
|
||||
#define __oacr_noexcept_mayterminate_ignore(FunctionName) \
|
||||
__declspec("_Noexcept_mayterminate_ignore_("_SA_SPECSTRIZE(FunctionName) ")")
|
||||
#define __oacr_dont_swap __declspec("_Dont_swap_")
|
||||
|
||||
__extern_c int __RequireNoThrow();
|
||||
#define __oacr_requireNoThrow __RequireNoThrow();
|
||||
|
|
|
@ -25,207 +25,349 @@
|
|||
|
||||
// Annotations for _t (pascal string) and _tz (nullterminated pascal string)
|
||||
|
||||
#define _In_t_ _Pre_t_ _Deref_pre_readonly_
|
||||
#define _In_opt_t_ _Pre_opt_t_ _Deref_pre_readonly_
|
||||
#define _In_tz_ _Pre_tz_ _Deref_pre_readonly_
|
||||
#define _In_opt_tz_ _Pre_opt_tz_ _Deref_pre_readonly_
|
||||
#define _In_t_ _Pre_t_ _Deref_pre_readonly_
|
||||
#define _In_opt_t_ _Pre_opt_t_ _Deref_pre_readonly_
|
||||
#define _In_tz_ _Pre_tz_ _Deref_pre_readonly_
|
||||
#define _In_opt_tz_ _Pre_opt_tz_ _Deref_pre_readonly_
|
||||
|
||||
#define _Out_t_cap_(size) _Pre_cap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_(size) _Pre_opt_cap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_(size) _Pre_bytecap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_(size) _Pre_opt_bytecap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_(size) _Pre_cap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_(size) _Pre_opt_cap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_(size) _Pre_bytecap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_(size) _Pre_opt_bytecap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_t_cap_(size) _Pre_cap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_(size) _Pre_opt_cap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_(size) _Pre_bytecap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_(size) _Pre_opt_bytecap_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_(size) _Pre_cap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_(size) _Pre_opt_cap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_(size) _Pre_bytecap_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_(size) _Pre_opt_bytecap_(size) _Pre_invalid_ _Post_tz_
|
||||
|
||||
#define _Out_t_cap_c_(size) _Pre_cap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_c_(size) _Pre_opt_cap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_c_(size) _Pre_bytecap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_c_(size) _Pre_opt_bytecap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_c_(size) _Pre_cap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_c_(size) _Pre_opt_cap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_c_(size) _Pre_bytecap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_c_(size) _Pre_opt_bytecap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_t_cap_c_(size) _Pre_cap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_c_(size) _Pre_opt_cap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_c_(size) _Pre_bytecap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_c_(size) _Pre_opt_bytecap_c_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_c_(size) _Pre_cap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_c_(size) _Pre_opt_cap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_c_(size) _Pre_bytecap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_c_(size) _Pre_opt_bytecap_c_(size) _Pre_invalid_ _Post_tz_
|
||||
|
||||
#define _Out_t_cap_x_(size) _Pre_cap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_x_(size) _Pre_opt_cap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_x_(size) _Pre_bytecap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_x_(size) _Pre_opt_bytecap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_x_(size) _Pre_cap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_x_(size) _Pre_opt_cap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_x_(size) _Pre_bytecap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_x_(size) _Pre_opt_bytecap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_t_cap_x_(size) _Pre_cap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_cap_x_(size) _Pre_opt_cap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_t_bytecap_x_(size) _Pre_bytecap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_opt_t_bytecap_x_(size) _Pre_opt_bytecap_x_(size) _Pre_invalid_ _Post_t_
|
||||
#define _Out_tz_cap_x_(size) _Pre_cap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_cap_x_(size) _Pre_opt_cap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_tz_bytecap_x_(size) _Pre_bytecap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
#define _Out_opt_tz_bytecap_x_(size) _Pre_opt_bytecap_x_(size) _Pre_invalid_ _Post_tz_
|
||||
|
||||
#define _Out_tz_cap_post_count_(cap,count) _Pre_cap_(cap) _Pre_invalid_ _Post_tz_count_(count)
|
||||
#define _Out_opt_tz_cap_post_count_(cap,count) _Pre_opt_cap_(cap) _Pre_invalid_ _Post_tz_count_(count)
|
||||
#define _Out_tz_bytecap_post_bytecount_(cap,count) _Pre_bytecap_(cap) _Pre_invalid_ _Post_tz_bytecount_(count)
|
||||
#define _Out_opt_tz_bytecap_post_bytecount_(cap,count) _Pre_opt_bytecap_(cap) _Pre_invalid_ _Post_tz_bytecount_(count)
|
||||
#define _Out_tz_cap_post_count_(cap, count) _Pre_cap_(cap) _Pre_invalid_ _Post_tz_count_(count)
|
||||
#define _Out_opt_tz_cap_post_count_(cap, count) _Pre_opt_cap_(cap) _Pre_invalid_ _Post_tz_count_(count)
|
||||
#define _Out_tz_bytecap_post_bytecount_(cap, count) _Pre_bytecap_(cap) _Pre_invalid_ _Post_tz_bytecount_(count)
|
||||
#define _Out_opt_tz_bytecap_post_bytecount_(cap, count) _Pre_opt_bytecap_(cap) _Pre_invalid_ _Post_tz_bytecount_(count)
|
||||
|
||||
#define _Out_tz_capcount_(capcount) _Pre_cap_(capcount) _Pre_invalid_ _Post_tz_count_(capcount)
|
||||
#define _Out_opt_tz_capcount_(capcount) _Pre_opt_cap_(capcount) _Pre_invalid_ _Post_tz_count_(capcount)
|
||||
#define _Out_tz_bytecapcount_(capcount) _Pre_bytecap_(capcount) _Pre_invalid_ _Post_tz_bytecount_(capcount)
|
||||
#define _Out_opt_tz_bytecapcount_(capcount) _Pre_opt_bytecap_(capcount) _Pre_invalid_ _Post_tz_bytecount_(capcount)
|
||||
#define _Out_tz_capcount_(capcount) _Pre_cap_(capcount) _Pre_invalid_ _Post_tz_count_(capcount)
|
||||
#define _Out_opt_tz_capcount_(capcount) _Pre_opt_cap_(capcount) _Pre_invalid_ _Post_tz_count_(capcount)
|
||||
#define _Out_tz_bytecapcount_(capcount) _Pre_bytecap_(capcount) _Pre_invalid_ _Post_tz_bytecount_(capcount)
|
||||
#define _Out_opt_tz_bytecapcount_(capcount) _Pre_opt_bytecap_(capcount) _Pre_invalid_ _Post_tz_bytecount_(capcount)
|
||||
|
||||
#define _Inout_t_ _Prepost_t_
|
||||
#define _Inout_opt_t_ _Prepost_opt_t_
|
||||
#define _Inout_tz_ _Prepost_tz_
|
||||
#define _Inout_opt_tz_ _Prepost_opt_tz_
|
||||
#define _Inout_t_ _Prepost_t_
|
||||
#define _Inout_opt_t_ _Prepost_opt_t_
|
||||
#define _Inout_tz_ _Prepost_tz_
|
||||
#define _Inout_opt_tz_ _Prepost_opt_tz_
|
||||
|
||||
#define _Inout_t_cap_(size) _Pre_t_cap_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_(size) _Pre_opt_t_cap_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_(size) _Pre_t_bytecap_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_(size) _Pre_opt_t_bytecap_(size) _Post_t_
|
||||
#define _Inout_tz_cap_(size) _Pre_tz_cap_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_(size) _Pre_opt_tz_cap_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_(size) _Pre_tz_bytecap_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_(size) _Pre_opt_tz_bytecap_(size) _Post_tz_
|
||||
#define _Inout_t_cap_(size) _Pre_t_cap_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_(size) _Pre_opt_t_cap_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_(size) _Pre_t_bytecap_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_(size) _Pre_opt_t_bytecap_(size) _Post_t_
|
||||
#define _Inout_tz_cap_(size) _Pre_tz_cap_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_(size) _Pre_opt_tz_cap_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_(size) _Pre_tz_bytecap_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_(size) _Pre_opt_tz_bytecap_(size) _Post_tz_
|
||||
|
||||
#define _Inout_t_cap_c_(size) _Pre_t_cap_c_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_c_(size) _Pre_opt_t_cap_c_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_c_(size) _Pre_t_bytecap_c_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_c_(size) _Pre_opt_t_bytecap_c_(size) _Post_t_
|
||||
#define _Inout_tz_cap_c_(size) _Pre_tz_cap_c_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_c_(size) _Pre_opt_tz_cap_c_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_c_(size) _Pre_tz_bytecap_c_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_c_(size) _Pre_opt_tz_bytecap_c_(size) _Post_tz_
|
||||
#define _Inout_t_cap_c_(size) _Pre_t_cap_c_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_c_(size) _Pre_opt_t_cap_c_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_c_(size) _Pre_t_bytecap_c_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_c_(size) _Pre_opt_t_bytecap_c_(size) _Post_t_
|
||||
#define _Inout_tz_cap_c_(size) _Pre_tz_cap_c_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_c_(size) _Pre_opt_tz_cap_c_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_c_(size) _Pre_tz_bytecap_c_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_c_(size) _Pre_opt_tz_bytecap_c_(size) _Post_tz_
|
||||
|
||||
#define _Inout_t_cap_x_(size) _Pre_t_cap_x_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_x_(size) _Pre_opt_t_cap_x_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_x_(size) _Pre_t_bytecap_x_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_x_(size) _Pre_opt_t_bytecap_x_(size) _Post_t_
|
||||
#define _Inout_tz_cap_x_(size) _Pre_tz_cap_x_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_x_(size) _Pre_opt_tz_cap_x_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_x_(size) _Pre_tz_bytecap_x_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_x_(size) _Pre_opt_tz_bytecap_x_(size) _Post_tz_
|
||||
#define _Inout_t_cap_x_(size) _Pre_t_cap_x_(size) _Post_t_
|
||||
#define _Inout_opt_t_cap_x_(size) _Pre_opt_t_cap_x_(size) _Post_t_
|
||||
#define _Inout_t_bytecap_x_(size) _Pre_t_bytecap_x_(size) _Post_t_
|
||||
#define _Inout_opt_t_bytecap_x_(size) _Pre_opt_t_bytecap_x_(size) _Post_t_
|
||||
#define _Inout_tz_cap_x_(size) _Pre_tz_cap_x_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_cap_x_(size) _Pre_opt_tz_cap_x_(size) _Post_tz_
|
||||
#define _Inout_tz_bytecap_x_(size) _Pre_tz_bytecap_x_(size) _Post_tz_
|
||||
#define _Inout_opt_tz_bytecap_x_(size) _Pre_opt_tz_bytecap_x_(size) _Post_tz_
|
||||
|
||||
#define _Ret_t_ _Ret2_impl_(__notnull_impl, __count_x_impl([0])) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTStr)
|
||||
#define _Ret_opt_t_ _Ret2_impl_(__maybenull_impl,__count_x_impl([0])) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTStr)
|
||||
#define _Ret_tz_ _Ret2_impl_(__notnull_impl, __zterm_impl) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTZStr)
|
||||
#define _Ret_opt_tz_ _Ret2_impl_(__maybenull_impl,__zterm_impl) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTZStr)
|
||||
#define _Ret_t_ _Ret2_impl_(__notnull_impl, __count_x_impl([0])) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTStr)
|
||||
#define _Ret_opt_t_ _Ret2_impl_(__maybenull_impl, __count_x_impl([0])) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTStr)
|
||||
#define _Ret_tz_ _Ret2_impl_(__notnull_impl, __zterm_impl) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTZStr)
|
||||
#define _Ret_opt_tz_ _Ret2_impl_(__maybenull_impl, __zterm_impl) _Ret_valid_impl_ _$RET_STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Pre_t_ _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_ _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_ _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_ _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_t_ \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_ \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_ _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_ \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Pre_t_cap_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_t_cap_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Pre_t_cap_c_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_c_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_c_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_c_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_c_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_c_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_c_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_c_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_t_cap_c_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_c_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_c_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_c_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_c_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_c_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_c_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_c_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Pre_t_cap_x_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_x_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_x_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_x_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_x_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_x_impl(_csize)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_x_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_x_(size) _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_x_(size) _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_t_cap_x_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_cap_x_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_t_bytecap_x_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_opt_t_bytecap_x_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTStr)
|
||||
#define _Pre_tz_cap_x_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_x_impl(_csize)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_cap_x_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_tz_bytecap_x_(size) \
|
||||
_Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
#define _Pre_opt_tz_bytecap_x_(size) \
|
||||
_Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl, __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Post_t_ _Post1_impl_(__count_x_impl([0])) _Post_valid_impl_ _$STR_TYPE(_$postTStr)
|
||||
#define _Post_tz_ _Post1_impl_(__zterm_impl) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_t_ _Post1_impl_(__count_x_impl([0])) _Post_valid_impl_ _$STR_TYPE(_$postTStr)
|
||||
#define _Post_tz_ _Post1_impl_(__zterm_impl) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
|
||||
// _Post_t_count family not possible, conflicting WritableElements property
|
||||
// _Post_t_count family not possible, conflicting WritableElements property
|
||||
|
||||
#define _Post_tz_count_(size) _Post2_impl_(__zterm_impl,__count_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_(size) _Post2_impl_(__zterm_impl,__bytecount_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_count_c_(size) _Post2_impl_(__zterm_impl,__count_c_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_c_(size) _Post2_impl_(__zterm_impl,__bytecount_c_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_count_x_(size) _Post2_impl_(__zterm_impl,__count_x_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_x_(size) _Post2_impl_(__zterm_impl,__bytecount_impl_x(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_count_(size) _Post2_impl_(__zterm_impl, __count_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_(size) \
|
||||
_Post2_impl_(__zterm_impl, __bytecount_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_count_c_(size) \
|
||||
_Post2_impl_(__zterm_impl, __count_c_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_c_(size) \
|
||||
_Post2_impl_(__zterm_impl, __bytecount_c_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_count_x_(size) \
|
||||
_Post2_impl_(__zterm_impl, __count_x_impl(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
#define _Post_tz_bytecount_x_(size) \
|
||||
_Post2_impl_(__zterm_impl, __bytecount_impl_x(size)) _Post_valid_impl_ _$STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Deref_out_t_ _Out_ _Deref_post_t_
|
||||
#define _Deref_out_opt_t_ _Out_ _Deref_post_opt_t_
|
||||
#define _Deref_out_tz_ _Out_ _Deref_post_tz_
|
||||
#define _Deref_out_opt_tz_ _Out_ _Deref_post_opt_tz_
|
||||
#define _Deref_out_t_ _Out_ _Deref_post_t_
|
||||
#define _Deref_out_opt_t_ _Out_ _Deref_post_opt_t_
|
||||
#define _Deref_out_tz_ _Out_ _Deref_post_tz_
|
||||
#define _Deref_out_opt_tz_ _Out_ _Deref_post_opt_tz_
|
||||
|
||||
#define _Deref_pre_t_ _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_ _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__count_x_impl([0])) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_ _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_ _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_t_ \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__count_x_impl([0])) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_ \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__count_x_impl([0])) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_ \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__zterm_impl) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_ \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__zterm_impl) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Deref_pre_t_cap_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_t_cap_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Deref_pre_t_cap_c_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_c_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_c_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_c_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_c_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_c_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_c_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_c_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_t_cap_c_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_c_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_c_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_c_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_c_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_c_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_c_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_c_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_c_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Deref_pre_t_cap_x_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_x_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_x_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_x_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_x_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_x_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_x_(size) _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_x_(size) _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_t_cap_x_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_cap_x_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_t_bytecap_x_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_opt_t_bytecap_x_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTStr)
|
||||
#define _Deref_pre_tz_cap_x_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_cap_x_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __cap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_tz_bytecap_x_(size) \
|
||||
_Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
#define _Deref_pre_opt_tz_bytecap_x_(size) \
|
||||
_Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl, __bytecap_x_impl(size)) \
|
||||
_Pre_valid_impl_ _$DEREF_STR_TYPE(_$preTZStr)
|
||||
|
||||
#define _Deref_post_t_ _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__count_x_impl([0])) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_ _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__count_x_impl([0])) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_tz_ _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__zterm_impl) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_ _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__zterm_impl) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_t_ \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__count_x_impl([0])) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_ \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__count_x_impl([0])) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_tz_ \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__zterm_impl) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_ \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__zterm_impl) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Deref_post_t_cap_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_cap_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_tz_cap_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_cap_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_tz_bytecap_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_bytecap_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Deref_post_t_cap_c_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_c_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_c_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_c_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_cap_c_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_c_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_c_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_c_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_c_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_c_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_tz_cap_c_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_cap_c_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_tz_bytecap_c_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_bytecap_c_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_c_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Deref_post_t_cap_x_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_x_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__cap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_x_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_x_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]),__bytecap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_cap_x_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_cap_x_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __cap_x_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_t_bytecap_x_(size) \
|
||||
_Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_opt_t_bytecap_x_(size) \
|
||||
_Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__count_x_impl([0]), __bytecap_x_impl(size)) \
|
||||
_Post_valid_impl_ _$DEREF_STR_TYPE(_$postTStr)
|
||||
#define _Deref_post_tz_cap_x_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_cap_x_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__cap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_tz_bytecap_x_(size) _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
#define _Deref_post_opt_tz_bytecap_x_(size) _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl),__bytecap_x_impl(size)) _Post_valid_impl_ _$DEREF_STR_TYPE(_$postTZStr)
|
||||
|
||||
#define _Prepost_t_ _Pre_t_ _Post_t_
|
||||
#define _Prepost_opt_t_ _Pre_opt_t_ _Post_t_
|
||||
#define _Prepost_tz_ _Pre_tz_ _Post_tz_
|
||||
#define _Prepost_opt_tz_ _Pre_opt_tz_ _Post_tz_
|
||||
#define _Prepost_t_ _Pre_t_ _Post_t_
|
||||
#define _Prepost_opt_t_ _Pre_opt_t_ _Post_t_
|
||||
#define _Prepost_tz_ _Pre_tz_ _Post_tz_
|
||||
#define _Prepost_opt_tz_ _Pre_opt_tz_ _Post_tz_
|
||||
|
||||
#define _Deref_prepost_t_ _Deref_pre_t_ _Deref_post_t_
|
||||
#define _Deref_prepost_opt_t_ _Deref_pre_opt_t_ _Deref_post_opt_t_
|
||||
#define _Deref_prepost_tz_ _Deref_pre_tz_ _Deref_post_tz_
|
||||
#define _Deref_prepost_opt_tz_ _Deref_pre_opt_tz_ _Deref_post_opt_tz_
|
||||
#define _Deref_prepost_t_ _Deref_pre_t_ _Deref_post_t_
|
||||
#define _Deref_prepost_opt_t_ _Deref_pre_opt_t_ _Deref_post_opt_t_
|
||||
#define _Deref_prepost_tz_ _Deref_pre_tz_ _Deref_post_tz_
|
||||
#define _Deref_prepost_opt_tz_ _Deref_pre_opt_tz_ _Deref_post_opt_tz_
|
||||
|
||||
// These are stronger than the related _opt_ annotations, in that the buffer pointer must be non-null if and only if
|
||||
// the associated count parameter is non-zero. For example:
|
||||
|
@ -243,9 +385,11 @@
|
|||
//
|
||||
// Read( rgBooks, 0 );
|
||||
//
|
||||
#define _In_reads_or_null_if_zero_(size) _In_reads_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
#define _Out_writes_or_null_if_zero_(size) _Out_writes_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
#define _Inout_updates_or_null_if_zero_(size) _Inout_updates_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
#define _In_reads_or_null_if_zero_(size) _In_reads_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
#define _Out_writes_or_null_if_zero_(size) \
|
||||
_Out_writes_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
#define _Inout_updates_or_null_if_zero_(size) \
|
||||
_Inout_updates_(size) _When_(0 == size, _Pre_null_) _When_(0 < size, _Pre_notnull_)
|
||||
|
||||
#define _$STR_TYPE(type)
|
||||
#define _$RET_STR_TYPE(type)
|
||||
|
@ -264,5 +408,5 @@
|
|||
#endif
|
||||
|
||||
#if !defined(OACR_CAPTURE_FIXUP)
|
||||
#define OACR_CAPTURE_FIXUP(tag,match,replace) _SA_annotes3(SAL_OACRCapture, tag_FIXUP, match, replace)
|
||||
#define OACR_CAPTURE_FIXUP(tag, match, replace) _SA_annotes3(SAL_OACRCapture, tag_FIXUP, match, replace)
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#define _Ret_z_
|
||||
#define _Ret_maybenull_
|
||||
#define _Ret_notnull_
|
||||
#define _Ret_range_(x,y)
|
||||
#define _Ret_range_(x, y)
|
||||
#define _Post_writable_byte_size_(x)
|
||||
#define _Deref_pre_valid_
|
||||
#define _Deref_post_valid_
|
||||
|
@ -35,7 +35,7 @@
|
|||
#define _Printf_format_string_
|
||||
#define _Use_decl_annotations_
|
||||
#define _Null_terminated_
|
||||
#define _Out_cap_post_count_(x,y)
|
||||
#define _Out_cap_post_count_(x, y)
|
||||
#define _Out_cap_(x)
|
||||
#define UNREFERENCED_PARAMETER(x)
|
||||
#define _In_reads_bytes_(x)
|
||||
|
|
|
@ -24,7 +24,8 @@ share the same reference counter.
|
|||
//
|
||||
// Mso::FixedSwarmBase does only one memory allocation for all objects in the swarm.
|
||||
// It is very compact: on 64 bit platform the size is 16 bytes for ObjectWeakRef
|
||||
// (8 bytes v-table + 4 bytes ref count + 4 bytes weak ref count) and one pointer for each swarm member to point to the ObjectWeakRef.
|
||||
// (8 bytes v-table + 4 bytes ref count + 4 bytes weak ref count) and one pointer for each swarm member to point to the
|
||||
// ObjectWeakRef.
|
||||
//
|
||||
// To create an Mso::FixedSwarmBase use Mso::Make method..
|
||||
// To create a swarm member call MakeMember() instance methods.
|
||||
|
@ -40,7 +41,7 @@ namespace Mso {
|
|||
namespace Details {
|
||||
|
||||
/**
|
||||
Storage for FixedSwarmBase class. In some sense this is a specialized std::tuple.
|
||||
Storage for FixedSwarmBase class. In some sense this is a specialized std::tuple.
|
||||
*/
|
||||
template <typename... Ts>
|
||||
struct FixedSwarmStorage;
|
||||
|
@ -48,39 +49,39 @@ struct FixedSwarmStorage;
|
|||
template <>
|
||||
struct FixedSwarmStorage<>
|
||||
{
|
||||
void DestroyObject() const noexcept
|
||||
{
|
||||
// Do nothing because there is no storage in this class
|
||||
UNREFERENCED_OACR(this);
|
||||
}
|
||||
void DestroyObject() const noexcept
|
||||
{
|
||||
// Do nothing because there is no storage in this class
|
||||
UNREFERENCED_OACR(this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T0, typename... Ts>
|
||||
struct FixedSwarmStorage<T0, Ts...> : public FixedSwarmStorage<Ts...>
|
||||
{
|
||||
using Super = FixedSwarmStorage<Ts...>;
|
||||
using Super = FixedSwarmStorage<Ts...>;
|
||||
|
||||
FixedSwarmStorage() noexcept
|
||||
{
|
||||
Mso::Details::SetWeakRef(&m_memberStorage, nullptr);
|
||||
}
|
||||
FixedSwarmStorage() noexcept
|
||||
{
|
||||
Mso::Details::SetWeakRef(&m_memberStorage, nullptr);
|
||||
}
|
||||
|
||||
void DestroyObject() const noexcept
|
||||
{
|
||||
if (Mso::Details::GetWeakRef(&m_memberStorage))
|
||||
{
|
||||
T0::RefCountPolicy::Deleter::template Delete(static_cast<typename T0::TypeToDelete*>((void*) &m_memberStorage));
|
||||
}
|
||||
void DestroyObject() const noexcept
|
||||
{
|
||||
if (Mso::Details::GetWeakRef(&m_memberStorage))
|
||||
{
|
||||
T0::RefCountPolicy::Deleter::template Delete(static_cast<typename T0::TypeToDelete*>((void*)&m_memberStorage));
|
||||
}
|
||||
|
||||
Super::DestroyObject();
|
||||
}
|
||||
Super::DestroyObject();
|
||||
}
|
||||
|
||||
ObjectWeakRef* m_weakRefPlaceholder; // reserves negative space before m_memberStorage.
|
||||
typename std::aligned_storage<sizeof(T0), std::alignment_of<T0>::value>::type m_memberStorage;
|
||||
ObjectWeakRef* m_weakRefPlaceholder; // reserves negative space before m_memberStorage.
|
||||
typename std::aligned_storage<sizeof(T0), std::alignment_of<T0>::value>::type m_memberStorage;
|
||||
};
|
||||
|
||||
/**
|
||||
A helper type to access FixedSwarmStorage member by index similar to std::tuple.
|
||||
A helper type to access FixedSwarmStorage member by index similar to std::tuple.
|
||||
*/
|
||||
template <size_t Index, typename Storage>
|
||||
struct FixedSwarmStorageMember;
|
||||
|
@ -88,13 +89,13 @@ struct FixedSwarmStorageMember;
|
|||
template <typename T, typename... Ts>
|
||||
struct FixedSwarmStorageMember<0, FixedSwarmStorage<T, Ts...>>
|
||||
{
|
||||
using Type = T;
|
||||
using StorageType = FixedSwarmStorage<T, Ts...>;
|
||||
using Type = T;
|
||||
using StorageType = FixedSwarmStorage<T, Ts...>;
|
||||
};
|
||||
|
||||
template <size_t Index, typename T, typename... Ts>
|
||||
struct FixedSwarmStorageMember<Index, FixedSwarmStorage<T, Ts...>>
|
||||
: public FixedSwarmStorageMember<Index - 1, FixedSwarmStorage<Ts...>>
|
||||
: public FixedSwarmStorageMember<Index - 1, FixedSwarmStorage<Ts...>>
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -103,123 +104,123 @@ struct FixedSwarmStorageMember<Index, FixedSwarmStorage<T, Ts...>>
|
|||
template <typename T, typename TContainer>
|
||||
struct FixedSwarmMemberMemoryGuard
|
||||
{
|
||||
using Type = T;
|
||||
using Type = T;
|
||||
|
||||
~FixedSwarmMemberMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
Container->ReleaseWeakRef();
|
||||
Mso::Details::SetWeakRef(ObjMemory, nullptr);
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialize method failed.
|
||||
T::RefCountPolicy::Deleter::template Delete(static_cast<typename T::TypeToDelete*>(Obj));
|
||||
Mso::Details::SetWeakRef(Obj, nullptr);
|
||||
}
|
||||
}
|
||||
~FixedSwarmMemberMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
Container->ReleaseWeakRef();
|
||||
Mso::Details::SetWeakRef(ObjMemory, nullptr);
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialize method failed.
|
||||
T::RefCountPolicy::Deleter::template Delete(static_cast<typename T::TypeToDelete*>(Obj));
|
||||
Mso::Details::SetWeakRef(Obj, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
public: // We use a public fields to reduce number of generated methods.
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
TContainer* Container;
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates
|
||||
// incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
TContainer* Container;
|
||||
};
|
||||
|
||||
/**
|
||||
FixedSwarmBase is a fixed size swarm which knows all its member type at the creation.
|
||||
It is like an std::tuple.
|
||||
By knowing all types upfront we can make the most compact representation of the swarm, and be very flexible about
|
||||
swarm member types because we know their type in swarm.
|
||||
Members can be IUnknown, IRefCounted, or object without v-table with AddRef() and Release() methods.
|
||||
The only requirement is that the members must support the weak ref count where pointer to ObjectWeakRef is in
|
||||
the nearest negative offset from the object.
|
||||
When FixedSwarmBase ref count goes to zero it destroys all objects in the Swarm.
|
||||
FixedSwarmBase is a fixed size swarm which knows all its member type at the creation.
|
||||
It is like an std::tuple.
|
||||
By knowing all types upfront we can make the most compact representation of the swarm, and be very flexible about
|
||||
swarm member types because we know their type in swarm.
|
||||
Members can be IUnknown, IRefCounted, or object without v-table with AddRef() and Release() methods.
|
||||
The only requirement is that the members must support the weak ref count where pointer to ObjectWeakRef is in
|
||||
the nearest negative offset from the object.
|
||||
When FixedSwarmBase ref count goes to zero it destroys all objects in the Swarm.
|
||||
*/
|
||||
template <typename TBase, typename... Ts>
|
||||
class FixedSwarmBase
|
||||
: public TBase
|
||||
class FixedSwarmBase : public TBase
|
||||
{
|
||||
static_assert(std::is_base_of<ObjectWeakRef, TBase>::value, "TBase must be inherited from ObjectWeakRef.");
|
||||
static_assert(std::is_base_of<ObjectWeakRef, TBase>::value, "TBase must be inherited from ObjectWeakRef.");
|
||||
|
||||
using Super = TBase;
|
||||
using StorageType = Details::FixedSwarmStorage<Ts...>;
|
||||
template <size_t Index> using MemberType = typename Details::FixedSwarmStorageMember<Index, StorageType>::Type;
|
||||
template <size_t Index> using MemberStorageType = typename Details::FixedSwarmStorageMember<Index, StorageType>::StorageType;
|
||||
using Super = TBase;
|
||||
using StorageType = Details::FixedSwarmStorage<Ts...>;
|
||||
template <size_t Index>
|
||||
using MemberType = typename Details::FixedSwarmStorageMember<Index, StorageType>::Type;
|
||||
template <size_t Index>
|
||||
using MemberStorageType = typename Details::FixedSwarmStorageMember<Index, StorageType>::StorageType;
|
||||
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
|
||||
FixedSwarmBase() noexcept
|
||||
: Super()
|
||||
, m_storage()
|
||||
{
|
||||
}
|
||||
FixedSwarmBase() noexcept : Super(), m_storage() {}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FixedSwarmBase);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(FixedSwarmBase);
|
||||
|
||||
template <size_t Index>
|
||||
MemberType<Index>* Get() const noexcept
|
||||
{
|
||||
void* item = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
if (Mso::Details::GetWeakRef(item))
|
||||
{
|
||||
return static_cast<MemberType<Index>*>(item);
|
||||
}
|
||||
template <size_t Index>
|
||||
MemberType<Index>* Get() const noexcept
|
||||
{
|
||||
void* item = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
if (Mso::Details::GetWeakRef(item))
|
||||
{
|
||||
return static_cast<MemberType<Index>*>(item);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <size_t Index, typename TResult = MemberType<Index>>
|
||||
Mso::WeakPtr<TResult> GetWeakPtr() const noexcept
|
||||
{
|
||||
Mso::WeakPtr<TResult> result;
|
||||
template <size_t Index, typename TResult = MemberType<Index>>
|
||||
Mso::WeakPtr<TResult> GetWeakPtr() const noexcept
|
||||
{
|
||||
Mso::WeakPtr<TResult> result;
|
||||
|
||||
void* member = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
if (Mso::Details::GetWeakRef(member))
|
||||
{
|
||||
result = Mso::WeakPtr<TResult>(reinterpret_cast<MemberType<Index>*>(member), const_cast<ObjectWeakRef*>(static_cast<const ObjectWeakRef*>(this)));
|
||||
}
|
||||
void* member = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
if (Mso::Details::GetWeakRef(member))
|
||||
{
|
||||
result = Mso::WeakPtr<TResult>(
|
||||
reinterpret_cast<MemberType<Index>*>(member),
|
||||
const_cast<ObjectWeakRef*>(static_cast<const ObjectWeakRef*>(this)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <size_t Index, typename... TArgs>
|
||||
void MakeMember(TArgs&&... args) noexcept(MemberType<Index>::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using TMember = MemberType<Index>;
|
||||
template <size_t Index, typename... TArgs>
|
||||
void MakeMember(TArgs&&... args) noexcept(MemberType<Index>::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using TMember = MemberType<Index>;
|
||||
|
||||
void* memberMemory = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
void* memberMemory = &static_cast<MemberStorageType<Index>*>(const_cast<StorageType*>(&m_storage))->m_memberStorage;
|
||||
|
||||
// Initialize weak reference for the member
|
||||
Mso::Details::SetWeakRef(memberMemory, this);
|
||||
Super::AddWeakRef(); // To match ReleaseWeakRef() call in member destructor.
|
||||
// Initialize weak reference for the member
|
||||
Mso::Details::SetWeakRef(memberMemory, this);
|
||||
Super::AddWeakRef(); // To match ReleaseWeakRef() call in member destructor.
|
||||
|
||||
FixedSwarmMemberMemoryGuard<TMember, FixedSwarmBase> memoryGuard = { memberMemory, nullptr, this };
|
||||
TMember::MakePolicy::template Make<TMember>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
FixedSwarmMemberMemoryGuard<TMember, FixedSwarmBase> memoryGuard = {memberMemory, nullptr, this};
|
||||
TMember::MakePolicy::template Make<TMember>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
|
||||
Debug(TMember::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
Debug(TMember::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
|
||||
protected:
|
||||
using FixedSwarmType = FixedSwarmBase;
|
||||
using FixedSwarmType = FixedSwarmBase;
|
||||
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
m_storage.DestroyObject();
|
||||
Super::ReleaseWeakRef();
|
||||
}
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
m_storage.DestroyObject();
|
||||
Super::ReleaseWeakRef();
|
||||
}
|
||||
|
||||
virtual void DestroyContainer() const noexcept override
|
||||
{
|
||||
this->~FixedSwarmBase();
|
||||
Super::RefCountPolicy::Allocator::Deallocate(const_cast<FixedSwarmBase*>(this));
|
||||
}
|
||||
virtual void DestroyContainer() const noexcept override
|
||||
{
|
||||
this->~FixedSwarmBase();
|
||||
Super::RefCountPolicy::Allocator::Deallocate(const_cast<FixedSwarmBase*>(this));
|
||||
}
|
||||
|
||||
private:
|
||||
StorageType m_storage;
|
||||
StorageType m_storage;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
|
|
|
@ -35,63 +35,64 @@ namespace Mso { namespace Object {
|
|||
template <class TObject, class TInterface = TObject>
|
||||
class LazyInit final
|
||||
{
|
||||
static_assert(std::is_base_of<Mso::IRefCounted, TInterface>::value || std::is_base_of<IUnknown, TInterface>::value,
|
||||
"The type must be derived from Mso::IRefCounted or IUnknown.");
|
||||
static_assert(
|
||||
std::is_base_of<Mso::IRefCounted, TInterface>::value || std::is_base_of<IUnknown, TInterface>::value,
|
||||
"The type must be derived from Mso::IRefCounted or IUnknown.");
|
||||
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(LazyInit);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(LazyInit);
|
||||
|
||||
explicit LazyInit() noexcept = default;
|
||||
explicit LazyInit() noexcept = default;
|
||||
|
||||
~LazyInit() noexcept
|
||||
{
|
||||
Release();
|
||||
}
|
||||
~LazyInit() noexcept
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
template <class... TArgs>
|
||||
TInterface& Get(TArgs&&... args) noexcept
|
||||
{
|
||||
bool unused{ false };
|
||||
return Get(unused, std::forward<TArgs>(args)...);
|
||||
}
|
||||
template <class... TArgs>
|
||||
TInterface& Get(TArgs&&... args) noexcept
|
||||
{
|
||||
bool unused{false};
|
||||
return Get(unused, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
template <class... TArgs>
|
||||
TInterface& Get(bool& created, TArgs&&... args) noexcept
|
||||
{
|
||||
created = false;
|
||||
auto instance{ m_instance.load(std::memory_order_acquire) };
|
||||
if (!instance)
|
||||
{
|
||||
auto newInstance{ Mso::Make<TObject, TInterface>(std::forward<TArgs>(args)...) };
|
||||
if (m_instance.compare_exchange_strong(instance, newInstance.Get()))
|
||||
{
|
||||
// The current thread won the race.
|
||||
// Ensure that a pointer with a reference count of 1 is stored.
|
||||
instance = newInstance.Detach();
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
template <class... TArgs>
|
||||
TInterface& Get(bool& created, TArgs&&... args) noexcept
|
||||
{
|
||||
created = false;
|
||||
auto instance{m_instance.load(std::memory_order_acquire)};
|
||||
if (!instance)
|
||||
{
|
||||
auto newInstance{Mso::Make<TObject, TInterface>(std::forward<TArgs>(args)...)};
|
||||
if (m_instance.compare_exchange_strong(instance, newInstance.Get()))
|
||||
{
|
||||
// The current thread won the race.
|
||||
// Ensure that a pointer with a reference count of 1 is stored.
|
||||
instance = newInstance.Detach();
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
bool IsInitialized() const noexcept
|
||||
{
|
||||
return m_instance.load(std::memory_order_acquire) != nullptr;
|
||||
}
|
||||
bool IsInitialized() const noexcept
|
||||
{
|
||||
return m_instance.load(std::memory_order_acquire) != nullptr;
|
||||
}
|
||||
|
||||
void Release() noexcept
|
||||
{
|
||||
auto instance{ m_instance.exchange(nullptr) };
|
||||
if (instance)
|
||||
{
|
||||
// Release the reference count added in Create().
|
||||
instance->Release();
|
||||
}
|
||||
}
|
||||
void Release() noexcept
|
||||
{
|
||||
auto instance{m_instance.exchange(nullptr)};
|
||||
if (instance)
|
||||
{
|
||||
// Release the reference count added in Create().
|
||||
instance->Release();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<TInterface*> m_instance{ nullptr };
|
||||
std::atomic<TInterface*> m_instance{nullptr};
|
||||
};
|
||||
|
||||
}} // namespace Mso::Object
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for reference counting.
|
||||
Support for reference counting.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -20,128 +20,127 @@
|
|||
namespace Mso {
|
||||
|
||||
/**
|
||||
Mso::Make creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
Mso::Make creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
|
||||
Method Make is noexcept depending on the Make policy IsNoExcept value.
|
||||
Method Make is noexcept depending on the Make policy IsNoExcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> Make(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x01117748 /* tag_bex3i */);
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x01117748 /* tag_bex3i */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef:*/ false);
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef:*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
Mso::MakeAlloc is an Mso::Make method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
Mso::MakeAlloc is an Mso::Make method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
|
||||
MakeAlloc creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
MakeAlloc creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
|
||||
Method MakeAlloc is noexcept depending on the Make policy IsNoExcept value.
|
||||
Method MakeAlloc is noexcept depending on the Make policy IsNoExcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeAlloc(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x01117749 /* tag_bex3j */);
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x01117749 /* tag_bex3j */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef:*/ false);
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef:*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
Mso::MakeElseNull creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
If memory cannot be allocated then it returns nullptr.
|
||||
Mso::MakeElseNull creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
If memory cannot be allocated then it returns nullptr.
|
||||
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
|
||||
Method MakeElseNull is noexcept depending on the Make policy IsNoExcept value.
|
||||
Method MakeElseNull is noexcept depending on the Make policy IsNoExcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeElseNull(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
Mso::TCntPtr<TResult> result; // Hopefully we can benefit from NRVO
|
||||
Mso::TCntPtr<TResult> result; // Hopefully we can benefit from NRVO
|
||||
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
if (memoryGuard.ObjMemory)
|
||||
{
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
if (memoryGuard.ObjMemory)
|
||||
{
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
result = Mso::TCntPtr<TResult>(memoryGuard.Obj, /*fDoAddRef:*/ false);
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
result = Mso::TCntPtr<TResult>(memoryGuard.Obj, /*fDoAddRef:*/ false);
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
Mso::MakeAllocElseNull is an Mso::MakeElseNull method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
If memory cannot be allocated then it returns nullptr.
|
||||
Mso::MakeAllocElseNull is an Mso::MakeElseNull method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
If memory cannot be allocated then it returns nullptr.
|
||||
|
||||
MakeAllocElseNull creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
MakeAllocElseNull creates a new instance of class T and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
|
||||
Method MakeAllocElseNull is noexcept depending on the Make policy IsNoExcept value.
|
||||
Method MakeAllocElseNull is noexcept depending on the Make policy IsNoExcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeAllocElseNull(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
inline Mso::TCntPtr<TResult> MakeAllocElseNull(TAllocArg&& allocArg, TArgs&&... args) noexcept(
|
||||
T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
Mso::TCntPtr<TResult> result; // Hopefully we can benefit from NRVO
|
||||
Mso::TCntPtr<TResult> result; // Hopefully we can benefit from NRVO
|
||||
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
if (memoryGuard.ObjMemory)
|
||||
{
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
typename T::RefCountPolicy::template MemoryGuard<T> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
if (memoryGuard.ObjMemory)
|
||||
{
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
result = Mso::TCntPtr<TResult>(memoryGuard.Obj, /*fDoAddRef:*/ false);
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
result = Mso::TCntPtr<TResult>(memoryGuard.Obj, /*fDoAddRef:*/ false);
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Make policy defines how an instance is created in the pre-allocated memory.
|
||||
namespace MakePolicy
|
||||
{
|
||||
namespace MakePolicy {
|
||||
|
||||
/// ThrowCtor MakePolicy passes all parameters to the constructor.
|
||||
/// ThrowCtor::Make may throw an exception if constructor throws.
|
||||
/// To allow this class to access private constructor add "friend MakePolicy;" to your class.
|
||||
struct ThrowCtor
|
||||
{
|
||||
static const bool IsNoExcept = false;
|
||||
static const bool IsNoExcept = false;
|
||||
|
||||
template <typename T, typename TMemoryGuard, typename ... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args)
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T(std::forward<TArgs>(args)...);
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
}
|
||||
template <typename T, typename TMemoryGuard, typename... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args)
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T(std::forward<TArgs>(args)...);
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
}
|
||||
};
|
||||
|
||||
/// NoThrowCtor MakePolicy passes all parameters to the constructor.
|
||||
|
@ -149,66 +148,66 @@ struct ThrowCtor
|
|||
/// To allow this class to access private constructor add "friend MakePolicy;" to your class.
|
||||
struct NoThrowCtor
|
||||
{
|
||||
static const bool IsNoExcept = true;
|
||||
static const bool IsNoExcept = true;
|
||||
|
||||
template <typename T, typename TMemoryGuard, typename ... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args) noexcept
|
||||
{
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T(std::forward<TArgs>(args)...);
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
}
|
||||
template <typename T, typename TMemoryGuard, typename... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args) noexcept
|
||||
{
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T(std::forward<TArgs>(args)...);
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
}
|
||||
};
|
||||
|
||||
/// ThrowCtorAndInitializeThis MakePolicy calls default constructor and then passes all parameters to InitializeThis method.
|
||||
/// ThrowCtor::Make may throw an exception if constructor or InitializeThis throw.
|
||||
/// To allow this class to access private constructor add "friend MakePolicy;" to your class.
|
||||
/// ThrowCtorAndInitializeThis MakePolicy calls default constructor and then passes all parameters to InitializeThis
|
||||
/// method. ThrowCtor::Make may throw an exception if constructor or InitializeThis throw. To allow this class to access
|
||||
/// private constructor add "friend MakePolicy;" to your class.
|
||||
struct ThrowCtorAndInitializeThis
|
||||
{
|
||||
static const bool IsNoExcept = false;
|
||||
static const bool IsNoExcept = false;
|
||||
|
||||
template <typename T, typename TMemoryGuard, typename ... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args)
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T();
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
memoryGuard.Obj->InitializeThis(std::forward<TArgs>(args)...);
|
||||
}
|
||||
template <typename T, typename TMemoryGuard, typename... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args)
|
||||
{
|
||||
OACR_POSSIBLE_THROW;
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T();
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
memoryGuard.Obj->InitializeThis(std::forward<TArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/// NoThrowCtorAndInitializeThis MakePolicy calls default constructor and then passes all parameters to InitializeThis method.
|
||||
/// NoThrowCtor::Make does not throw exception and crashes the app if constructor or InitializeThis throw.
|
||||
/// To allow this class to access private constructor add "friend MakePolicy;" to your class.
|
||||
/// NoThrowCtorAndInitializeThis MakePolicy calls default constructor and then passes all parameters to InitializeThis
|
||||
/// method. NoThrowCtor::Make does not throw exception and crashes the app if constructor or InitializeThis throw. To
|
||||
/// allow this class to access private constructor add "friend MakePolicy;" to your class.
|
||||
struct NoThrowCtorAndInitializeThis
|
||||
{
|
||||
static const bool IsNoExcept = true;
|
||||
static const bool IsNoExcept = true;
|
||||
|
||||
template <typename T, typename TMemoryGuard, typename ... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args) noexcept
|
||||
{
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T();
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
memoryGuard.Obj->InitializeThis(std::forward<TArgs>(args)...);
|
||||
}
|
||||
template <typename T, typename TMemoryGuard, typename... TArgs>
|
||||
static void Make(TMemoryGuard& memoryGuard, TArgs&&... args) noexcept
|
||||
{
|
||||
memoryGuard.Obj = ::new (memoryGuard.ObjMemory) T();
|
||||
memoryGuard.ObjMemory = nullptr; // Memory is now controlled by the object. Set to null to avoid memory destruction.
|
||||
memoryGuard.Obj->InitializeThis(std::forward<TArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace MakePolicy
|
||||
|
||||
/**
|
||||
Default memory allocator for ref counted objects.
|
||||
Default memory allocator for ref counted objects.
|
||||
*/
|
||||
struct MakeAllocator
|
||||
{
|
||||
static void* Allocate(size_t size) noexcept
|
||||
{
|
||||
Debug(Mso::Memory::AutoIgnoreLeakScope lazy);
|
||||
return Mso::Memory::AllocateEx(size, Mso::Memory::AllocFlags::ShutdownLeak);
|
||||
}
|
||||
static void* Allocate(size_t size) noexcept
|
||||
{
|
||||
Debug(Mso::Memory::AutoIgnoreLeakScope lazy);
|
||||
return Mso::Memory::AllocateEx(size, Mso::Memory::AllocFlags::ShutdownLeak);
|
||||
}
|
||||
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
Mso::Memory::Free(ptr);
|
||||
}
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
Mso::Memory::Free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Mso
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for reference counting.
|
||||
Support for reference counting.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -23,192 +23,201 @@
|
|||
#pragma push_macro("new")
|
||||
#undef new
|
||||
|
||||
#define _MSO_OBJECT_SIMPLEREFCOUNT(TObject) \
|
||||
public: \
|
||||
bool IsUniqueRef() const noexcept { return m_refCount.load(std::memory_order_acquire) == 1; } \
|
||||
Debug(uint32_t RefCount() const noexcept { return m_refCount.load(std::memory_order_acquire); }) \
|
||||
\
|
||||
template <typename UseMsoMakeInsteadOfOperatorNew> void* operator new(size_t, UseMsoMakeInsteadOfOperatorNew* = nullptr); \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
#define _MSO_OBJECT_SIMPLEREFCOUNT(TObject) \
|
||||
public: \
|
||||
bool IsUniqueRef() const noexcept \
|
||||
{ \
|
||||
return m_refCount.load(std::memory_order_acquire) == 1; \
|
||||
} \
|
||||
Debug(uint32_t RefCount() const noexcept { return m_refCount.load(std::memory_order_acquire); }) \
|
||||
\
|
||||
template <typename UseMsoMakeInsteadOfOperatorNew> \
|
||||
void* operator new(size_t, UseMsoMakeInsteadOfOperatorNew* = nullptr); \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
|
||||
#define _MSO_OBJECT_NOREFCOUNT(TObject) \
|
||||
public: \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
public: \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
|
||||
namespace Mso {
|
||||
|
||||
template <typename T, typename TAllocator = MakeAllocator>
|
||||
struct SimpleRefCountMemoryGuard
|
||||
{
|
||||
~SimpleRefCountMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
TAllocator::Deallocate(ObjMemory);
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
Obj->Release();
|
||||
}
|
||||
}
|
||||
~SimpleRefCountMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
TAllocator::Deallocate(ObjMemory);
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
Obj->Release();
|
||||
}
|
||||
}
|
||||
|
||||
public: // We use public fields to reduce number of generated methods.
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates
|
||||
// incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
};
|
||||
|
||||
template <typename TDeleter, typename TAllocator = MakeAllocator>
|
||||
struct SimpleRefCountPolicy
|
||||
{
|
||||
using Deleter = TDeleter;
|
||||
using Allocator = TAllocator;
|
||||
using Deleter = TDeleter;
|
||||
using Allocator = TAllocator;
|
||||
|
||||
template <typename T>
|
||||
using MemoryGuard = SimpleRefCountMemoryGuard<T, TAllocator>;
|
||||
template <typename T>
|
||||
using MemoryGuard = SimpleRefCountMemoryGuard<T, TAllocator>;
|
||||
|
||||
template <typename T>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T>& memoryGuard) noexcept
|
||||
{
|
||||
memoryGuard.ObjMemory = TAllocator::Allocate(sizeof(T));
|
||||
}
|
||||
template <typename T>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T>& memoryGuard) noexcept
|
||||
{
|
||||
memoryGuard.ObjMemory = TAllocator::Allocate(sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T, typename TAllocArg>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T>& memoryGuard, TAllocArg&& allocArg) noexcept
|
||||
{
|
||||
memoryGuard.ObjMemory = TAllocator::Allocate(sizeof(T), std::forward<TAllocArg>(allocArg));
|
||||
}
|
||||
template <typename T, typename TAllocArg>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T>& memoryGuard, TAllocArg&& allocArg) noexcept
|
||||
{
|
||||
memoryGuard.ObjMemory = TAllocator::Allocate(sizeof(T), std::forward<TAllocArg>(allocArg));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
obj->~T();
|
||||
TAllocator::Deallocate(obj);
|
||||
}
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
obj->~T();
|
||||
TAllocator::Deallocate(obj);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
template <typename T>
|
||||
static void ValidateObject(MemoryGuard<T>& memoryGuard) noexcept
|
||||
{
|
||||
static_assert(offsetof(typename T::TypeToDelete, m_refCount) == offsetof(T, m_refCount),
|
||||
"Ref counted object must be the first type in T inheritance list.");
|
||||
template <typename T>
|
||||
static void ValidateObject(MemoryGuard<T>& memoryGuard) noexcept
|
||||
{
|
||||
static_assert(
|
||||
offsetof(typename T::TypeToDelete, m_refCount) == offsetof(T, m_refCount),
|
||||
"Ref counted object must be the first type in T inheritance list.");
|
||||
|
||||
VerifyElseCrashSzTag(memoryGuard.Obj, "Object was not created", 0x01105590 /* tag_befwq */);
|
||||
VerifyElseCrashSzTag(reinterpret_cast<void*>(memoryGuard.Obj) == static_cast<typename T::TypeToDelete*>(memoryGuard.Obj),
|
||||
"Ref counted object must be the first type in T inheritance list.", 0x01105591 /* tag_befwr */);
|
||||
}
|
||||
VerifyElseCrashSzTag(memoryGuard.Obj, "Object was not created", 0x01105590 /* tag_befwq */);
|
||||
VerifyElseCrashSzTag(
|
||||
reinterpret_cast<void*>(memoryGuard.Obj) == static_cast<typename T::TypeToDelete*>(memoryGuard.Obj),
|
||||
"Ref counted object must be the first type in T inheritance list.",
|
||||
0x01105591 /* tag_befwr */);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DefaultRefCountedDeleter
|
||||
{
|
||||
template <typename TObject>
|
||||
static void Delete(TObject* obj) noexcept
|
||||
{
|
||||
TObject::RefCountPolicy::template Delete(obj);
|
||||
}
|
||||
template <typename TObject>
|
||||
static void Delete(TObject* obj) noexcept
|
||||
{
|
||||
TObject::RefCountPolicy::template Delete(obj);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Supported Object ref count strategies. They are used to select base class for ref counting and to choose Make algorithm.
|
||||
The struct can be changed to a namespace if in future we need many strategies in different files.
|
||||
Supported Object ref count strategies. They are used to select base class for ref counting and to choose Make
|
||||
algorithm. The struct can be changed to a namespace if in future we need many strategies in different files.
|
||||
*/
|
||||
namespace RefCountStrategy {
|
||||
using Simple = SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
struct SimpleNoQuery;
|
||||
struct NoRefCount;
|
||||
struct NoRefCountNoQuery;
|
||||
};
|
||||
|
||||
|
||||
using Simple = SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
struct SimpleNoQuery;
|
||||
struct NoRefCount;
|
||||
struct NoRefCountNoQuery;
|
||||
}; // namespace RefCountStrategy
|
||||
|
||||
/**
|
||||
std::shared_ptr<T> is large - it's 2 pointers.
|
||||
This is handy for cases where memory management (one pointer) is separated from the actual
|
||||
data (second pointer). But in cases where we have std::shared_ptr<T> and construct it using
|
||||
std::make_shared<T>() it does not make much sense - we waste one pointer.
|
||||
An example would be well-defined final types - like std::shared_ptr<std::vector<T>>.
|
||||
To avoid wasting memory, created 2 new concepts that mirror STL - RefCountedPtr<> & Make_RefCounted<>()
|
||||
Helper class RefCountedWrapper is used to facilitate change
|
||||
std::shared_ptr<T> is large - it's 2 pointers.
|
||||
This is handy for cases where memory management (one pointer) is separated from the actual
|
||||
data (second pointer). But in cases where we have std::shared_ptr<T> and construct it using
|
||||
std::make_shared<T>() it does not make much sense - we waste one pointer.
|
||||
An example would be well-defined final types - like std::shared_ptr<std::vector<T>>.
|
||||
To avoid wasting memory, created 2 new concepts that mirror STL - RefCountedPtr<> & Make_RefCounted<>()
|
||||
Helper class RefCountedWrapper is used to facilitate change
|
||||
|
||||
NOTE: there are potentially 2 types of objects we need:
|
||||
1) final & no vtable. Useful for case of std::shared_ptr<std::vector<T>>.
|
||||
2) with virtual destructor. Useful for type eraser, to make sure object is deleted properly.
|
||||
NOTE: there are potentially 2 types of objects we need:
|
||||
1) final & no vtable. Useful for case of std::shared_ptr<std::vector<T>>.
|
||||
2) with virtual destructor. Useful for type eraser, to make sure object is deleted properly.
|
||||
|
||||
Also we can do various optimizations / specializations, like
|
||||
- ref-counted without interlocked operations (where it's safe)
|
||||
- ref-counting using short (2 bytes), vs. int (useful if to pack better, like case of
|
||||
RefCountedWrapper<std::pair<short, T>> in CallbackStoreCombined
|
||||
That said, this comes at the cost of complexity - more types, harder to navigate and easier to make mistakes.
|
||||
Should be done through optional template args.
|
||||
Also we can do various optimizations / specializations, like
|
||||
- ref-counted without interlocked operations (where it's safe)
|
||||
- ref-counting using short (2 bytes), vs. int (useful if to pack better, like case of
|
||||
RefCountedWrapper<std::pair<short, T>> in CallbackStoreCombined
|
||||
That said, this comes at the cost of complexity - more types, harder to navigate and easier to make mistakes.
|
||||
Should be done through optional template args.
|
||||
*/
|
||||
class RefCountedWrapperBase
|
||||
{
|
||||
public:
|
||||
using RefCountPolicy = SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using RefCountPolicy = SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using TypeToDelete = RefCountedWrapperBase; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using TypeToDelete = RefCountedWrapperBase; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedWrapperBase);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedWrapperBase);
|
||||
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
uint32_t refCount = ++m_refCount;
|
||||
(void)(refCount);
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) > 1, "Ref count must not bounce from zero", 0x01105592 /* tag_befws */));
|
||||
}
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
uint32_t refCount = ++m_refCount;
|
||||
(void)(refCount);
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) > 1, "Ref count must not bounce from zero", 0x01105592 /* tag_befws */));
|
||||
}
|
||||
|
||||
void Release() const noexcept
|
||||
{
|
||||
uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x01105593 /* tag_befwt */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<RefCountedWrapperBase*>(this));
|
||||
}
|
||||
}
|
||||
void Release() const noexcept
|
||||
{
|
||||
uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x01105593 /* tag_befwt */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<RefCountedWrapperBase*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCountedWrapperBase() noexcept = default;
|
||||
virtual ~RefCountedWrapperBase() noexcept = default; // for type erasure
|
||||
RefCountedWrapperBase() noexcept = default;
|
||||
virtual ~RefCountedWrapperBase() noexcept = default; // for type erasure
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RefCountedWrapper : public RefCountedWrapperBase, public T
|
||||
class RefCountedWrapper
|
||||
: public RefCountedWrapperBase
|
||||
, public T
|
||||
{
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RefCountedWrapper);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RefCountedWrapper);
|
||||
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
template <typename... U>
|
||||
RefCountedWrapper(U&&... args) noexcept
|
||||
: T(std::forward<U>(args)...)
|
||||
{
|
||||
}
|
||||
template <typename... U>
|
||||
RefCountedWrapper(U&&... args) noexcept : T(std::forward<U>(args)...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Special case for base object that does not know about its type.
|
||||
Useful for generic containers, where container does not care about the type,
|
||||
but has external user that can differentiate between instances of these objects
|
||||
(for example by storing property ID as a first field, i.e.
|
||||
RefCountedWrapper<std::pair<PropertyId, SomeObject>>)
|
||||
Special case for base object that does not know about its type.
|
||||
Useful for generic containers, where container does not care about the type,
|
||||
but has external user that can differentiate between instances of these objects
|
||||
(for example by storing property ID as a first field, i.e.
|
||||
RefCountedWrapper<std::pair<PropertyId, SomeObject>>)
|
||||
*/
|
||||
template<>
|
||||
template <>
|
||||
class RefCountedWrapper<void> : public RefCountedWrapperBase
|
||||
{
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RefCountedWrapper);
|
||||
RefCountedWrapper() noexcept = default;
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(RefCountedWrapper);
|
||||
RefCountedWrapper() noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
The actual type / Make() function everybody would use
|
||||
The actual type / Make() function everybody would use
|
||||
*/
|
||||
template <typename T>
|
||||
using RefCountedPtr = Mso::TCntPtr<RefCountedWrapper<T>>;
|
||||
|
@ -216,7 +225,7 @@ using RefCountedPtr = Mso::TCntPtr<RefCountedWrapper<T>>;
|
|||
template <typename T, typename... U>
|
||||
RefCountedPtr<T> Make_RefCounted(U&&... args) noexcept
|
||||
{
|
||||
return Mso::Make<RefCountedWrapper<T>>(std::forward<U>(args)...);
|
||||
return Mso::Make<RefCountedWrapper<T>>(std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
} // namespace Mso
|
||||
|
|
|
@ -18,7 +18,7 @@ To implement your classes please use classes defined in the msoUnknownObject.h o
|
|||
// - Expose the reference counting control block as a public API - ObjectWeakRef class.
|
||||
// - Add a base class ObjectWithWeakRef to support TCntPtr.
|
||||
//
|
||||
// Classes must be inherited from the ObjectWithWeakRef and assigned an instance of ObjectWeakRef right after
|
||||
// Classes must be inherited from the ObjectWithWeakRef and assigned an instance of ObjectWeakRef right after
|
||||
// the construction. ObjectWithWeakRef delegates all ref counting to ObjectWeakRef.
|
||||
//
|
||||
// ObjectWeakRef is an ObjectRefCount that maintains a m_strongRefCount variable to control the lifetime of classes
|
||||
|
@ -34,11 +34,12 @@ To implement your classes please use classes defined in the msoUnknownObject.h o
|
|||
//
|
||||
// To allocate memory we use ObjectWeakRefContainer. It is inherited from the ObjectWeakRef and has storage
|
||||
// for the constructed object. So, we have only one memory allocation instead of two.
|
||||
//
|
||||
//
|
||||
// It is very similar to how the std::make_shared works.
|
||||
// The main difference here is that the base class for the ObjectWeakRefContainer is a template which
|
||||
// gives ability to have different base classes. They just required to have a virtual method DestroyOwnedObject() to signal
|
||||
// when object must be destroyed. The base class must also have a virtual destructor and be responsible for its destruction.
|
||||
// gives ability to have different base classes. They just required to have a virtual method DestroyOwnedObject() to
|
||||
// signal when object must be destroyed. The base class must also have a virtual destructor and be responsible for its
|
||||
// destruction.
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// | ObjectWeakRefContainer |
|
||||
|
@ -69,112 +70,121 @@ To implement your classes please use classes defined in the msoUnknownObject.h o
|
|||
#pragma push_macro("new")
|
||||
#undef new
|
||||
|
||||
#define _MSO_OBJECT_WEAKREFCOUNT(TObject) \
|
||||
public: \
|
||||
Mso::ObjectWeakRef& GetWeakRef() const noexcept { return *Mso::Details::GetWeakRef(this); } \
|
||||
bool IsUniqueRef() const noexcept { return GetWeakRef().IsUniqueRef(); } \
|
||||
Debug(uint32_t RefCount() const noexcept { return GetWeakRef().RefCount(); }) \
|
||||
\
|
||||
template <typename UseMsoMakeInsteadOfOperatorNew> void* operator new(size_t, UseMsoMakeInsteadOfOperatorNew* = nullptr); \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
#define _MSO_OBJECT_WEAKREFCOUNT(TObject) \
|
||||
public: \
|
||||
Mso::ObjectWeakRef& GetWeakRef() const noexcept \
|
||||
{ \
|
||||
return *Mso::Details::GetWeakRef(this); \
|
||||
} \
|
||||
bool IsUniqueRef() const noexcept \
|
||||
{ \
|
||||
return GetWeakRef().IsUniqueRef(); \
|
||||
} \
|
||||
Debug(uint32_t RefCount() const noexcept { return GetWeakRef().RefCount(); }) \
|
||||
\
|
||||
template <typename UseMsoMakeInsteadOfOperatorNew> \
|
||||
void* operator new(size_t, UseMsoMakeInsteadOfOperatorNew* = nullptr); \
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(TObject)
|
||||
|
||||
namespace Mso {
|
||||
|
||||
/**
|
||||
Reference counting block to support weak pointers.
|
||||
Reference counting block to support weak pointers.
|
||||
*/
|
||||
MSO_CLASS_GUID(ObjectWeakRef, "3063C26C-DB10-4BCC-AF5C-340E4D7AA0F6")
|
||||
class ObjectWeakRef
|
||||
{
|
||||
public:
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using TypeToDelete = ObjectWeakRef; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using TypeToDelete = ObjectWeakRef; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(ObjectWeakRef);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(ObjectWeakRef);
|
||||
|
||||
ObjectWeakRef() = default;
|
||||
ObjectWeakRef() = default;
|
||||
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105594 /* tag_befwu */));
|
||||
}
|
||||
}
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105594 /* tag_befwu */));
|
||||
}
|
||||
}
|
||||
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x008c2685 /* tag_a9c0f */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
const_cast<ObjectWeakRef*>(this)->DestroyObject();
|
||||
}
|
||||
}
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x008c2685 /* tag_a9c0f */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
const_cast<ObjectWeakRef*>(this)->DestroyObject();
|
||||
}
|
||||
}
|
||||
|
||||
void AddWeakRef() const noexcept
|
||||
{
|
||||
if (++m_weakRefCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Weak ref count must not bounce from zero", 0x01105595 /* tag_befwv */));
|
||||
}
|
||||
}
|
||||
void AddWeakRef() const noexcept
|
||||
{
|
||||
if (++m_weakRefCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Weak ref count must not bounce from zero", 0x01105595 /* tag_befwv */));
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseWeakRef() const noexcept
|
||||
{
|
||||
const uint32_t weakRefCount = --m_weakRefCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(weakRefCount) >= 0, "Weak ref count must not be negative.", 0x008c2686 /* tag_a9c0g */));
|
||||
if (weakRefCount == 0)
|
||||
{
|
||||
DestroyContainer();
|
||||
}
|
||||
}
|
||||
void ReleaseWeakRef() const noexcept
|
||||
{
|
||||
const uint32_t weakRefCount = --m_weakRefCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(weakRefCount) >= 0, "Weak ref count must not be negative.", 0x008c2686 /* tag_a9c0g */));
|
||||
if (weakRefCount == 0)
|
||||
{
|
||||
DestroyContainer();
|
||||
}
|
||||
}
|
||||
|
||||
Debug(uint32_t WeakRefCount() const noexcept { return m_weakRefCount.load(std::memory_order_acquire); })
|
||||
Debug(uint32_t WeakRefCount() const noexcept { return m_weakRefCount.load(std::memory_order_acquire); })
|
||||
|
||||
Mso::ObjectWeakRef& GetWeakRef() const noexcept
|
||||
{
|
||||
return *const_cast<ObjectWeakRef*>(this);
|
||||
}
|
||||
Mso::ObjectWeakRef& GetWeakRef() const noexcept
|
||||
{
|
||||
return *const_cast<ObjectWeakRef*>(this);
|
||||
}
|
||||
|
||||
virtual void* QueryCastWeakRef(const GUID& /*riid*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
virtual void* QueryCastWeakRef(const GUID& /*riid*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsExpired() const noexcept
|
||||
{
|
||||
return (m_refCount.load(std::memory_order_acquire) == 0);
|
||||
}
|
||||
bool IsExpired() const noexcept
|
||||
{
|
||||
return (m_refCount.load(std::memory_order_acquire) == 0);
|
||||
}
|
||||
|
||||
bool IncrementRefCountIfNotZero() noexcept
|
||||
{
|
||||
uint32_t count = m_refCount.load(std::memory_order_acquire);
|
||||
for (;;)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool IncrementRefCountIfNotZero() noexcept
|
||||
{
|
||||
uint32_t count = m_refCount.load(std::memory_order_acquire);
|
||||
for (;;)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_refCount.compare_exchange_weak(/*ref*/count, count + 1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_refCount.compare_exchange_weak(/*ref*/ count, count + 1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DestroyObject() noexcept {}
|
||||
virtual void DestroyObject() noexcept {}
|
||||
|
||||
protected:
|
||||
virtual void DestroyContainer() const noexcept = 0;
|
||||
virtual ~ObjectWeakRef() = default;
|
||||
virtual void DestroyContainer() const noexcept = 0;
|
||||
virtual ~ObjectWeakRef() = default;
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_weakRefCount { 1 }; // Controls ObjectWeakRef lifetime.
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
mutable std::atomic<uint32_t> m_weakRefCount{1}; // Controls ObjectWeakRef lifetime.
|
||||
};
|
||||
|
||||
namespace Details {
|
||||
|
@ -182,195 +192,213 @@ namespace Details {
|
|||
// Weak reference is always stored in object negative space.
|
||||
inline Mso::ObjectWeakRef* GetWeakRef(const void* obj) noexcept
|
||||
{
|
||||
return *(static_cast<Mso::ObjectWeakRef**>(const_cast<void*>(obj)) - 1);
|
||||
return *(static_cast<Mso::ObjectWeakRef**>(const_cast<void*>(obj)) - 1);
|
||||
}
|
||||
|
||||
inline void SetWeakRef(const void* obj, Mso::ObjectWeakRef* weakRef) noexcept
|
||||
{
|
||||
*(static_cast<Mso::ObjectWeakRef**>(const_cast<void*>(obj)) - 1) = weakRef;
|
||||
*(static_cast<Mso::ObjectWeakRef**>(const_cast<void*>(obj)) - 1) = weakRef;
|
||||
}
|
||||
|
||||
} // namespace Details
|
||||
|
||||
/**
|
||||
Allocates RefCountBlock and the target object in the same chunk of memory.
|
||||
TWeakRef is expected to have a virtual destructor and virtual DestroyOwnedObject method.
|
||||
Allocates RefCountBlock and the target object in the same chunk of memory.
|
||||
TWeakRef is expected to have a virtual destructor and virtual DestroyOwnedObject method.
|
||||
*/
|
||||
template <typename TObject, typename TWeakRef>
|
||||
class ObjectWeakRefContainer
|
||||
: public TWeakRef
|
||||
class ObjectWeakRefContainer : public TWeakRef
|
||||
{
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ObjectWeakRefContainer);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(ObjectWeakRefContainer);
|
||||
|
||||
ObjectWeakRefContainer() = default;
|
||||
ObjectWeakRefContainer() = default;
|
||||
|
||||
TObject* Get() noexcept
|
||||
{
|
||||
return reinterpret_cast<TObject*>(&m_objectStorage);
|
||||
}
|
||||
TObject* Get() noexcept
|
||||
{
|
||||
return reinterpret_cast<TObject*>(&m_objectStorage);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
TWeakRef::DestroyObject();
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
TWeakRef::DestroyObject();
|
||||
|
||||
TObject* obj = Get();
|
||||
if (Mso::Details::GetWeakRef(obj)) // Check if object was correctly initialized.
|
||||
{
|
||||
// Call the TObject destructor last: this call may destroy the ObjectWeakRefContainer because it may hold the last reference.
|
||||
TObject::RefCountPolicy::Deleter::Delete(static_cast<typename TObject::TypeToDelete*>(obj));
|
||||
}
|
||||
}
|
||||
TObject* obj = Get();
|
||||
if (Mso::Details::GetWeakRef(obj)) // Check if object was correctly initialized.
|
||||
{
|
||||
// Call the TObject destructor last: this call may destroy the ObjectWeakRefContainer because it may hold the last
|
||||
// reference.
|
||||
TObject::RefCountPolicy::Deleter::Delete(static_cast<typename TObject::TypeToDelete*>(obj));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DestroyContainer() const noexcept override
|
||||
{
|
||||
this->~ObjectWeakRefContainer();
|
||||
TObject::RefCountPolicy::Allocator::Deallocate(const_cast<ObjectWeakRefContainer*>(this));
|
||||
}
|
||||
virtual void DestroyContainer() const noexcept override
|
||||
{
|
||||
this->~ObjectWeakRefContainer();
|
||||
TObject::RefCountPolicy::Allocator::Deallocate(const_cast<ObjectWeakRefContainer*>(this));
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectWeakRef* m_weakRefPlaceholder; // reserves negative space before m_objectStorage
|
||||
typename std::aligned_storage<sizeof(TObject), std::alignment_of<TObject>::value>::type m_objectStorage;
|
||||
ObjectWeakRef* m_weakRefPlaceholder; // reserves negative space before m_objectStorage
|
||||
typename std::aligned_storage<sizeof(TObject), std::alignment_of<TObject>::value>::type m_objectStorage;
|
||||
};
|
||||
|
||||
template <typename T, typename TContainer>
|
||||
struct WeakRefCountMemoryGuard
|
||||
{
|
||||
using Type = T;
|
||||
using Type = T;
|
||||
|
||||
~WeakRefCountMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
// Constructor failed, Make sure that destructor is not called.
|
||||
ObjectWeakRef* weakRef = Mso::Details::GetWeakRef(ObjMemory);
|
||||
Mso::Details::SetWeakRef(ObjMemory, nullptr); // This should prevent destructor call.
|
||||
Container->Release(); // Release the strong reference which we add to object when we create it.
|
||||
weakRef->ReleaseWeakRef(); // WeakRef is released when object is destroyed. This call may destroy container and must be the last.
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialization failed. Use normal object destruction sequence.
|
||||
Obj->Release();
|
||||
}
|
||||
}
|
||||
~WeakRefCountMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
// Constructor failed, Make sure that destructor is not called.
|
||||
ObjectWeakRef* weakRef = Mso::Details::GetWeakRef(ObjMemory);
|
||||
Mso::Details::SetWeakRef(ObjMemory, nullptr); // This should prevent destructor call.
|
||||
Container->Release(); // Release the strong reference which we add to object when we create it.
|
||||
weakRef->ReleaseWeakRef(); // WeakRef is released when object is destroyed. This call may destroy container and
|
||||
// must be the last.
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialization failed. Use normal object destruction sequence.
|
||||
Obj->Release();
|
||||
}
|
||||
}
|
||||
|
||||
public: // We use a public fields to reduce number of generated methods.
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
TContainer* Container;
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates
|
||||
// incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
TContainer* Container;
|
||||
};
|
||||
|
||||
template <typename TDeleter, typename TAllocator = MakeAllocator>
|
||||
struct WeakRefCountPolicy
|
||||
{
|
||||
using Deleter = TDeleter;
|
||||
using Allocator = TAllocator;
|
||||
using Deleter = TDeleter;
|
||||
using Allocator = TAllocator;
|
||||
|
||||
template <typename T, typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>>
|
||||
using MemoryGuard = WeakRefCountMemoryGuard<T, TContainer>;
|
||||
template <typename T, typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>>
|
||||
using MemoryGuard = WeakRefCountMemoryGuard<T, TContainer>;
|
||||
|
||||
template <typename T, typename TContainer>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T, TContainer>& memoryGuard) noexcept
|
||||
{
|
||||
void* containerMemory = TAllocator::Allocate(sizeof(TContainer));
|
||||
if (containerMemory)
|
||||
{
|
||||
memoryGuard.Container = ::new (containerMemory) TContainer();
|
||||
memoryGuard.ObjMemory = memoryGuard.Container->Get();
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, memoryGuard.Container);
|
||||
}
|
||||
}
|
||||
template <typename T, typename TContainer>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T, TContainer>& memoryGuard) noexcept
|
||||
{
|
||||
void* containerMemory = TAllocator::Allocate(sizeof(TContainer));
|
||||
if (containerMemory)
|
||||
{
|
||||
memoryGuard.Container = ::new (containerMemory) TContainer();
|
||||
memoryGuard.ObjMemory = memoryGuard.Container->Get();
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, memoryGuard.Container);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename TContainer, typename TAllocArg>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T, TContainer>& memoryGuard, TAllocArg&& allocArg) noexcept
|
||||
{
|
||||
void* containerMemory = TAllocator::Allocate(sizeof(TContainer), std::forward<TAllocArg>(allocArg));
|
||||
if (containerMemory)
|
||||
{
|
||||
memoryGuard.Container = ::new (containerMemory) TContainer();
|
||||
memoryGuard.ObjMemory = memoryGuard.Container->Get();
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, memoryGuard.Container);
|
||||
}
|
||||
}
|
||||
template <typename T, typename TContainer, typename TAllocArg>
|
||||
static void AllocateMemory(_Out_ MemoryGuard<T, TContainer>& memoryGuard, TAllocArg&& allocArg) noexcept
|
||||
{
|
||||
void* containerMemory = TAllocator::Allocate(sizeof(TContainer), std::forward<TAllocArg>(allocArg));
|
||||
if (containerMemory)
|
||||
{
|
||||
memoryGuard.Container = ::new (containerMemory) TContainer();
|
||||
memoryGuard.ObjMemory = memoryGuard.Container->Get();
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, memoryGuard.Container);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
obj->~T();
|
||||
ObjectWeakRef* weakRef = Mso::Details::GetWeakRef(obj);
|
||||
Mso::Details::SetWeakRef(obj, nullptr);
|
||||
weakRef->ReleaseWeakRef(); // This call may destroy container and must be the last.
|
||||
}
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
obj->~T();
|
||||
ObjectWeakRef* weakRef = Mso::Details::GetWeakRef(obj);
|
||||
Mso::Details::SetWeakRef(obj, nullptr);
|
||||
weakRef->ReleaseWeakRef(); // This call may destroy container and must be the last.
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
template <typename TMemoryGuard>
|
||||
static void ValidateObject(TMemoryGuard& memoryGuard) noexcept
|
||||
{
|
||||
VerifyElseCrashSzTag(memoryGuard.Obj, "Object was not created", 0x01105596 /* tag_befww */);
|
||||
VerifyElseCrashSzTag(reinterpret_cast<void*>(memoryGuard.Obj) == static_cast<typename TMemoryGuard::Type::TypeToDelete*>(memoryGuard.Obj),
|
||||
"Ref counted object must be the first type in T inheritance list.", 0x008c2687 /* tag_a9c0h */);
|
||||
VerifyElseCrashSzTag(reinterpret_cast<void*>(memoryGuard.Container) == &memoryGuard.Obj->GetWeakRef(), "Bad WeakRef", 0x008c2688 /* tag_a9c0i */);
|
||||
}
|
||||
template <typename TMemoryGuard>
|
||||
static void ValidateObject(TMemoryGuard& memoryGuard) noexcept
|
||||
{
|
||||
VerifyElseCrashSzTag(memoryGuard.Obj, "Object was not created", 0x01105596 /* tag_befww */);
|
||||
VerifyElseCrashSzTag(
|
||||
reinterpret_cast<void*>(memoryGuard.Obj)
|
||||
== static_cast<typename TMemoryGuard::Type::TypeToDelete*>(memoryGuard.Obj),
|
||||
"Ref counted object must be the first type in T inheritance list.",
|
||||
0x008c2687 /* tag_a9c0h */);
|
||||
VerifyElseCrashSzTag(
|
||||
reinterpret_cast<void*>(memoryGuard.Container) == &memoryGuard.Obj->GetWeakRef(),
|
||||
"Bad WeakRef",
|
||||
0x008c2688 /* tag_a9c0i */);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
Extend the supported Object ref count strategies with WeakRef.
|
||||
Extend the supported Object ref count strategies with WeakRef.
|
||||
*/
|
||||
namespace RefCountStrategy {
|
||||
using WeakRef = WeakRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
using WeakRef = WeakRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
}
|
||||
|
||||
namespace Details {
|
||||
|
||||
/**
|
||||
Mso::MakeWeakRefObject creates a new instance of class T in container TContainer and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
Mso::MakeWeakRefObject creates a new instance of class T in container TContainer and returns a TCntPtr to the instance
|
||||
of type TResult. TResult is either the original type T (default), or one of its interfaces. Returning an interface
|
||||
type can help to avoid creation of unnecessary TCntPtr template instances.
|
||||
|
||||
Method MakeWeakRefObject is noexcept depending on the Make policy noexcept value.
|
||||
Method MakeWeakRefObject is noexcept depending on the Make policy noexcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>, typename... TArgs>
|
||||
template <
|
||||
typename T,
|
||||
typename TResult = T,
|
||||
typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>,
|
||||
typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeWeakRefObject(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, TContainer> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774a /* tag_bex3k */);
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, TContainer> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774a /* tag_bex3k */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
Mso::MakeAllocWeakRefObject is an Mso::MakeWeakRefObject method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
Mso::MakeAllocWeakRefObject is an Mso::MakeWeakRefObject method for types T that use allocators accepting an argument.
|
||||
The allocator argument allows to implement stateful allocators.
|
||||
|
||||
MakeAllocWeakRefObject creates a new instance of class T in container TContainer and returns a TCntPtr to the instance of type TResult.
|
||||
TResult is either the original type T (default), or one of its interfaces.
|
||||
Returning an interface type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
MakeAllocWeakRefObject creates a new instance of class T in container TContainer and returns a TCntPtr to the instance
|
||||
of type TResult. TResult is either the original type T (default), or one of its interfaces. Returning an interface
|
||||
type can help to avoid creation of unnecessary TCntPtr template instantiations.
|
||||
|
||||
Method MakeAllocWeakRefObject is noexcept depending on the Make policy noexcept value.
|
||||
Method MakeAllocWeakRefObject is noexcept depending on the Make policy noexcept value.
|
||||
*/
|
||||
template <typename T, typename TResult = T, typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>, typename TAllocArg, typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeAllocWeakRefObject(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
template <
|
||||
typename T,
|
||||
typename TResult = T,
|
||||
typename TContainer = ObjectWeakRefContainer<T, ObjectWeakRef>,
|
||||
typename TAllocArg,
|
||||
typename... TArgs>
|
||||
inline Mso::TCntPtr<TResult> MakeAllocWeakRefObject(TAllocArg&& allocArg, TArgs&&... args) noexcept(
|
||||
T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, TContainer> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774b /* tag_bex3l */);
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, TContainer> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774b /* tag_bex3l */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
|
||||
} // namespace Details
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for IUnknown::QueryInterface implementation.
|
||||
Support for IUnknown::QueryInterface implementation.
|
||||
*/
|
||||
#pragma once
|
||||
#include <debugAssertApi/debugAssertApi.h>
|
||||
|
@ -37,9 +37,12 @@ Here we provide a number of helper templates to implement QueryCast for a type:
|
|||
// Disable warnings 4625 and 4626 because templates below may have or may not have default copy constructor and
|
||||
// copy assignment operator depending on the template type parameter. We do not want just to delete them because
|
||||
// they could be useful in some scenarios when generated.
|
||||
#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible or deleted
|
||||
#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible or deleted
|
||||
#pragma warning(disable: 4995) // 'IsDebuggerPresent': name was marked as #pragma deprecated. It is part of VerifyElseCrash macro.
|
||||
#pragma warning(disable : 4625) // copy constructor could not be generated because a base class copy constructor is
|
||||
// inaccessible or deleted
|
||||
#pragma warning(disable : 4626) // assignment operator could not be generated because a base class assignment operator
|
||||
// is inaccessible or deleted
|
||||
#pragma warning(disable : 4995) // 'IsDebuggerPresent': name was marked as #pragma deprecated. It is part of
|
||||
// VerifyElseCrash macro.
|
||||
|
||||
namespace Mso {
|
||||
|
||||
|
@ -47,52 +50,52 @@ namespace Mso {
|
|||
/// Note that we do not call AddRef() here. It is a responsibility of a method that implements IUnknown::QueryInterface.
|
||||
struct QueryCastHelper
|
||||
{
|
||||
/// Implements QueryCast similar to the classic IUnknown::QueryInterface implementation.
|
||||
template <typename T>
|
||||
static void* QueryCast(_In_ void* obj, const GUID& riid) noexcept
|
||||
{
|
||||
// It has lower priority than the next overload because we use void* instead of T* for the obj.
|
||||
return (riid == __uuidof(T)) ? obj : nullptr;
|
||||
}
|
||||
/// Implements QueryCast similar to the classic IUnknown::QueryInterface implementation.
|
||||
template <typename T>
|
||||
static void* QueryCast(_In_ void* obj, const GUID& riid) noexcept
|
||||
{
|
||||
// It has lower priority than the next overload because we use void* instead of T* for the obj.
|
||||
return (riid == __uuidof(T)) ? obj : nullptr;
|
||||
}
|
||||
|
||||
/// Delegates the QueryCast implementation to the object if it has a QueryCast method.
|
||||
/// If QueryCast fails then we check type's GUID if it is assigned by MSO_STRUCT_GUID or MSO_CLASS_GUID.
|
||||
template <typename T>
|
||||
static auto QueryCast(_In_ T* obj, const GUID& riid) noexcept -> decltype(obj->QueryCast(riid))
|
||||
{
|
||||
void* result = obj->QueryCast(riid);
|
||||
return (result) ? result : GuidQueryCast<T>(obj, riid, static_cast<typename Mso::TypeHasGuid<T>::Type*>(nullptr));
|
||||
}
|
||||
/// Delegates the QueryCast implementation to the object if it has a QueryCast method.
|
||||
/// If QueryCast fails then we check type's GUID if it is assigned by MSO_STRUCT_GUID or MSO_CLASS_GUID.
|
||||
template <typename T>
|
||||
static auto QueryCast(_In_ T* obj, const GUID& riid) noexcept -> decltype(obj->QueryCast(riid))
|
||||
{
|
||||
void* result = obj->QueryCast(riid);
|
||||
return (result) ? result : GuidQueryCast<T>(obj, riid, static_cast<typename Mso::TypeHasGuid<T>::Type*>(nullptr));
|
||||
}
|
||||
|
||||
/// Recursively calls itself while we have two or more base types.
|
||||
template <typename TSource, typename TBase0, typename TBase1, typename... TBases>
|
||||
static void* QueryCastList(TSource& obj, const GUID& riid) noexcept
|
||||
{
|
||||
void* result = QueryCastList<TSource, TBase0>(obj, riid);
|
||||
return (result) ? result : QueryCastList<TSource, TBase1, TBases...>(obj, riid);
|
||||
}
|
||||
/// Recursively calls itself while we have two or more base types.
|
||||
template <typename TSource, typename TBase0, typename TBase1, typename... TBases>
|
||||
static void* QueryCastList(TSource& obj, const GUID& riid) noexcept
|
||||
{
|
||||
void* result = QueryCastList<TSource, TBase0>(obj, riid);
|
||||
return (result) ? result : QueryCastList<TSource, TBase1, TBases...>(obj, riid);
|
||||
}
|
||||
|
||||
/// An implementation for a type list with just one type.
|
||||
template <typename TSource, typename TBase>
|
||||
static void* QueryCastList(TSource& obj, const GUID& riid) noexcept
|
||||
{
|
||||
return QueryCast<TBase>(static_cast<TBase*>(&obj), riid);
|
||||
}
|
||||
/// An implementation for a type list with just one type.
|
||||
template <typename TSource, typename TBase>
|
||||
static void* QueryCastList(TSource& obj, const GUID& riid) noexcept
|
||||
{
|
||||
return QueryCast<TBase>(static_cast<TBase*>(&obj), riid);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static void* GuidQueryCast(_In_ T* /*obj*/, const GUID& /*riid*/, Mso::Details::GuidUtils::FalseType*) noexcept
|
||||
{
|
||||
// Type has no GUID: do nothing
|
||||
return nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
static void* GuidQueryCast(_In_ T* /*obj*/, const GUID& /*riid*/, Mso::Details::GuidUtils::FalseType*) noexcept
|
||||
{
|
||||
// Type has no GUID: do nothing
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void* GuidQueryCast(_In_ T* obj, const GUID& riid, Mso::Details::GuidUtils::TrueType*) noexcept
|
||||
{
|
||||
// Typecast to void* to force choosing QueryCast overload that checks type's GUID.
|
||||
return QueryCast<T>(static_cast<void*>(obj), riid);
|
||||
}
|
||||
template <typename T>
|
||||
static void* GuidQueryCast(_In_ T* obj, const GUID& riid, Mso::Details::GuidUtils::TrueType*) noexcept
|
||||
{
|
||||
// Typecast to void* to force choosing QueryCast overload that checks type's GUID.
|
||||
return QueryCast<T>(static_cast<void*>(obj), riid);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helps to implement static cast for the cases when multiple base types implement the same interface.
|
||||
|
@ -105,58 +108,61 @@ struct StaticCastHelper;
|
|||
template <typename TTarget>
|
||||
struct StaticCastHelper<TTarget*>
|
||||
{
|
||||
/// Try to cast the first base type if it is inherited from TTarget.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastFirst(TSource* source) noexcept
|
||||
{
|
||||
return CastIf<TSource, TBase0, TBases...>(typename std::is_base_of<TTarget, TBase0>::type(), source);
|
||||
}
|
||||
/// Try to cast the first base type if it is inherited from TTarget.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastFirst(TSource* source) noexcept
|
||||
{
|
||||
return CastIf<TSource, TBase0, TBases...>(typename std::is_base_of<TTarget, TBase0>::type(), source);
|
||||
}
|
||||
|
||||
/// A case when no base types are inherited from TTarget.
|
||||
template <typename TSource>
|
||||
static TTarget* CastFirst(TSource* /*source*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
/// A case when no base types are inherited from TTarget.
|
||||
template <typename TSource>
|
||||
static TTarget* CastFirst(TSource* /*source*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// First base type is inherited from TTarget. Cast it to TTarget.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastIf(std::true_type /*isConvertible?*/, TSource* source) noexcept
|
||||
{
|
||||
return Cast<TBase0>(/*tag:*/ 0, static_cast<TBase0*>(source));
|
||||
}
|
||||
/// First base type is inherited from TTarget. Cast it to TTarget.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastIf(std::true_type /*isConvertible?*/, TSource* source) noexcept
|
||||
{
|
||||
return Cast<TBase0>(/*tag:*/ 0, static_cast<TBase0*>(source));
|
||||
}
|
||||
|
||||
/// First base type is not inherited from TTarget. Recursively call CastFirst for the remaining base types.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastIf(std::false_type /*isConvertible?*/, TSource* source) noexcept
|
||||
{
|
||||
return CastFirst<TSource, TBases...>(source);
|
||||
}
|
||||
/// First base type is not inherited from TTarget. Recursively call CastFirst for the remaining base types.
|
||||
template <typename TSource, typename TBase0, typename... TBases>
|
||||
static TTarget* CastIf(std::false_type /*isConvertible?*/, TSource* source) noexcept
|
||||
{
|
||||
return CastFirst<TSource, TBases...>(source);
|
||||
}
|
||||
|
||||
/// To have a lower priority when matching an int tag parameter.
|
||||
struct IntWrapper { IntWrapper(int) noexcept {} };
|
||||
/// To have a lower priority when matching an int tag parameter.
|
||||
struct IntWrapper
|
||||
{
|
||||
IntWrapper(int) noexcept {}
|
||||
};
|
||||
|
||||
/// If TSource has a StaticCastElseNull method then call it to do the static cast.
|
||||
template <typename TSource>
|
||||
static auto Cast(int /*tag*/, TSource* source) noexcept -> decltype(source->template StaticCastElseNull<TTarget*>())
|
||||
{
|
||||
return source->template StaticCastElseNull<TTarget*>();
|
||||
}
|
||||
/// If TSource has a StaticCastElseNull method then call it to do the static cast.
|
||||
template <typename TSource>
|
||||
static auto Cast(int /*tag*/, TSource* source) noexcept -> decltype(source->template StaticCastElseNull<TTarget*>())
|
||||
{
|
||||
return source->template StaticCastElseNull<TTarget*>();
|
||||
}
|
||||
|
||||
/// Cast using static_cast. It has a lower priority than previous method because we use IntWrapper tag instead of int.
|
||||
template <typename TSource>
|
||||
static TTarget* Cast(IntWrapper /*tag*/, TSource* source) noexcept
|
||||
{
|
||||
return static_cast<TTarget*>(source);
|
||||
}
|
||||
/// Cast using static_cast. It has a lower priority than previous method because we use IntWrapper tag instead of int.
|
||||
template <typename TSource>
|
||||
static TTarget* Cast(IntWrapper /*tag*/, TSource* source) noexcept
|
||||
{
|
||||
return static_cast<TTarget*>(source);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Helper templates to implement QueryCast.
|
||||
//
|
||||
|
||||
/// Implements QueryCast for the case when object implements an interface derived from a base interface, and we want to
|
||||
/// Implements QueryCast for the case when object implements an interface derived from a base interface, and we want to
|
||||
/// to query the base interface by its GUID.
|
||||
/// E.g. struct IDerived : IBase {}
|
||||
/// class MyClass : Mso::QueryCastChain< IDerived, IBase >
|
||||
|
@ -164,32 +170,32 @@ template <typename T, typename... TBaseTypes>
|
|||
class DECLSPEC_NOVTABLE QueryCastChain : public T
|
||||
{
|
||||
public:
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
return InternalQueryCast<T, TBaseTypes...>(riid);
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
return InternalQueryCast<T, TBaseTypes...>(riid);
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
QueryCastChain(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
template <typename... TArgs>
|
||||
QueryCastChain(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// Recursively calls itself while we have two or more base types.
|
||||
template <typename TBaseType0, typename TBaseType1, typename... TOtherBaseTypes>
|
||||
void* InternalQueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
void* result = InternalQueryCast<TBaseType0>(riid);
|
||||
return (result) ? result : InternalQueryCast<TBaseType1, TOtherBaseTypes...>(riid);
|
||||
}
|
||||
// Recursively calls itself while we have two or more base types.
|
||||
template <typename TBaseType0, typename TBaseType1, typename... TOtherBaseTypes>
|
||||
void* InternalQueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
void* result = InternalQueryCast<TBaseType0>(riid);
|
||||
return (result) ? result : InternalQueryCast<TBaseType1, TOtherBaseTypes...>(riid);
|
||||
}
|
||||
|
||||
template <typename TBaseType>
|
||||
void* InternalQueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
static_assert(std::is_base_of<TBaseType, T>::value, "T must be inherited from TBaseType");
|
||||
return QueryCastHelper::QueryCast<TBaseType>(static_cast<TBaseType*>(this), riid);
|
||||
}
|
||||
template <typename TBaseType>
|
||||
void* InternalQueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
static_assert(std::is_base_of<TBaseType, T>::value, "T must be inherited from TBaseType");
|
||||
return QueryCastHelper::QueryCast<TBaseType>(static_cast<TBaseType*>(this), riid);
|
||||
}
|
||||
};
|
||||
|
||||
/// Implements the QueryCast that can return the derived type by its GUID.
|
||||
|
@ -198,39 +204,38 @@ template <typename T>
|
|||
class QueryCastDerived
|
||||
{
|
||||
public:
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(T))
|
||||
{
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(T))
|
||||
{
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// Allows to associate a GUID for a type that works only in the scope of the QueryCast operation.
|
||||
/// This method can be used if type has no associated GUID, but we still want to query for it.
|
||||
template <typename T, const GUID* piid = static_cast<const GUID *>(nullptr)>
|
||||
template <typename T, const GUID* piid = static_cast<const GUID*>(nullptr)>
|
||||
class DECLSPEC_NOVTABLE QueryCastGuid : public T
|
||||
{
|
||||
public:
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (*Mso::ResolveGuidPtr<T, piid>::Guid == riid)
|
||||
{
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (*Mso::ResolveGuidPtr<T, piid>::Guid == riid)
|
||||
{
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
QueryCastGuid(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
QueryCastGuid(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Implements the QueryCast that does not do any query cast for a type.
|
||||
|
@ -240,63 +245,67 @@ template <typename T>
|
|||
class DECLSPEC_NOVTABLE QueryCastHidden : public T
|
||||
{
|
||||
public:
|
||||
void* QueryCast(const GUID& /*riid*/) noexcept
|
||||
{
|
||||
(void)(this); // To avoid OACR warning that method can be static
|
||||
return nullptr;
|
||||
}
|
||||
void* QueryCast(const GUID& /*riid*/) noexcept
|
||||
{
|
||||
(void)(this); // To avoid OACR warning that method can be static
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
QueryCastHidden(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
template <typename... TArgs>
|
||||
QueryCastHidden(TArgs&&... args) noexcept : T(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Delegates the QueryCast implementation to the list of the base types.
|
||||
/// If a constructor with a non-empty set of parameters then they are forwarded to the TBase0.
|
||||
template <typename TBase0, typename... TBases>
|
||||
class DECLSPEC_NOVTABLE QueryCastList : public TBase0, public TBases...
|
||||
class DECLSPEC_NOVTABLE QueryCastList
|
||||
: public TBase0
|
||||
, public TBases...
|
||||
{
|
||||
using CastList = int; //TODO: This is a temporary variable. We will have to expand this concept to implement shallow inheritance tree for UnknownObject.
|
||||
// The goal is to have "using CastList = CastList<TBase0, TBases...>" in UnknownObject.
|
||||
using ThisType = QueryCastList;
|
||||
using CastList = int; // TODO: This is a temporary variable. We will have to expand this concept to implement shallow
|
||||
// inheritance tree for UnknownObject.
|
||||
// The goal is to have "using CastList = CastList<TBase0, TBases...>" in UnknownObject.
|
||||
using ThisType = QueryCastList;
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
QueryCastList(TArgs&&... args) noexcept : TBase0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
template <typename... TArgs>
|
||||
QueryCastList(TArgs&&... args) noexcept : TBase0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
return QueryCastHelper::QueryCastList<ThisType, TBase0, TBases...>(*this, riid);
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
return QueryCastHelper::QueryCastList<ThisType, TBase0, TBases...>(*this, riid);
|
||||
}
|
||||
|
||||
template <typename TTarget>
|
||||
TTarget StaticCastElseNull() noexcept
|
||||
{
|
||||
return StaticCastHelper<TTarget>::template CastFirst<ThisType, TBase0, TBases...>(this);
|
||||
}
|
||||
template <typename TTarget>
|
||||
TTarget StaticCastElseNull() noexcept
|
||||
{
|
||||
return StaticCastHelper<TTarget>::template CastFirst<ThisType, TBase0, TBases...>(this);
|
||||
}
|
||||
|
||||
#pragma warning(suppress: 4265) // class has virtual functions, but destructor is not virtual
|
||||
#pragma warning(suppress : 4265) // class has virtual functions, but destructor is not virtual
|
||||
};
|
||||
|
||||
/**
|
||||
A helper class to call QueryCast method for an object.
|
||||
There are two supported cases:
|
||||
(1) type has a QueryCast instance method
|
||||
(2) type is inherited from IUnknown and the implementation supports initialization of a QueryCastBridge instance
|
||||
inside its IUnknown::QueryInterface() implementation.
|
||||
The QueryCast typecasting does not call AddRef.
|
||||
A helper class to call QueryCast method for an object.
|
||||
There are two supported cases:
|
||||
(1) type has a QueryCast instance method
|
||||
(2) type is inherited from IUnknown and the implementation supports initialization of a QueryCastBridge instance
|
||||
inside its IUnknown::QueryInterface() implementation.
|
||||
The QueryCast typecasting does not call AddRef.
|
||||
*/
|
||||
MSO_STRUCT_GUID(QueryCastBridge, "A39D5FC8-0641-4EEE-8C97-DDEF114D487D")
|
||||
struct QueryCastBridge
|
||||
{
|
||||
// DECLARE_COPYCONSTR_AND_ASSIGNMENT(QueryCastBridge);
|
||||
void* Object; // It must be a first field. In that case **ppvObject in QueryInterface will have the same semantic as with the normal QueryInterface calls.
|
||||
const GUID& ObjectId;
|
||||
// DECLARE_COPYCONSTR_AND_ASSIGNMENT(QueryCastBridge);
|
||||
void* Object; // It must be a first field. In that case **ppvObject in QueryInterface will have the same semantic as
|
||||
// with the normal QueryInterface calls.
|
||||
const GUID& ObjectId;
|
||||
};
|
||||
|
||||
namespace Details {
|
||||
|
@ -309,69 +318,72 @@ struct QueryCastConverter; // Default case is undefined.
|
|||
template <typename TTarget>
|
||||
struct QueryCastConverter<TTarget&>
|
||||
{
|
||||
template <typename TSource>
|
||||
static TTarget& QueryCast(const TSource& source) noexcept
|
||||
{
|
||||
using SourceType = std::decay_t<TSource>;
|
||||
using TargetType = std::decay_t<TTarget>;
|
||||
static_assert(!std::is_pointer<SourceType>::value, "Cannot convert pointer to a reference.");
|
||||
template <typename TSource>
|
||||
static TTarget& QueryCast(const TSource& source) noexcept
|
||||
{
|
||||
using SourceType = std::decay_t<TSource>;
|
||||
using TargetType = std::decay_t<TTarget>;
|
||||
static_assert(!std::is_pointer<SourceType>::value, "Cannot convert pointer to a reference.");
|
||||
|
||||
SourceType* nonConstSource = const_cast<SourceType*>(&source);
|
||||
QueryCastBridge bridge = { nullptr, __uuidof(TargetType) };
|
||||
VerifySucceededElseCrashTag(nonConstSource->QueryInterface(__uuidof(QueryCastBridge), reinterpret_cast<void**>(&bridge)), 0x0100370d /* tag_bad2n */);
|
||||
SourceType* nonConstSource = const_cast<SourceType*>(&source);
|
||||
QueryCastBridge bridge = {nullptr, __uuidof(TargetType)};
|
||||
VerifySucceededElseCrashTag(
|
||||
nonConstSource->QueryInterface(__uuidof(QueryCastBridge), reinterpret_cast<void**>(&bridge)),
|
||||
0x0100370d /* tag_bad2n */);
|
||||
|
||||
TargetType* target = static_cast<TargetType*>(bridge.Object);
|
||||
VerifyElseCrashSzTag(target, "Query cast failed for a reference type.", 0x0100370e /* tag_bad2o */);
|
||||
return static_cast<TTarget&>(*target);
|
||||
}
|
||||
TargetType* target = static_cast<TargetType*>(bridge.Object);
|
||||
VerifyElseCrashSzTag(target, "Query cast failed for a reference type.", 0x0100370e /* tag_bad2o */);
|
||||
return static_cast<TTarget&>(*target);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for pointer conversion
|
||||
template <typename TTarget>
|
||||
struct QueryCastConverter<TTarget*>
|
||||
{
|
||||
template <typename TSource>
|
||||
static TTarget* QueryCast(const TSource& source) noexcept
|
||||
{
|
||||
using SourceType = std::remove_const_t<std::remove_pointer_t<std::decay_t<TSource>>>;
|
||||
using TargetType = std::decay_t<TTarget>;
|
||||
static_assert(std::is_pointer<TSource>::value, "Cannot convert non-pointer to a pointer.");
|
||||
template <typename TSource>
|
||||
static TTarget* QueryCast(const TSource& source) noexcept
|
||||
{
|
||||
using SourceType = std::remove_const_t<std::remove_pointer_t<std::decay_t<TSource>>>;
|
||||
using TargetType = std::decay_t<TTarget>;
|
||||
static_assert(std::is_pointer<TSource>::value, "Cannot convert non-pointer to a pointer.");
|
||||
|
||||
if (source != nullptr)
|
||||
{
|
||||
SourceType* nonConstSource = const_cast<SourceType*>(source);
|
||||
QueryCastBridge bridge = { nullptr, __uuidof(TargetType) };
|
||||
if (nonConstSource->QueryInterface(__uuidof(QueryCastBridge), reinterpret_cast<void**>(&bridge)) == S_OK)
|
||||
{
|
||||
TargetType* target = static_cast<TargetType*>(bridge.Object);
|
||||
return static_cast<TTarget*>(target);
|
||||
}
|
||||
}
|
||||
if (source != nullptr)
|
||||
{
|
||||
SourceType* nonConstSource = const_cast<SourceType*>(source);
|
||||
QueryCastBridge bridge = {nullptr, __uuidof(TargetType)};
|
||||
if (nonConstSource->QueryInterface(__uuidof(QueryCastBridge), reinterpret_cast<void**>(&bridge)) == S_OK)
|
||||
{
|
||||
TargetType* target = static_cast<TargetType*>(bridge.Object);
|
||||
return static_cast<TTarget*>(target);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Mso::Details
|
||||
} // namespace Details
|
||||
} // namespace Mso
|
||||
|
||||
/**
|
||||
query_cast does a dynamic cast based on a type GUID.
|
||||
It does not call AddRef. Caller of this method is responsible for the object's lifetime management.
|
||||
TTarget is required to be either a pointer or a reference (similar to dynamic_cast).
|
||||
E.g. query_cast< IMyIntf* >( x ), or query_cast< IMyIntf& >( x ).
|
||||
query_cast does a dynamic cast based on a type GUID.
|
||||
It does not call AddRef. Caller of this method is responsible for the object's lifetime management.
|
||||
TTarget is required to be either a pointer or a reference (similar to dynamic_cast).
|
||||
E.g. query_cast< IMyIntf* >( x ), or query_cast< IMyIntf& >( x ).
|
||||
|
||||
If query_cast fails to convert to a pointer type then it returns nullptr.
|
||||
If query_cast fails to convert to a reference type then it crashes the app.
|
||||
If query_cast fails to convert to a pointer type then it returns nullptr.
|
||||
If query_cast fails to convert to a reference type then it crashes the app.
|
||||
|
||||
For this method to work, the TSource class must support initialization of QueryCastBridge struct
|
||||
when queried for its GUID. UnknownObject in core\msoUnknownObject.h provides all necessary support.
|
||||
For this method to work, the TSource class must support initialization of QueryCastBridge struct
|
||||
when queried for its GUID. UnknownObject in core\msoUnknownObject.h provides all necessary support.
|
||||
|
||||
query_cast can be used when we want to do a dynamic cast and do not need to call AddRef to control object lifetime.
|
||||
query_cast can be used when we want to do a dynamic cast and do not need to call AddRef to control object lifetime.
|
||||
*/
|
||||
template <typename TTarget, typename TSource>
|
||||
TTarget query_cast(TSource&& source) noexcept
|
||||
{
|
||||
return Mso::Details::QueryCastConverter<TTarget>::QueryCast(std::forward<TSource>(source));
|
||||
return Mso::Details::QueryCastConverter<TTarget>::QueryCast(std::forward<TSource>(source));
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Helper classes for internal ref-counting
|
||||
Helper classes for internal ref-counting
|
||||
*/
|
||||
#pragma once
|
||||
#include <core/TCntPtr.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
IRefCounted implementation.
|
||||
IRefCounted implementation.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -21,528 +21,538 @@ BEGIN_DISABLE_WARNING_INCONSISTENT_MISSING_OVERRIDE()
|
|||
namespace Mso {
|
||||
|
||||
/**
|
||||
RefCountedObject is a class template to implement the IRefCounted interface.
|
||||
RefCountedObject is a class template to implement the IRefCounted interface.
|
||||
|
||||
Because RefCountedObject overrides AddRef and Release, at least one of your base types should
|
||||
declare virtual AddRef/Release methods. Usually you'd specify at least "Mso::IRefCounted" or
|
||||
some interface that derives from IRefCounted.
|
||||
Because RefCountedObject overrides AddRef and Release, at least one of your base types should
|
||||
declare virtual AddRef/Release methods. Usually you'd specify at least "Mso::IRefCounted" or
|
||||
some interface that derives from IRefCounted.
|
||||
|
||||
If you want to avoid the overhead of a v-table, you can use RefCountedObjectNoVTable, which
|
||||
defines non-virtual AddRef/Release methods. These are still compatible with templated
|
||||
smart pointers like TCntPtr and ComPtr because those smart pointers don't actually use
|
||||
virtual method dispatch to call AddRef/Release.
|
||||
If you want to avoid the overhead of a v-table, you can use RefCountedObjectNoVTable, which
|
||||
defines non-virtual AddRef/Release methods. These are still compatible with templated
|
||||
smart pointers like TCntPtr and ComPtr because those smart pointers don't actually use
|
||||
virtual method dispatch to call AddRef/Release.
|
||||
|
||||
To implement IUnknown, use Mso::UnknownObject.
|
||||
To implement IUnknown, use Mso::UnknownObject.
|
||||
|
||||
RefCountedObject provides variants that allow for various combinations of weak references,
|
||||
multiple inheritance, or just implementing IRefCounted.
|
||||
RefCountedObject provides variants that allow for various combinations of weak references,
|
||||
multiple inheritance, or just implementing IRefCounted.
|
||||
|
||||
Use Mso::Make to instantiate classes that derive from RefCountedObject.
|
||||
Use Mso::Make to instantiate classes that derive from RefCountedObject.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
1) A simple class that implements IRefCounted:
|
||||
1) A simple class that implements IRefCounted:
|
||||
|
||||
class Foo : public Mso::RefCountedObject<Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
2) A class that implements some interface that derives from IRefCounted:
|
||||
2) A class that implements some interface that derives from IRefCounted:
|
||||
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<IBar> spBar = Mso::Make<Foo, IBar>(); // Create a TCntPtr<IBar> directly.
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<IBar> spBar = Mso::Make<Foo, IBar>(); // Create a TCntPtr<IBar> directly.
|
||||
|
||||
|
||||
3) A class that inherits from multiple base types and implements IRefCounted:
|
||||
3) A class that inherits from multiple base types and implements IRefCounted:
|
||||
|
||||
class Foo : public Mso::RefCountedObject<SomeBaseType, SomeOtherBaseType, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<SomeBaseType, SomeOtherBaseType, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
4) A class that implements IRefCounted and has support for weak references:
|
||||
4) A class that implements IRefCounted and has support for weak references:
|
||||
|
||||
class Foo : public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
void Foo::RegisterForEventRequiringWeakRef(SomeEventSource& event)
|
||||
{
|
||||
// Use WeakPtr to capture a weak reference.
|
||||
Mso::WeakPtr<Foo> wrThis(this);
|
||||
event.Register([wrThis]
|
||||
{
|
||||
// Use GetStrongPtr to resolve the weak reference.
|
||||
auto spThis = wrThis.GetStrongPtr();
|
||||
if (spThis)
|
||||
spThis->DoSomethingInResponseToEvent();
|
||||
});
|
||||
}
|
||||
void Foo::RegisterForEventRequiringWeakRef(SomeEventSource& event)
|
||||
{
|
||||
// Use WeakPtr to capture a weak reference.
|
||||
Mso::WeakPtr<Foo> wrThis(this);
|
||||
event.Register([wrThis]
|
||||
{
|
||||
// Use GetStrongPtr to resolve the weak reference.
|
||||
auto spThis = wrThis.GetStrongPtr();
|
||||
if (spThis)
|
||||
spThis->DoSomethingInResponseToEvent();
|
||||
});
|
||||
}
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
5) A class that implements IRefCounted with weak references and a custom deleter:
|
||||
5) A class that implements IRefCounted with weak references and a custom deleter:
|
||||
|
||||
struct FooDeleter
|
||||
{
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
struct FooDeleter
|
||||
{
|
||||
template <typename T>
|
||||
static void Delete(T* obj) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
|
||||
using FooRefCountPolicy = Mso::WeakRefCountPolicy<FooDeleter>;
|
||||
using FooRefCountPolicy = Mso::WeakRefCountPolicy<FooDeleter>;
|
||||
|
||||
class Foo : public Mso::RefCountedObject<FooRefCountPolicy, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<FooRefCountPolicy, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
void Foo::RegisterForEventRequiringWeakRef(SomeEventSource& event)
|
||||
{
|
||||
// Use WeakPtr to capture a weak reference.
|
||||
Mso::WeakPtr<Foo> wrThis(this);
|
||||
event.Register([wrThis]
|
||||
{
|
||||
// Use GetStrongPtr to resolve the weak reference.
|
||||
auto spThis = wrThis.GetStrongPtr();
|
||||
if (spThis)
|
||||
spThis->DoSomethingInResponseToEvent();
|
||||
});
|
||||
}
|
||||
void Foo::RegisterForEventRequiringWeakRef(SomeEventSource& event)
|
||||
{
|
||||
// Use WeakPtr to capture a weak reference.
|
||||
Mso::WeakPtr<Foo> wrThis(this);
|
||||
event.Register([wrThis]
|
||||
{
|
||||
// Use GetStrongPtr to resolve the weak reference.
|
||||
auto spThis = wrThis.GetStrongPtr();
|
||||
if (spThis)
|
||||
spThis->DoSomethingInResponseToEvent();
|
||||
});
|
||||
}
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
6) A class that implements some IRefCounted-derived interface, with a private constructor.
|
||||
6) A class that implements some IRefCounted-derived interface, with a private constructor.
|
||||
|
||||
Use 'friend MakePolicy' to make your constructor private. This helps ensure that
|
||||
people only use Mso::Make to instantiate your class.
|
||||
Use 'friend MakePolicy' to make your constructor private. This helps ensure that
|
||||
people only use Mso::Make to instantiate your class.
|
||||
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
friend MakePolicy;
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
friend MakePolicy;
|
||||
|
||||
private:
|
||||
Foo(const Bar& bar) noexcept { ... } // Private constructor
|
||||
private:
|
||||
Foo(const Bar& bar) noexcept { ... } // Private constructor
|
||||
|
||||
...
|
||||
};
|
||||
...
|
||||
};
|
||||
|
||||
const Bar& bar = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make(bar);
|
||||
const Bar& bar = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make(bar);
|
||||
|
||||
|
||||
7) A class that uses the 'InitializeThis' pattern, which allows you to separate object construction
|
||||
from other initialization.
|
||||
7) A class that uses the 'InitializeThis' pattern, which allows you to separate object construction
|
||||
from other initialization.
|
||||
|
||||
If InitializeThis returns false, Mso::Make will crash the app.
|
||||
If InitializeThis returns false, Mso::Make will crash the app.
|
||||
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
class Foo : public Mso::RefCountedObject<IBar>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
Foo() noexcept { ... }
|
||||
Foo() noexcept { ... }
|
||||
|
||||
bool InitializeThis(const Baz& baz, const Qux& qux)
|
||||
{
|
||||
...
|
||||
bool InitializeThis(const Baz& baz, const Qux& qux)
|
||||
{
|
||||
...
|
||||
|
||||
// Return 'true' to indicate the object was successfully initialized. 'false' will crash the app.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// Return 'true' to indicate the object was successfully initialized. 'false' will crash the app.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const Baz& baz = ...;
|
||||
const Qux& qux = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make(baz, qux);
|
||||
const Baz& baz = ...;
|
||||
const Qux& qux = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make(baz, qux);
|
||||
|
||||
|
||||
8) A class that uses ref counting without a v-table.
|
||||
8) A class that uses ref counting without a v-table.
|
||||
|
||||
Non-virtual AddRef and Release methods are implemented. These are still compatible with most
|
||||
template-based smart-pointers (like TCntPtr and ComPtr) since they call 'AddRef' and 'Release'
|
||||
by name and not necessarily through virtual-method dispatch.
|
||||
Non-virtual AddRef and Release methods are implemented. These are still compatible with most
|
||||
template-based smart-pointers (like TCntPtr and ComPtr) since they call 'AddRef' and 'Release'
|
||||
by name and not necessarily through virtual-method dispatch.
|
||||
|
||||
Note that RefCountedObjectNoVTable uses the "Curiously Recursive Template Pattern" which requires
|
||||
that the derived type be passed in as a template parameter.
|
||||
Note that RefCountedObjectNoVTable uses the "Curiously Recursive Template Pattern" which requires
|
||||
that the derived type be passed in as a template parameter.
|
||||
|
||||
class Foo : public Mso::RefCountedObjectNoVTable<Foo>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObjectNoVTable<Foo>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
9) A class that implements IRefCounted without actual ref counting.
|
||||
9) A class that implements IRefCounted without actual ref counting.
|
||||
|
||||
This can be useful if the object's lifetime is managed via some other method, but the object
|
||||
still needs to be used with TCntPtr or other code that expects AddRef/Release.
|
||||
This often happens in two scenarios:
|
||||
This can be useful if the object's lifetime is managed via some other method, but the object
|
||||
still needs to be used with TCntPtr or other code that expects AddRef/Release.
|
||||
This often happens in two scenarios:
|
||||
|
||||
- Singletons that want to avoid any AddRef/Release because there is one long-lived instance.
|
||||
- Stack-based allocations for unit tests.
|
||||
- Singletons that want to avoid any AddRef/Release because there is one long-lived instance.
|
||||
- Stack-based allocations for unit tests.
|
||||
|
||||
class Foo : public Mso::RefCountedObject<Mso::RefCountStrategy::NoRefCount, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<Mso::RefCountStrategy::NoRefCount, Mso::IRefCounted>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
10) A class that implements IRefCounted with a custom stateless allocator.
|
||||
10) A class that implements IRefCounted with a custom stateless allocator.
|
||||
|
||||
struct FooAllocator
|
||||
{
|
||||
static void* Allocate(size_t size) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
struct FooAllocator
|
||||
{
|
||||
static void* Allocate(size_t size) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
|
||||
class Foo : public Mso::RefCountedObject<Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, FooAllocator>, IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, FooAllocator>,
|
||||
IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>();
|
||||
|
||||
|
||||
11) A class that implements IRefCounted with a custom stateful allocator.
|
||||
11) A class that implements IRefCounted with a custom stateful allocator.
|
||||
|
||||
struct ICustomHeap
|
||||
{
|
||||
virtual void* Alloc(size_t size) noexcept = 0;
|
||||
virtual void Free(void* ptr) noexcept = 0;
|
||||
};
|
||||
struct ICustomHeap
|
||||
{
|
||||
virtual void* Alloc(size_t size) noexcept = 0;
|
||||
virtual void Free(void* ptr) noexcept = 0;
|
||||
};
|
||||
|
||||
struct FooAllocator
|
||||
{
|
||||
static void* Allocate(size_t size, ICustomHeap* heap) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
struct FooAllocator
|
||||
{
|
||||
static void* Allocate(size_t size, ICustomHeap* heap) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
struct IBar : public Mso::IRefCounted { ... };
|
||||
|
||||
class Foo : public Mso::RefCountedObject<Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, FooAllocator>, IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
class Foo : public Mso::RefCountedObject<Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, FooAllocator>,
|
||||
IBar>
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
ICustomHeap& heap = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>(&heap);
|
||||
ICustomHeap& heap = ...;
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>(&heap);
|
||||
|
||||
|
||||
12) A class that has a throwing ctor and implements IRefCounted:
|
||||
12) A class that has a throwing ctor and implements IRefCounted:
|
||||
|
||||
struct Foo : public Mso::RefCountedObject<Mso::IRefCounted>
|
||||
{
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
struct Foo : public Mso::RefCountedObject<Mso::IRefCounted>
|
||||
{
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
Foo(); // can throw
|
||||
...
|
||||
};
|
||||
Foo(); // can throw
|
||||
...
|
||||
};
|
||||
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>(); // can throw
|
||||
Mso::TCntPtr<Foo> spFoo = Mso::Make<Foo>(); // can throw
|
||||
|
||||
*/
|
||||
|
||||
template <typename TBaseType0, typename... TBaseTypes>
|
||||
class RefCountedObject
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObject);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObject);
|
||||
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105597 /* tag_befwx */));
|
||||
}
|
||||
}
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105597 /* tag_befwx */));
|
||||
}
|
||||
}
|
||||
|
||||
void Release() const noexcept override
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x01105598 /* tag_befwy */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<RefCountedObject*>(this));
|
||||
}
|
||||
}
|
||||
void Release() const noexcept override
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x01105598 /* tag_befwy */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<RefCountedObject*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...) {}
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RefCountedObject() = default;
|
||||
virtual ~RefCountedObject() = default;
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
};
|
||||
|
||||
|
||||
template <typename TDeleter, typename TAllocator, typename TBaseType0, typename... TBaseTypes>
|
||||
class RefCountedObject<Mso::SimpleRefCountPolicy<TDeleter, TAllocator>, TBaseType0, TBaseTypes...>
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObject);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObject);
|
||||
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105599 /* tag_befwz */));
|
||||
}
|
||||
}
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x01105599 /* tag_befwz */));
|
||||
}
|
||||
}
|
||||
|
||||
void Release() const noexcept override
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559a /* tag_befw0 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
TDeleter::Delete(const_cast<RefCountedObject*>(this));
|
||||
}
|
||||
}
|
||||
void Release() const noexcept override
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559a /* tag_befw0 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
TDeleter::Delete(const_cast<RefCountedObject*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...) {}
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RefCountedObject() = default;
|
||||
virtual ~RefCountedObject() = default;
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
};
|
||||
|
||||
|
||||
template <typename TDeleter, typename TAllocator, typename TBaseType0, typename... TBaseTypes>
|
||||
class RefCountedObject<Mso::WeakRefCountPolicy<TDeleter, TAllocator>, TBaseType0, TBaseTypes...>
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::WeakRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::WeakRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using TypeToDelete = RefCountedObject; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_WEAKREFCOUNT(RefCountedObject);
|
||||
_MSO_OBJECT_WEAKREFCOUNT(RefCountedObject);
|
||||
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
GetWeakRef().AddRef();
|
||||
}
|
||||
void AddRef() const noexcept override
|
||||
{
|
||||
GetWeakRef().AddRef();
|
||||
}
|
||||
|
||||
void Release() const noexcept override
|
||||
{
|
||||
GetWeakRef().Release();
|
||||
}
|
||||
void Release() const noexcept override
|
||||
{
|
||||
GetWeakRef().Release();
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...) {}
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RefCountedObject() = default;
|
||||
virtual ~RefCountedObject() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename TBaseType0, typename... TBaseTypes>
|
||||
class RefCountedObject<Mso::RefCountStrategy::NoRefCount, TBaseType0, TBaseTypes...>
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
: public TBaseType0
|
||||
, public TBaseTypes...
|
||||
{
|
||||
public:
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
using RefCountedObjectType = RefCountedObject; // To use in derived class as "using Super = RefCountedObjectType"
|
||||
|
||||
_MSO_OBJECT_NOREFCOUNT(RefCountedObject);
|
||||
_MSO_OBJECT_NOREFCOUNT(RefCountedObject);
|
||||
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...) {}
|
||||
template <typename... TArgs>
|
||||
RefCountedObject(TArgs&&... args) noexcept : TBaseType0(std::forward<TArgs>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void AddRef() const noexcept override {}
|
||||
void Release() const noexcept override {}
|
||||
virtual ~RefCountedObject() = default;
|
||||
void AddRef() const noexcept override {}
|
||||
void Release() const noexcept override {}
|
||||
virtual ~RefCountedObject() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename... Ts>
|
||||
class RefCountedObjectNoVTable;
|
||||
|
||||
|
||||
/**
|
||||
A base class for ref counted objects that do not have v-table and need a simple ref count.
|
||||
A base class for ref counted objects that do not have v-table and need a simple ref count.
|
||||
*/
|
||||
template <typename TDerived>
|
||||
class RefCountedObjectNoVTable<TDerived>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<DefaultRefCountedDeleter, MakeAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectNoVTableType = RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectNoVTableType =
|
||||
RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObjectNoVTable);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObjectNoVTable);
|
||||
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x0110559b /* tag_befw1 */));
|
||||
}
|
||||
}
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x0110559b /* tag_befw1 */));
|
||||
}
|
||||
}
|
||||
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559c /* tag_befw2 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<TDerived*>(static_cast<const TDerived*>(this)));
|
||||
}
|
||||
}
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559c /* tag_befw2 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
RefCountPolicy::Delete(const_cast<TDerived*>(static_cast<const TDerived*>(this)));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCountedObjectNoVTable() = default;
|
||||
RefCountedObjectNoVTable() = default;
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
A base class for RefCounted objects that do not have v-table and need a simple ref count.
|
||||
A base class for RefCounted objects that do not have v-table and need a simple ref count.
|
||||
*/
|
||||
template <typename TDeleter, typename TAllocator, typename TDerived>
|
||||
class RefCountedObjectNoVTable<Mso::SimpleRefCountPolicy<TDeleter, TAllocator>, TDerived>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::SimpleRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectNoVTableType = RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectNoVTableType =
|
||||
RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObjectNoVTable);
|
||||
_MSO_OBJECT_SIMPLEREFCOUNT(RefCountedObjectNoVTable);
|
||||
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x0110559d /* tag_befw3 */));
|
||||
}
|
||||
}
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
if (++m_refCount == 1)
|
||||
{
|
||||
Debug(VerifyElseCrashSzTag(false, "Ref count must not bounce from zero", 0x0110559d /* tag_befw3 */));
|
||||
}
|
||||
}
|
||||
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559e /* tag_befw4 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
TDeleter::Delete(const_cast<TDerived*>(static_cast<const TDerived*>(this)));
|
||||
}
|
||||
}
|
||||
void Release() const noexcept
|
||||
{
|
||||
const uint32_t refCount = --m_refCount;
|
||||
Debug(VerifyElseCrashSzTag(
|
||||
static_cast<int32_t>(refCount) >= 0, "Ref count must not be negative.", 0x0110559e /* tag_befw4 */));
|
||||
if (refCount == 0)
|
||||
{
|
||||
TDeleter::Delete(const_cast<TDerived*>(static_cast<const TDerived*>(this)));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCountedObjectNoVTable() = default;
|
||||
RefCountedObjectNoVTable() = default;
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount { 1 };
|
||||
mutable std::atomic<uint32_t> m_refCount{1};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
A base class for RefCounted objects that do not have v-table and need support for weak ref count.
|
||||
A base class for RefCounted objects that do not have v-table and need support for weak ref count.
|
||||
*/
|
||||
template <typename TDeleter, typename TAllocator, typename TDerived>
|
||||
class RefCountedObjectNoVTable<Mso::WeakRefCountPolicy<TDeleter, TAllocator>, TDerived>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::WeakRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtor;
|
||||
using RefCountPolicy = Mso::WeakRefCountPolicy<TDeleter, TAllocator>;
|
||||
friend RefCountPolicy;
|
||||
|
||||
using RefCountedObjectNoVTableType = RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
using RefCountedObjectNoVTableType =
|
||||
RefCountedObjectNoVTable; // To use in derived class as "using Super = RefCountedObjectNoVTableType"
|
||||
using TypeToDelete = TDerived; // To verify that TypeToDelete is the first in the inheritance chain.
|
||||
|
||||
_MSO_OBJECT_WEAKREFCOUNT(RefCountedObjectNoVTable);
|
||||
_MSO_OBJECT_WEAKREFCOUNT(RefCountedObjectNoVTable);
|
||||
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
GetWeakRef().AddRef();
|
||||
}
|
||||
void AddRef() const noexcept
|
||||
{
|
||||
GetWeakRef().AddRef();
|
||||
}
|
||||
|
||||
void Release() const noexcept
|
||||
{
|
||||
GetWeakRef().Release();
|
||||
}
|
||||
void Release() const noexcept
|
||||
{
|
||||
GetWeakRef().Release();
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCountedObjectNoVTable() = default;
|
||||
RefCountedObjectNoVTable() = default;
|
||||
};
|
||||
|
||||
} // namespace Mso
|
||||
|
|
|
@ -21,7 +21,7 @@ share the same reference counter.
|
|||
//
|
||||
// Any class that is inherited from UnknownObjectWithWeakRef can be part of a swarm.
|
||||
//
|
||||
// Use SwarmMemberPtr or WeakPtr to reference swarm members between each other. Using TCntPtr between
|
||||
// Use SwarmMemberPtr or WeakPtr to reference swarm members between each other. Using TCntPtr between
|
||||
// swarm members may cause a memory leak because of an extra ref count to itself.
|
||||
// SwarmMemberPtr may be used for strong pointers and WeakPtr for weak pointers.
|
||||
// Do not use the ObjectWeakRef for weak pointers because there is only one ObjectWeakRef for all swarm members.
|
||||
|
@ -35,8 +35,10 @@ share the same reference counter.
|
|||
#pragma pack(push, _CRT_PACKING)
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible or deleted
|
||||
#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible or deleted
|
||||
#pragma warning(disable : 4625) // copy constructor could not be generated because a base class copy constructor is
|
||||
// inaccessible or deleted
|
||||
#pragma warning(disable : 4626) // assignment operator could not be generated because a base class assignment operator
|
||||
// is inaccessible or deleted
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
|
@ -48,44 +50,43 @@ namespace Details {
|
|||
template <typename T>
|
||||
struct ObjectWeakRefContainerDeleter
|
||||
{
|
||||
void operator ()(T* ptr) noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
ptr->DestroyContainer();
|
||||
}
|
||||
}
|
||||
void operator()(T* ptr) noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
ptr->DestroyContainer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Details
|
||||
|
||||
/**
|
||||
A base class to hold a swarm members.
|
||||
A swarm member is allocated as a part of the ObjectWeakRefContainer<T, SwarmMemberHolder> class instance.
|
||||
SwarmMemberHolder instances form a single linked list.
|
||||
A base class to hold a swarm members.
|
||||
A swarm member is allocated as a part of the ObjectWeakRefContainer<T, SwarmMemberHolder> class instance.
|
||||
SwarmMemberHolder instances form a single linked list.
|
||||
*/
|
||||
class SwarmMemberHolder
|
||||
{
|
||||
public:
|
||||
using UniquePtrType = std::unique_ptr<SwarmMemberHolder, Details::ObjectWeakRefContainerDeleter<SwarmMemberHolder>>;
|
||||
static_assert(sizeof(UniquePtrType) == sizeof(void*), "SwarmMemberHolder::UniquePtrType must have a pointer size. It is needed for using InterlockedCompareExchangePointer below.");
|
||||
using UniquePtrType = std::unique_ptr<SwarmMemberHolder, Details::ObjectWeakRefContainerDeleter<SwarmMemberHolder>>;
|
||||
static_assert(
|
||||
sizeof(UniquePtrType) == sizeof(void*),
|
||||
"SwarmMemberHolder::UniquePtrType must have a pointer size. It is needed for using InterlockedCompareExchangePointer below.");
|
||||
|
||||
virtual ~SwarmMemberHolder() noexcept
|
||||
{
|
||||
}
|
||||
virtual ~SwarmMemberHolder() noexcept {}
|
||||
|
||||
protected:
|
||||
virtual void DestroyObject() noexcept
|
||||
{
|
||||
}
|
||||
virtual void DestroyObject() noexcept {}
|
||||
|
||||
virtual void DestroyContainer() const noexcept = 0;
|
||||
virtual void DestroyContainer() const noexcept = 0;
|
||||
|
||||
private:
|
||||
UniquePtrType m_nextMember; // Pointer to the next member in a list.
|
||||
UniquePtrType m_nextMember; // Pointer to the next member in a list.
|
||||
|
||||
friend class Swarm;
|
||||
template <typename T> friend struct Details::ObjectWeakRefContainerDeleter;
|
||||
friend class Swarm;
|
||||
template <typename T>
|
||||
friend struct Details::ObjectWeakRefContainerDeleter;
|
||||
};
|
||||
|
||||
// Forward declaration
|
||||
|
@ -94,481 +95,474 @@ class Swarm;
|
|||
template <typename T, typename TContainer>
|
||||
struct SwarmMemberMemoryGuard
|
||||
{
|
||||
using Type = T;
|
||||
using Type = T;
|
||||
|
||||
~SwarmMemberMemoryGuard() noexcept;
|
||||
~SwarmMemberMemoryGuard() noexcept;
|
||||
|
||||
public: // We use a public fields to reduce number of generated methods.
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
Swarm* Container;
|
||||
TContainer* MemberContainer;
|
||||
// VC++ bug: Make sure that the order of the fields is the same for all memory guards. Otherwise, VC++ generates
|
||||
// incorrect code for ship builds.
|
||||
void* ObjMemory;
|
||||
T* Obj;
|
||||
Swarm* Container;
|
||||
TContainer* MemberContainer;
|
||||
};
|
||||
|
||||
/**
|
||||
Swarm is a special ObjectWeakRef that owns 1 or more objects.
|
||||
When Swarm ref count goes to zero it destroys all objects in the Swarm.
|
||||
Swarm may have any object inherited from a WeakSourceRefCount.
|
||||
Swarm is a special ObjectWeakRef that owns 1 or more objects.
|
||||
When Swarm ref count goes to zero it destroys all objects in the Swarm.
|
||||
Swarm may have any object inherited from a WeakSourceRefCount.
|
||||
*/
|
||||
MSO_CLASS_GUID(Swarm, "A0252DA6-7817-4536-B265-0A0152781652")
|
||||
class Swarm
|
||||
: public ObjectWeakRef
|
||||
class Swarm : public ObjectWeakRef
|
||||
{
|
||||
using Super = ObjectWeakRef;
|
||||
using UniquePtrType = SwarmMemberHolder::UniquePtrType;
|
||||
using Super = ObjectWeakRef;
|
||||
using UniquePtrType = SwarmMemberHolder::UniquePtrType;
|
||||
|
||||
public:
|
||||
Swarm() noexcept
|
||||
: ObjectWeakRef()
|
||||
, m_headMember()
|
||||
{
|
||||
}
|
||||
Swarm() noexcept : ObjectWeakRef(), m_headMember() {}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(Swarm);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(Swarm);
|
||||
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
static Mso::TCntPtr<TResult> Make(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, ObjectWeakRefContainer<T, Swarm>> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774c /* tag_bex3m */);
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
static Mso::TCntPtr<TResult> Make(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, ObjectWeakRefContainer<T, Swarm>> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard);
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774c /* tag_bex3m */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
static Mso::TCntPtr<TResult> MakeAlloc(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, ObjectWeakRefContainer<T, Swarm>> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774d /* tag_bex3n */);
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
static Mso::TCntPtr<TResult> MakeAlloc(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
typename T::RefCountPolicy::template MemoryGuard<T, ObjectWeakRefContainer<T, Swarm>> memoryGuard = {};
|
||||
T::RefCountPolicy::AllocateMemory(memoryGuard, std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(memoryGuard.ObjMemory, 0x0111774d /* tag_bex3n */);
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return Mso::TCntPtr<TResult>(result, /*fDoAddRef*/ false);
|
||||
}
|
||||
|
||||
// We return swarm member as a raw pointer because the new object shares ref count with the swarm and in many cases
|
||||
// we want to avoid the extra AddRef/Release because object's lifetime is already tracked.
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
TResult* MakeMember(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using Container = ObjectWeakRefContainer<T, SwarmMemberHolder>;
|
||||
// We return swarm member as a raw pointer because the new object shares ref count with the swarm and in many cases
|
||||
// we want to avoid the extra AddRef/Release because object's lifetime is already tracked.
|
||||
template <typename T, typename TResult = T, typename... TArgs>
|
||||
TResult* MakeMember(TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using Container = ObjectWeakRefContainer<T, SwarmMemberHolder>;
|
||||
|
||||
void* containerMemory = T::RefCountPolicy::Allocator::Allocate(sizeof(Container));
|
||||
VerifyAllocElseCrashTag(containerMemory, 0x0111774e /* tag_bex3o */);
|
||||
Container* container = ::new (containerMemory) Container();
|
||||
void* objMemory = container->Get();
|
||||
SwarmMemberMemoryGuard<T, Container> memoryGuard = { objMemory, nullptr, this, container };
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, this);
|
||||
AddWeakRef(); // To match ReleaseWeakRef() call in object destructor.
|
||||
void* containerMemory = T::RefCountPolicy::Allocator::Allocate(sizeof(Container));
|
||||
VerifyAllocElseCrashTag(containerMemory, 0x0111774e /* tag_bex3o */);
|
||||
Container* container = ::new (containerMemory) Container();
|
||||
void* objMemory = container->Get();
|
||||
SwarmMemberMemoryGuard<T, Container> memoryGuard = {objMemory, nullptr, this, container};
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, this);
|
||||
AddWeakRef(); // To match ReleaseWeakRef() call in object destructor.
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
AddSwarmMember(container);
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
AddSwarmMember(container);
|
||||
|
||||
memoryGuard.MemberContainer = nullptr; // To prevent memoryGuard from destroying the member container.
|
||||
memoryGuard.MemberContainer = nullptr; // To prevent memoryGuard from destroying the member container.
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return result;
|
||||
}
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
TResult* MakeMemberAlloc(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using Container = ObjectWeakRefContainer<T, SwarmMemberHolder>;
|
||||
template <typename T, typename TResult = T, typename TAllocArg, typename... TArgs>
|
||||
TResult* MakeMemberAlloc(TAllocArg&& allocArg, TArgs&&... args) noexcept(T::MakePolicy::IsNoExcept)
|
||||
{
|
||||
using Container = ObjectWeakRefContainer<T, SwarmMemberHolder>;
|
||||
|
||||
void* containerMemory = T::RefCountPolicy::Allocator::Allocate(sizeof(Container), std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(containerMemory, 0x0111774f /* tag_bex3p */);
|
||||
Container* container = ::new (containerMemory) Container();
|
||||
void* objMemory = container->Get();
|
||||
SwarmMemberMemoryGuard<T, Container> memoryGuard = { objMemory, nullptr, this, container };
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, this);
|
||||
AddWeakRef(); // To match ReleaseWeakRef() call in object destructor.
|
||||
void* containerMemory =
|
||||
T::RefCountPolicy::Allocator::Allocate(sizeof(Container), std::forward<TAllocArg>(allocArg));
|
||||
VerifyAllocElseCrashTag(containerMemory, 0x0111774f /* tag_bex3p */);
|
||||
Container* container = ::new (containerMemory) Container();
|
||||
void* objMemory = container->Get();
|
||||
SwarmMemberMemoryGuard<T, Container> memoryGuard = {objMemory, nullptr, this, container};
|
||||
Mso::Details::SetWeakRef(memoryGuard.ObjMemory, this);
|
||||
AddWeakRef(); // To match ReleaseWeakRef() call in object destructor.
|
||||
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
AddSwarmMember(container);
|
||||
T::MakePolicy::template Make<T>(memoryGuard, std::forward<TArgs>(args)...);
|
||||
Debug(T::RefCountPolicy::ValidateObject(memoryGuard));
|
||||
AddSwarmMember(container);
|
||||
|
||||
memoryGuard.MemberContainer = nullptr; // To prevent memoryGuard from destroying the member container.
|
||||
memoryGuard.MemberContainer = nullptr; // To prevent memoryGuard from destroying the member container.
|
||||
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return result;
|
||||
}
|
||||
TResult* result = memoryGuard.Obj;
|
||||
memoryGuard.Obj = nullptr; // To prevent memoryGuard from destroying the object.
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void* QueryCastWeakRef(const GUID& riid) noexcept override
|
||||
{
|
||||
if (riid == __uuidof(Swarm))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
virtual void* QueryCastWeakRef(const GUID& riid) noexcept override
|
||||
{
|
||||
if (riid == __uuidof(Swarm))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return Super::QueryCastWeakRef(riid);
|
||||
}
|
||||
return Super::QueryCastWeakRef(riid);
|
||||
}
|
||||
|
||||
static TCntPtr<Swarm> FromWeakRef(_In_opt_ const ObjectWeakRef* weakRef) noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
if (weakRef)
|
||||
{
|
||||
swarm = static_cast<Swarm*>(const_cast<ObjectWeakRef*>(weakRef)->QueryCastWeakRef(__uuidof(Swarm)));
|
||||
}
|
||||
|
||||
return swarm;
|
||||
}
|
||||
static TCntPtr<Swarm> FromWeakRef(_In_opt_ const ObjectWeakRef* weakRef) noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
if (weakRef)
|
||||
{
|
||||
swarm = static_cast<Swarm*>(const_cast<ObjectWeakRef*>(weakRef)->QueryCastWeakRef(__uuidof(Swarm)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static TCntPtr<Swarm> FromObject(_In_opt_ T* obj) noexcept
|
||||
{
|
||||
return FromObjectInternal(obj, OverloadTag());
|
||||
}
|
||||
return swarm;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Contains(_In_opt_ T* obj) const noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm = Swarm::FromObjectInternal(obj, OverloadTag());
|
||||
return this == swarm.Get();
|
||||
}
|
||||
template <typename T>
|
||||
static TCntPtr<Swarm> FromObject(_In_opt_ T* obj) noexcept
|
||||
{
|
||||
return FromObjectInternal(obj, OverloadTag());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Contains(_In_opt_ T* obj) const noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm = Swarm::FromObjectInternal(obj, OverloadTag());
|
||||
return this == swarm.Get();
|
||||
}
|
||||
|
||||
protected:
|
||||
using SwarmType = Swarm;
|
||||
using SwarmType = Swarm;
|
||||
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
for (SwarmMemberHolder* member = m_headMember.get(); member != nullptr; member = member->m_nextMember.get())
|
||||
{
|
||||
member->DestroyObject();
|
||||
}
|
||||
virtual void DestroyObject() noexcept override
|
||||
{
|
||||
for (SwarmMemberHolder* member = m_headMember.get(); member != nullptr; member = member->m_nextMember.get())
|
||||
{
|
||||
member->DestroyObject();
|
||||
}
|
||||
|
||||
Super::DestroyObject();
|
||||
}
|
||||
Super::DestroyObject();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static auto FromObjectInternal(_In_opt_ T* obj, OverloadTagP1) noexcept -> decltype(obj->GetWeakRef(), TCntPtr<Swarm>())
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
if (obj)
|
||||
{
|
||||
swarm = Swarm::FromWeakRef(&obj->GetWeakRef());
|
||||
}
|
||||
template <typename T>
|
||||
static auto FromObjectInternal(_In_opt_ T* obj, OverloadTagP1) noexcept
|
||||
-> decltype(obj->GetWeakRef(), TCntPtr<Swarm>())
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
if (obj)
|
||||
{
|
||||
swarm = Swarm::FromWeakRef(&obj->GetWeakRef());
|
||||
}
|
||||
|
||||
return swarm;
|
||||
}
|
||||
return swarm;
|
||||
}
|
||||
|
||||
// This method assumes that obj implements IUnknown.
|
||||
template <typename T>
|
||||
static TCntPtr<Swarm> FromObjectInternal(_In_opt_ T* obj, OverloadTagP2) noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
ObjectWeakRef* weakRef = query_cast<ObjectWeakRef*>(obj);
|
||||
if (weakRef)
|
||||
{
|
||||
swarm = FromWeakRef(weakRef);
|
||||
}
|
||||
// This method assumes that obj implements IUnknown.
|
||||
template <typename T>
|
||||
static TCntPtr<Swarm> FromObjectInternal(_In_opt_ T* obj, OverloadTagP2) noexcept
|
||||
{
|
||||
TCntPtr<Swarm> swarm;
|
||||
ObjectWeakRef* weakRef = query_cast<ObjectWeakRef*>(obj);
|
||||
if (weakRef)
|
||||
{
|
||||
swarm = FromWeakRef(weakRef);
|
||||
}
|
||||
|
||||
return swarm;
|
||||
}
|
||||
return swarm;
|
||||
}
|
||||
|
||||
void AddSwarmMember(SwarmMemberHolder* holder) noexcept
|
||||
{
|
||||
// Atomically set the new member as a head member in the single linked list
|
||||
for (;;)
|
||||
{
|
||||
SwarmMemberHolder* headMember = m_headMember.get();
|
||||
UniquePtrType& nextMember = holder->m_nextMember;
|
||||
nextMember.release(); // To make sure that reset will not delete value when we do second iteration.
|
||||
nextMember.reset(headMember);
|
||||
if (std::atomic_compare_exchange_weak((std::atomic<SwarmMemberHolder*>*)(void*)&m_headMember, &headMember, holder))
|
||||
{
|
||||
// Success: We were able to put pointer to our new member to the m_headMember without any conflicts.
|
||||
// The object is now owned by the m_headMember. Make sure that memberContainer does not delete it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void AddSwarmMember(SwarmMemberHolder* holder) noexcept
|
||||
{
|
||||
// Atomically set the new member as a head member in the single linked list
|
||||
for (;;)
|
||||
{
|
||||
SwarmMemberHolder* headMember = m_headMember.get();
|
||||
UniquePtrType& nextMember = holder->m_nextMember;
|
||||
nextMember.release(); // To make sure that reset will not delete value when we do second iteration.
|
||||
nextMember.reset(headMember);
|
||||
if (std::atomic_compare_exchange_weak(
|
||||
(std::atomic<SwarmMemberHolder*>*)(void*)&m_headMember, &headMember, holder))
|
||||
{
|
||||
// Success: We were able to put pointer to our new member to the m_headMember without any conflicts.
|
||||
// The object is now owned by the m_headMember. Make sure that memberContainer does not delete it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UniquePtrType m_headMember;
|
||||
UniquePtrType m_headMember;
|
||||
};
|
||||
|
||||
|
||||
// Define SwarmMemberMemoryGuard destructor here to avoid using Swarm class before it is defined.
|
||||
template <typename T, typename TContainer>
|
||||
inline SwarmMemberMemoryGuard<T, TContainer>::~SwarmMemberMemoryGuard() noexcept
|
||||
{
|
||||
if (ObjMemory)
|
||||
{
|
||||
// Object construction failed. Make sure that we release the weak reference added before construction.
|
||||
Container->ReleaseWeakRef();
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialize method failed.
|
||||
T::RefCountPolicy::Deleter::template Delete(static_cast<typename T::TypeToDelete*>(Obj));
|
||||
}
|
||||
if (ObjMemory)
|
||||
{
|
||||
// Object construction failed. Make sure that we release the weak reference added before construction.
|
||||
Container->ReleaseWeakRef();
|
||||
}
|
||||
else if (Obj)
|
||||
{
|
||||
// Initialize method failed.
|
||||
T::RefCountPolicy::Deleter::template Delete(static_cast<typename T::TypeToDelete*>(Obj));
|
||||
}
|
||||
|
||||
if (MemberContainer)
|
||||
{
|
||||
MemberContainer->~TContainer();
|
||||
T::RefCountPolicy::Allocator::Deallocate(MemberContainer);
|
||||
}
|
||||
if (MemberContainer)
|
||||
{
|
||||
MemberContainer->~TContainer();
|
||||
T::RefCountPolicy::Allocator::Deallocate(MemberContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
SwarmMemberPtr can be used by a swarm member to point to another swarm member.
|
||||
The pointer represents a strong reference. It does not contribute to the reference count if the pointed object
|
||||
is from the same swarm. This is done to avoid extra add ref count which may cause a memory leak.
|
||||
SwarmMemberPtr assumes that swarm members implement IUnknown.
|
||||
SwarmMemberPtr can be used by a swarm member to point to another swarm member.
|
||||
The pointer represents a strong reference. It does not contribute to the reference count if the pointed object
|
||||
is from the same swarm. This is done to avoid extra add ref count which may cause a memory leak.
|
||||
SwarmMemberPtr assumes that swarm members implement IUnknown.
|
||||
*/
|
||||
template <typename T, bool KnownSameSwarm = false>
|
||||
class SwarmMemberPtr
|
||||
{
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(SwarmMemberPtr);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(SwarmMemberPtr);
|
||||
|
||||
SwarmMemberPtr() noexcept
|
||||
{
|
||||
}
|
||||
SwarmMemberPtr() noexcept {}
|
||||
|
||||
#if __clang__
|
||||
//save the clang warning state and silence warnings about [-Wtautological-undefined-compare].
|
||||
// save the clang warning state and silence warnings about [-Wtautological-undefined-compare].
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
|
||||
#endif
|
||||
|
||||
template <typename TOther, typename = std::enable_if_t<std::is_base_of<T, TOther>::value>>
|
||||
SwarmMemberPtr(_In_opt_ TOther* ptr, _In_ const Swarm& ownerSwarm, _In_opt_ const Swarm* otherSwarm = nullptr) noexcept
|
||||
: m_ptr(ptr)
|
||||
, m_isDifferentSwarm(&ownerSwarm != (otherSwarm ? otherSwarm : Swarm::FromObject(ptr).Get()))
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->AddRef();
|
||||
}
|
||||
}
|
||||
template <typename TOther, typename = std::enable_if_t<std::is_base_of<T, TOther>::value>>
|
||||
SwarmMemberPtr(
|
||||
_In_opt_ TOther* ptr,
|
||||
_In_ const Swarm& ownerSwarm,
|
||||
_In_opt_ const Swarm* otherSwarm = nullptr) noexcept
|
||||
: m_ptr(ptr), m_isDifferentSwarm(&ownerSwarm != (otherSwarm ? otherSwarm : Swarm::FromObject(ptr).Get()))
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TOther, typename = std::enable_if_t<std::is_base_of<T, TOther>::value>>
|
||||
SwarmMemberPtr(_In_opt_ TOther* ptr, _In_ const Mso::ObjectWeakRef& ownerWeak, _In_opt_ const Mso::ObjectWeakRef* otherWeak = nullptr) noexcept
|
||||
: m_ptr(ptr)
|
||||
, m_isDifferentSwarm(&ownerWeak != (otherWeak ? otherWeak : Details::WeakPtrBase::GetWeakRef(ptr)))
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->AddRef();
|
||||
}
|
||||
}
|
||||
template <typename TOther, typename = std::enable_if_t<std::is_base_of<T, TOther>::value>>
|
||||
SwarmMemberPtr(
|
||||
_In_opt_ TOther* ptr,
|
||||
_In_ const Mso::ObjectWeakRef& ownerWeak,
|
||||
_In_opt_ const Mso::ObjectWeakRef* otherWeak = nullptr) noexcept
|
||||
: m_ptr(ptr), m_isDifferentSwarm(&ownerWeak != (otherWeak ? otherWeak : Details::WeakPtrBase::GetWeakRef(ptr)))
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
//resume clang warning state
|
||||
// resume clang warning state
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
SwarmMemberPtr(SwarmMemberPtr&& from) noexcept
|
||||
: m_ptr(from.m_ptr)
|
||||
, m_isDifferentSwarm(from.m_isDifferentSwarm)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_isDifferentSwarm = false;
|
||||
}
|
||||
SwarmMemberPtr(SwarmMemberPtr&& from) noexcept : m_ptr(from.m_ptr), m_isDifferentSwarm(from.m_isDifferentSwarm)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_isDifferentSwarm = false;
|
||||
}
|
||||
|
||||
~SwarmMemberPtr() noexcept
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
~SwarmMemberPtr() noexcept
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
SwarmMemberPtr& operator=(SwarmMemberPtr&& from) noexcept
|
||||
{
|
||||
if (this != &from)
|
||||
{
|
||||
SwarmMemberPtr<T>(std::move(from)).Swap(*this);
|
||||
}
|
||||
SwarmMemberPtr& operator=(SwarmMemberPtr&& from) noexcept
|
||||
{
|
||||
if (this != &from)
|
||||
{
|
||||
SwarmMemberPtr<T>(std::move(from)).Swap(*this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(SwarmMemberPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
swap(m_ptr, other.m_ptr);
|
||||
swap(m_isDifferentSwarm, other.m_isDifferentSwarm);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
void Swap(SwarmMemberPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
swap(m_ptr, other.m_ptr);
|
||||
swap(m_isDifferentSwarm, other.m_isDifferentSwarm);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
|
||||
void Reset() noexcept
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->Release();
|
||||
}
|
||||
void Reset() noexcept
|
||||
{
|
||||
if (m_ptr && m_isDifferentSwarm)
|
||||
{
|
||||
m_ptr->Release();
|
||||
}
|
||||
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
T* Get() const noexcept
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
T* Get() const noexcept
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
T** GetRaw() const noexcept
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
T** GetRaw() const noexcept
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return m_isDifferentSwarm;
|
||||
}
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return m_isDifferentSwarm;
|
||||
}
|
||||
|
||||
T* operator ->() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003713 /* tag_bad2t */);
|
||||
return Get();
|
||||
}
|
||||
T* operator->() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003713 /* tag_bad2t */);
|
||||
return Get();
|
||||
}
|
||||
|
||||
T& operator *() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003714 /* tag_bad2u */);
|
||||
return *m_ptr;
|
||||
}
|
||||
T& operator*() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003714 /* tag_bad2u */);
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_ptr = nullptr;
|
||||
bool m_isDifferentSwarm = false;
|
||||
T* m_ptr = nullptr;
|
||||
bool m_isDifferentSwarm = false;
|
||||
};
|
||||
|
||||
/**
|
||||
SwarmMemberPtr specialization for a case when we know for sure that we have a pointer inside of the same Swarm.
|
||||
In that case we can have just a single pointer field and have less code.
|
||||
It is preferable to use this smart pointer instead of raw pointer because we preserve semantic of the pointer.
|
||||
I.e. this is not just a random raw pointer, but a raw pointer which is made according to the specific design decision.
|
||||
SwarmMemberPtr specialization for a case when we know for sure that we have a pointer inside of the same Swarm.
|
||||
In that case we can have just a single pointer field and have less code.
|
||||
It is preferable to use this smart pointer instead of raw pointer because we preserve semantic of the pointer.
|
||||
I.e. this is not just a random raw pointer, but a raw pointer which is made according to the specific design decision.
|
||||
*/
|
||||
template <typename T>
|
||||
class SwarmMemberPtr<T, /*KnownSameSwarm*/true>
|
||||
class SwarmMemberPtr<T, /*KnownSameSwarm*/ true>
|
||||
{
|
||||
public:
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(SwarmMemberPtr);
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(SwarmMemberPtr);
|
||||
|
||||
SwarmMemberPtr() noexcept
|
||||
{
|
||||
}
|
||||
SwarmMemberPtr() noexcept {}
|
||||
|
||||
template <typename TOther, typename = typename std::enable_if<std::is_base_of<T, TOther>::value>::type>
|
||||
SwarmMemberPtr(_In_opt_ TOther* ptr) noexcept
|
||||
: m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
template <typename TOther, typename = typename std::enable_if<std::is_base_of<T, TOther>::value>::type>
|
||||
SwarmMemberPtr(_In_opt_ TOther* ptr) noexcept : m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
SwarmMemberPtr(SwarmMemberPtr&& from) noexcept
|
||||
: m_ptr(from.m_ptr)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
}
|
||||
SwarmMemberPtr(SwarmMemberPtr&& from) noexcept : m_ptr(from.m_ptr)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
SwarmMemberPtr& operator=(SwarmMemberPtr&& from) noexcept
|
||||
{
|
||||
if (this != &from)
|
||||
{
|
||||
SwarmMemberPtr<T, /*KnownSameSwarm*/true>(std::move(from)).Swap(*this);
|
||||
}
|
||||
SwarmMemberPtr& operator=(SwarmMemberPtr&& from) noexcept
|
||||
{
|
||||
if (this != &from)
|
||||
{
|
||||
SwarmMemberPtr<T, /*KnownSameSwarm*/ true>(std::move(from)).Swap(*this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(SwarmMemberPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
swap(m_ptr, other.m_ptr);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
void Swap(SwarmMemberPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
swap(m_ptr, other.m_ptr);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
|
||||
void Reset() noexcept
|
||||
{
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
void Reset() noexcept
|
||||
{
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
T* Get() const noexcept
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
T* Get() const noexcept
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
T** GetRaw() noexcept
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
T** GetRaw() noexcept
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* operator ->() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003715 /* tag_bad2v */);
|
||||
return m_ptr;
|
||||
}
|
||||
T* operator->() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003715 /* tag_bad2v */);
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
T& operator *() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003716 /* tag_bad2w */);
|
||||
return *m_ptr;
|
||||
}
|
||||
T& operator*() const noexcept
|
||||
{
|
||||
AssertTag(!IsEmpty(), 0x01003716 /* tag_bad2w */);
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_ptr = nullptr;
|
||||
T* m_ptr = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, bool KnownSameSwarm>
|
||||
void swap(SwarmMemberPtr<T, KnownSameSwarm>& left, SwarmMemberPtr<T, KnownSameSwarm>& right) noexcept
|
||||
{
|
||||
left.Swap(right);
|
||||
left.Swap(right);
|
||||
}
|
||||
|
||||
template <typename T, bool KnownSameSwarm>
|
||||
bool operator==(const SwarmMemberPtr<T, KnownSameSwarm>& left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.IsEmpty();
|
||||
return left.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T, bool KnownSameSwarm>
|
||||
bool operator==(std::nullptr_t, const SwarmMemberPtr<T, KnownSameSwarm>& right) noexcept
|
||||
{
|
||||
return right.IsEmpty();
|
||||
return right.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T, bool KnownSameSwarm>
|
||||
bool operator!=(const SwarmMemberPtr<T, KnownSameSwarm>& left, std::nullptr_t) noexcept
|
||||
{
|
||||
return !left.IsEmpty();
|
||||
return !left.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T, bool KnownSameSwarm>
|
||||
bool operator!=(std::nullptr_t, const SwarmMemberPtr<T, KnownSameSwarm>& right) noexcept
|
||||
{
|
||||
return !right.IsEmpty();
|
||||
return !right.IsEmpty();
|
||||
}
|
||||
|
||||
} // namespace Mso
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for object lifetime management using ref counting.
|
||||
Support for object lifetime management using ref counting.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -11,361 +11,359 @@
|
|||
|
||||
namespace Mso {
|
||||
|
||||
template <class T> class WeakPtr;
|
||||
template <class T>
|
||||
class WeakPtr;
|
||||
|
||||
namespace Details {
|
||||
|
||||
class WeakPtrBase
|
||||
{
|
||||
public:
|
||||
void Reset() noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = nullptr;
|
||||
m_weakRef = nullptr;
|
||||
}
|
||||
void Reset() noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = nullptr;
|
||||
m_weakRef = nullptr;
|
||||
}
|
||||
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
bool IsEmpty() const noexcept
|
||||
{
|
||||
return m_ptr == nullptr;
|
||||
}
|
||||
|
||||
bool IsExpired() const noexcept
|
||||
{
|
||||
return !m_ptr || m_weakRef->IsExpired();
|
||||
}
|
||||
bool IsExpired() const noexcept
|
||||
{
|
||||
return !m_ptr || m_weakRef->IsExpired();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static Mso::ObjectWeakRef* GetWeakRef(_In_opt_ T* from) noexcept
|
||||
{
|
||||
return GetWeakRefCore(from, 0, 0);
|
||||
}
|
||||
template <class T>
|
||||
static Mso::ObjectWeakRef* GetWeakRef(_In_opt_ T* from) noexcept
|
||||
{
|
||||
return GetWeakRefCore(from, 0, 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum { AttachTag = 0 };
|
||||
enum
|
||||
{
|
||||
AttachTag = 0
|
||||
};
|
||||
|
||||
WeakPtrBase() noexcept
|
||||
: m_ptr(nullptr)
|
||||
, m_weakRef(nullptr)
|
||||
{
|
||||
}
|
||||
WeakPtrBase() noexcept : m_ptr(nullptr), m_weakRef(nullptr) {}
|
||||
|
||||
WeakPtrBase(_In_opt_ void* ptr, _In_opt_ Mso::ObjectWeakRef* weakRef) noexcept
|
||||
: m_ptr(ptr)
|
||||
, m_weakRef(ptr ? weakRef : nullptr)
|
||||
{
|
||||
CheckedAddWeakRef();
|
||||
}
|
||||
WeakPtrBase(_In_opt_ void* ptr, _In_opt_ Mso::ObjectWeakRef* weakRef) noexcept
|
||||
: m_ptr(ptr), m_weakRef(ptr ? weakRef : nullptr)
|
||||
{
|
||||
CheckedAddWeakRef();
|
||||
}
|
||||
|
||||
WeakPtrBase(_In_opt_ void* ptr, _In_opt_ Mso::ObjectWeakRef* weakRef, int /*attachTag*/) noexcept
|
||||
: m_ptr(ptr)
|
||||
, m_weakRef(weakRef)
|
||||
{
|
||||
}
|
||||
WeakPtrBase(_In_opt_ void* ptr, _In_opt_ Mso::ObjectWeakRef* weakRef, int /*attachTag*/) noexcept
|
||||
: m_ptr(ptr), m_weakRef(weakRef)
|
||||
{
|
||||
}
|
||||
|
||||
~WeakPtrBase() noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
}
|
||||
~WeakPtrBase() noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
}
|
||||
|
||||
void Assign(_In_opt_ void* ptr, _In_opt_ ObjectWeakRef* weakRef) noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = ptr;
|
||||
m_weakRef = weakRef;
|
||||
CheckedAddWeakRef();
|
||||
}
|
||||
void Assign(_In_opt_ void* ptr, _In_opt_ ObjectWeakRef* weakRef) noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = ptr;
|
||||
m_weakRef = weakRef;
|
||||
CheckedAddWeakRef();
|
||||
}
|
||||
|
||||
void Assign(_In_opt_ void* ptr, _In_opt_ ObjectWeakRef* weakRef, int /*attachTag*/) noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = ptr;
|
||||
m_weakRef = weakRef;
|
||||
}
|
||||
void Assign(_In_opt_ void* ptr, _In_opt_ ObjectWeakRef* weakRef, int /*attachTag*/) noexcept
|
||||
{
|
||||
CheckedReleaseWeakRef();
|
||||
m_ptr = ptr;
|
||||
m_weakRef = weakRef;
|
||||
}
|
||||
|
||||
bool IncrementRefCountIfNotZero() const noexcept
|
||||
{
|
||||
return m_weakRef && m_weakRef->IncrementRefCountIfNotZero();
|
||||
}
|
||||
bool IncrementRefCountIfNotZero() const noexcept
|
||||
{
|
||||
return m_weakRef && m_weakRef->IncrementRefCountIfNotZero();
|
||||
}
|
||||
|
||||
private:
|
||||
void CheckedAddWeakRef() const noexcept
|
||||
{
|
||||
if (m_weakRef) m_weakRef->AddWeakRef();
|
||||
}
|
||||
|
||||
void CheckedReleaseWeakRef() const noexcept
|
||||
{
|
||||
if (m_weakRef) m_weakRef->ReleaseWeakRef();
|
||||
}
|
||||
void CheckedAddWeakRef() const noexcept
|
||||
{
|
||||
if (m_weakRef)
|
||||
m_weakRef->AddWeakRef();
|
||||
}
|
||||
|
||||
// Called when T has GetWeakRef method.
|
||||
template <class T>
|
||||
static auto GetWeakRefCore(_In_opt_ T* from, int, int) noexcept -> decltype(&from->GetWeakRef())
|
||||
{
|
||||
if (from)
|
||||
{
|
||||
Mso::ObjectWeakRef* result = &from->GetWeakRef();
|
||||
VerifyElseCrashSzTag(result, "GetWeakRef() returned null", 0x0100371a /* tag_bad20 */);
|
||||
return result;
|
||||
}
|
||||
void CheckedReleaseWeakRef() const noexcept
|
||||
{
|
||||
if (m_weakRef)
|
||||
m_weakRef->ReleaseWeakRef();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
// Called when T has GetWeakRef method.
|
||||
template <class T>
|
||||
static auto GetWeakRefCore(_In_opt_ T* from, int, int) noexcept -> decltype(&from->GetWeakRef())
|
||||
{
|
||||
if (from)
|
||||
{
|
||||
Mso::ObjectWeakRef* result = &from->GetWeakRef();
|
||||
VerifyElseCrashSzTag(result, "GetWeakRef() returned null", 0x0100371a /* tag_bad20 */);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Called when T has QueryInterface method.
|
||||
template <class T>
|
||||
static auto GetWeakRefCore(_In_opt_ T* from, int, ...) noexcept -> decltype(from->QueryInterface(__uuidof(Mso::ObjectWeakRef), nullptr), (Mso::ObjectWeakRef*)nullptr)
|
||||
{
|
||||
if (from)
|
||||
{
|
||||
Mso::ObjectWeakRef* result = query_cast<Mso::ObjectWeakRef*>(from);
|
||||
VerifyElseCrashSzTag(result, "query_cast<Mso::ObjectWeakRef*>() returned null", 0x0100371b /* tag_bad21 */);
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
// Called when T has QueryInterface method.
|
||||
template <class T>
|
||||
static auto GetWeakRefCore(_In_opt_ T* from, int, ...) noexcept
|
||||
-> decltype(from->QueryInterface(__uuidof(Mso::ObjectWeakRef), nullptr), (Mso::ObjectWeakRef*)nullptr)
|
||||
{
|
||||
if (from)
|
||||
{
|
||||
Mso::ObjectWeakRef* result = query_cast<Mso::ObjectWeakRef*>(from);
|
||||
VerifyElseCrashSzTag(result, "query_cast<Mso::ObjectWeakRef*>() returned null", 0x0100371b /* tag_bad21 */);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Called when T does not have GetWeakRef method.
|
||||
template <class T>
|
||||
static Mso::ObjectWeakRef* GetWeakRefCore(_In_opt_ T* /*from*/, ...) noexcept
|
||||
{
|
||||
VerifyElseCrashSzTag(false, "Cannot get ObjectWeakRef for an object.", 0x0100371c /* tag_bad22 */);
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
friend const void* GetUnsafePtr(const WeakPtrBase& weakPtrBase) noexcept
|
||||
{
|
||||
return weakPtrBase.m_ptr;
|
||||
}
|
||||
// Called when T does not have GetWeakRef method.
|
||||
template <class T>
|
||||
static Mso::ObjectWeakRef* GetWeakRefCore(_In_opt_ T* /*from*/, ...) noexcept
|
||||
{
|
||||
VerifyElseCrashSzTag(false, "Cannot get ObjectWeakRef for an object.", 0x0100371c /* tag_bad22 */);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
friend const void* GetUnsafePtr(const WeakPtrBase& weakPtrBase) noexcept
|
||||
{
|
||||
return weakPtrBase.m_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_ptr;
|
||||
Mso::ObjectWeakRef* m_weakRef;
|
||||
void* m_ptr;
|
||||
Mso::ObjectWeakRef* m_weakRef;
|
||||
|
||||
template <class T> friend class Mso::WeakPtr;
|
||||
template <class T>
|
||||
friend class Mso::WeakPtr;
|
||||
};
|
||||
|
||||
} // namespace Details
|
||||
|
||||
/**
|
||||
A weak pointer based on the ObjectWeakRef.
|
||||
It has two pointers: a pointer to the ObjectWeakRef and a pointer to the object.
|
||||
Having two pointers allow us to address the following scenarios:
|
||||
- We do not want to do any casting when retrieving a strong pointer.
|
||||
- Multiple objects share the same ObjectWeakRef.
|
||||
A weak pointer based on the ObjectWeakRef.
|
||||
It has two pointers: a pointer to the ObjectWeakRef and a pointer to the object.
|
||||
Having two pointers allow us to address the following scenarios:
|
||||
- We do not want to do any casting when retrieving a strong pointer.
|
||||
- Multiple objects share the same ObjectWeakRef.
|
||||
*/
|
||||
template <class T>
|
||||
class WeakPtr final : public Details::WeakPtrBase
|
||||
{
|
||||
using Super = Details::WeakPtrBase;
|
||||
using Super = Details::WeakPtrBase;
|
||||
|
||||
public:
|
||||
WeakPtr() noexcept : Super() {}
|
||||
WeakPtr(std::nullptr_t) noexcept : Super() {}
|
||||
WeakPtr() noexcept : Super() {}
|
||||
WeakPtr(std::nullptr_t) noexcept : Super() {}
|
||||
|
||||
WeakPtr(_In_opt_ T* from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(from, weakRef ? weakRef : GetWeakRefCore(from, 0, 0))
|
||||
{
|
||||
}
|
||||
WeakPtr(_In_opt_ T* from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(from, weakRef ? weakRef : GetWeakRefCore(from, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(_In_opt_ TOther* from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(static_cast<T*>(from), weakRef ? weakRef : GetWeakRefCore(from, 0, 0))
|
||||
{
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(_In_opt_ TOther* from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(static_cast<T*>(from), weakRef ? weakRef : GetWeakRefCore(from, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
WeakPtr(const Mso::TCntPtr<T>& from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(from.Get(), weakRef ? weakRef : GetWeakRefCore(from.Get(), 0, 0))
|
||||
{
|
||||
}
|
||||
WeakPtr(const Mso::TCntPtr<T>& from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(from.Get(), weakRef ? weakRef : GetWeakRefCore(from.Get(), 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(const Mso::TCntPtr<TOther>& from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(static_cast<T*>(from.Get()), weakRef ? weakRef : GetWeakRefCore(from.Get(), 0, 0))
|
||||
{
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(const Mso::TCntPtr<TOther>& from, _In_opt_ Mso::ObjectWeakRef* weakRef = nullptr) noexcept
|
||||
: Super(static_cast<T*>(from.Get()), weakRef ? weakRef : GetWeakRefCore(from.Get(), 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
WeakPtr(const WeakPtr& from) noexcept
|
||||
: Super(from.m_ptr, from.m_weakRef)
|
||||
{
|
||||
}
|
||||
WeakPtr(const WeakPtr& from) noexcept : Super(from.m_ptr, from.m_weakRef) {}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(const WeakPtr<TOther>& from) noexcept
|
||||
: Super(static_cast<T*>(static_cast<TOther*>(from.m_ptr)), from.m_weakRef)
|
||||
{
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(const WeakPtr<TOther>& from) noexcept
|
||||
: Super(static_cast<T*>(static_cast<TOther*>(from.m_ptr)), from.m_weakRef)
|
||||
{
|
||||
}
|
||||
|
||||
WeakPtr(WeakPtr&& from) noexcept
|
||||
: Super(from.m_ptr, from.m_weakRef, AttachTag)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
WeakPtr(WeakPtr&& from) noexcept : Super(from.m_ptr, from.m_weakRef, AttachTag)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(WeakPtr<TOther>&& from) noexcept
|
||||
: Super(static_cast<T*>(static_cast<TOther*>(from.m_ptr)), from.m_weakRef, AttachTag)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr(WeakPtr<TOther>&& from) noexcept
|
||||
: Super(static_cast<T*>(static_cast<TOther*>(from.m_ptr)), from.m_weakRef, AttachTag)
|
||||
{
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
|
||||
WeakPtr& operator=(std::nullptr_t) noexcept
|
||||
{
|
||||
Reset();
|
||||
return *this;
|
||||
}
|
||||
WeakPtr& operator=(std::nullptr_t) noexcept
|
||||
{
|
||||
Reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeakPtr& operator=(_In_opt_ T* from) noexcept
|
||||
{
|
||||
if (m_ptr != from)
|
||||
{
|
||||
Assign(from, GetWeakRefCore(from, 0, 0));
|
||||
}
|
||||
WeakPtr& operator=(_In_opt_ T* from) noexcept
|
||||
{
|
||||
if (m_ptr != from)
|
||||
{
|
||||
Assign(from, GetWeakRefCore(from, 0, 0));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(_In_opt_ TOther* from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(from);
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(from, 0, 0));
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(_In_opt_ TOther* from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(from);
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(from, 0, 0));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeakPtr& operator=(const Mso::TCntPtr<T>& from) noexcept
|
||||
{
|
||||
T* ptr = from.Get();
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(ptr, 0, 0));
|
||||
}
|
||||
WeakPtr& operator=(const Mso::TCntPtr<T>& from) noexcept
|
||||
{
|
||||
T* ptr = from.Get();
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(ptr, 0, 0));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(const Mso::TCntPtr<TOther>& from) noexcept
|
||||
{
|
||||
TOther* otherPtr = from.Get();
|
||||
T* ptr = static_cast<T*>(otherPtr);
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(otherPtr, 0, 0));
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(const Mso::TCntPtr<TOther>& from) noexcept
|
||||
{
|
||||
TOther* otherPtr = from.Get();
|
||||
T* ptr = static_cast<T*>(otherPtr);
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, GetWeakRefCore(otherPtr, 0, 0));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeakPtr& operator=(const WeakPtr& from) noexcept
|
||||
{
|
||||
if (m_ptr != from.m_ptr)
|
||||
{
|
||||
Assign(from.m_ptr, from.m_weakRef);
|
||||
}
|
||||
WeakPtr& operator=(const WeakPtr& from) noexcept
|
||||
{
|
||||
if (m_ptr != from.m_ptr)
|
||||
{
|
||||
Assign(from.m_ptr, from.m_weakRef);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(const WeakPtr<TOther>& from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(static_cast<TOther*>(from.m_ptr));
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, from.m_weakRef);
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(const WeakPtr<TOther>& from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(static_cast<TOther*>(from.m_ptr));
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, from.m_weakRef);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeakPtr& operator=(WeakPtr&& from) noexcept
|
||||
{
|
||||
if (m_ptr != from.m_ptr)
|
||||
{
|
||||
Assign(from.m_ptr, from.m_weakRef, AttachTag);
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
WeakPtr& operator=(WeakPtr&& from) noexcept
|
||||
{
|
||||
if (m_ptr != from.m_ptr)
|
||||
{
|
||||
Assign(from.m_ptr, from.m_weakRef, AttachTag);
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(WeakPtr<TOther>&& from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(static_cast<TOther*>(from.m_ptr));
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, from.m_weakRef, AttachTag);
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
template <class TOther, class = typename std::enable_if<std::is_convertible<TOther*, T*>::value>::type>
|
||||
WeakPtr& operator=(WeakPtr<TOther>&& from) noexcept
|
||||
{
|
||||
T* ptr = static_cast<T*>(static_cast<TOther*>(from.m_ptr));
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
Assign(ptr, from.m_weakRef, AttachTag);
|
||||
from.m_ptr = nullptr;
|
||||
from.m_weakRef = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Mso::TCntPtr<T> GetStrongPtr() const noexcept
|
||||
{
|
||||
Mso::TCntPtr<T> ptr;
|
||||
if (IncrementRefCountIfNotZero())
|
||||
{
|
||||
*ptr.GetRaw() = static_cast<T*>(m_ptr);
|
||||
}
|
||||
Mso::TCntPtr<T> GetStrongPtr() const noexcept
|
||||
{
|
||||
Mso::TCntPtr<T> ptr;
|
||||
if (IncrementRefCountIfNotZero())
|
||||
{
|
||||
*ptr.GetRaw() = static_cast<T*>(m_ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Swap(WeakPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
std::swap(m_ptr, other.m_ptr);
|
||||
std::swap(m_weakRef, other.m_weakRef);
|
||||
}
|
||||
void Swap(WeakPtr& other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
std::swap(m_ptr, other.m_ptr);
|
||||
std::swap(m_weakRef, other.m_weakRef);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool operator==(const WeakPtr<T1>& left, const WeakPtr<T2>& right) noexcept
|
||||
{
|
||||
return GetUnsafePtr(left) == GetUnsafePtr(right);
|
||||
return GetUnsafePtr(left) == GetUnsafePtr(right);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const WeakPtr<T>& left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.IsEmpty();
|
||||
return left.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(std::nullptr_t, const WeakPtr<T>& right) noexcept
|
||||
{
|
||||
return right.IsEmpty();
|
||||
return right.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool operator!=(const WeakPtr<T1>& left, const WeakPtr<T2>& right) noexcept
|
||||
{
|
||||
return GetUnsafePtr(left) != GetUnsafePtr(right);
|
||||
return GetUnsafePtr(left) != GetUnsafePtr(right);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const WeakPtr<T>& left, std::nullptr_t) noexcept
|
||||
{
|
||||
return !left.IsEmpty();
|
||||
return !left.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(std::nullptr_t, const WeakPtr<T>& right) noexcept
|
||||
{
|
||||
return !right.IsEmpty();
|
||||
return !right.IsEmpty();
|
||||
}
|
||||
|
||||
} // namespace Mso
|
||||
|
|
|
@ -16,177 +16,189 @@ Unit tests for the TCntPtrRef class
|
|||
MSO_STRUCT_GUID(ISimpleUnknown, "C70B7853-1EDB-4AB8-B2D2-F9951A99A847")
|
||||
struct DECLSPEC_NOVTABLE ISimpleUnknown : public IUnknown
|
||||
{
|
||||
virtual void DoSomething() = 0;
|
||||
virtual void DoSomething() = 0;
|
||||
};
|
||||
|
||||
struct ISimple : public Mso::IRefCounted
|
||||
{
|
||||
virtual void DoSomething() = 0;
|
||||
virtual void DoSomething() = 0;
|
||||
};
|
||||
|
||||
class SimpleTestRef : public Mso::TRefCountedImpl<ISimple>
|
||||
{
|
||||
void DoSomething() override {}
|
||||
void DoSomething() override {}
|
||||
};
|
||||
|
||||
inline static std::wstring ToString(ISimple* q) { return L""; }
|
||||
inline static std::wstring ToString(ISimple** q) { return L""; }
|
||||
inline static std::wstring ToString(const ISimple* q) { return L""; }
|
||||
inline static std::wstring ToString(ISimple* const* q) { return L""; }
|
||||
inline static std::wstring ToString(const Mso::TCntPtr<ISimple>* q) { return L""; }
|
||||
|
||||
TestClassComponent(TCntPtrRefTest, Mso.TCntPtrRef)
|
||||
TEST_CLASS(TCntPtrRefTest)
|
||||
inline static std::wstring ToString(ISimple* q)
|
||||
{
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_GetRaw)
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(ISimple** q)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetRaw(), (&spObj).GetRaw());
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(const ISimple* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(ISimple* const* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(const Mso::TCntPtr<ISimple>* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_StarOperator)
|
||||
TestClassComponent(TCntPtrRefTest, Mso.TCntPtrRef) TEST_CLASS (TCntPtrRefTest)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.Get(), *(&spObj));
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_GetRaw)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetRaw(), (&spObj).GetRaw());
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_GetAddressOf)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetAddressOf(), const_cast<ISimple**>((&spObj).GetAddressOf()));
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_StarOperator)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.Get(), *(&spObj));
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_ClearAndGetAddressOf)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj = new SimpleTestRef();
|
||||
TestAssert::AreEqual(spObj.GetRaw(), (&spObj).ClearAndGetAddressOf());
|
||||
TestAssert::IsNull(spObj.Get());
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_GetAddressOf)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetAddressOf(), const_cast<ISimple**>((&spObj).GetAddressOf()));
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_IID_PPV_ARGS_Helper)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj;
|
||||
void** ppObj = IID_PPV_ARGS_Helper(&spObj);
|
||||
void** ppDesired = &spObj;
|
||||
TestAssert::AreEqual((void*)ppDesired, (void*)ppObj);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_ClearAndGetAddressOf)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj = new SimpleTestRef();
|
||||
TestAssert::AreEqual(spObj.GetRaw(), (&spObj).ClearAndGetAddressOf());
|
||||
TestAssert::IsNull(spObj.Get());
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_EqualOperator)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj1;
|
||||
Mso::TCntPtr<IUnknown> spObj2;
|
||||
TestAssert::IsTrue(&spObj1 == &spObj1);
|
||||
TestAssert::IsTrue(&spObj2 == &spObj2);
|
||||
TestAssert::IsFalse(&spObj1 == &spObj2);
|
||||
TestAssert::IsFalse(&spObj2 == &spObj1);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_IID_PPV_ARGS_Helper)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj;
|
||||
void** ppObj = IID_PPV_ARGS_Helper(&spObj);
|
||||
void** ppDesired = &spObj;
|
||||
TestAssert::AreEqual((void*)ppDesired, (void*)ppObj);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NotEqualOperator)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj1;
|
||||
Mso::TCntPtr<IUnknown> spObj2;
|
||||
TestAssert::IsTrue(&spObj1 != &spObj2);
|
||||
TestAssert::IsTrue(&spObj2 != &spObj1);
|
||||
TestAssert::IsFalse(&spObj2 != &spObj2);
|
||||
TestAssert::IsFalse(&spObj1 != &spObj1);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_EqualOperator)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj1;
|
||||
Mso::TCntPtr<IUnknown> spObj2;
|
||||
TestAssert::IsTrue(&spObj1 == &spObj1);
|
||||
TestAssert::IsTrue(&spObj2 == &spObj2);
|
||||
TestAssert::IsFalse(&spObj1 == &spObj2);
|
||||
TestAssert::IsFalse(&spObj2 == &spObj1);
|
||||
}
|
||||
|
||||
static void TestFunc1(_In_ _Notnull_ ISimple* pObj, _Out_ ISimple** ppObj)
|
||||
{
|
||||
*ppObj = pObj;
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NotEqualOperator)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> spObj1;
|
||||
Mso::TCntPtr<IUnknown> spObj2;
|
||||
TestAssert::IsTrue(&spObj1 != &spObj2);
|
||||
TestAssert::IsTrue(&spObj2 != &spObj1);
|
||||
TestAssert::IsFalse(&spObj2 != &spObj2);
|
||||
TestAssert::IsFalse(&spObj1 != &spObj1);
|
||||
}
|
||||
|
||||
static void TestFunc2(_In_ _Notnull_ ISimple* pObj, _Out_ Mso::TCntPtr<ISimple>* spObj)
|
||||
{
|
||||
*spObj = Mso::TCntPtr<ISimple>(pObj, false);
|
||||
}
|
||||
static void TestFunc1(_In_ _Notnull_ ISimple * pObj, _Out_ ISimple * *ppObj)
|
||||
{
|
||||
*ppObj = pObj;
|
||||
}
|
||||
|
||||
static void TestFunc3(_In_ _Notnull_ ISimple* pObj, _Out_ void** ppT)
|
||||
{
|
||||
*ppT = pObj;
|
||||
}
|
||||
static void TestFunc2(_In_ _Notnull_ ISimple * pObj, _Out_ Mso::TCntPtr<ISimple> * spObj)
|
||||
{
|
||||
*spObj = Mso::TCntPtr<ISimple>(pObj, false);
|
||||
}
|
||||
|
||||
static void TestFunc4(_In_ _Notnull_ const ISimple* pObj, _Out_ const void** ppT)
|
||||
{
|
||||
*ppT = pObj;
|
||||
}
|
||||
static void TestFunc3(_In_ _Notnull_ ISimple * pObj, _Out_ void** ppT)
|
||||
{
|
||||
*ppT = pObj;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static void TestTCntPtrRef(U&& func)
|
||||
{
|
||||
T spObjOutput;
|
||||
Mso::TCntPtr<ISimple> spObjInput = new SimpleTestRef();
|
||||
ISimple* pObjInput = spObjInput.Detach();
|
||||
static void TestFunc4(_In_ _Notnull_ const ISimple* pObj, _Out_ const void** ppT)
|
||||
{
|
||||
*ppT = pObj;
|
||||
}
|
||||
|
||||
func(pObjInput, &spObjOutput);
|
||||
template <typename T, typename U>
|
||||
static void TestTCntPtrRef(U && func)
|
||||
{
|
||||
T spObjOutput;
|
||||
Mso::TCntPtr<ISimple> spObjInput = new SimpleTestRef();
|
||||
ISimple* pObjInput = spObjInput.Detach();
|
||||
|
||||
TestAssert::AreEqual(pObjInput, spObjOutput.Get());
|
||||
}
|
||||
func(pObjInput, &spObjOutput);
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppT)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc1);
|
||||
}
|
||||
TestAssert::AreEqual(pObjInput, spObjOutput.Get());
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_pTCntPtr)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc2);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppT)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc1);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppVoid)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc3);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_pTCntPtr)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc2);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppConstVoid)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc4);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppVoid)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc3);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_Const_TCntPtr)
|
||||
{
|
||||
const Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetThis(), &spObj);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_ppConstVoid)
|
||||
{
|
||||
TestTCntPtrRef<Mso::TCntPtr<ISimple>>(TestFunc4);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static void TestTCntPtrRef_Reinterpret_Cast()
|
||||
{
|
||||
using TNonConst = typename std::remove_const<T>::type;
|
||||
T spObj;
|
||||
U ppObjDesired = reinterpret_cast<U>(const_cast<TNonConst&>(spObj).GetAddressOf());
|
||||
U ppObj = static_cast<U>(&spObj);
|
||||
TestAssert::AreEqual((void*)ppObjDesired, (void*)ppObj);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_Const_TCntPtr)
|
||||
{
|
||||
const Mso::TCntPtr<ISimple> spObj;
|
||||
TestAssert::AreEqual(spObj.GetThis(), &spObj);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Reinterpret_Cast_ppVoid)
|
||||
{
|
||||
TestTCntPtrRef_Reinterpret_Cast<Mso::TCntPtr<ISimple>, void**>();
|
||||
}
|
||||
template <typename T, typename U>
|
||||
static void TestTCntPtrRef_Reinterpret_Cast()
|
||||
{
|
||||
using TNonConst = typename std::remove_const<T>::type;
|
||||
T spObj;
|
||||
U ppObjDesired = reinterpret_cast<U>(const_cast<TNonConst&>(spObj).GetAddressOf());
|
||||
U ppObj = static_cast<U>(&spObj);
|
||||
TestAssert::AreEqual((void*)ppObjDesired, (void*)ppObj);
|
||||
}
|
||||
|
||||
class SimpleTestRefUnknown : public Mso::UnknownObject<ISimpleUnknown>
|
||||
{
|
||||
void DoSomething() override {}
|
||||
};
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Reinterpret_Cast_ppVoid)
|
||||
{
|
||||
TestTCntPtrRef_Reinterpret_Cast<Mso::TCntPtr<ISimple>, void**>();
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Reinterpret_Cast_ppIUnknown)
|
||||
{
|
||||
TestTCntPtrRef_Reinterpret_Cast<Mso::TCntPtr<SimpleTestRefUnknown>, IUnknown**>();
|
||||
}
|
||||
class SimpleTestRefUnknown : public Mso::UnknownObject<ISimpleUnknown>
|
||||
{
|
||||
void DoSomething() override {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static void TestTCntPtrRef_Cast_pVoid()
|
||||
{
|
||||
T spObj;
|
||||
void* pVoid = &spObj;
|
||||
Mso::TCntPtr<ISimple>* pTCntPtr = &spObj;
|
||||
TestAssert::AreEqual(pVoid, (void*)pTCntPtr);
|
||||
}
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Reinterpret_Cast_ppIUnknown)
|
||||
{
|
||||
TestTCntPtrRef_Reinterpret_Cast<Mso::TCntPtr<SimpleTestRefUnknown>, IUnknown**>();
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Cast_pVoid)
|
||||
{
|
||||
TestTCntPtrRef_Cast_pVoid<Mso::TCntPtr<ISimple>>();
|
||||
}
|
||||
template <typename T>
|
||||
static void TestTCntPtrRef_Cast_pVoid()
|
||||
{
|
||||
T spObj;
|
||||
void* pVoid = &spObj;
|
||||
Mso::TCntPtr<ISimple>* pTCntPtr = &spObj;
|
||||
TestAssert::AreEqual(pVoid, (void*)pTCntPtr);
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_TCntPtrRef_NonConst_Cast_pVoid)
|
||||
{
|
||||
TestTCntPtrRef_Cast_pVoid<Mso::TCntPtr<ISimple>>();
|
||||
}
|
||||
};
|
|
@ -15,430 +15,411 @@ Unit tests for the TCntPtr smart pointer
|
|||
MSO_STRUCT_GUID(IUnkSimple, "9FEAB33F-E5D0-4A52-9216-6BA8BA9990A4")
|
||||
struct IUnkSimple : public IUnknown
|
||||
{
|
||||
virtual void DoSomething() = 0;
|
||||
virtual void DoSomething() = 0;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(IUnkSample, "8A2560F5-E28D-4342-8716-1BBD3A4603B3")
|
||||
struct IUnkSample : public IUnknown
|
||||
{
|
||||
virtual void DoAnything() = 0;
|
||||
virtual void DoAnything() = 0;
|
||||
};
|
||||
|
||||
struct ISimple : public Mso::IRefCounted
|
||||
{
|
||||
virtual void DoSomething() = 0;
|
||||
virtual void DoSomething() = 0;
|
||||
};
|
||||
|
||||
typedef std::function<void(bool inc)> RefCountChangedCallback;
|
||||
class SimpleClass : public ISimple
|
||||
{
|
||||
public:
|
||||
SimpleClass(RefCountChangedCallback&& onRefCountChanged)
|
||||
: m_refCount(0)
|
||||
, m_onRefCountChanged(std::move(onRefCountChanged))
|
||||
{
|
||||
}
|
||||
SimpleClass(RefCountChangedCallback&& onRefCountChanged)
|
||||
: m_refCount(0), m_onRefCountChanged(std::move(onRefCountChanged))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void AddRef() const noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
virtual void AddRef() const noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
|
||||
m_onRefCountChanged(/*incremented*/true);
|
||||
m_onRefCountChanged(/*incremented*/ true);
|
||||
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
|
||||
++m_refCount;
|
||||
}
|
||||
++m_refCount;
|
||||
}
|
||||
|
||||
virtual void Release() const noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
virtual void Release() const noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
|
||||
m_onRefCountChanged(/*incremented*/false);
|
||||
m_onRefCountChanged(/*incremented*/ false);
|
||||
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DoSomething() noexcept override
|
||||
{
|
||||
}
|
||||
virtual void DoSomething() noexcept override {}
|
||||
|
||||
void ClassDoSomething() noexcept
|
||||
{
|
||||
OACR_USE_PTR(this); // simulates access to 'this' for OACR build
|
||||
}
|
||||
void ClassDoSomething() noexcept
|
||||
{
|
||||
OACR_USE_PTR(this); // simulates access to 'this' for OACR build
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount;
|
||||
RefCountChangedCallback m_onRefCountChanged;
|
||||
mutable std::atomic<uint32_t> m_refCount;
|
||||
RefCountChangedCallback m_onRefCountChanged;
|
||||
};
|
||||
|
||||
class UnkSimpleClass : public IUnkSimple
|
||||
{
|
||||
public:
|
||||
UnkSimpleClass(RefCountChangedCallback&& onRefCountChanged)
|
||||
: m_refCount(0)
|
||||
, m_onRefCountChanged(std::move(onRefCountChanged))
|
||||
{
|
||||
}
|
||||
UnkSimpleClass(RefCountChangedCallback&& onRefCountChanged)
|
||||
: m_refCount(0), m_onRefCountChanged(std::move(onRefCountChanged))
|
||||
{
|
||||
}
|
||||
|
||||
_Success_(return == S_OK)
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, _Outptr_ void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
_Success_(return == S_OK)
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, _Outptr_ void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onRefCountChanged(/*incremented*/true);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
++m_refCount;
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onRefCountChanged(/*incremented*/ true);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
++m_refCount;
|
||||
return 1;
|
||||
}
|
||||
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onRefCountChanged(/*incremented*/false);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onRefCountChanged(/*incremented*/ false);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void DoSomething() noexcept override
|
||||
{
|
||||
}
|
||||
virtual void DoSomething() noexcept override {}
|
||||
|
||||
void ClassDoSomething() noexcept
|
||||
{
|
||||
OACR_USE_PTR(this); // simulates access to 'this' for OACR build
|
||||
}
|
||||
void ClassDoSomething() noexcept
|
||||
{
|
||||
OACR_USE_PTR(this); // simulates access to 'this' for OACR build
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::atomic<uint32_t> m_refCount;
|
||||
RefCountChangedCallback m_onRefCountChanged;
|
||||
mutable std::atomic<uint32_t> m_refCount;
|
||||
RefCountChangedCallback m_onRefCountChanged;
|
||||
};
|
||||
|
||||
inline static std::wstring ToString(UnkSimpleClass* q) { return L""; }
|
||||
inline static std::wstring ToString(SimpleClass* q) { return L""; }
|
||||
inline static std::wstring ToString(const UnkSimpleClass* q) { return L""; }
|
||||
inline static std::wstring ToString(const SimpleClass* q) { return L""; }
|
||||
inline static std::wstring ToString(UnkSimpleClass* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(SimpleClass* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(const UnkSimpleClass* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
inline static std::wstring ToString(const SimpleClass* q)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
class AggregatedObject : public Mso::UnknownObject<IUnkSimple, IUnkSample>
|
||||
{
|
||||
public:
|
||||
virtual void DoSomething() noexcept override
|
||||
{
|
||||
}
|
||||
virtual void DoSomething() noexcept override {}
|
||||
|
||||
virtual void DoAnything() noexcept override
|
||||
{
|
||||
}
|
||||
virtual void DoAnything() noexcept override {}
|
||||
};
|
||||
|
||||
template <typename TAction>
|
||||
static void ValidateRefCount(uint32_t expectedIncRefCountCallCount, TAction action)
|
||||
{
|
||||
uint32_t actualIncRefCountCallCount = 0;
|
||||
uint32_t actualDecRefCountCallCount = 0;
|
||||
auto callback = [&actualIncRefCountCallCount, &actualDecRefCountCallCount](bool incremented) noexcept
|
||||
{
|
||||
if (incremented)
|
||||
{
|
||||
++actualIncRefCountCallCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++actualDecRefCountCallCount;
|
||||
}
|
||||
};
|
||||
uint32_t actualIncRefCountCallCount = 0;
|
||||
uint32_t actualDecRefCountCallCount = 0;
|
||||
auto callback = [&actualIncRefCountCallCount, &actualDecRefCountCallCount](bool incremented) noexcept {
|
||||
if (incremented)
|
||||
{
|
||||
++actualIncRefCountCallCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++actualDecRefCountCallCount;
|
||||
}
|
||||
};
|
||||
|
||||
action(RefCountChangedCallback(callback));
|
||||
action(RefCountChangedCallback(callback));
|
||||
|
||||
TestAssert::AreEqual(actualIncRefCountCallCount, actualDecRefCountCallCount, L"IncCount != DecCount.");
|
||||
TestAssert::AreEqual(expectedIncRefCountCallCount, actualIncRefCountCallCount, L"Unexpected IncCount.");
|
||||
TestAssert::AreEqual(actualIncRefCountCallCount, actualDecRefCountCallCount, L"IncCount != DecCount.");
|
||||
TestAssert::AreEqual(expectedIncRefCountCallCount, actualIncRefCountCallCount, L"Unexpected IncCount.");
|
||||
}
|
||||
|
||||
TestClassComponent(TCntPtrTest, Mso.TCntPtr)
|
||||
TEST_CLASS(TCntPtrTest)
|
||||
{
|
||||
TestClassComponent(TCntPtrTest, Mso.TCntPtr) TEST_CLASS (TCntPtrTest){
|
||||
|
||||
TEST_METHOD(TCntPtr_EmptyCtor)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj;
|
||||
TEST_METHOD(TCntPtr_EmptyCtor){Mso::TCntPtr<SimpleClass> spObj;
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_NullCtor)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj = nullptr;
|
||||
Mso::TCntPtr<SimpleClass> spObj = nullptr;
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_DeprecatedNullCtor)
|
||||
{
|
||||
//TODO: Remove when we stop using NULL
|
||||
Mso::TCntPtr<SimpleClass> spObj;
|
||||
// TODO: Remove when we stop using NULL
|
||||
Mso::TCntPtr<SimpleClass> spObj;
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_Create)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CreateInterface)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<ISimple> spIntf(ptr);
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<ISimple> spIntf(ptr);
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyConstructor)
|
||||
{
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<SimpleClass> spSameObj(spObj);
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<SimpleClass> spSameObj(spObj);
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spSameObj.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spSameObj.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyConstructorInterface)
|
||||
{
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf(spObj);
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf(spObj);
|
||||
|
||||
TestAssert::AreEqual((void*) ptr, (void*) spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_MoveConstructor)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<SimpleClass> spSameObj(std::move(spObj));
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<SimpleClass> spSameObj(std::move(spObj));
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spSameObj.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spSameObj.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_MoveConstructorInterface)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf(std::move(spObj));
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf(std::move(spObj));
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
// Factory method to get benefits from using the move constructor
|
||||
static Mso::TCntPtr<SimpleClass> CreateSimpleClass(RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(std::move(onRefCountChanged));
|
||||
spObj->ClassDoSomething();
|
||||
return spObj; // std::move() not needed because the same type allows the named return value optimization.
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(std::move(onRefCountChanged));
|
||||
spObj->ClassDoSomething();
|
||||
return spObj; // std::move() not needed because the same type allows the named return value optimization.
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CallCreateSimpleClass)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spObj = CreateSimpleClass(std::move(onRefCountChanged));
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<ISimple> spObj = CreateSimpleClass(std::move(onRefCountChanged));
|
||||
|
||||
TestAssert::IsNotNull(spObj.Get(), L"Expected not a null value");
|
||||
});
|
||||
TestAssert::IsNotNull(spObj.Get(), L"Expected not a null value");
|
||||
});
|
||||
}
|
||||
|
||||
// Factory method to get benefits from using the move constructor
|
||||
static Mso::TCntPtr<ISimple> CreateISimple(RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(std::move(onRefCountChanged));
|
||||
spObj->ClassDoSomething();
|
||||
return std::move(spObj); // We should use std::move() here to avoid use of copy constructor.
|
||||
// Named value return optimization will not work because we have different types.
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(std::move(onRefCountChanged));
|
||||
spObj->ClassDoSomething();
|
||||
return std::move(spObj); // We should use std::move() here to avoid use of copy constructor.
|
||||
// Named value return optimization will not work because we have different types.
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CallCreateISimple)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<ISimple> spIntf = CreateISimple(std::move(onRefCountChanged));
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<ISimple> spIntf = CreateISimple(std::move(onRefCountChanged));
|
||||
|
||||
TestAssert::IsNotNull(spIntf.Get(), L"Expected not a null value");
|
||||
});
|
||||
TestAssert::IsNotNull(spIntf.Get(), L"Expected not a null value");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyAssignment)
|
||||
{
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj1(new SimpleClass(RefCountChangedCallback(onRefCountChanged)));
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj2(ptr);
|
||||
spObj1 = spObj2;
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<SimpleClass> spObj1(new SimpleClass(RefCountChangedCallback(onRefCountChanged)));
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj2(ptr);
|
||||
spObj1 = spObj2;
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj2.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj2.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyAssignmentInterface)
|
||||
{
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf = new SimpleClass(std::move(onRefCountChanged));
|
||||
spIntf = spObj;
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf = new SimpleClass(std::move(onRefCountChanged));
|
||||
spIntf = spObj;
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyAssignmentSameObject)
|
||||
{
|
||||
// See what happens when we assign TCntPtr to itself.
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
// See what happens when we assign TCntPtr to itself.
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
|
||||
OACR_WARNING_SUPPRESS(IDENTITY_ASSIGNMENT, "We want to test our code that nothing bad happens in this case");
|
||||
spObj = spObj;
|
||||
OACR_WARNING_SUPPRESS(IDENTITY_ASSIGNMENT, "We want to test our code that nothing bad happens in this case");
|
||||
spObj = spObj;
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_CopyAssignmentConst)
|
||||
{
|
||||
// Test that TCntPtr can accept a const variable and AddRef/Release methods are not const.
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<const UnkSimpleClass> spObj1(new UnkSimpleClass(RefCountChangedCallback(onRefCountChanged)));
|
||||
const UnkSimpleClass* ptr = new UnkSimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<const UnkSimpleClass> spObj2(ptr);
|
||||
spObj1 = spObj2;
|
||||
// Test that TCntPtr can accept a const variable and AddRef/Release methods are not const.
|
||||
ValidateRefCount(3, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<const UnkSimpleClass> spObj1(new UnkSimpleClass(RefCountChangedCallback(onRefCountChanged)));
|
||||
const UnkSimpleClass* ptr = new UnkSimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<const UnkSimpleClass> spObj2(ptr);
|
||||
spObj1 = spObj2;
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj2.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj2.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_MoveAssignment)
|
||||
{
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj1 = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj2(ptr);
|
||||
spObj1 = std::move(spObj2);
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<SimpleClass> spObj1 = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj2(ptr);
|
||||
spObj1 = std::move(spObj2);
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::IsNull(spObj2.Get(), L"Expected null");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj1.Get(), L"Expected ptr");
|
||||
TestAssert::IsNull(spObj2.Get(), L"Expected null");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_MoveAssignmentInterface)
|
||||
{
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf = new SimpleClass(std::move(onRefCountChanged));
|
||||
spIntf = std::move(spObj);
|
||||
ValidateRefCount(2, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
Mso::TCntPtr<ISimple> spIntf = new SimpleClass(std::move(onRefCountChanged));
|
||||
spIntf = std::move(spObj);
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spIntf.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_MoveAssignmentSameObject)
|
||||
{
|
||||
// Our copy assignment does not check if we use the same object. This test is to see that nothing bad happens.
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
spObj = std::move(spObj);
|
||||
// Our copy assignment does not check if we use the same object. This test is to see that nothing bad happens.
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
SimpleClass* ptr = new SimpleClass(std::move(onRefCountChanged));
|
||||
Mso::TCntPtr<SimpleClass> spObj(ptr);
|
||||
spObj = std::move(spObj);
|
||||
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
TestAssert::AreEqual((void*)ptr, (void*)spObj.Get(), L"Expected ptr");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_NullAssignment)
|
||||
{
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged)
|
||||
{
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
spObj = nullptr;
|
||||
ValidateRefCount(1, [](RefCountChangedCallback&& onRefCountChanged) {
|
||||
Mso::TCntPtr<SimpleClass> spObj = new SimpleClass(RefCountChangedCallback(onRefCountChanged));
|
||||
spObj = nullptr;
|
||||
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
});
|
||||
TestAssert::IsNull(spObj.Get(), L"Expected null");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_IsEqualObject_BothISimpleClass_AreEqual)
|
||||
{
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSimple> spObjTwo = spObj;
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSimple> spObjTwo = spObj;
|
||||
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(spObj, spObjTwo));
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(spObj, spObjTwo));
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_IsEqualObject_DifferentInterfaceTypes_AreEqual)
|
||||
{
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSample> spSample;
|
||||
TestAssert::HrSucceeded(Mso::ComUtil::HrQueryFrom(spSample, spObj));
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSample> spSample;
|
||||
TestAssert::HrSucceeded(Mso::ComUtil::HrQueryFrom(spSample, spObj));
|
||||
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(spObj, spSample));
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(spObj, spSample));
|
||||
}
|
||||
|
||||
TEST_METHOD(TCntPtr_IsEqualObject_DifferentObject_AreNotEqual)
|
||||
{
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSimple> spObjTwo = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSimple> spObj = Mso::Make<AggregatedObject>();
|
||||
Mso::TCntPtr<IUnkSimple> spObjTwo = Mso::Make<AggregatedObject>();
|
||||
|
||||
TestAssert::IsFalse(Mso::ComUtil::AreEqualObjects(spObj, spObjTwo));
|
||||
TestAssert::IsFalse(Mso::ComUtil::AreEqualObjects(spObj, spObjTwo));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
;
|
||||
|
|
|
@ -12,337 +12,319 @@ Unit tests for classes in the ObjectSwarm.h
|
|||
#include <test/skipSEHUT.h>
|
||||
#include <test/testCheck.h>
|
||||
|
||||
//#define TEST_BAD_INHERITANCE1 // Uncomment to confirm VEC, but observe a memory leak. We cannot safely destroy this class.
|
||||
//#define TEST_BAD_INHERITANCE1 // Uncomment to confirm VEC, but observe a memory leak. We cannot safely destroy this
|
||||
//class.
|
||||
|
||||
MSO_STRUCT_GUID(IFixedSwarmSample1, "AA2EB60A-06DD-486F-AC9B-DBF1DDE21408")
|
||||
struct DECLSPEC_NOVTABLE IFixedSwarmSample1 : IUnknown
|
||||
{
|
||||
virtual int GetValue1() = 0;
|
||||
virtual int GetValue1() = 0;
|
||||
};
|
||||
|
||||
struct DECLSPEC_NOVTABLE IFixedSwarmSample2 : Mso::IRefCounted
|
||||
{
|
||||
virtual int GetValue2() = 0;
|
||||
virtual int GetValue2() = 0;
|
||||
};
|
||||
|
||||
struct DECLSPEC_NOVTABLE IFixedSwarmSample3 : Mso::IRefCounted
|
||||
{
|
||||
virtual int GetValue3() = 0;
|
||||
virtual int GetValue3() = 0;
|
||||
};
|
||||
|
||||
class FixedSwarmMemberSample1 final
|
||||
: public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample1>
|
||||
class FixedSwarmMemberSample1 final : public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample1>
|
||||
{
|
||||
public:
|
||||
FixedSwarmMemberSample1(bool& created, bool& deleted) noexcept
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
created = true;
|
||||
}
|
||||
FixedSwarmMemberSample1(bool& created, bool& deleted) noexcept : m_deleted(deleted)
|
||||
{
|
||||
created = true;
|
||||
}
|
||||
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~FixedSwarmMemberSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
virtual ~FixedSwarmMemberSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
// We can add to FixedSwarm IRefCounted objects
|
||||
class FixedSwarmMemberSample2 final
|
||||
: public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample2>
|
||||
class FixedSwarmMemberSample2 final : public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample2>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
class FixedSwarmMemberSample3 final
|
||||
: public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample3>
|
||||
class FixedSwarmMemberSample3 final : public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample3>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue3() noexcept override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue3() noexcept override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
|
||||
class FixedSwarmMemberSampleBase
|
||||
: public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample2>
|
||||
class FixedSwarmMemberSampleBase : public Mso::RefCountedObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample2>
|
||||
{
|
||||
public:
|
||||
FixedSwarmMemberSampleBase() noexcept
|
||||
{
|
||||
}
|
||||
FixedSwarmMemberSampleBase() noexcept {}
|
||||
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~FixedSwarmMemberSampleBase() noexcept
|
||||
{
|
||||
}
|
||||
virtual ~FixedSwarmMemberSampleBase() noexcept {}
|
||||
};
|
||||
|
||||
class FixedSwarmMemberSample4Throw
|
||||
: public FixedSwarmMemberSampleBase
|
||||
class FixedSwarmMemberSample4Throw : public FixedSwarmMemberSampleBase
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
FixedSwarmMemberSample4Throw()
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
FixedSwarmMemberSample4Throw()
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
};
|
||||
|
||||
class FixedSwarmMemberSample5InitThrow
|
||||
: public FixedSwarmMemberSampleBase
|
||||
class FixedSwarmMemberSample5InitThrow : public FixedSwarmMemberSampleBase
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
void InitializeThis()
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis()
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef TEST_BAD_INHERITANCE1
|
||||
class SomeVirtualClass
|
||||
{
|
||||
public:
|
||||
virtual ~SomeVirtualClass() = default;
|
||||
virtual ~SomeVirtualClass() = default;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// !!! Mso::UnknownObject must be always the first one in the inheritance !!!
|
||||
class BadFixedSwarmMember1 final
|
||||
: public SomeVirtualClass
|
||||
, public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample1>
|
||||
: public SomeVirtualClass
|
||||
, public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, IFixedSwarmSample1>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
TEST_CLASS(ObjectFixedSwarmTest)
|
||||
TEST_CLASS (ObjectFixedSwarmTest)
|
||||
{
|
||||
TEST_METHOD(ObjectFixedSwarm_NoMemberInit)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
TestAssert::IsFalse(swarm1.IsEmpty());
|
||||
TEST_METHOD(ObjectFixedSwarm_NoMemberInit)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
TestAssert::IsFalse(swarm1.IsEmpty());
|
||||
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2>;
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
TestAssert::IsFalse(swarm2.IsEmpty());
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2>;
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
TestAssert::IsFalse(swarm2.IsEmpty());
|
||||
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
TestAssert::IsFalse(swarm3.IsEmpty());
|
||||
}
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
TestAssert::IsFalse(swarm3.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_NoMemberRefCount)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
TestAssert::IsFalse(swarm1.IsEmpty());
|
||||
|
||||
Debug(TestAssert::AreEqual(1, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1, swarm1->WeakRefCount()));
|
||||
TEST_METHOD(ObjectFixedSwarm_NoMemberRefCount)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
TestAssert::IsFalse(swarm1.IsEmpty());
|
||||
|
||||
Mso::TCntPtr<Swarm1> swarm11 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1, swarm1->WeakRefCount()));
|
||||
Debug(TestAssert::AreEqual(1, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1, swarm1->WeakRefCount()));
|
||||
|
||||
Mso::WeakPtr<Swarm1> weakSwarm1 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
Mso::TCntPtr<Swarm1> swarm11 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1, swarm1->WeakRefCount()));
|
||||
|
||||
Mso::WeakPtr<Swarm1> weakSwarm11 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(3, swarm1->WeakRefCount()));
|
||||
}
|
||||
Mso::WeakPtr<Swarm1> weakSwarm1 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_GetMember)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
TestAssert::IsFalse(swarm3.IsEmpty());
|
||||
Mso::WeakPtr<Swarm1> weakSwarm11 = swarm1;
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(3, swarm1->WeakRefCount()));
|
||||
}
|
||||
|
||||
FixedSwarmMemberSample1* sample1 = swarm3->Get<0>();
|
||||
TestAssert::IsNull(sample1);
|
||||
TEST_METHOD(ObjectFixedSwarm_GetMember)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
TestAssert::IsFalse(swarm3.IsEmpty());
|
||||
|
||||
FixedSwarmMemberSample2* sample2 = swarm3->Get<1>();
|
||||
TestAssert::IsNull(sample2);
|
||||
FixedSwarmMemberSample1* sample1 = swarm3->Get<0>();
|
||||
TestAssert::IsNull(sample1);
|
||||
|
||||
FixedSwarmMemberSample3* sample3 = swarm3->Get<2>();
|
||||
TestAssert::IsNull(sample3);
|
||||
}
|
||||
FixedSwarmMemberSample2* sample2 = swarm3->Get<1>();
|
||||
TestAssert::IsNull(sample2);
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_OneMember)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
FixedSwarmMemberSample3* sample3 = swarm3->Get<2>();
|
||||
TestAssert::IsNull(sample3);
|
||||
}
|
||||
|
||||
{
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
TEST_METHOD(ObjectFixedSwarm_OneMember)
|
||||
{
|
||||
using Swarm1 = Mso::FixedSwarm<FixedSwarmMemberSample1>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
|
||||
{
|
||||
swarm1->MakeMember<0>(created, deleted);
|
||||
Mso::TCntPtr<IFixedSwarmSample1> member0 = swarm1->Get<0>();
|
||||
TestAssert::IsFalse(member0.IsEmpty());
|
||||
TestAssert::IsTrue(created);
|
||||
TestAssert::IsFalse(deleted);
|
||||
{
|
||||
Mso::TCntPtr<Swarm1> swarm1 = Mso::Make<Swarm1>();
|
||||
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
}
|
||||
{
|
||||
swarm1->MakeMember<0>(created, deleted);
|
||||
Mso::TCntPtr<IFixedSwarmSample1> member0 = swarm1->Get<0>();
|
||||
TestAssert::IsFalse(member0.IsEmpty());
|
||||
TestAssert::IsTrue(created);
|
||||
TestAssert::IsFalse(deleted);
|
||||
|
||||
TestAssert::IsFalse(deleted);
|
||||
Debug(TestAssert::AreEqual(1, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsFalse(deleted);
|
||||
Debug(TestAssert::AreEqual(1, swarm1->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm1->WeakRefCount()));
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_ThreeMembers)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
{
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
TEST_METHOD(ObjectFixedSwarm_ThreeMembers)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
|
||||
{
|
||||
swarm3->MakeMember<0>(created, deleted);
|
||||
Mso::TCntPtr<IFixedSwarmSample1> member0 = swarm3->Get<0>();
|
||||
TestAssert::IsFalse(member0.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(2, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm3->WeakRefCount()));
|
||||
{
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
|
||||
swarm3->MakeMember<1>();
|
||||
Mso::TCntPtr<IFixedSwarmSample2> member1 = swarm3->Get<1>();
|
||||
TestAssert::IsFalse(member1.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(3, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(3, swarm3->WeakRefCount()));
|
||||
{
|
||||
swarm3->MakeMember<0>(created, deleted);
|
||||
Mso::TCntPtr<IFixedSwarmSample1> member0 = swarm3->Get<0>();
|
||||
TestAssert::IsFalse(member0.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(2, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2, swarm3->WeakRefCount()));
|
||||
|
||||
swarm3->MakeMember<2>();
|
||||
Mso::TCntPtr<FixedSwarmMemberSample3> member2 = swarm3->Get<2>();
|
||||
TestAssert::IsFalse(member2.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(4, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(4, swarm3->WeakRefCount()));
|
||||
}
|
||||
swarm3->MakeMember<1>();
|
||||
Mso::TCntPtr<IFixedSwarmSample2> member1 = swarm3->Get<1>();
|
||||
TestAssert::IsFalse(member1.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(3, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(3, swarm3->WeakRefCount()));
|
||||
|
||||
TestAssert::IsFalse(deleted);
|
||||
Debug(TestAssert::AreEqual(1, swarm3->RefCount()));
|
||||
// Each object +1 for weak reference count, +1 weak reference if strong ref count != 0.
|
||||
Debug(TestAssert::AreEqual(4, swarm3->WeakRefCount()));
|
||||
}
|
||||
swarm3->MakeMember<2>();
|
||||
Mso::TCntPtr<FixedSwarmMemberSample3> member2 = swarm3->Get<2>();
|
||||
TestAssert::IsFalse(member2.IsEmpty());
|
||||
Debug(TestAssert::AreEqual(4, swarm3->RefCount()));
|
||||
Debug(TestAssert::AreEqual(4, swarm3->WeakRefCount()));
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsFalse(deleted);
|
||||
Debug(TestAssert::AreEqual(1, swarm3->RefCount()));
|
||||
// Each object +1 for weak reference count, +1 weak reference if strong ref count != 0.
|
||||
Debug(TestAssert::AreEqual(4, swarm3->WeakRefCount()));
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_WeakPtr)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
Mso::WeakPtr<IFixedSwarmSample1> weakMember0;
|
||||
Mso::WeakPtr<FixedSwarmMemberSample2> weakMember1;
|
||||
Mso::WeakPtr<IFixedSwarmSample3> weakMember2;
|
||||
TEST_METHOD(ObjectFixedSwarm_WeakPtr)
|
||||
{
|
||||
using Swarm3 = Mso::FixedSwarm<FixedSwarmMemberSample1, FixedSwarmMemberSample2, FixedSwarmMemberSample3>;
|
||||
bool created = false;
|
||||
bool deleted = false;
|
||||
|
||||
{
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
swarm3->MakeMember<0>(created, deleted);
|
||||
swarm3->MakeMember<1>();
|
||||
swarm3->MakeMember<2>();
|
||||
Mso::WeakPtr<IFixedSwarmSample1> weakMember0;
|
||||
Mso::WeakPtr<FixedSwarmMemberSample2> weakMember1;
|
||||
Mso::WeakPtr<IFixedSwarmSample3> weakMember2;
|
||||
|
||||
weakMember0 = swarm3->GetWeakPtr<0>();
|
||||
weakMember1 = swarm3->GetWeakPtr<1>();
|
||||
weakMember2 = swarm3->GetWeakPtr<2>();
|
||||
{
|
||||
Mso::TCntPtr<Swarm3> swarm3 = Mso::Make<Swarm3>();
|
||||
swarm3->MakeMember<0>(created, deleted);
|
||||
swarm3->MakeMember<1>();
|
||||
swarm3->MakeMember<2>();
|
||||
|
||||
TestAssert::IsFalse(weakMember0.IsExpired());
|
||||
TestAssert::IsFalse(weakMember1.IsExpired());
|
||||
TestAssert::IsFalse(weakMember2.IsExpired());
|
||||
weakMember0 = swarm3->GetWeakPtr<0>();
|
||||
weakMember1 = swarm3->GetWeakPtr<1>();
|
||||
weakMember2 = swarm3->GetWeakPtr<2>();
|
||||
|
||||
TestAssert::AreEqual(1, weakMember0.GetStrongPtr()->GetValue1());
|
||||
TestAssert::AreEqual(2, weakMember1.GetStrongPtr()->GetValue2());
|
||||
TestAssert::AreEqual(3, weakMember2.GetStrongPtr()->GetValue3());
|
||||
}
|
||||
TestAssert::IsFalse(weakMember0.IsExpired());
|
||||
TestAssert::IsFalse(weakMember1.IsExpired());
|
||||
TestAssert::IsFalse(weakMember2.IsExpired());
|
||||
|
||||
TestAssert::IsTrue(weakMember0.IsExpired());
|
||||
TestAssert::IsTrue(weakMember1.IsExpired());
|
||||
TestAssert::IsTrue(weakMember2.IsExpired());
|
||||
TestAssert::AreEqual(1, weakMember0.GetStrongPtr()->GetValue1());
|
||||
TestAssert::AreEqual(2, weakMember1.GetStrongPtr()->GetValue2());
|
||||
TestAssert::AreEqual(3, weakMember2.GetStrongPtr()->GetValue3());
|
||||
}
|
||||
|
||||
TestAssert::IsNull(weakMember0.GetStrongPtr().Get());
|
||||
TestAssert::IsNull(weakMember1.GetStrongPtr().Get());
|
||||
TestAssert::IsNull(weakMember2.GetStrongPtr().Get());
|
||||
}
|
||||
TestAssert::IsTrue(weakMember0.IsExpired());
|
||||
TestAssert::IsTrue(weakMember1.IsExpired());
|
||||
TestAssert::IsTrue(weakMember2.IsExpired());
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_ConstructorThrows)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample3, FixedSwarmMemberSample4Throw>;
|
||||
TestAssert::IsNull(weakMember0.GetStrongPtr().Get());
|
||||
TestAssert::IsNull(weakMember1.GetStrongPtr().Get());
|
||||
TestAssert::IsNull(weakMember2.GetStrongPtr().Get());
|
||||
}
|
||||
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
swarm2->MakeMember<0>();
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
swarm2->MakeMember<1>();
|
||||
});
|
||||
TEST_METHOD(ObjectFixedSwarm_ConstructorThrows)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample3, FixedSwarmMemberSample4Throw>;
|
||||
|
||||
Debug(TestAssert::AreEqual(1u, swarm2->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, swarm2->WeakRefCount()));
|
||||
}
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
swarm2->MakeMember<0>();
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() { swarm2->MakeMember<1>(); });
|
||||
|
||||
TEST_METHOD(ObjectFixedSwarm_InitializeThrows)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample3, FixedSwarmMemberSample5InitThrow>;
|
||||
Debug(TestAssert::AreEqual(1u, swarm2->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, swarm2->WeakRefCount()));
|
||||
}
|
||||
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
swarm2->MakeMember<0>();
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
swarm2->MakeMember<1>();
|
||||
});
|
||||
TEST_METHOD(ObjectFixedSwarm_InitializeThrows)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<FixedSwarmMemberSample3, FixedSwarmMemberSample5InitThrow>;
|
||||
|
||||
Debug(TestAssert::AreEqual(1u, swarm2->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, swarm2->WeakRefCount()));
|
||||
}
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
swarm2->MakeMember<0>();
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() { swarm2->MakeMember<1>(); });
|
||||
|
||||
Debug(TestAssert::AreEqual(1u, swarm2->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, swarm2->WeakRefCount()));
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(TEST_BAD_INHERITANCE1)
|
||||
TESTMETHOD_REQUIRES_SEH(ObjectFixedSwarm_BadInheritance1)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<BadFixedSwarmMember1, FixedSwarmMemberSample2>;
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
TestAssert::ExpectVEC([&]() noexcept
|
||||
{
|
||||
// You will see a memory leak here because we cannot destroy object correctly.
|
||||
swarm2->MakeMember<0>();
|
||||
});
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(ObjectFixedSwarm_BadInheritance1)
|
||||
{
|
||||
using Swarm2 = Mso::FixedSwarm<BadFixedSwarmMember1, FixedSwarmMemberSample2>;
|
||||
Mso::TCntPtr<Swarm2> swarm2 = Mso::Make<Swarm2>();
|
||||
TestAssert::ExpectVEC([&]() noexcept {
|
||||
// You will see a memory leak here because we cannot destroy object correctly.
|
||||
swarm2->MakeMember<0>();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/****************************************************************************
|
||||
Holder.cpp
|
||||
Holder.cpp
|
||||
|
||||
Unit tests for the smart pointers
|
||||
Unit tests for the smart pointers
|
||||
|
||||
****************************************************************************/
|
||||
#include "precomp.h"
|
||||
|
@ -17,277 +17,295 @@
|
|||
#include <cppExtensions/autoRestore.h>
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: e308ef40-4b24-4cd2-98cb-a269c6345d81
|
||||
%%Id: e308ef40-4b24-4cd2-98cb-a269c6345d81
|
||||
------------------------------------------------------------------------------*/
|
||||
class CUnknown : public IUnknown
|
||||
{
|
||||
{
|
||||
public:
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 8cf178db-8f93-40d9-beb1-c2cbe04feb60
|
||||
---------------------------------------------------------------------------*/
|
||||
CUnknown() noexcept : m_cRef(0) {}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: b3d41d41-7d5f-4190-a215-abbdf1149345
|
||||
---------------------------------------------------------------------------*/
|
||||
virtual ~CUnknown() noexcept {}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: eae6ae85-b193-4bdc-9e77-032456318605
|
||||
---------------------------------------------------------------------------*/
|
||||
STDMETHOD(QueryInterface)(REFIID riid, void **ppv) noexcept override { if (riid == __uuidof(IUnknown)) { *ppv = this; AddRef(); return S_OK; } return E_NOTIMPL; }
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 997c38a7-d5fe-456f-aaff-f3061bd07693
|
||||
---------------------------------------------------------------------------*/
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return ++m_cRef; }
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 997c38a7-d5fe-456f-aaff-f3061bd07693
|
||||
---------------------------------------------------------------------------*/
|
||||
OACR_WARNING_SUPPRESS(NOTHROW_FUNC_THROWS, "This is test code and we want to use throwing TestAssert::IsTrue. It does not affect ship code.")
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { ULONG cRef = --m_cRef; TestAssert::IsTrue(static_cast<LONG>(cRef) >= 0, L""); if (cRef == 0) delete this; return cRef; }
|
||||
ULONG m_cRef;
|
||||
};
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 8cf178db-8f93-40d9-beb1-c2cbe04feb60
|
||||
---------------------------------------------------------------------------*/
|
||||
CUnknown() noexcept : m_cRef(0) {}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: b3d41d41-7d5f-4190-a215-abbdf1149345
|
||||
---------------------------------------------------------------------------*/
|
||||
virtual ~CUnknown() noexcept {}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: eae6ae85-b193-4bdc-9e77-032456318605
|
||||
---------------------------------------------------------------------------*/
|
||||
STDMETHOD(QueryInterface)(REFIID riid, void** ppv) noexcept override
|
||||
{
|
||||
if (riid == __uuidof(IUnknown))
|
||||
{
|
||||
*ppv = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 997c38a7-d5fe-456f-aaff-f3061bd07693
|
||||
---------------------------------------------------------------------------*/
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return ++m_cRef;
|
||||
}
|
||||
/*---------------------------------------------------------------------------
|
||||
%%Id: 997c38a7-d5fe-456f-aaff-f3061bd07693
|
||||
---------------------------------------------------------------------------*/
|
||||
OACR_WARNING_SUPPRESS(
|
||||
NOTHROW_FUNC_THROWS,
|
||||
"This is test code and we want to use throwing TestAssert::IsTrue. It does not affect ship code.")
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
ULONG cRef = --m_cRef;
|
||||
TestAssert::IsTrue(static_cast<LONG>(cRef) >= 0, L"");
|
||||
if (cRef == 0)
|
||||
delete this;
|
||||
return cRef;
|
||||
}
|
||||
ULONG m_cRef;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(ICustom, "5c58ced6-4f43-4b56-8d47-2236d00f7ccd")
|
||||
struct ICustom : IUnknown
|
||||
{
|
||||
virtual void SomeMethod() = 0;
|
||||
virtual void SomeMethod() = 0;
|
||||
};
|
||||
|
||||
TEST_CLASS(HolderTests)
|
||||
TEST_CLASS (HolderTests)
|
||||
{
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Ctor)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2(NULL);
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb3 = NULL;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb4 = new BYTE;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Assignment)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb3 = new BYTE;
|
||||
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb1 == pb2, L"");
|
||||
TestAssert::IsTrue(pb1 != pb3, L"");
|
||||
TestAssert::IsTrue(pb3 != nullptr, L"");
|
||||
|
||||
const BYTE* pbRaw = pb1;
|
||||
TestAssert::IsTrue(pbRaw == nullptr, L"");
|
||||
TestAssert::IsTrue(pbRaw == pb1, L"");
|
||||
TestAssert::IsTrue(pbRaw != pb3, L"");
|
||||
|
||||
pbRaw = pb3;
|
||||
TestAssert::IsTrue(pbRaw != nullptr, L"");
|
||||
TestAssert::IsTrue(pbRaw != pb1, L"");
|
||||
TestAssert::IsTrue(pbRaw == pb3, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Swap)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2 = new BYTE;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb3 = new BYTE;
|
||||
const BYTE* pb1Raw = pb1;
|
||||
const BYTE* pb2Raw = pb2;
|
||||
const BYTE* pb3Raw = pb3;
|
||||
TestAssert::IsTrue(pb1Raw == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2Raw != nullptr, L"");
|
||||
TestAssert::IsTrue(pb3Raw != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2Raw != pb3Raw, L"");
|
||||
|
||||
pb2.Swap(pb3);
|
||||
TestAssert::IsTrue(pb2Raw != pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw == pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw != pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw == pb2, L"");
|
||||
|
||||
pb3.Swap(pb2);
|
||||
TestAssert::IsTrue(pb2Raw == pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw != pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw == pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw != pb2, L"");
|
||||
|
||||
pb1.Swap(pb2);
|
||||
TestAssert::IsTrue(pb2Raw != pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw == pb1, L"");
|
||||
TestAssert::IsTrue(pb1Raw != pb1, L"");
|
||||
TestAssert::IsTrue(pb1Raw == pb2, L"");
|
||||
pb2.Swap(pb1);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 4a8bb941-2f5c-488a-8bc9-9f5b8c0897be
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Transfer)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Transfer(pb2);
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Place)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Place(pb2.Detach());
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Attach)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1 = new BYTE;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Attach(pb2.Detach());
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 040b6350-5468-48ff-b8cf-defc6ef412ad
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Address)
|
||||
{
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb1;
|
||||
Mso::THolder<BYTE*, Mso::TDeleteHelper<BYTE*>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
BYTE** ppb = &pb1;
|
||||
TestAssert::IsTrue(ppb == pb1.Address(), L"");
|
||||
TestAssert::IsTrue(ppb == pb1.GetRaw(), L"");
|
||||
|
||||
*ppb = pb2.Detach();
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: afe52e28-6314-446a-bb29-d2d07ce4a845
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(CntPtrCore)
|
||||
{
|
||||
Mso::TCntPtr<CUnknown> punk1;
|
||||
Mso::TCntPtr<CUnknown> punk2(new CUnknown());
|
||||
Mso::TCntPtr<CUnknown> punk3(new CUnknown(), false);
|
||||
punk3->AddRef();
|
||||
Mso::TCntPtr<CUnknown> punk4(punk3);
|
||||
Mso::TCntPtr<CUnknown> punk5;
|
||||
punk5 = new CUnknown();
|
||||
|
||||
TestAssert::IsTrue(punk1 == nullptr, L"");
|
||||
TestAssert::IsTrue(punk2 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk3 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk4 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk5 != nullptr, L"");
|
||||
|
||||
punk1 = punk2;
|
||||
TestAssert::IsTrue(punk1->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 2, L"");
|
||||
punk1 = punk4;
|
||||
TestAssert::IsTrue(punk1->m_cRef == 3, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 1, L"");
|
||||
TestAssert::IsTrue(punk4->m_cRef == 3, L"");
|
||||
punk1 = NULL;
|
||||
|
||||
punk1.Swap(punk3);
|
||||
punk3.Swap(punk5);
|
||||
punk5.Swap(punk1);
|
||||
punk3.Swap(punk5);
|
||||
|
||||
punk1.Transfer(punk2);
|
||||
punk2.Transfer(punk1);
|
||||
punk1.Place(punk2.Detach());
|
||||
punk2.Place(punk1.Detach());
|
||||
punk1.Attach(punk2.Detach());
|
||||
punk2.Attach(punk1.Detach());
|
||||
|
||||
TestAssert::IsTrue(punk1 == nullptr, L"");
|
||||
TestAssert::IsTrue(punk2 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 1, L"");
|
||||
TestAssert::IsTrue(punk3 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk3->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk4 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk4->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk3 == punk4, L"");
|
||||
TestAssert::IsTrue(punk5 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk5->m_cRef == 1, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 5938c5b2-5d47-40a6-bd0a-903a7e126059
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(CntPtrMixed)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> punk;
|
||||
Mso::TCntPtr<ICustom> pistm;
|
||||
TestAssert::IsTrue(pistm == nullptr, L"");
|
||||
TestAssert::IsTrue(punk == nullptr, L"");
|
||||
punk = pistm;
|
||||
punk = &*pistm;
|
||||
punk = *pistm.GetRaw();
|
||||
punk = pistm.Copy();
|
||||
*punk.Address() = pistm.Copy();
|
||||
*punk.GetRaw() = pistm.Copy();
|
||||
pistm = static_cast<ICustom*>(static_cast<IUnknown*>(punk));
|
||||
pistm = static_cast<ICustom*>(&*punk);
|
||||
pistm = static_cast<ICustom*>(*punk.GetRaw());
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Ctor)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2(NULL);
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb3 = NULL;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb4 = new BYTE;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Assignment)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb3 = new BYTE;
|
||||
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb1 == pb2, L"");
|
||||
TestAssert::IsTrue(pb1 != pb3, L"");
|
||||
TestAssert::IsTrue(pb3 != nullptr, L"");
|
||||
|
||||
const BYTE *pbRaw = pb1;
|
||||
TestAssert::IsTrue(pbRaw == nullptr, L"");
|
||||
TestAssert::IsTrue(pbRaw == pb1, L"");
|
||||
TestAssert::IsTrue(pbRaw != pb3, L"");
|
||||
|
||||
pbRaw = pb3;
|
||||
TestAssert::IsTrue(pbRaw != nullptr, L"");
|
||||
TestAssert::IsTrue(pbRaw != pb1, L"");
|
||||
TestAssert::IsTrue(pbRaw == pb3, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Swap)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2 = new BYTE;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb3 = new BYTE;
|
||||
const BYTE *pb1Raw = pb1;
|
||||
const BYTE *pb2Raw = pb2;
|
||||
const BYTE *pb3Raw = pb3;
|
||||
TestAssert::IsTrue(pb1Raw == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2Raw != nullptr, L"");
|
||||
TestAssert::IsTrue(pb3Raw != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2Raw != pb3Raw, L"");
|
||||
|
||||
pb2.Swap(pb3);
|
||||
TestAssert::IsTrue(pb2Raw != pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw == pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw != pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw == pb2, L"");
|
||||
|
||||
pb3.Swap(pb2);
|
||||
TestAssert::IsTrue(pb2Raw == pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw != pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw == pb3, L"");
|
||||
TestAssert::IsTrue(pb3Raw != pb2, L"");
|
||||
|
||||
pb1.Swap(pb2);
|
||||
TestAssert::IsTrue(pb2Raw != pb2, L"");
|
||||
TestAssert::IsTrue(pb2Raw == pb1, L"");
|
||||
TestAssert::IsTrue(pb1Raw != pb1, L"");
|
||||
TestAssert::IsTrue(pb1Raw == pb2, L"");
|
||||
pb2.Swap(pb1);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 4a8bb941-2f5c-488a-8bc9-9f5b8c0897be
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Transfer)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Transfer(pb2);
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Place)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Place(pb2.Detach());
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Attach)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1 = new BYTE;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
pb1.Attach(pb2.Detach());
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 040b6350-5468-48ff-b8cf-defc6ef412ad
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(Address)
|
||||
{
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb1;
|
||||
Mso::THolder<BYTE *, Mso::TDeleteHelper<BYTE *>> pb2 = new BYTE;
|
||||
TestAssert::IsTrue(pb1 == nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 != nullptr, L"");
|
||||
|
||||
BYTE **ppb = &pb1;
|
||||
TestAssert::IsTrue(ppb == pb1.Address(), L"");
|
||||
TestAssert::IsTrue(ppb == pb1.GetRaw(), L"");
|
||||
|
||||
*ppb = pb2.Detach();
|
||||
TestAssert::IsTrue(pb1 != nullptr, L"");
|
||||
TestAssert::IsTrue(pb2 == nullptr, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: afe52e28-6314-446a-bb29-d2d07ce4a845
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(CntPtrCore)
|
||||
{
|
||||
Mso::TCntPtr<CUnknown> punk1;
|
||||
Mso::TCntPtr<CUnknown> punk2( new CUnknown() );
|
||||
Mso::TCntPtr<CUnknown> punk3( new CUnknown(), false );
|
||||
punk3->AddRef();
|
||||
Mso::TCntPtr<CUnknown> punk4(punk3);
|
||||
Mso::TCntPtr<CUnknown> punk5;
|
||||
punk5 = new CUnknown();
|
||||
|
||||
TestAssert::IsTrue(punk1 == nullptr, L"");
|
||||
TestAssert::IsTrue(punk2 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk3 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk4 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk5 != nullptr, L"");
|
||||
|
||||
punk1 = punk2;
|
||||
TestAssert::IsTrue(punk1->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 2, L"");
|
||||
punk1 = punk4;
|
||||
TestAssert::IsTrue(punk1->m_cRef == 3, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 1, L"");
|
||||
TestAssert::IsTrue(punk4->m_cRef == 3, L"");
|
||||
punk1 = NULL;
|
||||
|
||||
punk1.Swap(punk3);
|
||||
punk3.Swap(punk5);
|
||||
punk5.Swap(punk1);
|
||||
punk3.Swap(punk5);
|
||||
|
||||
punk1.Transfer(punk2);
|
||||
punk2.Transfer(punk1);
|
||||
punk1.Place(punk2.Detach());
|
||||
punk2.Place(punk1.Detach());
|
||||
punk1.Attach(punk2.Detach());
|
||||
punk2.Attach(punk1.Detach());
|
||||
|
||||
TestAssert::IsTrue(punk1 == nullptr, L"");
|
||||
TestAssert::IsTrue(punk2 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk2->m_cRef == 1, L"");
|
||||
TestAssert::IsTrue(punk3 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk3->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk4 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk4->m_cRef == 2, L"");
|
||||
TestAssert::IsTrue(punk3 == punk4, L"");
|
||||
TestAssert::IsTrue(punk5 != nullptr, L"");
|
||||
TestAssert::IsTrue(punk5->m_cRef == 1, L"");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: 5938c5b2-5d47-40a6-bd0a-903a7e126059
|
||||
------------------------------------------------------------------------------*/
|
||||
TEST_METHOD(CntPtrMixed)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> punk;
|
||||
Mso::TCntPtr<ICustom> pistm;
|
||||
TestAssert::IsTrue(pistm == nullptr, L"");
|
||||
TestAssert::IsTrue(punk == nullptr, L"");
|
||||
punk = pistm;
|
||||
punk = &*pistm;
|
||||
punk = *pistm.GetRaw();
|
||||
punk = pistm.Copy();
|
||||
*punk.Address() = pistm.Copy();
|
||||
*punk.GetRaw() = pistm.Copy();
|
||||
pistm = static_cast<ICustom*>(static_cast<IUnknown*>(punk));
|
||||
pistm = static_cast<ICustom*>(&*punk);
|
||||
pistm = static_cast<ICustom*>(*punk.GetRaw());
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
%%Id: b32deb10-ad98-452e-a307-8eb5d212429e
|
||||
%%Id: b32deb10-ad98-452e-a307-8eb5d212429e
|
||||
------------------------------------------------------------------------------*/
|
||||
#ifdef MS_TARGET_WINDOWS
|
||||
TEST_METHOD(CntQIPtrCore)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> punk1(new CUnknown());
|
||||
Mso::TCntPtr<IUnknown> punk2(new CUnknown());
|
||||
Mso::TCntPtr<IStream> pistmQI;
|
||||
|
||||
TestAssert::IsTrue(FAILED(Mso::ComUtil::HrQueryFrom(pistmQI, punk1)), L"");
|
||||
TestAssert::IsTrue(SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &pistmQI)), L"");
|
||||
punk1 = pistmQI;
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(pistmQI, punk1), L"");
|
||||
pistmQI.Empty();
|
||||
TestAssert::AreEqual(S_OK, Mso::ComUtil::HrQueryFrom(pistmQI, punk1));
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(pistmQI, punk1), L"");
|
||||
}
|
||||
TEST_METHOD(CntQIPtrCore)
|
||||
{
|
||||
Mso::TCntPtr<IUnknown> punk1(new CUnknown());
|
||||
Mso::TCntPtr<IUnknown> punk2(new CUnknown());
|
||||
Mso::TCntPtr<IStream> pistmQI;
|
||||
|
||||
TestAssert::IsTrue(FAILED(Mso::ComUtil::HrQueryFrom(pistmQI, punk1)), L"");
|
||||
TestAssert::IsTrue(SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &pistmQI)), L"");
|
||||
punk1 = pistmQI;
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(pistmQI, punk1), L"");
|
||||
pistmQI.Empty();
|
||||
TestAssert::AreEqual(S_OK, Mso::ComUtil::HrQueryFrom(pistmQI, punk1));
|
||||
TestAssert::IsTrue(Mso::ComUtil::AreEqualObjects(pistmQI, punk1), L"");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_METHOD(TCleanup)
|
||||
// Use TCleanup to increment the variable.
|
||||
{
|
||||
int i = 0;
|
||||
{
|
||||
auto cuIncrement = Mso::TCleanup::Make([&]() noexcept { ++i; });
|
||||
TestAssert::AreEqual(0, i, L"");
|
||||
}
|
||||
TestAssert::AreEqual(1, i, L"");
|
||||
}
|
||||
|
||||
TEST_METHOD(TCleanup)
|
||||
// Use TCleanup to increment the variable.
|
||||
{
|
||||
int i = 0;
|
||||
{
|
||||
auto cuIncrement = Mso::TCleanup::Make([&]() noexcept { ++i; });
|
||||
TestAssert::AreEqual(0, i, L"");
|
||||
}
|
||||
TestAssert::AreEqual(1, i, L"");
|
||||
}
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,127 +12,114 @@ Unit tests for classes in the ObjectRefCount.h
|
|||
#include "testAllocators.h"
|
||||
#include <test/testCheck.h>
|
||||
|
||||
class ObjectRefCountSample1 final
|
||||
: public Mso::RefCountedObjectNoVTable<ObjectRefCountSample1>
|
||||
class ObjectRefCountSample1 final : public Mso::RefCountedObjectNoVTable<ObjectRefCountSample1>
|
||||
{
|
||||
public:
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectRefCountSample1(int value, bool& deleted) noexcept
|
||||
: m_value(value)
|
||||
, m_deleted(deleted)
|
||||
{
|
||||
}
|
||||
ObjectRefCountSample1(int value, bool& deleted) noexcept : m_value(value), m_deleted(deleted) {}
|
||||
|
||||
~ObjectRefCountSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample2Init final
|
||||
: public Mso::RefCountedObjectNoVTable<ObjectRefCountSample2Init>
|
||||
class ObjectRefCountSample2Init final : public Mso::RefCountedObjectNoVTable<ObjectRefCountSample2Init>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
|
||||
ObjectRefCountSample2Init() noexcept = default;
|
||||
ObjectRefCountSample2Init() noexcept = default;
|
||||
|
||||
~ObjectRefCountSample2Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample2Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample3CannotAllocate final
|
||||
: public Mso::RefCountedObjectNoVTable<BadAllocSimpleRefCount, ObjectRefCountSample3CannotAllocate>
|
||||
: public Mso::RefCountedObjectNoVTable<BadAllocSimpleRefCount, ObjectRefCountSample3CannotAllocate>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample4Throw final
|
||||
: public Mso::RefCountedObjectNoVTable<ObjectRefCountSample4Throw>
|
||||
class ObjectRefCountSample4Throw final : public Mso::RefCountedObjectNoVTable<ObjectRefCountSample4Throw>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
private:
|
||||
ObjectRefCountSample4Throw(bool& deleted)
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
ObjectRefCountSample4Throw(bool& deleted) : m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
~ObjectRefCountSample4Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample4Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample5InitThrow final
|
||||
: public Mso::RefCountedObjectNoVTable<ObjectRefCountSample5InitThrow>
|
||||
class ObjectRefCountSample5InitThrow final : public Mso::RefCountedObjectNoVTable<ObjectRefCountSample5InitThrow>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
private:
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
ObjectRefCountSample5InitThrow() noexcept = default;
|
||||
ObjectRefCountSample5InitThrow() noexcept = default;
|
||||
|
||||
~ObjectRefCountSample5InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample5InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool* m_deleted { nullptr };
|
||||
bool* m_deleted{nullptr};
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
@ -140,397 +127,372 @@ private:
|
|||
//=============================================================================
|
||||
|
||||
class ObjectRefCountSample11 final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample11>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample11>
|
||||
{
|
||||
public:
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectRefCountSample11(int value, bool& deleted) noexcept
|
||||
: m_value(value)
|
||||
, m_deleted(deleted)
|
||||
{
|
||||
}
|
||||
ObjectRefCountSample11(int value, bool& deleted) noexcept : m_value(value), m_deleted(deleted) {}
|
||||
|
||||
~ObjectRefCountSample11() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample11() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample21Init final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample21Init>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample21Init>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
|
||||
ObjectRefCountSample21Init() noexcept = default;
|
||||
ObjectRefCountSample21Init() noexcept = default;
|
||||
|
||||
~ObjectRefCountSample21Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample21Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample31CannotAllocate final
|
||||
: public Mso::RefCountedObjectNoVTable<BadStatefulAllocSimpleRefCount, ObjectRefCountSample31CannotAllocate>
|
||||
: public Mso::RefCountedObjectNoVTable<BadStatefulAllocSimpleRefCount, ObjectRefCountSample31CannotAllocate>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample41Throw final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample41Throw>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample41Throw>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
private:
|
||||
ObjectRefCountSample41Throw(bool& deleted)
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
ObjectRefCountSample41Throw(bool& deleted) : m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
~ObjectRefCountSample41Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample41Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectRefCountSample51InitThrow final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample51InitThrow>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocSimpleRefCount, ObjectRefCountSample51InitThrow>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
private:
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
ObjectRefCountSample51InitThrow() noexcept = default;
|
||||
ObjectRefCountSample51InitThrow() noexcept = default;
|
||||
|
||||
~ObjectRefCountSample51InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectRefCountSample51InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool* m_deleted { nullptr };
|
||||
bool* m_deleted{nullptr};
|
||||
};
|
||||
|
||||
TEST_CLASS(ObjectRefCountTest)
|
||||
TEST_CLASS (ObjectRefCountTest)
|
||||
{
|
||||
TEST_METHOD(ObjectRefCount_Make)
|
||||
{
|
||||
TestAssert::AreEqual(sizeof(uint32_t), sizeof(Mso::RefCountedObjectNoVTable<ObjectRefCountSample1>));
|
||||
TEST_METHOD(ObjectRefCount_Make)
|
||||
{
|
||||
TestAssert::AreEqual(sizeof(uint32_t), sizeof(Mso::RefCountedObjectNoVTable<ObjectRefCountSample1>));
|
||||
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample1> obj1 = Mso::Make<ObjectRefCountSample1>(5, /*ref*/deleted);
|
||||
{
|
||||
Debug(TestAssert::AreEqual(1u, obj1->RefCount()));
|
||||
TestAssert::AreEqual(5, obj1->Value());
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample1> obj1 = Mso::Make<ObjectRefCountSample1>(5, /*ref*/ deleted);
|
||||
{
|
||||
Debug(TestAssert::AreEqual(1u, obj1->RefCount()));
|
||||
TestAssert::AreEqual(5, obj1->Value());
|
||||
|
||||
Mso::TCntPtr<ObjectRefCountSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(2u, obj1->RefCount()));
|
||||
TestAssert::IsNotNull(obj2.Get());
|
||||
TestAssert::IsTrue(obj1.Get() == obj2.Get());
|
||||
}
|
||||
Debug(TestAssert::AreEqual(1u, obj1->RefCount()));
|
||||
}
|
||||
Mso::TCntPtr<ObjectRefCountSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(2u, obj1->RefCount()));
|
||||
TestAssert::IsNotNull(obj2.Get());
|
||||
TestAssert::IsTrue(obj1.Get() == obj2.Get());
|
||||
}
|
||||
Debug(TestAssert::AreEqual(1u, obj1->RefCount()));
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_Make_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::Make<ObjectRefCountSample2Init>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_Make_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::Make<ObjectRefCountSample2Init>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_Make_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample3CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() noexcept
|
||||
{
|
||||
obj = Mso::Make<ObjectRefCountSample3CannotAllocate>();
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_Make_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample3CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() noexcept { obj = Mso::Make<ObjectRefCountSample3CannotAllocate>(); });
|
||||
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_Make_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::Make<ObjectRefCountSample4Throw>(/*ref*/deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_Make_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::Make<ObjectRefCountSample4Throw>(/*ref*/ deleted); });
|
||||
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_Make_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::Make<ObjectRefCountSample5InitThrow>(/*ref*/deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_Make_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::Make<ObjectRefCountSample5InitThrow>(/*ref*/ deleted); });
|
||||
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample1>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample1>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample2Init>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample2Init>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_CannotAllocate)
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample3CannotAllocate>();
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_CannotAllocate)
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectRefCountSample3CannotAllocate>();
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::MakeElseNull<ObjectRefCountSample4Throw>(/*ref*/deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::MakeElseNull<ObjectRefCountSample4Throw>(/*ref*/ deleted); });
|
||||
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::MakeElseNull<ObjectRefCountSample5InitThrow>(/*ref*/deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::MakeElseNull<ObjectRefCountSample5InitThrow>(/*ref*/ deleted); });
|
||||
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
struct AllocTestState
|
||||
{
|
||||
bool AllocCalled;
|
||||
bool FreeCalled;
|
||||
bool Deleted;
|
||||
};
|
||||
struct AllocTestState
|
||||
{
|
||||
bool AllocCalled;
|
||||
bool FreeCalled;
|
||||
bool Deleted;
|
||||
};
|
||||
|
||||
static void AssertAllocState(const AllocTestState& state, bool deleted = true) noexcept
|
||||
{
|
||||
TestAssert::IsTrue(state.AllocCalled, L"Allocate expected to be called");
|
||||
TestAssert::IsTrue(state.FreeCalled, L"Deallocate expected to be called");
|
||||
TestAssert::AreEqual(deleted, state.Deleted, L"Destructor to be called");
|
||||
}
|
||||
static void AssertAllocState(const AllocTestState& state, bool deleted = true) noexcept
|
||||
{
|
||||
TestAssert::IsTrue(state.AllocCalled, L"Allocate expected to be called");
|
||||
TestAssert::IsTrue(state.FreeCalled, L"Deallocate expected to be called");
|
||||
TestAssert::AreEqual(deleted, state.Deleted, L"Destructor to be called");
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectRefCountSample11>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectRefCountSample11>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectRefCountSample21Init>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectRefCountSample21Init>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample31CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() noexcept
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample31CannotAllocate>(&memHeap);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample31CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() noexcept {
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample31CannotAllocate>(&memHeap);
|
||||
});
|
||||
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample41Throw>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample41Throw>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state, /*deleted:*/false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state, /*deleted:*/ false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample51InitThrow>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeAlloc_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectRefCountSample51InitThrow>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample11>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample11>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample21Init>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample21Init>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_CannotAllocate)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample31CannotAllocate>(&memHeap);
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_CannotAllocate)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectRefCountSample31CannotAllocate>(&memHeap);
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectRefCountSample41Throw>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectRefCountSample41Throw>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state, /*deleted:*/false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state, /*deleted:*/ false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectRefCountSample51InitThrow>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectRefCount_MakeAllocElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectRefCountSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectRefCountSample51InitThrow>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,570 +12,534 @@ Unit tests for classes in the ObjectRefCount.h
|
|||
#include <test/testCheck.h>
|
||||
|
||||
class ObjectWithWeakRefSample1 final
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample1>
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample1>
|
||||
{
|
||||
public:
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectWithWeakRefSample1(int value, bool& deleted) noexcept
|
||||
: m_value(value)
|
||||
, m_deleted(deleted)
|
||||
{
|
||||
}
|
||||
ObjectWithWeakRefSample1(int value, bool& deleted) noexcept : m_value(value), m_deleted(deleted) {}
|
||||
|
||||
~ObjectWithWeakRefSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
class ObjectWithWeakRefSample2Init final
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample2Init>
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample2Init>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
|
||||
ObjectWithWeakRefSample2Init() noexcept = default;
|
||||
ObjectWithWeakRefSample2Init() noexcept = default;
|
||||
|
||||
~ObjectWithWeakRefSample2Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample2Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample3CannotAllocate final
|
||||
: public Mso::RefCountedObjectNoVTable<BadAllocWeakRefCount, ObjectWithWeakRefSample3CannotAllocate>
|
||||
: public Mso::RefCountedObjectNoVTable<BadAllocWeakRefCount, ObjectWithWeakRefSample3CannotAllocate>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample4Throw final
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample4Throw>
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample4Throw>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
private:
|
||||
ObjectWithWeakRefSample4Throw(bool& deleted)
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
ObjectWithWeakRefSample4Throw(bool& deleted) : m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
~ObjectWithWeakRefSample4Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample4Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample5InitThrow final
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample5InitThrow>
|
||||
: public Mso::RefCountedObjectNoVTable<Mso::RefCountStrategy::WeakRef, ObjectWithWeakRefSample5InitThrow>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
private:
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
ObjectWithWeakRefSample5InitThrow() noexcept = default;
|
||||
ObjectWithWeakRefSample5InitThrow() noexcept = default;
|
||||
|
||||
~ObjectWithWeakRefSample5InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample5InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool* m_deleted { nullptr };
|
||||
bool* m_deleted{nullptr};
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// Classes below use stateful allocator
|
||||
//=============================================================================
|
||||
|
||||
class ObjectWithWeakRefSample11 final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample11>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample11>
|
||||
{
|
||||
public:
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectWithWeakRefSample11(int value, bool& deleted) noexcept
|
||||
: m_value(value)
|
||||
, m_deleted(deleted)
|
||||
{
|
||||
}
|
||||
ObjectWithWeakRefSample11(int value, bool& deleted) noexcept : m_value(value), m_deleted(deleted) {}
|
||||
|
||||
~ObjectWithWeakRefSample11() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample11() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
int m_value;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
class ObjectWithWeakRefSample21Init final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample21Init>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample21Init>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
int Value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
void InitializeThis(int value, bool& deleted) noexcept
|
||||
{
|
||||
m_value = value;
|
||||
m_deleted = &deleted;
|
||||
}
|
||||
|
||||
ObjectWithWeakRefSample21Init() noexcept = default;
|
||||
ObjectWithWeakRefSample21Init() noexcept = default;
|
||||
|
||||
~ObjectWithWeakRefSample21Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample21Init() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
int m_value;
|
||||
bool* m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample31CannotAllocate final
|
||||
: public Mso::RefCountedObjectNoVTable<BadStatefulAllocWeakRefCount, ObjectWithWeakRefSample31CannotAllocate>
|
||||
: public Mso::RefCountedObjectNoVTable<BadStatefulAllocWeakRefCount, ObjectWithWeakRefSample31CannotAllocate>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample41Throw final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample41Throw>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample41Throw>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
private:
|
||||
ObjectWithWeakRefSample41Throw(bool& deleted)
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
ObjectWithWeakRefSample41Throw(bool& deleted) : m_deleted(deleted)
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
~ObjectWithWeakRefSample41Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample41Throw() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
|
||||
class ObjectWithWeakRefSample51InitThrow final
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample51InitThrow>
|
||||
: public Mso::RefCountedObjectNoVTable<StatefulAllocWeakRefCount, ObjectWithWeakRefSample51InitThrow>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
private:
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis(bool& deleted)
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
|
||||
ObjectWithWeakRefSample51InitThrow() noexcept = default;
|
||||
ObjectWithWeakRefSample51InitThrow() noexcept = default;
|
||||
|
||||
~ObjectWithWeakRefSample51InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
~ObjectWithWeakRefSample51InitThrow() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
friend MakePolicy; // To support private constructor & InitializeThis
|
||||
friend RefCountPolicy; // To support private destructor
|
||||
|
||||
private:
|
||||
bool* m_deleted { nullptr };
|
||||
bool* m_deleted{nullptr};
|
||||
};
|
||||
|
||||
|
||||
TEST_CLASS(ObjectWithWeakRefTest)
|
||||
TEST_CLASS (ObjectWithWeakRefTest)
|
||||
{
|
||||
TEST_METHOD(ObjectWithWeakRef_Make)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/deleted);
|
||||
{
|
||||
Debug(TestAssert::AreEqual(1u, obj1->GetWeakRef().RefCount()));
|
||||
TestAssert::AreEqual(5, obj1->Value());
|
||||
TEST_METHOD(ObjectWithWeakRef_Make)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/ deleted);
|
||||
{
|
||||
Debug(TestAssert::AreEqual(1u, obj1->GetWeakRef().RefCount()));
|
||||
TestAssert::AreEqual(5, obj1->Value());
|
||||
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(2u, obj1->GetWeakRef().RefCount()));
|
||||
TestAssert::IsNotNull(obj2.Get());
|
||||
TestAssert::IsTrue(obj1.Get() == obj2.Get());
|
||||
}
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(2u, obj1->GetWeakRef().RefCount()));
|
||||
TestAssert::IsNotNull(obj2.Get());
|
||||
TestAssert::IsTrue(obj1.Get() == obj2.Get());
|
||||
}
|
||||
|
||||
Debug(TestAssert::AreEqual(1u, obj1->GetWeakRef().RefCount()));
|
||||
}
|
||||
Debug(TestAssert::AreEqual(1u, obj1->GetWeakRef().RefCount()));
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_WeakRef)
|
||||
{
|
||||
// Ref count for ObjectWeakRef behaves the same way as for the object.
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/deleted);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef = &obj1->GetWeakRef();
|
||||
{
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, weakRef->WeakRefCount()));
|
||||
TEST_METHOD(ObjectWithWeakRef_WeakRef)
|
||||
{
|
||||
// Ref count for ObjectWeakRef behaves the same way as for the object.
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/ deleted);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef = &obj1->GetWeakRef();
|
||||
{
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, weakRef->WeakRefCount()));
|
||||
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(3u, weakRef->RefCount()));
|
||||
}
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(3u, weakRef->RefCount()));
|
||||
}
|
||||
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
TestAssert::IsFalse(deleted);
|
||||
TestAssert::IsFalse(weakRef->IsExpired());
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
TestAssert::IsFalse(deleted);
|
||||
TestAssert::IsFalse(weakRef->IsExpired());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_WeakPtr)
|
||||
{
|
||||
bool deleted = false;
|
||||
Mso::WeakPtr<ObjectWithWeakRefSample1> weakPtr;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/deleted);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef = &obj1->GetWeakRef();
|
||||
weakPtr = obj1;
|
||||
{
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->WeakRefCount()));
|
||||
TEST_METHOD(ObjectWithWeakRef_WeakPtr)
|
||||
{
|
||||
bool deleted = false;
|
||||
Mso::WeakPtr<ObjectWithWeakRefSample1> weakPtr;
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj1 = Mso::Make<ObjectWithWeakRefSample1>(5, /*ref*/ deleted);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef = &obj1->GetWeakRef();
|
||||
weakPtr = obj1;
|
||||
{
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->WeakRefCount()));
|
||||
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(3u, weakRef->RefCount()));
|
||||
}
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample1> obj2(obj1);
|
||||
Debug(TestAssert::AreEqual(3u, weakRef->RefCount()));
|
||||
}
|
||||
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
TestAssert::IsFalse(deleted);
|
||||
TestAssert::IsFalse(weakPtr.IsExpired());
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2u, weakRef->RefCount()));
|
||||
TestAssert::IsFalse(deleted);
|
||||
TestAssert::IsFalse(weakPtr.IsExpired());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
TestAssert::IsTrue(weakPtr.IsExpired());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
TestAssert::IsTrue(weakPtr.IsExpired());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::Make<ObjectWithWeakRefSample2Init>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->GetWeakRef().RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::Make<ObjectWithWeakRefSample2Init>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->GetWeakRef().RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample3CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() { obj = Mso::Make<ObjectWithWeakRefSample3CannotAllocate>(); });
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample3CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]()
|
||||
{
|
||||
obj = Mso::Make<ObjectWithWeakRefSample3CannotAllocate>();
|
||||
});
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::Make<ObjectWithWeakRefSample4Throw>(/*ref*/ deleted); });
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::Make<ObjectWithWeakRefSample4Throw>(/*ref*/deleted);
|
||||
});
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TestAssert::IsFalse(deleted); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::Make<ObjectWithWeakRefSample5InitThrow>(/*ref*/ deleted); });
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_Make_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::Make<ObjectWithWeakRefSample5InitThrow>(/*ref*/deleted);
|
||||
});
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample1>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample1>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample2Init>(5, /*ref*/ deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_InitializeThis)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample2Init>(5, /*ref*/deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_CannotAllocate)
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample3CannotAllocate>();
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_CannotAllocate)
|
||||
{
|
||||
auto obj = Mso::MakeElseNull<ObjectWithWeakRefSample3CannotAllocate>();
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::MakeElseNull<ObjectWithWeakRefSample4Throw>(/*ref*/ deleted); });
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample4Throw> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::MakeElseNull<ObjectWithWeakRefSample4Throw>(/*ref*/deleted);
|
||||
});
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>(
|
||||
[&]() { obj = Mso::MakeElseNull<ObjectWithWeakRefSample5InitThrow>(/*ref*/ deleted); });
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample5InitThrow> obj;
|
||||
bool deleted = false;
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
obj = Mso::MakeElseNull<ObjectWithWeakRefSample5InitThrow>(/*ref*/deleted);
|
||||
});
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TestAssert::IsTrue(deleted); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
struct AllocTestState
|
||||
{
|
||||
bool AllocCalled;
|
||||
bool FreeCalled;
|
||||
bool Deleted;
|
||||
};
|
||||
|
||||
static void AssertAllocState(const AllocTestState& state, bool deleted = true) noexcept
|
||||
{
|
||||
TestAssert::IsTrue(state.AllocCalled, L"Allocate expected to be called");
|
||||
TestAssert::IsTrue(state.FreeCalled, L"Deallocate expected to be called");
|
||||
TestAssert::AreEqual(deleted, state.Deleted, L"Destructor to be called");
|
||||
}
|
||||
|
||||
struct AllocTestState
|
||||
{
|
||||
bool AllocCalled;
|
||||
bool FreeCalled;
|
||||
bool Deleted;
|
||||
};
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectWithWeakRefSample11>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
static void AssertAllocState(const AllocTestState& state, bool deleted = true) noexcept
|
||||
{
|
||||
TestAssert::IsTrue(state.AllocCalled, L"Allocate expected to be called");
|
||||
TestAssert::IsTrue(state.FreeCalled, L"Deallocate expected to be called");
|
||||
TestAssert::AreEqual(deleted, state.Deleted, L"Destructor to be called");
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectWithWeakRefSample11>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectWithWeakRefSample21Init>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAlloc<ObjectWithWeakRefSample21Init>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample31CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]() {
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample31CannotAllocate>(&memHeap);
|
||||
});
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_CannotAllocate)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample31CannotAllocate> obj;
|
||||
TestAssert::ExpectVEC([&]()
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample31CannotAllocate>(&memHeap);
|
||||
});
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample41Throw>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state, /*deleted:*/ false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample41Throw>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample51InitThrow>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state, /*deleted:*/false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAlloc_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAlloc<ObjectWithWeakRefSample51InitThrow>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample11>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state); // If InitializeThis throws then destructor must be called.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample11>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample21Init>(&memHeap, 5, /*ref*/ state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
AssertAllocState(state);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_InitializeThis)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample21Init>(&memHeap, 5, /*ref*/state.Deleted);
|
||||
Debug(TestAssert::AreEqual(1u, obj->RefCount()));
|
||||
TestAssert::AreEqual(5, obj->Value());
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_CannotAllocate)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample31CannotAllocate>(&memHeap);
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
|
||||
AssertAllocState(state);
|
||||
}
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample41Throw>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_CannotAllocate)
|
||||
{
|
||||
AllocTestState state = {};
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
auto obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample31CannotAllocate>(&memHeap);
|
||||
TestAssert::IsNull(obj.Get());
|
||||
}
|
||||
AssertAllocState(state, /*deleted:*/ false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_CtorThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample41Throw> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample41Throw>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() {
|
||||
MyMemHeap memHeap(/*ref*/ state.AllocCalled, /*ref*/ state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample51InitThrow>(&memHeap, /*ref*/ state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state, /*deleted:*/false); // Destructor is not called if constructor throws.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectWithWeakRef_MakeAllocElseNull_InitializeThisThrows)
|
||||
{
|
||||
Mso::TCntPtr<ObjectWithWeakRefSample51InitThrow> obj;
|
||||
AllocTestState state = {};
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
MyMemHeap memHeap(/*ref*/state.AllocCalled, /*ref*/state.FreeCalled);
|
||||
obj = Mso::MakeAllocElseNull<ObjectWithWeakRefSample51InitThrow>(&memHeap, /*ref*/state.Deleted);
|
||||
});
|
||||
|
||||
AssertAllocState(state); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
AssertAllocState(state); // If InitializeThis throws then we must call destructor.
|
||||
TestAssert::IsTrue(obj.IsEmpty());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,51 +10,56 @@ Unit tests for classes in the ObjectQueryCast.h
|
|||
#include <test/testCheck.h>
|
||||
|
||||
#define DEFINE_STRING_GUID(var, guid) \
|
||||
MSO_STRUCT_GUID(_test_##var, guid) \
|
||||
struct _test_##var {}; \
|
||||
GUID var = __uuidof(_test_##var);
|
||||
MSO_STRUCT_GUID(_test_##var, guid) \
|
||||
struct _test_##var \
|
||||
{ \
|
||||
}; \
|
||||
GUID var = __uuidof(_test_##var);
|
||||
|
||||
MSO_STRUCT_GUID(IQueryCastBase1, "EAE07273-AEFC-4E94-8087-E72F5D028BFC")
|
||||
struct DECLSPEC_NOVTABLE IQueryCastBase1
|
||||
{
|
||||
virtual int GetValue1() = 0;
|
||||
virtual int GetValue1() = 0;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(IQueryCastBase2, "D62D6F01-0A54-40C0-9D23-A1C95EAF234D")
|
||||
struct DECLSPEC_NOVTABLE IQueryCastBase2
|
||||
{
|
||||
virtual int GetValue2() = 0;
|
||||
virtual int GetValue2() = 0;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(IQueryCastBase3, "54A5CAC7-4788-499E-BAD1-1BBEF60A30F9")
|
||||
struct DECLSPEC_NOVTABLE IQueryCastBase3
|
||||
{
|
||||
virtual int GetValue3() = 0;
|
||||
virtual int GetValue3() = 0;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(IQueryCastDerived1, "26747765-CB3C-42BC-8CBE-810F19779169")
|
||||
struct DECLSPEC_NOVTABLE IQueryCastDerived1 : public IQueryCastBase1
|
||||
{
|
||||
virtual int GetValue11() = 0;
|
||||
virtual int GetValue11() = 0;
|
||||
};
|
||||
|
||||
class QueryCastTraitsSample1 : public IQueryCastBase1
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct QueryCastTraitsBase2
|
||||
{
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(IQueryCastBase1))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(IQueryCastBase1))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastTraitsSample2 : public QueryCastTraitsBase2
|
||||
|
@ -64,91 +69,140 @@ class QueryCastTraitsSample2 : public QueryCastTraitsBase2
|
|||
MSO_STRUCT_GUID(QueryCastTraitsBase3, "5AF7D77B-7620-403D-9684-98AC591CF1A6")
|
||||
struct QueryCastTraitsBase3
|
||||
{
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(IQueryCastBase1))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
void* QueryCast(const GUID& riid) noexcept
|
||||
{
|
||||
if (riid == __uuidof(IQueryCastBase1))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastTraitsSample3 : public QueryCastTraitsBase3
|
||||
{
|
||||
};
|
||||
|
||||
class QueryCastChainSample1
|
||||
: public Mso::QueryCastChain<IQueryCastDerived1, IQueryCastBase1>
|
||||
class QueryCastChainSample1 : public Mso::QueryCastChain<IQueryCastDerived1, IQueryCastBase1>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue11() override { return 11; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue11() override
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastChainSample2
|
||||
: public Mso::QueryCastList<Mso::QueryCastChain<IQueryCastDerived1, IQueryCastBase1>, IQueryCastBase2>
|
||||
: public Mso::QueryCastList<Mso::QueryCastChain<IQueryCastDerived1, IQueryCastBase1>, IQueryCastBase2>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue11() override { return 11; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue11() override
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
MSO_CLASS_GUID(QueryCastDerivedSample1, "1B7B2202-8B80-4E08-B20B-85235696BE02")
|
||||
class QueryCastDerivedSample1 : public Mso::QueryCastList<IQueryCastBase1, Mso::QueryCastDerived<QueryCastDerivedSample1>>
|
||||
class QueryCastDerivedSample1
|
||||
: public Mso::QueryCastList<IQueryCastBase1, Mso::QueryCastDerived<QueryCastDerivedSample1>>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_STRING_GUID(MyTestGuid1, "65ADDF73-B4EE-4E36-AB61-FEEA1F01A169");
|
||||
|
||||
struct DECLSPEC_NOVTABLE IQueryCastNoGuid1
|
||||
{
|
||||
virtual int GetValue1() = 0;
|
||||
virtual int GetValue1() = 0;
|
||||
};
|
||||
|
||||
class QueryCastGuidSample1
|
||||
: public Mso::QueryCastList<Mso::QueryCastGuid<IQueryCastNoGuid1, &MyTestGuid1>, Mso::QueryCastGuid<IQueryCastBase2>>
|
||||
: public Mso::
|
||||
QueryCastList<Mso::QueryCastGuid<IQueryCastNoGuid1, &MyTestGuid1>, Mso::QueryCastGuid<IQueryCastBase2>>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastHiddenSample1 : public Mso::QueryCastHidden<IQueryCastBase1>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastListSample1 : public Mso::QueryCastList<IQueryCastBase1>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastListSample2 : public Mso::QueryCastList<IQueryCastBase1, IQueryCastBase2>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 12; }
|
||||
virtual int GetValue2() override { return 22; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 22;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastListSample3 : public Mso::QueryCastList<IQueryCastBase1, IQueryCastBase2, IQueryCastBase3>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override { return 13; }
|
||||
virtual int GetValue2() override { return 23; }
|
||||
virtual int GetValue3() override { return 33; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 23;
|
||||
}
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 33;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastListDerivedSample1 : public Mso::QueryCastList<QueryCastListSample2, IQueryCastBase3>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
|
||||
struct StaticCastSample1 : public Mso::QueryCastList<QueryCastListSample2, QueryCastListSample3>
|
||||
|
@ -161,357 +215,487 @@ struct StaticCastSample1 : public Mso::QueryCastList<QueryCastListSample2, Query
|
|||
|
||||
struct IUnkStaticBase1 : IUnknown
|
||||
{
|
||||
virtual int GetValue1() = 0;
|
||||
virtual int GetValue1() = 0;
|
||||
};
|
||||
|
||||
struct IUnkStaticBase2 : IUnknown
|
||||
{
|
||||
virtual int GetValue2() = 0;
|
||||
virtual int GetValue2() = 0;
|
||||
};
|
||||
|
||||
struct IStaticBase3
|
||||
{
|
||||
virtual int GetValue3() = 0;
|
||||
virtual int GetValue3() = 0;
|
||||
};
|
||||
|
||||
struct IStaticBase4
|
||||
{
|
||||
virtual int GetValue4() = 0;
|
||||
virtual int GetValue4() = 0;
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> works for one interface inherited from IUnknown
|
||||
struct StaticSample1 : Mso::QueryCastList<IUnkStaticBase1>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> works for two interfaces inherited from IUnknown
|
||||
struct StaticSample2 : Mso::QueryCastList<IUnkStaticBase1, IUnkStaticBase2>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> returns nullptr for one interface not inherited from IUnknown
|
||||
struct StaticSample3 : Mso::QueryCastList<IStaticBase3>
|
||||
{
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> returns nullptr for two interfaces not inherited from IUnknown
|
||||
struct StaticSample4 : Mso::QueryCastList<IStaticBase3, IStaticBase4>
|
||||
{
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue4() override { return 4; }
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue4() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> works for two interfaces inherited from IUnknown preceded by
|
||||
// an interface not inherited from IUnknown.
|
||||
struct StaticSample5 : Mso::QueryCastList<IStaticBase3, IUnkStaticBase1, IUnkStaticBase2>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue3() override { return 3; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To see if StaticCastElseNull<IUnknown*> works for two interfaces inherited from IUnknown preceded by
|
||||
// two interfaces not inherited from IUnknown.
|
||||
struct StaticSample6 : Mso::QueryCastList<IStaticBase3, IStaticBase4, IUnkStaticBase1, IUnkStaticBase2>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue4() override { return 4; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue4() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To test nested hierarchy: IUnknown is implemented by nested QueryCastList.
|
||||
struct StaticSample7 : Mso::QueryCastList<IStaticBase3, Mso::QueryCastList<IStaticBase4, IUnkStaticBase1, IUnkStaticBase2>>
|
||||
struct StaticSample7
|
||||
: Mso::QueryCastList<IStaticBase3, Mso::QueryCastList<IStaticBase4, IUnkStaticBase1, IUnkStaticBase2>>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue4() override { return 4; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue4() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To test nested hierarchy: IUnknown is implemented by nested QueryCastList.
|
||||
struct StaticSample8 : Mso::QueryCastList<Mso::QueryCastList<IStaticBase3, IStaticBase4>, Mso::QueryCastList<IUnkStaticBase1, IUnkStaticBase2>>
|
||||
struct StaticSample8
|
||||
: Mso::QueryCastList<
|
||||
Mso::QueryCastList<IStaticBase3, IStaticBase4>,
|
||||
Mso::QueryCastList<IUnkStaticBase1, IUnkStaticBase2>>
|
||||
{
|
||||
virtual int GetValue1() override { return 1; }
|
||||
virtual int GetValue2() override { return 2; }
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue4() override { return 4; }
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override { return E_NOINTERFACE; }
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override { return 1; }
|
||||
STDMETHOD_(ULONG, Release)() noexcept override { return 1; }
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue2() override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue4() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
STDMETHOD(QueryInterface)(const GUID& /*riid*/, void** /*ppvObject*/) noexcept override
|
||||
{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
STDMETHOD_(ULONG, AddRef)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
STDMETHOD_(ULONG, Release)() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// To test nested hierarchy: IUnknown is not implemented.
|
||||
struct StaticSample9 : Mso::QueryCastList<Mso::QueryCastList<IStaticBase3, IStaticBase4>>
|
||||
{
|
||||
virtual int GetValue3() override { return 3; }
|
||||
virtual int GetValue4() override { return 4; }
|
||||
virtual int GetValue3() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
virtual int GetValue4() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
};
|
||||
|
||||
class QueryCastBase1WithArgs : public IQueryCastBase1
|
||||
{
|
||||
public:
|
||||
int m_int;
|
||||
std::string m_string;
|
||||
int m_int;
|
||||
std::string m_string;
|
||||
|
||||
QueryCastBase1WithArgs(int i, const std::string& str) : m_int(i), m_string(str) {}
|
||||
virtual int GetValue1() override { return 1; }
|
||||
QueryCastBase1WithArgs(int i, const std::string& str) : m_int(i), m_string(str) {}
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper so that we can call protected constructors
|
||||
template <typename T>
|
||||
struct StructWithBase : public T
|
||||
{
|
||||
template <typename... Args>
|
||||
StructWithBase(Args&&... args) : T(std::forward<Args>(args)...) {}
|
||||
template <typename... Args>
|
||||
StructWithBase(Args&&... args) : T(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// A helper method to call QueryCastTraits::QueryCast.
|
||||
template <typename TTarget, typename TSource>
|
||||
inline TTarget* TraitsQueryCast(const TSource& source, const GUID &riid = __uuidof(TTarget)) noexcept
|
||||
inline TTarget* TraitsQueryCast(const TSource& source, const GUID& riid = __uuidof(TTarget)) noexcept
|
||||
{
|
||||
if (source != nullptr)
|
||||
{
|
||||
return static_cast<TTarget*>(Mso::QueryCastHelper::QueryCast<typename std::remove_pointer<TSource>::type>(source, riid));
|
||||
}
|
||||
if (source != nullptr)
|
||||
{
|
||||
return static_cast<TTarget*>(
|
||||
Mso::QueryCastHelper::QueryCast<typename std::remove_pointer<TSource>::type>(source, riid));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TestClassComponent(ObjectQueryCastTest, Mso.ObjectQueryCast)
|
||||
TEST_CLASS(ObjectQueryCastTest)
|
||||
TEST_CLASS (ObjectQueryCastTest){// Use GUID by default.
|
||||
TEST_METHOD(QueryCastTraits_Guid){QueryCastTraitsSample1 obj;
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1, IQueryCastBase1*>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
TestAssert::IsTrue((void*)&obj == (void*)base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase1, IQueryCastBase1*>(&obj, __uuidof(IQueryCastBase2));
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
|
||||
// Use QueryCast method if it is present.
|
||||
TEST_METHOD(QueryCastTraits_QueryCast)
|
||||
{
|
||||
// Use GUID by default.
|
||||
TEST_METHOD(QueryCastTraits_Guid)
|
||||
{
|
||||
QueryCastTraitsSample1 obj;
|
||||
QueryCastTraitsSample2 obj;
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1, IQueryCastBase1*>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
TestAssert::IsTrue((void*)&obj == (void*)base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase1, IQueryCastBase1*>(&obj, __uuidof(IQueryCastBase2));
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
|
||||
// Use QueryCast method if it is present.
|
||||
TEST_METHOD(QueryCastTraits_QueryCast)
|
||||
{
|
||||
QueryCastTraitsSample2 obj;
|
||||
// QueryCast has higher priority over the assigned GUID.
|
||||
TEST_METHOD(QueryCastTraits_QueryCastOverridesGuid)
|
||||
{
|
||||
QueryCastTraitsSample3 obj;
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<QueryCastTraitsBase3>(&obj);
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
|
||||
// QueryCast has higher priority over the assigned GUID.
|
||||
TEST_METHOD(QueryCastTraits_QueryCastOverridesGuid)
|
||||
{
|
||||
QueryCastTraitsSample3 obj;
|
||||
// Test use of QueryCastChain to query for a base interface.
|
||||
TEST_METHOD(QueryCastChain_Test)
|
||||
{
|
||||
QueryCastChainSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t), sizeof(obj), L"There must be only one v-table.");
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<QueryCastTraitsBase3>(&obj);
|
||||
TestAssert::IsNull(base2);
|
||||
}
|
||||
auto derived1 = TraitsQueryCast<IQueryCastDerived1>(&obj);
|
||||
TestAssert::IsNotNull(derived1);
|
||||
}
|
||||
|
||||
// Test use of QueryCastChain to query for a base interface.
|
||||
TEST_METHOD(QueryCastChain_Test)
|
||||
{
|
||||
QueryCastChainSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t), sizeof(obj), L"There must be only one v-table.");
|
||||
// Test use of QueryCastChain to query for a base interface.
|
||||
TEST_METHOD(QueryCastChain_InList)
|
||||
{
|
||||
QueryCastChainSample2 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t) * 2, sizeof(obj), L"There must be only two v-tables.");
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto derived1 = TraitsQueryCast<IQueryCastDerived1>(&obj);
|
||||
TestAssert::IsNotNull(derived1);
|
||||
}
|
||||
auto derived1 = TraitsQueryCast<IQueryCastDerived1>(&obj);
|
||||
TestAssert::IsNotNull(derived1);
|
||||
|
||||
// Test use of QueryCastChain to query for a base interface.
|
||||
TEST_METHOD(QueryCastChain_InList)
|
||||
{
|
||||
QueryCastChainSample2 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t) * 2, sizeof(obj), L"There must be only two v-tables.");
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
// Test use of QueryCastDerived to query the derived type by its GUID.
|
||||
TEST_METHOD(QueryCastDerived_Test)
|
||||
{
|
||||
QueryCastDerivedSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t), sizeof(obj), L"There must be only one v-table.");
|
||||
|
||||
auto derived1 = TraitsQueryCast<IQueryCastDerived1>(&obj);
|
||||
TestAssert::IsNotNull(derived1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
auto derived = TraitsQueryCast<QueryCastDerivedSample1>(&obj);
|
||||
TestAssert::IsNotNull(derived);
|
||||
}
|
||||
|
||||
// Test use of QueryCastDerived to query the derived type by its GUID.
|
||||
TEST_METHOD(QueryCastDerived_Test)
|
||||
{
|
||||
QueryCastDerivedSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t), sizeof(obj), L"There must be only one v-table.");
|
||||
// Test use of QueryCastGuid to query for a base interface.
|
||||
TEST_METHOD(QueryCastGuid_Test)
|
||||
{
|
||||
QueryCastGuidSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t) * 2, sizeof(obj), L"There must be only two v-tables.");
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastNoGuid1>(&obj, MyTestGuid1);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto derived = TraitsQueryCast<QueryCastDerivedSample1>(&obj);
|
||||
TestAssert::IsNotNull(derived);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
|
||||
// Test use of QueryCastGuid to query for a base interface.
|
||||
TEST_METHOD(QueryCastGuid_Test)
|
||||
{
|
||||
QueryCastGuidSample1 obj;
|
||||
TestAssert::AreEqual(sizeof(uintptr_t) * 2, sizeof(obj), L"There must be only two v-tables.");
|
||||
// QueryCastHidden hides the base interface from the query cast.
|
||||
TEST_METHOD(QueryCastHidden_Test)
|
||||
{
|
||||
QueryCastHiddenSample1 obj;
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastNoGuid1>(&obj, MyTestGuid1);
|
||||
TestAssert::IsNotNull(base1);
|
||||
IQueryCastBase1* intf = &obj;
|
||||
TestAssert::IsNotNull(intf);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNull(base1);
|
||||
}
|
||||
|
||||
// QueryCastHidden hides the base interface from the query cast.
|
||||
TEST_METHOD(QueryCastHidden_Test)
|
||||
{
|
||||
QueryCastHiddenSample1 obj;
|
||||
// Test use of QueryCastList with one base type.
|
||||
TEST_METHOD(QueryCastList_OneBaseType)
|
||||
{
|
||||
QueryCastListSample1 obj;
|
||||
|
||||
IQueryCastBase1* intf = &obj;
|
||||
TestAssert::IsNotNull(intf);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
}
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNull(base1);
|
||||
}
|
||||
// Test use of QueryCastList with two base types.
|
||||
TEST_METHOD(QueryCastList_TwoBaseTypes)
|
||||
{
|
||||
QueryCastListSample2 obj;
|
||||
|
||||
// Test use of QueryCastList with one base type.
|
||||
TEST_METHOD(QueryCastList_OneBaseType)
|
||||
{
|
||||
QueryCastListSample1 obj;
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
|
||||
// Test use of QueryCastList with two base types.
|
||||
TEST_METHOD(QueryCastList_TwoBaseTypes)
|
||||
{
|
||||
QueryCastListSample2 obj;
|
||||
// Test use of QueryCastList with three base types.
|
||||
TEST_METHOD(QueryCastList_ThreeBaseTypes)
|
||||
{
|
||||
QueryCastListSample3 obj;
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
|
||||
// Test use of QueryCastList with three base types.
|
||||
TEST_METHOD(QueryCastList_ThreeBaseTypes)
|
||||
{
|
||||
QueryCastListSample3 obj;
|
||||
auto base3 = TraitsQueryCast<IQueryCastBase3>(&obj);
|
||||
TestAssert::IsNotNull(base3);
|
||||
}
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
// Test use of QueryCastList with a type that already inherits from the QueryCastList.
|
||||
TEST_METHOD(QueryCastList_DerivedQueryCastList)
|
||||
{
|
||||
QueryCastListDerivedSample1 obj;
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
|
||||
auto base3 = TraitsQueryCast<IQueryCastBase3>(&obj);
|
||||
TestAssert::IsNotNull(base3);
|
||||
}
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
|
||||
// Test use of QueryCastList with a type that already inherits from the QueryCastList.
|
||||
TEST_METHOD(QueryCastList_DerivedQueryCastList)
|
||||
{
|
||||
QueryCastListDerivedSample1 obj;
|
||||
auto base3 = TraitsQueryCast<IQueryCastBase3>(&obj);
|
||||
TestAssert::IsNotNull(base3);
|
||||
}
|
||||
|
||||
auto base1 = TraitsQueryCast<IQueryCastBase1>(&obj);
|
||||
TestAssert::IsNotNull(base1);
|
||||
TEST_METHOD(QueryCastList_StaticCast)
|
||||
{
|
||||
StaticCastSample1 obj;
|
||||
|
||||
auto base2 = TraitsQueryCast<IQueryCastBase2>(&obj);
|
||||
TestAssert::IsNotNull(base2);
|
||||
IQueryCastBase1* base1 = obj.StaticCastElseNull<IQueryCastBase1*>();
|
||||
TestAssert::IsNotNull(base1);
|
||||
TestAssert::AreEqual(12, base1->GetValue1());
|
||||
|
||||
auto base3 = TraitsQueryCast<IQueryCastBase3>(&obj);
|
||||
TestAssert::IsNotNull(base3);
|
||||
}
|
||||
IQueryCastBase2* base2 = obj.StaticCastElseNull<IQueryCastBase2*>();
|
||||
TestAssert::IsNotNull(base2);
|
||||
TestAssert::AreEqual(22, base2->GetValue2());
|
||||
|
||||
TEST_METHOD(QueryCastList_StaticCast)
|
||||
{
|
||||
StaticCastSample1 obj;
|
||||
IQueryCastBase3* base3 = obj.StaticCastElseNull<IQueryCastBase3*>();
|
||||
TestAssert::IsNotNull(base3);
|
||||
TestAssert::AreEqual(33, base3->GetValue3());
|
||||
}
|
||||
|
||||
IQueryCastBase1* base1 = obj.StaticCastElseNull<IQueryCastBase1*>();
|
||||
TestAssert::IsNotNull(base1);
|
||||
TestAssert::AreEqual(12, base1->GetValue1());
|
||||
TEST_METHOD(QueryCastList_StaticCast2)
|
||||
{
|
||||
StaticSample1 sample1;
|
||||
StaticSample2 sample2;
|
||||
StaticSample3 sample3;
|
||||
StaticSample4 sample4;
|
||||
StaticSample5 sample5;
|
||||
StaticSample6 sample6;
|
||||
StaticSample7 sample7;
|
||||
StaticSample8 sample8;
|
||||
StaticSample9 sample9;
|
||||
TestAssert::IsNotNull(sample1.StaticCastElseNull<IUnknown*>(), L"sample1");
|
||||
TestAssert::IsNotNull(sample2.StaticCastElseNull<IUnknown*>(), L"sample2");
|
||||
TestAssert::IsNull(sample3.StaticCastElseNull<IUnknown*>(), L"sample3");
|
||||
TestAssert::IsNull(sample4.StaticCastElseNull<IUnknown*>(), L"sample4");
|
||||
TestAssert::IsNotNull(sample5.StaticCastElseNull<IUnknown*>(), L"sample5");
|
||||
TestAssert::IsNotNull(sample6.StaticCastElseNull<IUnknown*>(), L"sample6");
|
||||
TestAssert::IsNotNull(sample7.StaticCastElseNull<IUnknown*>(), L"sample7");
|
||||
TestAssert::IsNotNull(sample8.StaticCastElseNull<IUnknown*>(), L"sample8");
|
||||
TestAssert::IsNull(sample9.StaticCastElseNull<IUnknown*>(), L"sample9");
|
||||
}
|
||||
|
||||
IQueryCastBase2* base2 = obj.StaticCastElseNull<IQueryCastBase2*>();
|
||||
TestAssert::IsNotNull(base2);
|
||||
TestAssert::AreEqual(22, base2->GetValue2());
|
||||
TEST_METHOD(QueryCastChain_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastChain<QueryCastBase1WithArgs, IQueryCastBase1>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
|
||||
IQueryCastBase3* base3 = obj.StaticCastElseNull<IQueryCastBase3*>();
|
||||
TestAssert::IsNotNull(base3);
|
||||
TestAssert::AreEqual(33, base3->GetValue3());
|
||||
}
|
||||
TEST_METHOD(QueryCastGuid_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastGuid<QueryCastBase1WithArgs, &MyTestGuid1>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD(QueryCastList_StaticCast2)
|
||||
{
|
||||
StaticSample1 sample1;
|
||||
StaticSample2 sample2;
|
||||
StaticSample3 sample3;
|
||||
StaticSample4 sample4;
|
||||
StaticSample5 sample5;
|
||||
StaticSample6 sample6;
|
||||
StaticSample7 sample7;
|
||||
StaticSample8 sample8;
|
||||
StaticSample9 sample9;
|
||||
TestAssert::IsNotNull(sample1.StaticCastElseNull<IUnknown*>(), L"sample1");
|
||||
TestAssert::IsNotNull(sample2.StaticCastElseNull<IUnknown*>(), L"sample2");
|
||||
TestAssert::IsNull(sample3.StaticCastElseNull<IUnknown*>(), L"sample3");
|
||||
TestAssert::IsNull(sample4.StaticCastElseNull<IUnknown*>(), L"sample4");
|
||||
TestAssert::IsNotNull(sample5.StaticCastElseNull<IUnknown*>(), L"sample5");
|
||||
TestAssert::IsNotNull(sample6.StaticCastElseNull<IUnknown*>(), L"sample6");
|
||||
TestAssert::IsNotNull(sample7.StaticCastElseNull<IUnknown*>(), L"sample7");
|
||||
TestAssert::IsNotNull(sample8.StaticCastElseNull<IUnknown*>(), L"sample8");
|
||||
TestAssert::IsNull(sample9.StaticCastElseNull<IUnknown*>(), L"sample9");
|
||||
}
|
||||
|
||||
TEST_METHOD(QueryCastChain_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastChain<QueryCastBase1WithArgs, IQueryCastBase1>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD(QueryCastGuid_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastGuid<QueryCastBase1WithArgs, &MyTestGuid1>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD(QueryCastHidden_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastHidden<QueryCastBase1WithArgs>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
};
|
||||
TEST_METHOD(QueryCastHidden_ForwardCtorArgs)
|
||||
{
|
||||
StructWithBase<Mso::QueryCastHidden<QueryCastBase1WithArgs>> testStruct(5, "asdf");
|
||||
TestAssert::AreEqual(5, testStruct.m_int);
|
||||
TestAssert::AreEqual("asdf", testStruct.m_string.c_str());
|
||||
}
|
||||
}
|
||||
;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,562 +12,523 @@ Unit tests for classes in the ObjectSwarm.h
|
|||
#include "testAllocators.h"
|
||||
#include <test/testCheck.h>
|
||||
|
||||
//#define TEST_BAD_INHERITANCE1 // Uncomment to confirm VEC, but observe a memory leak. We cannot safely destroy this class.
|
||||
//#define TEST_BAD_INHERITANCE1 // Uncomment to confirm VEC, but observe a memory leak. We cannot safely destroy this
|
||||
//class.
|
||||
|
||||
MSO_STRUCT_GUID(ISwarmSample1, "962D2470-7452-43AB-9F74-63545A3E8A58")
|
||||
struct DECLSPEC_NOVTABLE ISwarmSample1 : public IUnknown
|
||||
{
|
||||
virtual int GetValue1() = 0;
|
||||
virtual int GetValue1() = 0;
|
||||
};
|
||||
|
||||
MSO_STRUCT_GUID(ISwarmSample2, "B7E82DB0-436F-4B05-950F-FC1FE1ACE651")
|
||||
struct DECLSPEC_NOVTABLE ISwarmSample2 : public IUnknown
|
||||
{
|
||||
virtual int GetValue2() = 0;
|
||||
virtual int GetValue2() = 0;
|
||||
};
|
||||
|
||||
class SwarmMemberSample1 final
|
||||
: public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
class SwarmMemberSample1 final : public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
{
|
||||
public:
|
||||
SwarmMemberSample1(bool& deleted) noexcept
|
||||
: m_deleted(deleted)
|
||||
{
|
||||
}
|
||||
SwarmMemberSample1(bool& deleted) noexcept : m_deleted(deleted) {}
|
||||
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~SwarmMemberSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
virtual ~SwarmMemberSample1() noexcept
|
||||
{
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_deleted;
|
||||
bool& m_deleted;
|
||||
};
|
||||
|
||||
class SwarmMemberSample2 final
|
||||
: public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample2>
|
||||
class SwarmMemberSample2 final : public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample2>
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::NoThrowCtorAndInitializeThis;
|
||||
|
||||
SwarmMemberSample2() noexcept
|
||||
: m_deleted(nullptr)
|
||||
, m_other()
|
||||
{
|
||||
}
|
||||
SwarmMemberSample2() noexcept : m_deleted(nullptr), m_other() {}
|
||||
|
||||
bool InitializeThis(bool& deleted) noexcept
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
return true;
|
||||
}
|
||||
bool InitializeThis(bool& deleted) noexcept
|
||||
{
|
||||
m_deleted = &deleted;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
virtual int GetValue2() noexcept override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample1> GetOther() noexcept
|
||||
{
|
||||
return m_other.Get();
|
||||
}
|
||||
Mso::TCntPtr<SwarmMemberSample1> GetOther() noexcept
|
||||
{
|
||||
return m_other.Get();
|
||||
}
|
||||
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return m_other.IsDifferentSwarm();
|
||||
}
|
||||
bool IsDifferentSwarm() const noexcept
|
||||
{
|
||||
return m_other.IsDifferentSwarm();
|
||||
}
|
||||
|
||||
void SetOther(SwarmMemberSample1& other) noexcept
|
||||
{
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(this);
|
||||
VerifyElseCrashSzTag(!swarm.IsEmpty(), "This object must be part of a swarm.", 0x01003708 /* tag_bad2i */);
|
||||
m_other = Mso::SwarmMemberPtr<SwarmMemberSample1>(&other, *swarm);
|
||||
}
|
||||
void SetOther(SwarmMemberSample1& other) noexcept
|
||||
{
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(this);
|
||||
VerifyElseCrashSzTag(!swarm.IsEmpty(), "This object must be part of a swarm.", 0x01003708 /* tag_bad2i */);
|
||||
m_other = Mso::SwarmMemberPtr<SwarmMemberSample1>(&other, *swarm);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~SwarmMemberSample2() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
virtual ~SwarmMemberSample2() noexcept
|
||||
{
|
||||
*m_deleted = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool* m_deleted;
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> m_other;
|
||||
bool* m_deleted;
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> m_other;
|
||||
};
|
||||
|
||||
class SwarmMemberSample2Base
|
||||
: public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
class SwarmMemberSample2Base : public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
{
|
||||
public:
|
||||
SwarmMemberSample2Base(int index, const std::function<void(int)>& onDelete)
|
||||
: m_index(index)
|
||||
, m_onDelete(onDelete)
|
||||
{
|
||||
}
|
||||
SwarmMemberSample2Base(int index, const std::function<void(int)>& onDelete) : m_index(index), m_onDelete(onDelete) {}
|
||||
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~SwarmMemberSample2Base() noexcept
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onDelete(m_index);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
virtual ~SwarmMemberSample2Base() noexcept
|
||||
{
|
||||
OACR_ASSUME_NOTHROW_BEGIN
|
||||
m_onDelete(m_index);
|
||||
OACR_ASSUME_NOTHROW_END
|
||||
}
|
||||
|
||||
private:
|
||||
int m_index;
|
||||
std::function<void(int)> m_onDelete;
|
||||
int m_index;
|
||||
std::function<void(int)> m_onDelete;
|
||||
};
|
||||
|
||||
class SwarmMemberSample21 : public SwarmMemberSample2Base
|
||||
{
|
||||
public:
|
||||
SwarmMemberSample21(int index, const std::function<void(int)>& onDelete)
|
||||
: SwarmMemberSample2Base(index, onDelete)
|
||||
{
|
||||
}
|
||||
SwarmMemberSample21(int index, const std::function<void(int)>& onDelete) : SwarmMemberSample2Base(index, onDelete) {}
|
||||
};
|
||||
|
||||
class SwarmMemberSample22 : public SwarmMemberSample2Base
|
||||
{
|
||||
public:
|
||||
SwarmMemberSample22(int index, const std::function<void(int)>& onDelete)
|
||||
: SwarmMemberSample2Base(index, onDelete)
|
||||
{
|
||||
}
|
||||
SwarmMemberSample22(int index, const std::function<void(int)>& onDelete) : SwarmMemberSample2Base(index, onDelete) {}
|
||||
};
|
||||
|
||||
class SwarmMemberSample3CannotAllocate
|
||||
: public Mso::UnknownObject<BadAllocWeakRefCount, ISwarmSample1>
|
||||
class SwarmMemberSample3CannotAllocate : public Mso::UnknownObject<BadAllocWeakRefCount, ISwarmSample1>
|
||||
{
|
||||
public:
|
||||
SwarmMemberSample3CannotAllocate() noexcept
|
||||
{
|
||||
}
|
||||
SwarmMemberSample3CannotAllocate() noexcept {}
|
||||
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~SwarmMemberSample3CannotAllocate() noexcept
|
||||
{
|
||||
}
|
||||
virtual ~SwarmMemberSample3CannotAllocate() noexcept {}
|
||||
};
|
||||
|
||||
class SwarmMemberSampleBase
|
||||
: public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
class SwarmMemberSampleBase : public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
{
|
||||
public:
|
||||
SwarmMemberSampleBase() noexcept
|
||||
{
|
||||
}
|
||||
SwarmMemberSampleBase() noexcept {}
|
||||
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~SwarmMemberSampleBase() noexcept
|
||||
{
|
||||
}
|
||||
virtual ~SwarmMemberSampleBase() noexcept {}
|
||||
};
|
||||
|
||||
class SwarmMemberSample4Throw
|
||||
: public SwarmMemberSampleBase
|
||||
class SwarmMemberSample4Throw : public SwarmMemberSampleBase
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtor;
|
||||
|
||||
SwarmMemberSample4Throw()
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
SwarmMemberSample4Throw()
|
||||
{
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
};
|
||||
|
||||
class SwarmMemberSample5InitThrow
|
||||
: public SwarmMemberSampleBase
|
||||
class SwarmMemberSample5InitThrow : public SwarmMemberSampleBase
|
||||
{
|
||||
public:
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
using MakePolicy = Mso::MakePolicy::ThrowCtorAndInitializeThis;
|
||||
|
||||
void InitializeThis()
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
void InitializeThis()
|
||||
{
|
||||
UNREFERENCED_OACR(this);
|
||||
throw std::runtime_error("Test");
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef TEST_BAD_INHERITANCE1
|
||||
class SomeVirtualClass
|
||||
{
|
||||
public:
|
||||
virtual ~SomeVirtualClass() noexcept{}
|
||||
virtual ~SomeVirtualClass() noexcept {}
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// !!! Mso::UnknownObject must be always the first one in the inheritance !!!
|
||||
class BadSwarmMember1 final
|
||||
: public SomeVirtualClass
|
||||
, public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
: public SomeVirtualClass
|
||||
, public Mso::UnknownObject<Mso::RefCountStrategy::WeakRef, ISwarmSample1>
|
||||
{
|
||||
public:
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual int GetValue1() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
TEST_CLASS(ObjectSwarmTest)
|
||||
TEST_CLASS (ObjectSwarmTest)
|
||||
{
|
||||
TEST_METHOD(ObjectSwarm_OneMember)
|
||||
{
|
||||
bool deleted;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TEST_METHOD(ObjectSwarm_OneMember)
|
||||
{
|
||||
bool deleted;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_TwoMembers)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
TEST_METHOD(ObjectSwarm_TwoMembers)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_ThreeMembers)
|
||||
{
|
||||
// Test that members are deleted in reverse order to how they were created.
|
||||
const int memberCount = 3;
|
||||
int deleteOrderIndex = 0;
|
||||
int deleteOrder[memberCount];
|
||||
std::function<void(int)> onDelete = [&deleteOrderIndex, &deleteOrder](int index) noexcept
|
||||
{
|
||||
deleteOrder[--deleteOrderIndex] = index;
|
||||
};
|
||||
TEST_METHOD(ObjectSwarm_ThreeMembers)
|
||||
{
|
||||
// Test that members are deleted in reverse order to how they were created.
|
||||
const int memberCount = 3;
|
||||
int deleteOrderIndex = 0;
|
||||
int deleteOrder[memberCount];
|
||||
std::function<void(int)> onDelete = [&deleteOrderIndex, &deleteOrder](int index) noexcept {
|
||||
deleteOrder[--deleteOrderIndex] = index;
|
||||
};
|
||||
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample21> member1 = Mso::Swarm::Make<SwarmMemberSample21>(deleteOrderIndex++, onDelete);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample22> member2 = swarm->MakeMember<SwarmMemberSample22>(deleteOrderIndex++, onDelete);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
// Members can be of the same type
|
||||
Mso::TCntPtr<SwarmMemberSample22> member3 = swarm->MakeMember<SwarmMemberSample22>(deleteOrderIndex++, onDelete);
|
||||
TestAssert::IsNotNull(member3.Get());
|
||||
}
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample21> member1 = Mso::Swarm::Make<SwarmMemberSample21>(deleteOrderIndex++, onDelete);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample22> member2 = swarm->MakeMember<SwarmMemberSample22>(deleteOrderIndex++, onDelete);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
// Members can be of the same type
|
||||
Mso::TCntPtr<SwarmMemberSample22> member3 = swarm->MakeMember<SwarmMemberSample22>(deleteOrderIndex++, onDelete);
|
||||
TestAssert::IsNotNull(member3.Get());
|
||||
}
|
||||
|
||||
TestAssert::AreEqual(0, deleteOrderIndex, L"Not all members are deleted");
|
||||
TestAssert::AreEqual(0, deleteOrderIndex, L"Not all members are deleted");
|
||||
|
||||
for (int i = 0; i < memberCount; i++)
|
||||
{
|
||||
TestAssert::AreEqual(i, deleteOrder[i], L"Wrong deletion order");
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < memberCount; i++)
|
||||
{
|
||||
TestAssert::AreEqual(i, deleteOrder[i], L"Wrong deletion order");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_FromObject)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
TEST_METHOD(ObjectSwarm_FromObject)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf1 = qi_cast<ISwarmSample1>(member1.Get());
|
||||
TestAssert::IsNotNull(memberIntf1.Get());
|
||||
swarm = Mso::Swarm::FromObject(memberIntf1.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf1 = qi_cast<ISwarmSample1>(member1.Get());
|
||||
TestAssert::IsNotNull(memberIntf1.Get());
|
||||
swarm = Mso::Swarm::FromObject(memberIntf1.Get());
|
||||
TestAssert::IsNotNull(swarm.Get());
|
||||
|
||||
//Mso::TCntPtr<ISwarmSample2> memberIntf2 = qi_cast<ISwarmSample2>(member2.Get());
|
||||
//TestAssert::IsNotNull(memberIntf2.Get());
|
||||
//swarm = Mso::Swarm::FromObject(memberIntf2.Get());
|
||||
//TestAssert::IsNotNull(swarm.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
// Mso::TCntPtr<ISwarmSample2> memberIntf2 = qi_cast<ISwarmSample2>(member2.Get());
|
||||
// TestAssert::IsNotNull(memberIntf2.Get());
|
||||
// swarm = Mso::Swarm::FromObject(memberIntf2.Get());
|
||||
// TestAssert::IsNotNull(swarm.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_WeakPtr)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::WeakPtr<SwarmMemberSample1> memberWeak1;
|
||||
Mso::WeakPtr<SwarmMemberSample2> memberWeak2;
|
||||
Mso::WeakPtr<ISwarmSample1> memberIntfWeak1;
|
||||
Mso::WeakPtr<ISwarmSample2> memberIntfWeak2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
TEST_METHOD(ObjectSwarm_WeakPtr)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::WeakPtr<SwarmMemberSample1> memberWeak1;
|
||||
Mso::WeakPtr<SwarmMemberSample2> memberWeak2;
|
||||
Mso::WeakPtr<ISwarmSample1> memberIntfWeak1;
|
||||
Mso::WeakPtr<ISwarmSample2> memberIntfWeak2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = swarm->MakeMember<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
|
||||
memberWeak1 = member1;
|
||||
memberWeak2 = member2;
|
||||
memberIntfWeak1 = qi_cast<ISwarmSample1>(member1.Get());
|
||||
memberIntfWeak2 = qi_cast<ISwarmSample2>(member2.Get());
|
||||
memberWeak1 = member1;
|
||||
memberWeak2 = member2;
|
||||
memberIntfWeak1 = qi_cast<ISwarmSample1>(member1.Get());
|
||||
memberIntfWeak2 = qi_cast<ISwarmSample2>(member2.Get());
|
||||
|
||||
TestAssert::IsFalse(memberWeak1.IsExpired());
|
||||
TestAssert::IsFalse(memberWeak2.IsExpired());
|
||||
TestAssert::IsFalse(memberIntfWeak1.IsExpired());
|
||||
TestAssert::IsFalse(memberIntfWeak2.IsExpired());
|
||||
TestAssert::IsFalse(memberWeak1.IsExpired());
|
||||
TestAssert::IsFalse(memberWeak2.IsExpired());
|
||||
TestAssert::IsFalse(memberIntfWeak1.IsExpired());
|
||||
TestAssert::IsFalse(memberIntfWeak2.IsExpired());
|
||||
|
||||
Debug(TestAssert::AreEqual(/*members*/2u + /*weakptr*/4u, member1->GetWeakRef().WeakRefCount()));
|
||||
Debug(TestAssert::AreEqual(/*members*/2u + /*swarm*/1u, member1->GetWeakRef().RefCount()));
|
||||
Debug(TestAssert::AreEqual(/*members*/ 2u + /*weakptr*/ 4u, member1->GetWeakRef().WeakRefCount()));
|
||||
Debug(TestAssert::AreEqual(/*members*/ 2u + /*swarm*/ 1u, member1->GetWeakRef().RefCount()));
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample1> member11 = memberWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<SwarmMemberSample2> member21 = memberWeak2.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf11 = memberIntfWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample2> memberIntf21 = memberIntfWeak2.GetStrongPtr();
|
||||
TestAssert::IsNotNull(member11.Get());
|
||||
TestAssert::IsNotNull(member21.Get());
|
||||
TestAssert::IsNotNull(memberIntf11.Get());
|
||||
TestAssert::IsNotNull(memberIntf21.Get());
|
||||
}
|
||||
TestAssert::IsTrue(memberWeak1.IsExpired());
|
||||
TestAssert::IsTrue(memberWeak2.IsExpired());
|
||||
TestAssert::IsTrue(memberIntfWeak1.IsExpired());
|
||||
TestAssert::IsTrue(memberIntfWeak2.IsExpired());
|
||||
Mso::TCntPtr<SwarmMemberSample1> member11 = memberWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<SwarmMemberSample2> member21 = memberWeak2.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf11 = memberIntfWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample2> memberIntf21 = memberIntfWeak2.GetStrongPtr();
|
||||
TestAssert::IsNotNull(member11.Get());
|
||||
TestAssert::IsNotNull(member21.Get());
|
||||
TestAssert::IsNotNull(memberIntf11.Get());
|
||||
TestAssert::IsNotNull(memberIntf21.Get());
|
||||
}
|
||||
TestAssert::IsTrue(memberWeak1.IsExpired());
|
||||
TestAssert::IsTrue(memberWeak2.IsExpired());
|
||||
TestAssert::IsTrue(memberIntfWeak1.IsExpired());
|
||||
TestAssert::IsTrue(memberIntfWeak2.IsExpired());
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample1> member11 = memberWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<SwarmMemberSample2> member21 = memberWeak2.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf11 = memberIntfWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample2> memberIntf21 = memberIntfWeak2.GetStrongPtr();
|
||||
TestAssert::IsNull(member11.Get());
|
||||
TestAssert::IsNull(member21.Get());
|
||||
TestAssert::IsNull(memberIntf11.Get());
|
||||
TestAssert::IsNull(memberIntf21.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
Mso::TCntPtr<SwarmMemberSample1> member11 = memberWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<SwarmMemberSample2> member21 = memberWeak2.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample1> memberIntf11 = memberIntfWeak1.GetStrongPtr();
|
||||
Mso::TCntPtr<ISwarmSample2> memberIntf21 = memberIntfWeak2.GetStrongPtr();
|
||||
TestAssert::IsNull(member11.Get());
|
||||
TestAssert::IsNull(member21.Get());
|
||||
TestAssert::IsNull(memberIntf11.Get());
|
||||
TestAssert::IsNull(memberIntf21.Get());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1, L"First Swarm member is not deleted.");
|
||||
TestAssert::IsTrue(deleted2, L"Second Swarm member is not deleted.");
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_SameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = swarm->MakeMember<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_SameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = swarm->MakeMember<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
|
||||
member2->SetOther(*member1);
|
||||
TestAssert::IsTrue(member1.Get() == member2->GetOther().Get());
|
||||
TestAssert::IsFalse(member2->IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
member2->SetOther(*member1);
|
||||
TestAssert::IsTrue(member1.Get() == member2->GetOther().Get());
|
||||
TestAssert::IsFalse(member2->IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_DifferentSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_DifferentSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
|
||||
member2->SetOther(*member1);
|
||||
TestAssert::IsTrue(member1.Get() == member2->GetOther().Get());
|
||||
TestAssert::IsTrue(member2->IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
member2->SetOther(*member1);
|
||||
TestAssert::IsTrue(member1.Get() == member2->GetOther().Get());
|
||||
TestAssert::IsTrue(member2->IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_KnownSameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member0 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member0.Get());
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_KnownSameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member0 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member0.Get());
|
||||
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/true> member1 =
|
||||
swarm->MakeMember<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/ true> member1 =
|
||||
swarm->MakeMember<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/true> member2 = std::move(member1);
|
||||
TestAssert::IsNull(member1.Get());
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/ true> member2 = std::move(member1);
|
||||
TestAssert::IsNull(member1.Get());
|
||||
TestAssert::IsNotNull(member2.Get());
|
||||
|
||||
using std::swap;
|
||||
swap(member1, member2);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
TestAssert::IsNull(member2.Get());
|
||||
TestAssert::IsFalse(member1.IsEmpty());
|
||||
TestAssert::IsTrue(member2.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
using std::swap;
|
||||
swap(member1, member2);
|
||||
TestAssert::IsNotNull(member1.Get());
|
||||
TestAssert::IsNull(member2.Get());
|
||||
TestAssert::IsFalse(member1.IsEmpty());
|
||||
TestAssert::IsTrue(member2.IsEmpty());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_CannotAllocate)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_CannotAllocate)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
|
||||
TestAssert::ExpectVEC([&]()
|
||||
{
|
||||
swarm->MakeMember<SwarmMemberSample3CannotAllocate>();
|
||||
});
|
||||
TestAssert::ExpectVEC([&]() { swarm->MakeMember<SwarmMemberSample3CannotAllocate>(); });
|
||||
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_ConstructorThrows)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_ConstructorThrows)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
swarm->MakeMember<SwarmMemberSample4Throw>();
|
||||
});
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() { swarm->MakeMember<SwarmMemberSample4Throw>(); });
|
||||
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_InitializeThrows)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
TEST_METHOD(ObjectSwarm_MakeMember_InitializeThrows)
|
||||
{
|
||||
bool deleted1;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member1.Get());
|
||||
|
||||
TestAssert::ExpectException<std::runtime_error>([&]()
|
||||
{
|
||||
swarm->MakeMember<SwarmMemberSample5InitThrow>();
|
||||
});
|
||||
TestAssert::ExpectException<std::runtime_error>([&]() { swarm->MakeMember<SwarmMemberSample5InitThrow>(); });
|
||||
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
Debug(TestAssert::AreEqual(2u, swarm->RefCount()));
|
||||
Debug(TestAssert::AreEqual(1u, swarm->WeakRefCount()));
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_ObjectWeakRef_SameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef2 = &member2->GetWeakRef();
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_ObjectWeakRef_SameSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
Mso::TCntPtr<Mso::Swarm> swarm = Mso::Swarm::FromObject(member2.Get());
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef2 = &member2->GetWeakRef();
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = swarm->MakeMember<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = swarm->MakeMember<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr = Mso::SwarmMemberPtr<SwarmMemberSample1>(member1.Get(), *weakRef2);
|
||||
TestAssert::IsTrue(swarmPtr.Get() == member1.Get());
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr =
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1>(member1.Get(), *weakRef2);
|
||||
TestAssert::IsTrue(swarmPtr.Get() == member1.Get());
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_ObjectWeakRef_DifferentSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted1);
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_ObjectWeakRef_DifferentSwarm)
|
||||
{
|
||||
bool deleted1;
|
||||
bool deleted2;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member1 = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted1);
|
||||
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/deleted2);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef2 = &member2->GetWeakRef();
|
||||
Mso::TCntPtr<SwarmMemberSample2> member2 = Mso::Swarm::Make<SwarmMemberSample2>(/*ref*/ deleted2);
|
||||
Mso::TCntPtr<Mso::ObjectWeakRef> weakRef2 = &member2->GetWeakRef();
|
||||
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr = Mso::SwarmMemberPtr<SwarmMemberSample1>(member1.Get(), *weakRef2);
|
||||
TestAssert::IsTrue(swarmPtr.Get() == member1.Get());
|
||||
TestAssert::IsTrue(swarmPtr.IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr =
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1>(member1.Get(), *weakRef2);
|
||||
TestAssert::IsTrue(swarmPtr.Get() == member1.Get());
|
||||
TestAssert::IsTrue(swarmPtr.IsDifferentSwarm());
|
||||
}
|
||||
TestAssert::IsTrue(deleted1);
|
||||
TestAssert::IsTrue(deleted2);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_EqualityToNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted);
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr = Mso::SwarmMemberPtr<SwarmMemberSample1>(member.Get(), *Mso::Swarm::FromObject(member.Get()));
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_EqualityToNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted);
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1> swarmPtr =
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1>(member.Get(), *Mso::Swarm::FromObject(member.Get()));
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
|
||||
TestAssert::IsTrue(swarmPtr != nullptr);
|
||||
TestAssert::IsTrue(nullptr != swarmPtr);
|
||||
TestAssert::IsTrue(swarmPtr != nullptr);
|
||||
TestAssert::IsTrue(nullptr != swarmPtr);
|
||||
|
||||
swarmPtr.Reset();
|
||||
swarmPtr.Reset();
|
||||
|
||||
TestAssert::IsTrue(swarmPtr == nullptr);
|
||||
TestAssert::IsTrue(nullptr == swarmPtr);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(swarmPtr == nullptr);
|
||||
TestAssert::IsTrue(nullptr == swarmPtr);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_KnownSameSwarm_EqualityToNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/deleted);
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/true> swarmPtr = Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/true>(member.Get());
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
TEST_METHOD(ObjectSwarm_SwarmMemberPtr_KnownSameSwarm_EqualityToNull)
|
||||
{
|
||||
bool deleted = false;
|
||||
{
|
||||
Mso::TCntPtr<SwarmMemberSample1> member = Mso::Swarm::Make<SwarmMemberSample1>(/*ref*/ deleted);
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/ true> swarmPtr =
|
||||
Mso::SwarmMemberPtr<SwarmMemberSample1, /*KnownSameSwarm*/ true>(member.Get());
|
||||
TestAssert::IsFalse(swarmPtr.IsDifferentSwarm());
|
||||
|
||||
TestAssert::IsTrue(swarmPtr != nullptr);
|
||||
TestAssert::IsTrue(nullptr != swarmPtr);
|
||||
TestAssert::IsTrue(swarmPtr != nullptr);
|
||||
TestAssert::IsTrue(nullptr != swarmPtr);
|
||||
|
||||
swarmPtr.Reset();
|
||||
swarmPtr.Reset();
|
||||
|
||||
TestAssert::IsTrue(swarmPtr == nullptr);
|
||||
TestAssert::IsTrue(nullptr == swarmPtr);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
TestAssert::IsTrue(swarmPtr == nullptr);
|
||||
TestAssert::IsTrue(nullptr == swarmPtr);
|
||||
}
|
||||
TestAssert::IsTrue(deleted);
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(TEST_BAD_INHERITANCE1)
|
||||
TESTMETHOD_REQUIRES_SEH(ObjectFixedSwarm_BadInheritance1)
|
||||
{
|
||||
TestAssert::ExpectVEC([&]() noexcept
|
||||
{
|
||||
// You will see a memory leak here because we cannot destroy object correctly.
|
||||
Mso::Swarm::Make<BadSwarmMember1>();
|
||||
});
|
||||
}
|
||||
TESTMETHOD_REQUIRES_SEH(ObjectFixedSwarm_BadInheritance1)
|
||||
{
|
||||
TestAssert::ExpectVEC([&]() noexcept {
|
||||
// You will see a memory leak here because we cannot destroy object correctly.
|
||||
Mso::Swarm::Make<BadSwarmMember1>();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
|
|
@ -14,96 +14,87 @@
|
|||
// Special test allocator that cannot allocate memory and returns nullptr.
|
||||
struct BadMakeAllocator
|
||||
{
|
||||
static void* Allocate(size_t /*size*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
static void* Allocate(size_t /*size*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void Deallocate(void* /*buffer*/) noexcept
|
||||
{
|
||||
}
|
||||
static void Deallocate(void* /*buffer*/) noexcept {}
|
||||
};
|
||||
|
||||
using BadAllocSimpleRefCount = Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, BadMakeAllocator>;
|
||||
using BadAllocWeakRefCount = Mso::WeakRefCountPolicy<Mso::DefaultRefCountedDeleter, BadMakeAllocator>;
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// StatefulMakeAllocator helps testing stateful allocators
|
||||
//=============================================================================
|
||||
|
||||
struct IMyMemHeap
|
||||
{
|
||||
virtual void* Alloc(size_t size) noexcept = 0;
|
||||
virtual void Free(void* ptr) noexcept = 0;
|
||||
virtual void* Alloc(size_t size) noexcept = 0;
|
||||
virtual void Free(void* ptr) noexcept = 0;
|
||||
};
|
||||
|
||||
struct MyMemHeap : public IMyMemHeap
|
||||
{
|
||||
MyMemHeap(bool& allocCalled, bool& freeCalled) noexcept
|
||||
: m_allocCalled(allocCalled)
|
||||
, m_freeCalled(freeCalled)
|
||||
{
|
||||
}
|
||||
MyMemHeap(bool& allocCalled, bool& freeCalled) noexcept : m_allocCalled(allocCalled), m_freeCalled(freeCalled) {}
|
||||
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(MyMemHeap);
|
||||
|
||||
virtual void* Alloc(size_t size) noexcept override
|
||||
{
|
||||
m_allocCalled = true;
|
||||
return Mso::Memory::AllocateEx(size, Mso::Memory::AllocFlags::ShutdownLeak);
|
||||
}
|
||||
DECLARE_COPYCONSTR_AND_ASSIGNMENT(MyMemHeap);
|
||||
|
||||
virtual void Free(void* ptr) noexcept override
|
||||
{
|
||||
m_freeCalled = true;
|
||||
Mso::Memory::Free(ptr);
|
||||
}
|
||||
virtual void* Alloc(size_t size) noexcept override
|
||||
{
|
||||
m_allocCalled = true;
|
||||
return Mso::Memory::AllocateEx(size, Mso::Memory::AllocFlags::ShutdownLeak);
|
||||
}
|
||||
|
||||
virtual void Free(void* ptr) noexcept override
|
||||
{
|
||||
m_freeCalled = true;
|
||||
Mso::Memory::Free(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_allocCalled;
|
||||
bool& m_freeCalled;
|
||||
bool& m_allocCalled;
|
||||
bool& m_freeCalled;
|
||||
};
|
||||
|
||||
struct StatefulMakeAllocator
|
||||
{
|
||||
static const size_t HeaderSize = 8; // To make sure that we can store a pointer and to have 8 byte alignment for object.
|
||||
static const size_t HeaderSize =
|
||||
8; // To make sure that we can store a pointer and to have 8 byte alignment for object.
|
||||
|
||||
static void* Allocate(size_t size, IMyMemHeap* memHeap) noexcept
|
||||
{
|
||||
size_t bufferSize = size + HeaderSize;
|
||||
void* buffer = memHeap->Alloc(bufferSize);
|
||||
*reinterpret_cast<IMyMemHeap**>(buffer) = memHeap;
|
||||
return static_cast<uint8_t*>(buffer) + HeaderSize;
|
||||
}
|
||||
static void* Allocate(size_t size, IMyMemHeap* memHeap) noexcept
|
||||
{
|
||||
size_t bufferSize = size + HeaderSize;
|
||||
void* buffer = memHeap->Alloc(bufferSize);
|
||||
*reinterpret_cast<IMyMemHeap**>(buffer) = memHeap;
|
||||
return static_cast<uint8_t*>(buffer) + HeaderSize;
|
||||
}
|
||||
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
void* buffer = static_cast<uint8_t*>(ptr) - HeaderSize;
|
||||
IMyMemHeap* memHeap = *reinterpret_cast<IMyMemHeap**>(buffer);
|
||||
memHeap->Free(buffer);
|
||||
}
|
||||
static void Deallocate(void* ptr) noexcept
|
||||
{
|
||||
void* buffer = static_cast<uint8_t*>(ptr) - HeaderSize;
|
||||
IMyMemHeap* memHeap = *reinterpret_cast<IMyMemHeap**>(buffer);
|
||||
memHeap->Free(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
using StatefulAllocSimpleRefCount = Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, StatefulMakeAllocator>;
|
||||
using StatefulAllocWeakRefCount = Mso::WeakRefCountPolicy<Mso::DefaultRefCountedDeleter, StatefulMakeAllocator>;
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// BadStatefulAllocator helps testing stateful allocators that may return
|
||||
// BadStatefulAllocator helps testing stateful allocators that may return
|
||||
// null from Allocate method.
|
||||
//=============================================================================
|
||||
|
||||
struct BadStatefulAllocator
|
||||
{
|
||||
static void* Allocate(size_t /*size*/, IMyMemHeap* /*memHeap*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
static void* Allocate(size_t /*size*/, IMyMemHeap* /*memHeap*/) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void Deallocate(void* /*ptr*/) noexcept
|
||||
{
|
||||
}
|
||||
static void Deallocate(void* /*ptr*/) noexcept {}
|
||||
};
|
||||
|
||||
using BadStatefulAllocSimpleRefCount = Mso::SimpleRefCountPolicy<Mso::DefaultRefCountedDeleter, BadStatefulAllocator>;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -27,7 +27,8 @@ MSO_STRUCT_GUID(IUnknown, "00000000-0000-0000-C000-000000000046")
|
|||
|
||||
#define FACILITY_WIN32 7
|
||||
|
||||
#define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))
|
||||
#define __HRESULT_FROM_WIN32(x) \
|
||||
((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT)(((x)&0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))
|
||||
#define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)
|
||||
|
||||
#ifndef __LP64__
|
||||
|
@ -36,18 +37,18 @@ typedef long HRESULT;
|
|||
typedef int HRESULT;
|
||||
#endif
|
||||
#ifndef __GNUC__
|
||||
#define STDMETHODCALLTYPE __stdcall
|
||||
#define STDMETHODCALLTYPE __stdcall
|
||||
#else
|
||||
#define STDMETHODCALLTYPE
|
||||
#endif
|
||||
#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method
|
||||
#define STDMETHOD_(type,method) virtual type STDMETHODCALLTYPE method
|
||||
#define STDMETHODIMP HRESULT STDMETHODCALLTYPE
|
||||
#define STDMETHODIMP_(type) type STDMETHODCALLTYPE
|
||||
#define STDMETHODIMPNOTHROW COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE
|
||||
#define STDMETHODIMPNOTHROW_(type) COM_DECLSPEC_NOTHROW type STDMETHODCALLTYPE
|
||||
#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method
|
||||
#define STDMETHOD_(type, method) virtual type STDMETHODCALLTYPE method
|
||||
#define STDMETHODIMP HRESULT STDMETHODCALLTYPE
|
||||
#define STDMETHODIMP_(type) type STDMETHODCALLTYPE
|
||||
#define STDMETHODIMPNOTHROW COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE
|
||||
#define STDMETHODIMPNOTHROW_(type) COM_DECLSPEC_NOTHROW type STDMETHODCALLTYPE
|
||||
|
||||
#define S_OK ((HRESULT)0L)
|
||||
#define S_OK ((HRESULT)0L)
|
||||
#define S_FALSE ((HRESULT)1L)
|
||||
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122L
|
||||
|
@ -82,9 +83,9 @@ MSO_STRUCT_GUID(IUnknown, "00000000-0000-0000-C000-000000000046")
|
|||
#endif
|
||||
struct IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) = 0;
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
|
||||
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) = 0;
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
|
||||
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
|
||||
};
|
||||
|
||||
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
Support for multi-platform GUID implementation.
|
||||
Support for multi-platform GUID implementation.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef LIBLET_PLATFORMADAPTERS_MSOGUID_H
|
||||
|
@ -18,17 +18,17 @@
|
|||
MSO_STRUCT_GUID or MSO_CLASS_GUID are used to associate a GUID with a type in platform independent way:
|
||||
|
||||
|
||||
MSO_STRUCT_GUID(IMyInterface, "8665E4CE-50ED-4C12-A96D-13BD1432C219")
|
||||
struct DECLSPEC_NOVTABLE IMyInterface : IBaseInterface
|
||||
{
|
||||
virtual ReturnType1 Method1() = 0;
|
||||
virtual ReturnType2 Method1(ArgType1 arg1) = 0;
|
||||
};
|
||||
MSO_STRUCT_GUID(IMyInterface, "8665E4CE-50ED-4C12-A96D-13BD1432C219")
|
||||
struct DECLSPEC_NOVTABLE IMyInterface : IBaseInterface
|
||||
{
|
||||
virtual ReturnType1 Method1() = 0;
|
||||
virtual ReturnType2 Method1(ArgType1 arg1) = 0;
|
||||
};
|
||||
|
||||
MSO_CLASS_GUID(MyClass, "8665E4CE-50ED-4C12-A96D-13BD1432C219")
|
||||
class MyClass : public IBaseInterface
|
||||
{
|
||||
};
|
||||
MSO_CLASS_GUID(MyClass, "8665E4CE-50ED-4C12-A96D-13BD1432C219")
|
||||
class MyClass : public IBaseInterface
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
NOTE: GUID must not use curly braces around it.
|
||||
|
@ -36,10 +36,10 @@ NOTE: GUID must not use curly braces around it.
|
|||
To retrieve GUID for a type use the __uuidof():
|
||||
|
||||
|
||||
const GUID& myTypeGuid = __uuidof(MyType);
|
||||
const GUID& myTypeGuid = __uuidof(MyType);
|
||||
|
||||
template <typename T, typename TSource>
|
||||
T* qi_cast(TSource* s, const GUID& riid = __uuidof(T));
|
||||
template <typename T, typename TSource>
|
||||
T* qi_cast(TSource* s, const GUID& riid = __uuidof(T));
|
||||
|
||||
|
||||
The __uuidof() is a Visual C++ specific operator that accepts a variety of inputs.
|
||||
|
@ -50,12 +50,11 @@ This macro can only accept a type. We recommend to change code that uses an expr
|
|||
- Replace __uuidof(expr) with __uuidof_expr(expr)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
++ FAQ ++
|
||||
|
||||
Q: Why to use macro to associate a GUID with a type?
|
||||
A: There is no C++ standard way to assign a GUID attribute to a type. Any solution that we provide at this point
|
||||
A: There is no C++ standard way to assign a GUID attribute to a type. Any solution that we provide at this point
|
||||
is a workaround to this limitation. Macro can hide any such platform specific workarounds.
|
||||
|
||||
Q: How does it work?
|
||||
|
@ -63,7 +62,8 @@ A: For Visual C++ we use the __declspec(uuid) attribute.
|
|||
For Clang we use an overload of a function MsoGetGuid(T*).
|
||||
This function is used to initialize GuidOf<T>::Value static field returned by __uuidof() macro.
|
||||
The main benefit of using function is that it allows us to associate GUID with a type in the type's namespace.
|
||||
The function is discovered with help of C++ argument-dependent name lookup http://en.wikipedia.org/wiki/Argument_dependent_name_lookup .
|
||||
The function is discovered with help of C++ argument-dependent name lookup
|
||||
http://en.wikipedia.org/wiki/Argument_dependent_name_lookup .
|
||||
|
||||
Q: Why not to use a template specialization for GUID association?
|
||||
A: This is what we tried first, but a template cannot be specialized in a different namespace. In our case we want to
|
||||
|
@ -74,49 +74,51 @@ A: See the msoGuidDetails.h
|
|||
|
||||
*/
|
||||
|
||||
namespace Mso
|
||||
{
|
||||
namespace Mso {
|
||||
OACR_WARNING_PUSH
|
||||
OACR_WARNING_DISABLE(VAR_IN_HEADER, "False Positive for variable in header with template. (see Bug 1689514) ")
|
||||
|
||||
/// To return false in TypeHasGuid<T>::Value if type has no GUID defined by MSO_STRUCT_GUID/MSO_CLASS_GUID
|
||||
template <typename T> ::Mso::Details::GuidUtils::FalseType MsoTypeHasGuid(T*);
|
||||
template <typename T>
|
||||
::Mso::Details::GuidUtils::FalseType MsoTypeHasGuid(T*);
|
||||
|
||||
/// Type traits to check if type has a GUID defined by MSO_STRUCT_GUID/MSO_CLASS_GUID
|
||||
template <typename T>
|
||||
struct TypeHasGuid
|
||||
{
|
||||
typedef decltype(MsoTypeHasGuid(static_cast<T*>(nullptr))) Type;
|
||||
enum { Value = Type::Value };
|
||||
typedef decltype(MsoTypeHasGuid(static_cast<T*>(nullptr))) Type;
|
||||
enum
|
||||
{
|
||||
Value = Type::Value
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
This template is to address an issue with NDK linker which cannot properly compile
|
||||
a template that uses a pointer to GUID as a template parameter.
|
||||
E.g. the " struct TCntQIPtr<T, const GUID* piid = &__uuidof(T)> " will fail to compile
|
||||
because linker cannot find a static field which we use for __uuidof(T) implementation.
|
||||
Instead, this line must be changed to " struct TCntQIPtr<T, const GUID* piid = nullptr> "
|
||||
and we should use Mso::ResolveGuidPtr<T, piid>::Guid instead of piid inside of the template.
|
||||
The Mso::ResolveGuidPtr has a specialization for nullptr that does all the "magic".
|
||||
This template is to address an issue with NDK linker which cannot properly compile
|
||||
a template that uses a pointer to GUID as a template parameter.
|
||||
E.g. the " struct TCntQIPtr<T, const GUID* piid = &__uuidof(T)> " will fail to compile
|
||||
because linker cannot find a static field which we use for __uuidof(T) implementation.
|
||||
Instead, this line must be changed to " struct TCntQIPtr<T, const GUID* piid = nullptr> "
|
||||
and we should use Mso::ResolveGuidPtr<T, piid>::Guid instead of piid inside of the template.
|
||||
The Mso::ResolveGuidPtr has a specialization for nullptr that does all the "magic".
|
||||
*/
|
||||
template <typename T, const GUID* guidPtr>
|
||||
struct ResolveGuidPtr
|
||||
{
|
||||
static const GUID* Guid;
|
||||
static const GUID* Guid;
|
||||
};
|
||||
|
||||
template <typename T, const GUID* guidPtr>
|
||||
const GUID* ResolveGuidPtr<T, guidPtr>::Guid = guidPtr;
|
||||
|
||||
template <typename T>
|
||||
struct ResolveGuidPtr<T, static_cast<const GUID *>(nullptr)>
|
||||
struct ResolveGuidPtr<T, static_cast<const GUID*>(nullptr)>
|
||||
{
|
||||
static const GUID* Guid;
|
||||
static const GUID* Guid;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const GUID* ResolveGuidPtr<T, static_cast<const GUID *>(nullptr)>::Guid = &__uuidof(T);
|
||||
const GUID* ResolveGuidPtr<T, static_cast<const GUID*>(nullptr)>::Guid = &__uuidof(T);
|
||||
|
||||
OACR_WARNING_POP
|
||||
} // namespace Mso
|
||||
|
|
|
@ -20,32 +20,36 @@ See the msoGuid.h for the usage guidelines.
|
|||
|
||||
#include <compilerAdapters/compilerFeatures.h>
|
||||
#if MS_TARGET_POSIX
|
||||
# include <guiddef.h>
|
||||
#include <guiddef.h>
|
||||
#endif // MS_TARGET_POSIX
|
||||
|
||||
// Private macro for MSO_STRUCT_GUID/MSO_CLASS_GUID macro implementations.
|
||||
#if COMPILER_SUPPORTS_UUID // For Visual C++
|
||||
# define MSO_GUID_IMPL(keyword, type, guidString) \
|
||||
static_assert(sizeof(guidString) == 37, "GUID string must have the following format: \"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\""); \
|
||||
keyword __declspec(uuid(guidString)) type; \
|
||||
__pragma(warning(suppress:25352)) \
|
||||
extern "C++" ::Mso::Details::GuidUtils::TrueType MsoTypeHasGuid(type*);
|
||||
#define MSO_GUID_IMPL(keyword, type, guidString) \
|
||||
static_assert( \
|
||||
sizeof(guidString) == 37, \
|
||||
"GUID string must have the following format: \"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\""); \
|
||||
keyword __declspec(uuid(guidString)) type; \
|
||||
__pragma(warning(suppress : 25352)) extern "C++" ::Mso::Details::GuidUtils::TrueType MsoTypeHasGuid(type*);
|
||||
#else // For Clang
|
||||
# define MSO_GUID_IMPL(keyword, type, guidString) \
|
||||
keyword type; \
|
||||
extern "C++" inline constexpr GUID MsoGetGuid(type*) noexcept { return ::Mso::Details::GuidUtils::StringToGuid(guidString); } \
|
||||
extern "C++" ::Mso::Details::GuidUtils::TrueType MsoTypeHasGuid(type*);
|
||||
#define MSO_GUID_IMPL(keyword, type, guidString) \
|
||||
keyword type; \
|
||||
extern "C++" inline constexpr GUID MsoGetGuid(type*) noexcept \
|
||||
{ \
|
||||
return ::Mso::Details::GuidUtils::StringToGuid(guidString); \
|
||||
} \
|
||||
extern "C++" ::Mso::Details::GuidUtils::TrueType MsoTypeHasGuid(type*);
|
||||
#endif // COMPILER_SUPPORTS_UUID
|
||||
|
||||
/// Associates type with a GUID. To be used before type declaration.
|
||||
#define MSO_STRUCT_GUID(type, guidString) MSO_GUID_IMPL(struct, type, guidString)
|
||||
#define MSO_CLASS_GUID( type, guidString) MSO_GUID_IMPL(class, type, guidString)
|
||||
#define MSO_CLASS_GUID(type, guidString) MSO_GUID_IMPL(class, type, guidString)
|
||||
#define MSO_GUID(type, guidString) MSO_STRUCT_GUID(type, guidString)
|
||||
|
||||
//==========================================================================================================================
|
||||
// GUID token is a special type that can be used to define and access GUIDs using only header files.
|
||||
// For example, to define a GUID in a header file without declaring a type, you can write:
|
||||
//
|
||||
//
|
||||
// MSO_DEFINE_GUID_TOKEN(MyToken, "d807b970-b875-4491-a88e-99038236d60f")
|
||||
//
|
||||
// Then to access the GUID:
|
||||
|
@ -74,29 +78,26 @@ See the msoGuid.h for the usage guidelines.
|
|||
//
|
||||
// Each alias will have its own unique GUID associated with it.
|
||||
// To get GUID of an alias:
|
||||
//
|
||||
//
|
||||
// const GUID& intGuid = __uuidof(MyIntTemplate::GuidType);
|
||||
// const GUID& floatGuid = __uuidof(MyFloatTemplate::GuidType);
|
||||
//
|
||||
|
||||
/// Gets type from the GUID token.
|
||||
#define MSO_GET_GUID_TOKEN_TYPE(guidToken) \
|
||||
guidToken##_GuidToken
|
||||
#define MSO_GET_GUID_TOKEN_TYPE(guidToken) guidToken##_GuidToken
|
||||
|
||||
/// Defines a GUID token type.
|
||||
#define MSO_DEFINE_GUID_TOKEN(guidToken, guidString) \
|
||||
MSO_STRUCT_GUID(MSO_GET_GUID_TOKEN_TYPE(guidToken), guidString)
|
||||
/// Defines a GUID token type.
|
||||
#define MSO_DEFINE_GUID_TOKEN(guidToken, guidString) MSO_STRUCT_GUID(MSO_GET_GUID_TOKEN_TYPE(guidToken), guidString)
|
||||
|
||||
/// A macro to access GUID by a token defined by MSO_DEFINE_GUID_TOKEN
|
||||
#define __uuidof_token(guidToken) \
|
||||
__uuidof(MSO_GET_GUID_TOKEN_TYPE(guidToken))
|
||||
#define __uuidof_token(guidToken) __uuidof(MSO_GET_GUID_TOKEN_TYPE(guidToken))
|
||||
|
||||
//==========================================================================================================================
|
||||
|
||||
/// Replacement for the MIDL_INTERFACE based on MSO_STRUCT_GUID. To be used in MIDL generated files.
|
||||
#define MIDL_INTERFACE2(type, guidString) \
|
||||
MSO_STRUCT_GUID(type, guidString) \
|
||||
struct DECLSPEC_NOVTABLE
|
||||
MSO_STRUCT_GUID(type, guidString) \
|
||||
struct DECLSPEC_NOVTABLE
|
||||
|
||||
// __uuidof(type) implementation for Clang. In VC++ we use the native __uuidof() operator.
|
||||
#if !COMPILER_SUPPORTS_UUID
|
||||
|
@ -105,64 +106,64 @@ See the msoGuid.h for the usage guidelines.
|
|||
#endif
|
||||
|
||||
/// A macro to be used instead of __uuidof(expr). For types use __uuidof(type). E.g. __uuidof_expr(this).
|
||||
#define __uuidof_expr(expr) \
|
||||
__uuidof(typename std::remove_pointer<typename std::decay<decltype(expr)>::type>::type)
|
||||
|
||||
#define __uuidof_expr(expr) __uuidof(typename std::remove_pointer<typename std::decay<decltype(expr)>::type>::type)
|
||||
|
||||
// We must explicitly set the C++ extern context for the MIDL generated files where it is set to "C".
|
||||
extern "C++"
|
||||
{
|
||||
namespace Mso { namespace Details { namespace GuidUtils {
|
||||
|
||||
// To indicate that type has or does not have GUID. We do not want to add dependency on "type_traits"
|
||||
struct TrueType { enum { Value = true }; };
|
||||
struct FalseType { enum { Value = false }; };
|
||||
namespace Mso { namespace Details { namespace GuidUtils {
|
||||
|
||||
// To indicate that type has or does not have GUID. We do not want to add dependency on "type_traits"
|
||||
struct TrueType
|
||||
{
|
||||
enum
|
||||
{
|
||||
Value = true
|
||||
};
|
||||
};
|
||||
struct FalseType
|
||||
{
|
||||
enum
|
||||
{
|
||||
Value = false
|
||||
};
|
||||
};
|
||||
|
||||
#if !COMPILER_SUPPORTS_UUID
|
||||
|
||||
/// Default implementation of the MsoGetGuid. It is used when we cannot find type specific implementation
|
||||
/// provided by MSO_STRUCT_GUID/MSO_CLASS_GUID macro.
|
||||
template <typename T>
|
||||
inline GUID MsoGetGuid(T*) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) == 0, "GUID is undefined for the type T");
|
||||
return {};
|
||||
}
|
||||
/// Default implementation of the MsoGetGuid. It is used when we cannot find type specific implementation
|
||||
/// provided by MSO_STRUCT_GUID/MSO_CLASS_GUID macro.
|
||||
template <typename T>
|
||||
inline GUID MsoGetGuid(T*) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) == 0, "GUID is undefined for the type T");
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Statically associates a GUID with a type. It is used by __uuidof() macro implementation.
|
||||
template <typename T>
|
||||
struct GuidOf
|
||||
{
|
||||
static const GUID Value;
|
||||
};
|
||||
/// Statically associates a GUID with a type. It is used by __uuidof() macro implementation.
|
||||
template <typename T>
|
||||
struct GuidOf
|
||||
{
|
||||
static const GUID Value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const GUID GuidOf<T>::Value = MsoGetGuid(static_cast<T*>(nullptr));
|
||||
template <typename T>
|
||||
const GUID GuidOf<T>::Value = MsoGetGuid(static_cast<T*>(nullptr));
|
||||
|
||||
/// To represent a GUID string without curly braces: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
|
||||
typedef char GuidString[37];
|
||||
|
||||
/// To represent a GUID string without curly braces: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
|
||||
typedef char GuidString[37];
|
||||
|
||||
/// Converts a hexadecimal ASCII character to an unsigned char.
|
||||
constexpr const unsigned char H2U[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
/// Converts a hexadecimal ASCII character to an unsigned char.
|
||||
constexpr const unsigned char H2U[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic push
|
||||
|
@ -170,26 +171,24 @@ constexpr const unsigned char H2U[256] = {
|
|||
#pragma clang diagnostic ignored "-Wchar-subscripts"
|
||||
#endif // __clang__
|
||||
|
||||
/// Converts string to a GUID at compile time. Expected format: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
|
||||
constexpr GUID StringToGuid(const GuidString& g) noexcept
|
||||
{
|
||||
return
|
||||
{
|
||||
static_cast<unsigned int>((H2U[g[0]] << 28) | (H2U[g[1]] << 24) | (H2U[g[2]] << 20) | (H2U[g[3]] << 16) | (H2U[g[4]] << 12) | (H2U[g[5]] << 8) | (H2U[g[6]] << 4) | H2U[g[7]]),
|
||||
static_cast<unsigned short>((H2U[g[9]] << 12) | (H2U[g[10]] << 8) | (H2U[g[11]] << 4) | H2U[g[12]]),
|
||||
static_cast<unsigned short>((H2U[g[14]] << 12) | (H2U[g[15]] << 8) | (H2U[g[16]] << 4) | H2U[g[17]]),
|
||||
{
|
||||
static_cast<unsigned char>((H2U[g[19]] << 4) | H2U[g[20]]),
|
||||
static_cast<unsigned char>((H2U[g[21]] << 4) | H2U[g[22]]),
|
||||
static_cast<unsigned char>((H2U[g[24]] << 4) | H2U[g[25]]),
|
||||
static_cast<unsigned char>((H2U[g[26]] << 4) | H2U[g[27]]),
|
||||
static_cast<unsigned char>((H2U[g[28]] << 4) | H2U[g[29]]),
|
||||
static_cast<unsigned char>((H2U[g[30]] << 4) | H2U[g[31]]),
|
||||
static_cast<unsigned char>((H2U[g[32]] << 4) | H2U[g[33]]),
|
||||
static_cast<unsigned char>((H2U[g[34]] << 4) | H2U[g[35]])
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Converts string to a GUID at compile time. Expected format: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
|
||||
constexpr GUID StringToGuid(const GuidString& g) noexcept
|
||||
{
|
||||
return {
|
||||
static_cast<unsigned int>(
|
||||
(H2U[g[0]] << 28) | (H2U[g[1]] << 24) | (H2U[g[2]] << 20) | (H2U[g[3]] << 16) | (H2U[g[4]] << 12)
|
||||
| (H2U[g[5]] << 8) | (H2U[g[6]] << 4) | H2U[g[7]]),
|
||||
static_cast<unsigned short>((H2U[g[9]] << 12) | (H2U[g[10]] << 8) | (H2U[g[11]] << 4) | H2U[g[12]]),
|
||||
static_cast<unsigned short>((H2U[g[14]] << 12) | (H2U[g[15]] << 8) | (H2U[g[16]] << 4) | H2U[g[17]]),
|
||||
{static_cast<unsigned char>((H2U[g[19]] << 4) | H2U[g[20]]),
|
||||
static_cast<unsigned char>((H2U[g[21]] << 4) | H2U[g[22]]),
|
||||
static_cast<unsigned char>((H2U[g[24]] << 4) | H2U[g[25]]),
|
||||
static_cast<unsigned char>((H2U[g[26]] << 4) | H2U[g[27]]),
|
||||
static_cast<unsigned char>((H2U[g[28]] << 4) | H2U[g[29]]),
|
||||
static_cast<unsigned char>((H2U[g[30]] << 4) | H2U[g[31]]),
|
||||
static_cast<unsigned char>((H2U[g[32]] << 4) | H2U[g[33]]),
|
||||
static_cast<unsigned char>((H2U[g[34]] << 4) | H2U[g[35]])}};
|
||||
}
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
|
@ -197,7 +196,7 @@ constexpr GUID StringToGuid(const GuidString& g) noexcept
|
|||
|
||||
#endif // !COMPILER_SUPPORTS_UUID
|
||||
|
||||
} } } // namespace Mso::Details::GuidUtils
|
||||
}}} // namespace Mso::Details::GuidUtils
|
||||
} // extern "C++"
|
||||
|
||||
#endif // LIBLET_PLATFORMADAPTERS_MSOGUIDDETAILS_H
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#ifndef _BYTE_DEFINED
|
||||
#define _BYTE_DEFINED
|
||||
typedef unsigned char BYTE;//__clang__ : conflict with libc++17 byte definition
|
||||
typedef unsigned char BYTE; //__clang__ : conflict with libc++17 byte definition
|
||||
#endif // !_BYTE_DEFINED
|
||||
|
||||
#ifndef _WORD_DEFINED
|
||||
|
@ -39,7 +39,7 @@ typedef unsigned int UINT;
|
|||
|
||||
#ifndef _LONG_DEFINED
|
||||
#define _LONG_DEFINED
|
||||
typedef int32_t LONG; //MS_TARGET_APPLE LONG as a Windows type must stay 32-bit even in 64-bit code
|
||||
typedef int32_t LONG; // MS_TARGET_APPLE LONG as a Windows type must stay 32-bit even in 64-bit code
|
||||
#endif // !_LONG_DEFINED
|
||||
|
||||
#ifndef _ULONG_DEFINED
|
||||
|
@ -49,7 +49,7 @@ typedef uint32_t ULONG;
|
|||
|
||||
#ifndef _BOOL_DEFINED
|
||||
#define _BOOL_DEFINED
|
||||
typedef int32_t BOOL; //MS_TARGET_APPLE
|
||||
typedef int32_t BOOL; // MS_TARGET_APPLE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif // !_BOOL_DEFINED
|
||||
|
@ -71,7 +71,7 @@ typedef UINT_PTR WPARAM;
|
|||
|
||||
#ifndef _DWORD_DEFINED
|
||||
#define _DWORD_DEFINED
|
||||
typedef uint32_t DWORD; //MS_TARGET_APPLE DWORD as a Windows type must stay 32-bit even in 64-bit code
|
||||
typedef uint32_t DWORD; // MS_TARGET_APPLE DWORD as a Windows type must stay 32-bit even in 64-bit code
|
||||
#endif // !_DWORD_DEFINED
|
||||
|
||||
#ifndef _LPARAM_DEFINED
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче