diff --git a/Externals/WIL/.gitattributes b/Externals/WIL/.gitattributes new file mode 100644 index 0000000000..498ce57650 --- /dev/null +++ b/Externals/WIL/.gitattributes @@ -0,0 +1,2 @@ +# Disable CRLF-mapping for all files in the depot. +* -text diff --git a/Externals/WIL/.gitignore b/Externals/WIL/.gitignore new file mode 100644 index 0000000000..3973a16bf4 --- /dev/null +++ b/Externals/WIL/.gitignore @@ -0,0 +1,336 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Visual Studio Code directory +.vscode/ + +# CMake/Build output +build/ diff --git a/Externals/WIL/CMakeLists.txt b/Externals/WIL/CMakeLists.txt new file mode 100644 index 0000000000..27765fd1c9 --- /dev/null +++ b/Externals/WIL/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.11) +project(WIL) + +# Set by build server to speed up build/reduce file/object size +option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF) + +if (NOT DEFINED WIL_BUILD_VERSION) + set(WIL_BUILD_VERSION "0.0.0") +endif() + +# Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise +# we'll need to assume that this value comes from the command line (e.g. through the VS command prompt) +if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + set(WIL_WINDOWS_SDK_VERSION ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}) +else() + # This has a trailing backslash for whatever reason... + string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}") +endif() + +add_subdirectory(packaging) +add_subdirectory(tests) diff --git a/Externals/WIL/CODE_OF_CONDUCT.md b/Externals/WIL/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..4c00ba1ad1 --- /dev/null +++ b/Externals/WIL/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/Externals/WIL/LICENSE b/Externals/WIL/LICENSE new file mode 100644 index 0000000000..21071075c2 --- /dev/null +++ b/Externals/WIL/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/Externals/WIL/README.md b/Externals/WIL/README.md new file mode 100644 index 0000000000..8d4e538f81 --- /dev/null +++ b/Externals/WIL/README.md @@ -0,0 +1,93 @@ +# Windows Implementation Libraries (WIL) + +[![Build Status](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_apis/build/status/Microsoft.wil?branchName=master)](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_build/latest?definitionId=1&branchName=master) + +The Windows Implementation Libraries (WIL) is a header-only C++ library created to make life easier +for developers on Windows through readable type-safe C++ interfaces for common Windows coding patterns. + +Some things that WIL includes to whet your appetite: + +- [`include/wil/resource.h`](include/wil/resource.h) + ([documentation](https://github.com/Microsoft/wil/wiki/RAII-resource-wrappers)): + Smart pointers and auto-releasing resource wrappers to let you manage Windows + API HANDLEs, HWNDs, and other resources and resource handles with + [RAII](https://en.cppreference.com/w/cpp/language/raii) semantics. +- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h): Wrappers for API functions + that save you the work of manually specifying buffer sizes, calling a function twice + to get the needed buffer size and then allocate and pass the right-size buffer, + casting or converting between types, and so on. +- [`include/wil/registry.h`](include/wil/registry.h): Registry watchers that can + call a lambda function or callback you provide whenever a certain tree within + the Windows registry changes. +- [`include/wil/result.h`](include/wil/result.h) + ([documentation](https://github.com/Microsoft/wil/wiki/Error-handling-helpers)): + Preprocessor macros to help you check for errors from Windows API functions, + in many of the myriad ways those errors are reported, and surface them as + error codes or C++ exceptions in your code. + +WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned +error codes to report errors. All of WIL can be used from user-space Windows code, +and some (such as the RAII resource wrappers) can even be used in kernel mode. + +# Documentation + +This project is documented in [its GitHub wiki](https://github.com/Microsoft/wil/wiki). Feel free to contribute to it! + +# Consuming WIL +WIL follows the "live at head" philosophy, so you should feel free to consume WIL directly from the GitHub repo however you please: as a GIT submodule, symbolic link, download and copy files, etc. and update to the latest version at your own cadence. Alternatively, WIL is available using a few package managers, mentioned below. These packages will be updated periodically, likely to average around once or twice per month. + +## Consuming WIL via NuGet +WIL is available on nuget.org under the name [Microsoft.Windows.ImplementationLibrary](https://www.nuget.org/packages/Microsoft.Windows.ImplementationLibrary/). This package includes the header files under the [include](include) directory as well as a [.targets](packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets) file. + +## Consuming WIL via vcpkg +WIL is also available using [vcpkg](https://github.com/microsoft/vcpkg) under the name [wil](https://github.com/microsoft/vcpkg/blob/master/ports/wil/portfile.cmake). Instructions for installing packages can be found in the [vcpkg GitHub docs](https://github.com/microsoft/vcpkg/blob/master/docs/examples/installing-and-using-packages.md). In general, once vcpkg is set up on the system, you can run: +```cmd +C:\vcpkg> vcpkg install wil:x86-windows +C:\vcpkg> vcpkg install wil:x64-windows +``` +Note that even though WIL is a header-only library, you still need to install the package for all architectures/platforms you wish to use it with. Otherwise, WIL won't be added to the include path for the missing architectures/platforms. Execute `vcpkg help triplet` for a list of available options. + +# Building/Testing +To get started testing WIL, first make sure that you have a recent version of [Visual Studio](https://visualstudio.microsoft.com/downloads/) and the most recent [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed. If you are doing +any non-trivial work, also be sure to have a recent version of [Clang](http://releases.llvm.org/download.html) installed. Once everything is installed, open a VS +native command window (e.g. "x64 Native Tools Command Prompt for VS 2019"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory: +```cmd +C:\wil> scripts\init.cmd -c clang -g ninja -b debug +``` +You can execute `init.cmd --help` for a summary of available options. The scripts use a common directory pattern of `build/$(compiler)$(arch)$(type)` for the build output root. E.g. `build/clang64debug` when using Clang as the compiler, x64 as the architecture, and Debug as the build type. It is this directory where you will want to build from. For example, if you initialized using the command above, you can build the tests like so: +```cmd +C:\wil\build\clang64debug> ninja +``` +Or, if you want to only build a single test (e.g. for improved compile times): +```cmd +C:\wil\build\clang64debug> ninja witest.noexcept +``` +If you initialized using MSBuild as the generator, there will be a `.sln` file in the root of the build directory. You +can either open the solution in Visual Studio or invoke MSBuild directly to build. + +The output is a number of test executables. If you used the initialization script(s) mentioned above, or if you followed +the same directory naming convention of those scripts, you can use the [runtests.cmd](scripts/runtests.cmd) script, +which will execute any test executables that have been built, erroring out - and preserving the exit code - if any test +fails. Note that MSBuild will modify the output directory names, so this script is only compatible with using Ninja as the +generator. If you are at the tail end of of a change, you can execute the following to get a wide range of coverage: +```cmd +C:\wil> scripts\init_all.cmd +C:\wil> scripts\build_all.cmd +C:\wil> scripts\runtests.cmd +``` +Note that this will only test for the architecture that corresponds to the command window you opened. You will want to +repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2019") + +# Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/Externals/WIL/ThirdPartyNotices.txt b/Externals/WIL/ThirdPartyNotices.txt new file mode 100644 index 0000000000..eee568b198 --- /dev/null +++ b/Externals/WIL/ThirdPartyNotices.txt @@ -0,0 +1,60 @@ +THIRD PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +Libc++ + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Catch2 + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Externals/WIL/cmake/common_build_flags.cmake b/Externals/WIL/cmake/common_build_flags.cmake new file mode 100644 index 0000000000..c9f953fc5a --- /dev/null +++ b/Externals/WIL/cmake/common_build_flags.cmake @@ -0,0 +1,69 @@ + +# E.g. replace_cxx_flag("/W[0-4]", "/W4") +macro(replace_cxx_flag pattern text) + foreach (flag + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + + string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}") + + endforeach() +endmacro() + +macro(append_cxx_flag text) + foreach (flag + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + + string(APPEND ${flag} " ${text}") + + endforeach() +endmacro() + +# Fixup default compiler settings + +# Be as strict as reasonably possible, since we want to support consumers using strict warning levels +replace_cxx_flag("/W[0-4]" "/W4") +append_cxx_flag("/WX") + +# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl) +append_cxx_flag("/permissive-") + +# wistd::function has padding due to alignment. This is expected +append_cxx_flag("/wd4324") + +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + # Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed + append_cxx_flag("-Wno-switch") + append_cxx_flag("-Wno-c++17-compat-mangling") + append_cxx_flag("-Wno-missing-field-initializers") + + # For tests, we want to be able to test self assignment, so disable this warning + append_cxx_flag("-Wno-self-assign-overloaded") + append_cxx_flag("-Wno-self-move") + + # clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar + # results through the following flags. + append_cxx_flag("-fno-delayed-template-parsing") + + # NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That + # said, errors that originate from WIL headers may benefit + # append_cxx_flag("-fno-ms-compatibility") + # append_cxx_flag("-ferror-limit=999") + # append_cxx_flag("-fmacro-backtrace-limit=0") + # -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is + # available (i.e. >= C++20) + # append_cxx_flag("-Xclang -std=c++2a") +else() + # Flags that are either ignored or unrecognized by clang-cl + # TODO: https://github.com/Microsoft/wil/issues/6 + # append_cxx_flag("/experimental:preprocessor") + + # CRT headers are not yet /experimental:preprocessor clean, so work around the known issues + # append_cxx_flag("/Wv:18") + + append_cxx_flag("/bigobj") + + # NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated + append_cxx_flag("/d2FH4-") +endif() diff --git a/Externals/WIL/include/wil/com.h b/Externals/WIL/include/wil/com.h new file mode 100644 index 0000000000..6a994590d7 --- /dev/null +++ b/Externals/WIL/include/wil/com.h @@ -0,0 +1,2832 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_COM_INCLUDED +#define __WIL_COM_INCLUDED + +#include +#include +#include "result.h" +#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available + +// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx) +/// @cond +namespace Microsoft +{ + namespace WRL + { + template + class ComPtr; + } +} +/// @endcond + +namespace wil +{ + /// @cond + namespace details + { + // We can't directly use wistd::is_convertible as it returns TRUE for an ambiguous conversion. + // Adding is_abstract to the mix, enables us to allow conversion for interfaces, but deny it for + // classes (where the multiple inheritance causes ambiguity). + // NOTE: I've reached out to vcsig on this topic and it turns out that __is_convertible_to should NEVER + // return true for ambiguous conversions. This was a bug in our compiler that has since been fixed. + // Eventually, once that fix propagates we can move to a more efficient __is_convertible_to without + // the added complexity. + template + struct is_com_convertible : + wistd::bool_constant<__is_convertible_to(TFrom, TTo) && (__is_abstract(TFrom) || wistd::is_same::value)> + { + }; + + typedef wistd::integral_constant tag_com_query; + typedef wistd::integral_constant tag_try_com_query; + typedef wistd::integral_constant tag_com_copy; + typedef wistd::integral_constant tag_try_com_copy; + + class default_query_policy + { + public: + template + inline static HRESULT query(_In_ T* ptr, REFIID riid, _COM_Outptr_ void** result) + { + return ptr->QueryInterface(riid, result); + } + + template + inline static HRESULT query(_In_ T* ptr, _COM_Outptr_ TResult** result) + { + return query_dispatch(ptr, typename details::is_com_convertible::type(), result); + } + + private: + template + inline static HRESULT query_dispatch(_In_ T* ptr, wistd::true_type, _COM_Outptr_ TResult** result) // convertible + { + *result = ptr; + (*result)->AddRef(); + return S_OK; + } + + template + inline static HRESULT query_dispatch(_In_ T* ptr, wistd::false_type, _COM_Outptr_ TResult** result) // not convertible + { + auto hr = ptr->QueryInterface(IID_PPV_ARGS(result)); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + return hr; + } + }; + + template + struct query_policy_helper + { + typedef default_query_policy type; + }; + + class weak_query_policy + { + public: + inline static HRESULT query(_In_ IWeakReference* ptr, REFIID riid, _COM_Outptr_ void** result) + { + WI_ASSERT_MSG(riid != __uuidof(IWeakReference), "Cannot resolve a weak reference to IWeakReference"); + *result = nullptr; + + IInspectable* temp; + HRESULT hr = ptr->Resolve(__uuidof(IInspectable), reinterpret_cast(&temp)); + if (SUCCEEDED(hr)) + { + if (temp == nullptr) + { + return E_NOT_SET; + } + hr = temp->QueryInterface(riid, result); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + temp->Release(); + } + + return hr; + } + + template + inline static HRESULT query(_In_ IWeakReference* ptr, _COM_Outptr_ TResult** result) + { + static_assert(!wistd::is_same::value, "Cannot resolve a weak reference to IWeakReference"); + return query_dispatch(ptr, wistd::is_base_of(), result); + } + + private: + template + static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::true_type, _COM_Outptr_ TResult** result) + { + auto hr = ptr->Resolve(__uuidof(TResult), reinterpret_cast(result)); + if (SUCCEEDED(hr) && (*result == nullptr)) + { + hr = E_NOT_SET; + } + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + return hr; + } + + template + static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::false_type, _COM_Outptr_ TResult** result) + { + return query(ptr, IID_PPV_ARGS(result)); + } + }; + + template <> + struct query_policy_helper + { + typedef weak_query_policy type; + }; + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + class agile_query_policy + { + public: + inline static HRESULT query(_In_ IAgileReference* ptr, REFIID riid, _COM_Outptr_ void** result) + { + WI_ASSERT_MSG(riid != __uuidof(IAgileReference), "Cannot resolve a agile reference to IAgileReference"); + auto hr = ptr->Resolve(riid, result); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); // IAgileReference::Resolve not annotated correctly + return hr; + } + + template + static HRESULT query(_In_ IAgileReference* ptr, _COM_Outptr_ TResult** result) + { + static_assert(!wistd::is_same::value, "Cannot resolve a agile reference to IAgileReference"); + return query(ptr, __uuidof(TResult), reinterpret_cast(result)); + } + }; + + template <> + struct query_policy_helper + { + typedef agile_query_policy type; + }; +#endif + + template + using query_policy_t = typename query_policy_helper::type>::type; + + } // details + /// @endcond + + //! Represents the base template type that implements com_ptr, com_weak_ref, and com_agile_ref. + //! See @ref page_comptr for more background. See @ref page_query for more information on querying with WIL. + //! @tparam T Represents the type being held by the com_ptr_t. + //! For com_ptr, this will always be the interface being represented. For com_weak_ref, this will always be + //! IWeakReference. For com_agile_ref, this will always be IAgileReference. + //! @tparam err_policy Represents the error policy for the class (error codes, exceptions, or fail fast; see @ref page_errors) + template + class com_ptr_t + { + private: + typedef typename wistd::add_lvalue_reference::type element_type_reference; + typedef details::query_policy_t query_policy; + public: + //! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors). + typedef typename err_policy::result result; + //! The template type `T` being held by the com_ptr_t. + typedef T element_type; + //! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns). + typedef T* pointer; + + //! @name Constructors + //! @{ + + //! Default constructor (holds nullptr). + com_ptr_t() WI_NOEXCEPT : + m_ptr(nullptr) + { + } + + //! Implicit construction from nullptr_t (holds nullptr). + com_ptr_t(wistd::nullptr_t) WI_NOEXCEPT : + com_ptr_t() + { + } + + //! Implicit construction from a compatible raw interface pointer (AddRef's the parameter). + com_ptr_t(pointer ptr) WI_NOEXCEPT : + m_ptr(ptr) + { + if (m_ptr) + { + m_ptr->AddRef(); + } + } + + //! Copy-construction from a like `com_ptr_t` (copies and AddRef's the parameter). + com_ptr_t(const com_ptr_t& other) WI_NOEXCEPT : + com_ptr_t(other.get()) + { + } + + //! Copy-construction from a convertible `com_ptr_t` (copies and AddRef's the parameter). + template > + com_ptr_t(const com_ptr_t& other) WI_NOEXCEPT : + com_ptr_t(static_cast(other.get())) + { + } + + //! Move construction from a like `com_ptr_t` (avoids AddRef/Release by moving from the parameter). + com_ptr_t(com_ptr_t&& other) WI_NOEXCEPT : + m_ptr(other.detach()) + { + } + + //! Move construction from a compatible `com_ptr_t` (avoids AddRef/Release by moving from the parameter). + template > + com_ptr_t(com_ptr_t&& other) WI_NOEXCEPT : + m_ptr(other.detach()) + { + } + //! @} + + //! Destructor (releases the pointer). + ~com_ptr_t() WI_NOEXCEPT + { + if (m_ptr) + { + m_ptr->Release(); + } + } + + //! @name Assignment operators + //! @{ + + //! Assign to nullptr (releases the current pointer, holds nullptr). + com_ptr_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + return *this; + } + + //! Assign a compatible raw interface pointer (releases current pointer, copies and AddRef's the parameter). + com_ptr_t& operator=(pointer other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other; + if (m_ptr) + { + m_ptr->AddRef(); + } + if (ptr) + { + ptr->Release(); + } + return *this; + } + + //! Assign a like `com_ptr_t` (releases current pointer, copies and AddRef's the parameter). + com_ptr_t& operator=(const com_ptr_t& other) WI_NOEXCEPT + { + return operator=(other.get()); + } + + //! Assign a convertible `com_ptr_t` (releases current pointer, copies and AddRef's the parameter). + template > + com_ptr_t& operator=(const com_ptr_t& other) WI_NOEXCEPT + { + return operator=(static_cast(other.get())); + } + + //! Move assign from a like `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving the parameter). + com_ptr_t& operator=(com_ptr_t&& other) WI_NOEXCEPT + { + attach(other.detach()); + return *this; + } + + //! Move assignment from a compatible `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving from the parameter). + template > + com_ptr_t& operator=(com_ptr_t&& other) WI_NOEXCEPT + { + attach(other.detach()); + return *this; + } + //! @} + + //! @name Modifiers + //! @{ + + //! Swap pointers with an another named com_ptr_t object. + template + void swap(com_ptr_t& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other.m_ptr; + other.m_ptr = ptr; + } + + //! Swap pointers with a rvalue reference to another com_ptr_t object. + template + void swap(com_ptr_t&& other) WI_NOEXCEPT + { + swap(other); + } + + //! Releases the pointer and sets it to nullptr. + void reset() WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = nullptr; + if (ptr) + { + ptr->Release(); + } + } + + //! Releases the pointer and sets it to nullptr. + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + } + + //! Takes ownership of a compatible raw interface pointer (releases pointer, copies but DOES NOT AddRef the parameter). + void attach(pointer other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other; + if (ptr) + { + ULONG ref; + ref = ptr->Release(); + WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer"); + } + } + + //! Relinquishes ownership and returns the internal interface pointer (DOES NOT release the detached pointer, sets class pointer to null). + WI_NODISCARD pointer detach() WI_NOEXCEPT + { + auto temp = m_ptr; + m_ptr = nullptr; + return temp; + } + + //! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address). + //! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that + //! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use. + //! @see addressof + //! ~~~~ + //! STDAPI GetMuffin(IMuffin **muffin); + //! wil::com_ptr myMuffin; + //! THROW_IF_FAILED(GetMuffin(myMuffin.put())); + //! ~~~~ + pointer* put() WI_NOEXCEPT + { + reset(); + return &m_ptr; + } + + //! Returns the address of the internal pointer casted to void** (releases ownership of the pointer BEFORE returning the address). + //! @see put + void** put_void() WI_NOEXCEPT + { + return reinterpret_cast(put()); + } + + //! Returns the address of the internal pointer casted to IUnknown** (releases ownership of the pointer BEFORE returning the address). + //! @see put + IUnknown** put_unknown() WI_NOEXCEPT + { + return reinterpret_cast(put()); + } + + //! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address). + //! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that + //! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use. Since this behavior is not always immediately + //! apparent, prefer to scope variables as close to use as possible (generally avoiding use of the same com_ptr variable in successive calls to + //! receive an output interface). + //! @see addressof + pointer* operator&() WI_NOEXCEPT + { + return put(); + } + + //! Returns the address of the internal pointer (does not release the pointer; should not be used for `_Out_` parameters) + pointer* addressof() WI_NOEXCEPT + { + return &m_ptr; + } + //! @} + + //! @name Inspection + //! @{ + + //! Returns the address of the const internal pointer (does not release the pointer) + const pointer* addressof() const WI_NOEXCEPT + { + return &m_ptr; + } + + //! Returns 'true' if the pointer is assigned (NOT nullptr) + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != nullptr); + } + + //! Returns the pointer + pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Allows direct calls against the pointer (AV on internal nullptr) + pointer operator->() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Dereferences the pointer (AV on internal nullptr) + element_type_reference operator*() const WI_NOEXCEPT + { + return *m_ptr; + } + //! @} + + //! @name Query helpers + //! * Retrieves the requested interface + //! * AV if the pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + + //! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.query();`. + //! See @ref page_query for more information. + //! + //! This method is the primary method that should be used to query a com_ptr in exception-based or fail-fast based code. + //! Error-code returning code should use @ref query_to so that the returned HRESULT can be examined. In the following + //! examples, `m_ptr` is an exception-based or fail-fast based com_ptr, com_weak_ref, or com_agile_ref: + //! ~~~~ + //! auto foo = ptr.query(); + //! foo->Method1(); + //! foo->Method2(); + //! ~~~~ + //! For simple single-method calls, this method allows removing the temporary that holds the com_ptr: + //! ~~~~ + //! ptr.query()->Method1(); + //! ~~~~ + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The pointer is guaranteed not null. The returned + //! `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling form of the + //! pointer being queried (exception based or fail-fast). + template + inline com_ptr_t query() const + { + static_assert(wistd::is_same::value, "query requires exceptions or fail fast; use try_query or query_to"); + return com_ptr_t(m_ptr, details::tag_com_query()); + } + + //! Query for the interface of the given out parameter `U`: `ptr.query_to(&foo);`. + //! See @ref page_query for more information. + //! + //! For fail-fast and exception-based behavior this routine should primarily be used to write to out parameters and @ref query should + //! be used to perform most queries. For error-code based code, this routine is the primary method that should be used to query a com_ptr. + //! + //! Error-code based samples: + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // simple query example: + //! wil::com_ptr_nothrow foo; + //! RETURN_IF_FAILED(m_ptr.query_to(&foo)); + //! foo->FooMethod1(); + //! + //! // output parameter example: + //! HRESULT GetFoo(_COM_Outptr_ IFoo** fooPtr) + //! { + //! RETURN_IF_FAILED(m_ptr.query_to(fooPtr)); + //! return S_OK; + //! } + //! ~~~~ + //! Exception or fail-fast samples: + //! ~~~~ + //! // class member being queried + //! wil::com_ptr m_ptr; + //! + //! void GetFoo(_COM_Outptr_ IFoo** fooPtr) + //! { + //! m_ptr.query_to(fooPtr); + //! } + //! ~~~~ + //! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need to + //! be specified directly. Rely upon template type deduction to pick up the type from the output parameter. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Exception-based and fail-fast based classes + //! do not return a value (void). + template + result query_to(_COM_Outptr_ U** ptrResult) const + { + // Prefast cannot see through the error policy + query_policy mapping and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop all of the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, ptrResult)); +#endif + } + + //! Query for the requested interface using the iid, ppv pattern: `ptr.query_to(riid, ptr);`. + //! See @ref page_query for more information. + //! + //! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and void** pointer + //! pattern (like QueryInterface). This pattern should not be used outside of that pattern (through IID_PPV_ARGS) as it is less efficient + //! than the typed version of @ref query_to which can elide the QueryInterface in favor of AddRef when the types are convertible. + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // output parameter example: + //! HRESULT GetFoo(REFIID riid, _COM_Outptr_ void** ptrResult) + //! { + //! RETURN_IF_FAILED(m_ptr.query_to(riid, ptrResult)); + //! return S_OK; + //! } + //! ~~~~ + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Exception-based and fail-fast based classes + //! do not return a value (void). + result query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult)); +#endif + } + //! @} + + //! @name Try query helpers + //! * Attempts to retrieves the requested interface + //! * AV if the pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful + //! + //! See @ref page_query for more information. + //! @{ + + //! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query();` (null result when interface is unsupported). + //! See @ref page_query for more information. + //! + //! This method can be used to query a com_ptr for an interface when it's known that support for that interface is + //! optional (failing the query should not produce an error). The caller must examine the returned pointer to see + //! if it's null before using it: + //! ~~~~ + //! auto foo = ptr.try_query(); + //! if (foo) + //! { + //! foo->Method1(); + //! foo->Method2(); + //! } + //! ~~~~ + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface is + //! not supported. The returned `com_ptr_t` will have the same error handling policy (exceptions, failfast or error codes) as + //! the pointer being queried. + template + inline com_ptr_t try_query() const + { + return com_ptr_t(m_ptr, details::tag_try_com_query()); + } + + //! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was successful (non-null). + //! See @ref page_query for more information. + //! + //! This method can be used to perform a query against a non-null interface when it's known that support for that interface is + //! optional (failing the query should not produce an error). The caller must examine the returned bool before using the returned pointer. + //! ~~~~ + //! wil::com_ptr_nothrow foo; + //! if (ptr.try_query_to(&foo)) + //! { + //! foo->Method1(); + //! foo->Method2(); + //! } + //! ~~~~ + //! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do not specify + //! the type directly to the template. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + template + _Success_return_ bool try_query_to(_COM_Outptr_ U** ptrResult) const + { + return SUCCEEDED(query_policy::query(m_ptr, ptrResult)); + } + + //! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);`. + //! See @ref page_query for more information. + //! + //! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and void** pointer + //! pattern (like QueryInterface). The key distinction is that this routine does not produce an error if the request isn't fulfilled, so + //! it's appropriate for `_COM_Outptr_result_maybenull_` cases. This pattern should not be used outside of that pattern (through IID_PPV_ARGS) as + //! it is less efficient than the typed version of @ref try_query_to which can elide the QueryInterface in favor of AddRef when the types are convertible. + //! The caller must examine the returned bool before using the returned pointer. + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // output parameter example (result may be null): + //! HRESULT GetFoo(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + //! { + //! m_ptr.try_query_to(riid, ptrResult); + //! return S_OK; + //! } + //! ~~~~ + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + _Success_return_ bool try_query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const + { + return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult)); + } + //! @} + + //! @name Copy helpers + //! * Retrieves the requested interface + //! * Succeeds with null if the pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information. + //! @{ + + //! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.copy();` (succeeds and returns a null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! This method is identical to @ref query with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The pointer will be null ONLY if the pointer being queried is null. The returned + //! `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling form of the + //! pointer being queried (exception based or fail-fast). + template + inline com_ptr_t copy() const + { + static_assert(wistd::is_same::value, "copy requires exceptions or fail fast; use the try_copy or copy_to method"); + return com_ptr_t(m_ptr, details::tag_com_copy()); + } + + //! Query for the interface of the given out parameter `U`: `ptr.copy_to(&foo);` (succeeds and returns null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! This method is identical to @ref query_to with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need to + //! be specified directly. Rely upon template type deduction to pick up the type from the output parameter. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure OR assigned null + //! when the source pointer is null. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Copying a null value is considered success. Exception-based + //! and fail-fast based classes do not return a value (void). + template + result copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const + { + if (m_ptr) + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, ptrResult)); +#endif + } + *ptrResult = nullptr; + return err_policy::OK(); + } + + //! Query for the requested interface using the iid, ppv pattern: `ptr.copy_to(riid, ptr);`. (succeeds and returns null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure OR assigned null + //! when the source pointer is null. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Copying a null value is considered success. Exception-based + //! and fail-fast based classes do not return a value (void). + result copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const + { + if (m_ptr) + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult)); +#endif + } + *ptrResult = nullptr; + return err_policy::OK(); + } + //! @} + + //! @name Try copy helpers + //! * Attempts to retrieves the requested interface + //! * Successfully produces null if the queried pointer is already null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'false' ONLY when the queried pointer is not null and the requested interface is unsupported + //! + //! See @ref page_query for more information. + //! @{ + + //! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query();` (null result when interface is unsupported or queried pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface was + //! not supported or the pointer being queried is null. The returned `com_ptr_t` will have the same error handling + //! policy (exceptions, failfast or error codes) as the pointer being queried. + template + inline com_ptr_t try_copy() const + { + return com_ptr_t(m_ptr, details::tag_try_com_copy()); + } + + //! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was successful (returns `false` if the pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will be null and the return value will be `false`. + //! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do not specify + //! the type directly to the template. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + template + _Success_return_ bool try_copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const + { + if (m_ptr) + { + return SUCCEEDED(query_policy::query(m_ptr, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + + //! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);` (returns `false` if the pointer is null) + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will be null and the return value will be `false`. + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure or + //! if the source pointer being queried is null. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). Querying a null + //! pointer will return `false` with a null result. + _Success_return_ bool try_copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const + { + if (m_ptr) + { + return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + //! @} + + //! @name WRL compatibility + //! @{ + + //! Copy construct from a compatible WRL ComPtr. + template > + com_ptr_t(const Microsoft::WRL::ComPtr& other) WI_NOEXCEPT : + com_ptr_t(static_cast(other.Get())) + { + } + + //! Move construct from a compatible WRL ComPtr. + template > + com_ptr_t(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT : + m_ptr(other.Detach()) + { + } + + //! Assign from a compatible WRL ComPtr. + template > + com_ptr_t& operator=(const Microsoft::WRL::ComPtr& other) WI_NOEXCEPT + { + return operator=(static_cast(other.Get())); + } + + //! Move assign from a compatible WRL ComPtr. + template > + com_ptr_t& operator=(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT + { + attach(other.Detach()); + return *this; + } + + //! Swap pointers with a WRL ComPtr to the same interface. + void swap(Microsoft::WRL::ComPtr& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other.Detach(); + other.Attach(ptr); + } + + //! Swap pointers with a rvalue reference to a WRL ComPtr to the same interface. + void swap(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT + { + swap(other); + } + //! @} // WRL compatibility + + public: + // Internal Helpers + /// @cond + template + inline com_ptr_t(_In_ U* ptr, details::tag_com_query) + { + err_policy::HResult(details::query_policy_t::query(ptr, &m_ptr)); + } + + template + inline com_ptr_t(_In_ U* ptr, details::tag_try_com_query) WI_NOEXCEPT : m_ptr(nullptr) + { + details::query_policy_t::query(ptr, &m_ptr); + } + + template + inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy) + { + if (ptr) + { + err_policy::HResult(details::query_policy_t::query(ptr, &m_ptr)); + return; + } + m_ptr = nullptr; + } + + template + inline com_ptr_t(_In_opt_ U* ptr, details::tag_try_com_copy) WI_NOEXCEPT : m_ptr(nullptr) + { + if (ptr) + { + details::query_policy_t::query(ptr, &m_ptr); + } + } + /// @endcond + + private: + pointer m_ptr; + }; + + // Error-policy driven forms of com_ptr + +#ifdef WIL_ENABLE_EXCEPTIONS + //! COM pointer, errors throw exceptions (see @ref com_ptr_t for details) + template + using com_ptr = com_ptr_t; +#endif + + //! COM pointer, errors return error codes (see @ref com_ptr_t for details) + template + using com_ptr_nothrow = com_ptr_t; + + //! COM pointer, errors fail-fast (see @ref com_ptr_t for details) + template + using com_ptr_failfast = com_ptr_t; + + + // Global operators / swap + + //! Swaps the given com pointers that have different error handling. + //! Note that there are also corresponding versions to allow you to swap any wil com_ptr with a WRL ComPtr. + template + inline void swap(com_ptr_t& left, com_ptr_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + //! Swaps the given com pointers that have the same error handling. + template + inline void swap(com_ptr_t& left, com_ptr_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + //! Compare two com pointers. + //! Compares the two raw com pointers for equivalence. Does NOT compare object identity with a QI for IUnknown. + //! + //! Note that documentation for all of the various comparators has not been generated to reduce global function + //! clutter, but ALL standard comparison operators are supported between wil com_ptr objects, nullptr_t, and + //! WRL ComPtr. + template + inline bool operator==(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right.get()); + } + + // We don't document all of the global comparison operators (reduce clutter) + /// @cond + template + inline bool operator<(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right.get()); + } + + template + inline bool operator==(const com_ptr_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + return (left.get() == nullptr); + } + + template + inline bool operator!=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline bool operator==(wistd::nullptr_t, const com_ptr_t& right) WI_NOEXCEPT + { + return (right.get() == nullptr); + } + + template + inline bool operator!=(const com_ptr_t& left, wistd::nullptr_t) WI_NOEXCEPT + { return (!(left == nullptr)); } + + template + inline bool operator!=(wistd::nullptr_t, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right == nullptr)); } + + // WRL ComPtr support + + template + inline void swap(com_ptr_t& left, Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + inline bool operator==(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right.Get()); + } + + template + inline bool operator<(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right.Get()); + } + + template + inline bool operator!=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline void swap(Microsoft::WRL::ComPtr& left, com_ptr_t& right) WI_NOEXCEPT + { + right.swap(left); + } + + template + inline bool operator==(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.Get() == right.get()); + } + + template + inline bool operator<(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.Get() < right.get()); + } + + template + inline bool operator!=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + // raw COM pointer support + // + // Use these for convenience and to avoid unnecessary AddRef/Release cyles when using raw + // pointers to access STL containers. Specify std::less<> to benefit from operator<. + // + // Example: std::set, std::less<>> set; + + template + inline bool operator==(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right); + } + + template + inline bool operator<(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right); + } + + template + inline bool operator!=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline bool operator==(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left == right.get()); + } + + template + inline bool operator<(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left < right.get()); + } + + template + inline bool operator!=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + // suppress documentation of every single comparison operator + /// @endcond + + + //! An overloaded function that retrieves the raw com pointer from a raw pointer, wil::com_ptr_t, WRL ComPtr, or Platform::Object^. + //! This function is primarily useful by library or helper code. It allows code to be written to accept a forwarding reference + //! template that can be used as an input com pointer. That input com pointer is allowed to be any of: + //! * Raw Pointer: `T* com_raw_ptr(T* ptr)` + //! * Wil com_ptr: `T* com_raw_ptr(const wil::com_ptr_t& ptr)` + //! * WRL ComPtr: `T* com_raw_ptr(const Microsoft::WRL::ComPtr& ptr)` + //! * C++/CX hat: `IInspectable* com_raw_ptr(Platform::Object^ ptr)` + //! + //! Which in turn allows code like the following to be written: + //! ~~~~ + //! template + //! void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + //! { + //! auto raw = com_raw_ptr(wistd::forward(ptrSource)); + //! // decltype(raw) has the type of the inner pointer and raw is guaranteed to be a raw com pointer + //! ~~~~ + template + T* com_raw_ptr(T* ptr) + { + return ptr; + } + + /// @cond + template + T* com_raw_ptr(const wil::com_ptr_t& ptr) + { + return ptr.get(); + } + + template + T* com_raw_ptr(const Microsoft::WRL::ComPtr& ptr) + { + return ptr.Get(); + } + +#ifdef __cplusplus_winrt + + template + inline IInspectable* com_raw_ptr(T^ ptr) + { + return reinterpret_cast(static_cast<::Platform::Object^>(ptr)); + } + +#endif + /// @endcond + + + //! @name Stand-alone query helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested interface + //! * AV if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is guaranteed not null. + template + inline com_ptr com_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_com_query()); + } +#endif + + //! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is guaranteed not null. + template + inline com_ptr_failfast com_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_com_query()); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + THROW_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } +#endif + + //! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to_failfast(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } + + //! Queries for the interface specified by the type of the output parameter (returns an error if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure. + //! @return Returns an HRESULT representing whether the query succeeded. + template + HRESULT com_query_to_nothrow(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::query_policy_t::query(raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + THROW_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } +#endif + + //! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } + + //! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure. + template + HRESULT com_query_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::query_policy_t::query(raw, riid, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + //! @} + + //! @name Stand-alone try query helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Attempts to retrieves the requested interface + //! * AV if the source pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful (non-null return result) + //! + //! See @ref page_query for more information. + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr try_com_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_try_com_query()); + } +#endif + + //! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_failfast` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_failfast try_com_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_try_com_query()); + } + + //! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_nothrow` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_nothrow try_com_query_nothrow(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_nothrow(raw, details::tag_try_com_query()); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null. + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return (SUCCEEDED(details::query_policy_t::query(raw, ptrResult))); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null. + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return (SUCCEEDED(details::query_policy_t::query(raw, riid, ptrResult))); + } + //! @} + + + //! @name Stand-alone copy helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested interface + //! * Succeeds with null if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer will be null only if the source is null. + template + inline com_ptr com_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_com_copy()); + } +#endif + + //! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer will be null only if the source is null. + template + inline com_ptr_failfast com_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_com_copy()); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + THROW_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + return; + } + *ptrResult = nullptr; + } +#endif + + //! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to_failfast(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + return; + } + *ptrResult = nullptr; + } + + //! Queries for the interface specified by the type of the output parameter (returns an error if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the source is null. + //! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null). + template + HRESULT com_copy_to_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::query_policy_t::query(raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + THROW_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + return; + } + *ptrResult = nullptr; + } +#endif + + //! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + return; + } + *ptrResult = nullptr; + } + + //! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the source is null. + //! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null). + template + HRESULT com_copy_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::query_policy_t::query(raw, riid, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + //! @} + + + //! @name Stand-alone try copy helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Attempts to retrieves the requested interface + //! * Succeeds with null if the source pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful (non-null return result) + //! + //! See @ref page_query for more information. + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr try_com_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_try_com_copy()); + } +#endif + + //! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_failfast` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_failfast try_com_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_try_com_copy()); + } + + //! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_nothrow` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_nothrow try_com_copy_nothrow(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_nothrow(raw, details::tag_try_com_copy()); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null. + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + return SUCCEEDED(details::query_policy_t::query(raw, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null. + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + return SUCCEEDED(details::query_policy_t::query(raw, riid, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + //! @} + +#ifdef __cplusplus_winrt + //! @name Stand-alone helpers to query for CX ref ("hat") types from ABI COM types. + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested C++/CX interface or ref class. + //! * Preserves null if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + + template + ::Platform::Object^ cx_object_from_abi(T&& ptr) WI_NOEXCEPT + { + IInspectable* const inspectable = com_raw_ptr(wistd::forward(ptr)); + return reinterpret_cast<::Platform::Object^>(inspectable); + } + + template + inline U^ cx_safe_cast(T&& ptrSource) + { + return safe_cast(cx_object_from_abi(wistd::forward(ptrSource))); + } + + template + inline U^ cx_dynamic_cast(T&& ptrSource) WI_NOEXCEPT + { + return dynamic_cast(cx_object_from_abi(wistd::forward(ptrSource))); + } + //! @} +#endif + + + //***************************************************************************** + // Agile References + //***************************************************************************** + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +#ifdef WIL_ENABLE_EXCEPTIONS + //! Agile reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_agile_query for details) + using com_agile_ref = com_ptr; +#endif + //! Agile reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_agile_query_nothrow for details) + using com_agile_ref_nothrow = com_ptr_nothrow; + //! Agile reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_agile_query_failfast for details) + using com_agile_ref_failfast = com_ptr_failfast; + + //! @name Create agile reference helpers + //! * Attempts to retrieve an agile reference to the requested interface (see [RoGetAgileReference](https://msdn.microsoft.com/en-us/library/dn269839.aspx)) + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * `query` methods AV if the source pointer is null + //! * `copy` methods succeed with null if the source pointer is null + //! * Accept optional [AgileReferenceOptions](https://msdn.microsoft.com/en-us/library/dn269836.aspx) + //! + //! See @ref page_query for more information on resolving an agile ref + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_agile_ref representing the given source pointer (throws an exception on failure) + template + com_agile_ref com_agile_query(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref agileRef; + THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + return agileRef; + } +#endif + + //! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure) + template + com_agile_ref_failfast com_agile_query_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref_failfast agileRef; + FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + return agileRef; + } + + //! return a com_agile_ref_nothrow representing the given source pointer (returns an HRESULT on failure) + template + HRESULT com_agile_query_nothrow(T&& ptrSource, _COM_Outptr_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_agile_ref representing the given source pointer (throws an exception on failure, source maybe null) + template + com_agile_ref com_agile_copy(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref agileRef; + if (raw) + { + THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + } + return agileRef; + } +#endif + + //! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + com_agile_ref_failfast com_agile_copy_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref_failfast agileRef; + if (raw) + { + FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + } + return agileRef; + } + + //! return an agile ref (com_agile_ref_XXX or other representation) representing the given source pointer (return error on failure, source maybe null) + template + HRESULT com_agile_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + //! @} +#endif + + //***************************************************************************** + // Weak References + //***************************************************************************** + + namespace details + { + template + HRESULT GetWeakReference(T* ptr, _COM_Outptr_ IWeakReference** weakReference) + { + static_assert(!wistd::is_same::value, "Cannot get an IWeakReference to an IWeakReference"); + + *weakReference = nullptr; + com_ptr_nothrow source; + HRESULT hr = ptr->QueryInterface(IID_PPV_ARGS(&source)); + if (SUCCEEDED(hr)) + { + hr = source->GetWeakReference(weakReference); + } + return hr; + } + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Weak reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_weak_query for details) + using com_weak_ref = com_ptr; +#endif + //! Weak reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_weak_query_nothrow for details) + using com_weak_ref_nothrow = com_ptr_nothrow; + //! Weak reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_weak_query_failfast for details) + using com_weak_ref_failfast = com_ptr_failfast; + + //! @name Create weak reference helpers + //! * Attempts to retrieve a weak reference to the requested interface (see WRL's similar [WeakRef](https://msdn.microsoft.com/en-us/library/br244853.aspx)) + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * `query` methods AV if the source pointer is null + //! * `copy` methods succeed with null if the source pointer is null + //! + //! See @ref page_query for more information on resolving a weak ref + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_weak_ref representing the given source pointer (throws an exception on failure) + template + com_weak_ref com_weak_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref weakRef; + THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + return weakRef; + } +#endif + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure) + template + com_weak_ref_failfast com_weak_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref_failfast weakRef; + FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + return weakRef; + } + + //! return a com_weak_ref_nothrow representing the given source pointer (returns an HRESULT on failure) + template + HRESULT com_weak_query_nothrow(T&& ptrSource, _COM_Outptr_ IWeakReference** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::GetWeakReference(raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_weak_ref representing the given source pointer (throws an exception on failure, source maybe null) + template + com_weak_ref com_weak_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref weakRef; + if (raw) + { + THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + } + return weakRef; + } +#endif + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + com_weak_ref_failfast com_weak_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref_failfast weakRef; + if (raw) + { + FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + } + return weakRef; + } + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + HRESULT com_weak_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IWeakReference** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::GetWeakReference(raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + +#pragma region COM Object Helpers + + template + inline bool is_agile(T&& ptrSource) + { + wil::com_ptr_nothrow agileObject; + return SUCCEEDED(com_raw_ptr(wistd::forward(ptrSource))->QueryInterface(IID_PPV_ARGS(&agileObject))); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown.*/ + template + wil::com_ptr_t CoCreateInstance(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + wil::com_ptr_t result; + error_policy::HResult(::CoCreateInstance(rclsid, nullptr, dwClsContext, IID_PPV_ARGS(&result))); + return result; + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */ + template + wil::com_ptr_t CoCreateInstance(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstance(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown. */ + template + wil::com_ptr_failfast CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstance(rclsid, dwClsContext); + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */ + template + wil::com_ptr_failfast CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstanceFailFast(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstance(rclsid, dwClsContext); + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstanceNoThrow(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */ + template + wil::com_ptr_t CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + wil::com_ptr_t result; + error_policy::HResult(CoGetClassObject(rclsid, dwClsContext, nullptr, IID_PPV_ARGS(&result))); + return result; + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. */ + template + wil::com_ptr_t CoGetClassObject(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */ + template + wil::com_ptr_failfast CoGetClassObjectFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(rclsid, dwClsContext); + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. */ + template + wil::com_ptr_failfast CoGetClassObjectFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObjectFailFast(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoGetClassObjectNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(rclsid, dwClsContext); + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoGetClassObjectNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObjectNoThrow(__uuidof(Class), dwClsContext); + } +#pragma endregion + +#pragma region Stream helpers + + /** Read data from a stream into a buffer. + Reads up to a certain number of bytes into a buffer. Returns the amount of data written, which + may be less than the amount requested if the stream ran out. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + size_t read = 0; + RETURN_IF_FAILED(wil::stream_read_partial_nothrow(source, &dataBlob, sizeof(dataBlob), &read)); + if (read != sizeof(dataBlob)) + { + // end of stream, probably + } + else if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @param wrote The amount, in bytes, of data read from `stream` into `data` + */ + inline HRESULT stream_read_partial_nothrow(_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, *wrote) void* data, unsigned long size, unsigned long *wrote) + { + RETURN_HR(stream->Read(data, size, wrote)); + } + + /** Read an exact number of bytes from a stream into a buffer. + Fails if the stream didn't read all the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + RETURN_IF_FAILED(wil::stream_read_nothrow(source, &dataBlob, sizeof(dataBlob))); + if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream + did not read the complete buffer. + */ + inline HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size) + { + unsigned long didRead; + RETURN_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), didRead != size); + + return S_OK; + } + + /** Read from a stream into a POD type. + Fails if the stream didn't have enough bytes. + ~~~~ + IStream* source = // ... + MY_HEADER header{}; + RETURN_IF_FAILED(wil::stream_read_nothrow(source, &header)); + if (header.Version == 0x8675309) + { + ConsumeOldHeader(stream, header); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param pThing The POD data type to read from the stream. + @return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream + did not read the complete buffer. + */ + template HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_ T* pThing) + { + static_assert(__is_pod(T), "Type must be POD."); + return stream_read_nothrow(stream, pThing, sizeof(T)); + } + + /** Write an exact number of bytes to a stream from a buffer. + Fails if the stream didn't read write the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0x8675309; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, &dataBlob, sizeof(dataBlob))); + ~~~~ + @param stream The stream to which to write at most `size` bytes. + @param data A buffer from which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size) + { + unsigned long wrote; + RETURN_IF_FAILED(stream->Write(data, size, &wrote)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), wrote != size); + + return S_OK; + } + + /** Write a POD type to a stream. + Fails if not all the bytes were written. + ~~~~ + IStream* source = // ... + MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 }; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, header)); + + ULONGLONG value = 16; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, value)); + ~~~~ + @param stream The stream to which to write `thing` + @param thing The POD data type to write to the stream. + */ + template inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, const T& thing) + { + return stream_write_nothrow(stream, wistd::addressof(thing), sizeof(thing)); + } + + /** Retrieve the size of this stream, in bytes + ~~~~ + IStream* source = // ... + ULONGLONG size; + RETURN_IF_FAILED(wil::stream_size_nothrow(source, &size)); + RETURN_HR_IF(E_INVALIDARG, size > ULONG_MAX); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param value The size, in bytes, reported by `stream` + */ + inline HRESULT stream_size_nothrow(_In_ IStream* stream, _Out_ unsigned long long* value) + { + STATSTG st{}; + RETURN_IF_FAILED(stream->Stat(&st, STATFLAG_NONAME)); + *value = st.cbSize.QuadPart; + + return S_OK; + } + + /** Seek a stream to a relative offset or absolute position + ~~~~ + IStream* source = // ... + unsigned long long landed; + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, 16, STREAM_SEEK_CUR, &landed)); + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, -5, STREAM_SEEK_END)); + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, LLONG_MAX, STREAM_SEEK_CUR)); + ~~~~ + @param stream The stream to seek + @param offset The position, in bytes from the current position, to seek + @param from The starting point from which to seek, from the STREAM_SEEK_* set of values + @param value Optionally recieves the new absolute position from the stream + */ + inline HRESULT stream_seek_nothrow(_In_ IStream* stream, long long offset, unsigned long from, _Out_opt_ unsigned long long* value = nullptr) + { + LARGE_INTEGER amount; + ULARGE_INTEGER landed{}; + amount.QuadPart = offset; + RETURN_IF_FAILED(stream->Seek(amount, from, value ? &landed : nullptr)); + assign_to_opt_param(value, landed.QuadPart); + + return S_OK; + } + + /** Seek a stream to an absolute offset + ~~~~ + IStream* source = // ... + RETURN_HR(wil::stream_set_position_nothrow(source, 16)); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param offset The position, in bytes from the start of the stream, to seek to + @param value Optionally recieves the new absolute position from the stream + */ + inline HRESULT stream_set_position_nothrow(_In_ IStream* stream, unsigned long long offset, _Out_opt_ unsigned long long* value = nullptr) + { + // IStream::Seek(..., _SET) interprets the first parameter as an unsigned value. + return stream_seek_nothrow(stream, static_cast(offset), STREAM_SEEK_SET, value); + } + + /** Seek a relative amount in a stream + ~~~~ + IStream* source = // ... + RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, -16)); + + ULONGLONG newPosition; + RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, 16, &newPosition)); + ~~~~ + @param stream The stream whose location is to be moved + @param amount The offset, in bytes, to seek the stream. + @param value Set to the new absolute steam position, in bytes + */ + inline HRESULT stream_seek_from_current_position_nothrow(_In_ IStream* stream, long long amount, _Out_opt_ unsigned long long* value = nullptr) + { + return stream_seek_nothrow(stream, amount, STREAM_SEEK_CUR, value); + } + + /** Determine the current byte position in the stream + ~~~~ + IStream* source = // ... + ULONGLONG currentPos; + RETURN_IF_FAILED(wil::stream_get_position_nothrow(source, ¤tPos)); + ~~~~ + @param stream The stream whose location is to be moved + @param position Set to the current absolute steam position, in bytes + */ + inline HRESULT stream_get_position_nothrow(_In_ IStream* stream, _Out_ unsigned long long* position) + { + return stream_seek_from_current_position_nothrow(stream, 0, position); + } + + /** Moves the stream to absolute position 0 + ~~~~ + IStream* source = // ... + RETURN_IF_FAILED(wil::stream_reset_nothrow(source)); + ~~~~ + @param stream The stream whose location is to be moved + */ + inline HRESULT stream_reset_nothrow(_In_ IStream* stream) + { + return stream_set_position_nothrow(stream, 0); + } + + /** Copy data from one stream to another, returning the final amount copied. + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied; + RETURN_IF_FAILED(wil::stream_copy_bytes_nothrow(source, target, sizeof(MyType), &copied)); + if (copied < sizeof(MyType)) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The maximum number of bytes to copy from `source` to `target` + @param pCopied If non-null, set to the number of bytes copied between the two. + */ + inline HRESULT stream_copy_bytes_nothrow(_In_ IStream* source, _In_ IStream* target, unsigned long long amount, _Out_opt_ unsigned long long* pCopied = nullptr) + { + ULARGE_INTEGER toCopy; + ULARGE_INTEGER copied; + toCopy.QuadPart = amount; + RETURN_IF_FAILED(source->CopyTo(target, toCopy, nullptr, &copied)); + assign_to_opt_param(pCopied, copied.QuadPart); + + return S_OK; + } + + /** Copy all data from one stream to another, returning the final amount copied. + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied; + RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, &copied)); + if (copied < 8) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy all content + @param target The steam to which to copy all content + @param pCopied If non-null, set to the number of bytes copied between the two. + */ + inline HRESULT stream_copy_all_nothrow(_In_ IStream* source, _In_ IStream* target, _Out_opt_ unsigned long long* pCopied = nullptr) + { + return stream_copy_bytes_nothrow(source, target, ULLONG_MAX, pCopied); + } + + /** Copies an exact amount of data from one stream to another, failing otherwise + ~~~~ + IStream* source = // ... + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, 16)); + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The number of bytes to copy from `source` to `target` + */ + inline HRESULT stream_copy_exact_nothrow(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + unsigned long long copied; + RETURN_IF_FAILED(stream_copy_bytes_nothrow(source, target, ULLONG_MAX, &copied)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), copied != amount); + + return S_OK; + } + + //! Controls behavior when reading a zero-length string from a stream + enum class empty_string_options + { + //! Zero-length strings are returned as nullptr + returns_null, + + //! Zero-length strings are allocated and returned with zero characters + returns_empty, + }; + +#ifdef __WIL_OBJBASE_H_ + + /** Read a string from a stream and returns an allocated copy + Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format + is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc. + Returns a zero-length (but non-null) string if the stream contained a zero-length string. + ~~~~ + IStream* source = // ... + wil::unique_cotaskmem_string content; + RETURN_IF_FAILED(wil::stream_read_string_nothrow(source, &content)); + if (wcscmp(content.get(), L"waffles") == 0) + { + // Waffles! + } + ~~~~ + @param source The stream from which to read a string + @param value Set to point to the allocated result of reading a string from `source` + */ + inline HRESULT stream_read_string_nothrow( + _In_ ISequentialStream* source, + _When_(options == empty_string_options::returns_empty, _Outptr_result_z_) _When_(options == empty_string_options::returns_null, _Outptr_result_maybenull_z_) wchar_t** value, + empty_string_options options = empty_string_options::returns_empty) + { + unsigned short cch; + RETURN_IF_FAILED(stream_read_nothrow(source, &cch)); + + if ((cch == 0) && (options == empty_string_options::returns_null)) + { + *value = nullptr; + } + else + { + auto allocated = make_unique_cotaskmem_nothrow(static_cast(cch) + 1); + RETURN_IF_NULL_ALLOC(allocated); + RETURN_IF_FAILED(stream_read_nothrow(source, allocated.get(), static_cast(cch) * sizeof(wchar_t))); + allocated[cch] = 0; + + *value = allocated.release(); + } + + return S_OK; + } + +#endif // __WIL_OBJBASE_H + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles", 3)); + // Produces wchar_t[] { 0x3, L'W', L'a', L'f' }; + ~~~~ + @param target The stream to which to write a string + @param source The string to write. Can be null if `writeLength` is zero + @param writeLength The number of characters to write from source into `target` + */ + inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_reads_opt_(writeLength) const wchar_t* source, _In_ size_t writeLength) + { + FAIL_FAST_IF(writeLength > USHRT_MAX); + + RETURN_IF_FAILED(stream_write_nothrow(target, static_cast(writeLength))); + + if (writeLength > 0) + { + RETURN_IF_FAILED(stream_write_nothrow(target, source, static_cast(writeLength) * sizeof(wchar_t))); + } + + return S_OK; + } + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles")); + // Produces wchar_t[] { 0x3, L'W', L'a', L'f', L'f', L'l', L'e', L's' }; + ~~~~ + @param target The stream to which to write a string + @param source The string to write. When nullptr, a zero-length string is written. + */ + inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source) + { + return stream_write_string_nothrow(target, source, source ? wcslen(source) : 0); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + + /** Read data from a stream into a buffer. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + auto read = wil::stream_read_partial(source, &dataBlob, sizeof(dataBlob)); + if (read != sizeof(dataBlob)) + { + // end of stream, probably + } + else if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @return The amount, in bytes, of data read from `stream` into `data` + */ + inline unsigned long stream_read_partial(_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, return) void* data, unsigned long size) + { + unsigned long didRead; + THROW_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead)); + + return didRead; + } + + /** Read an exact number of bytes from a stream into a buffer. + Fails if the stream didn't read all the bytes requested by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA). + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + wil::stream_read(source, &dataBlob, sizeof(dataBlob)); + if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline void stream_read(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_read_partial(stream, data, size) != size); + } + + /** Read from a stream into a POD type. + Fails if the stream didn't have enough bytes by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA). + ~~~~ + IStream* source = // ... + MY_HEADER header = wil::stream_read(source); + if (header.Version == 0x8675309) + { + ConsumeOldHeader(stream, header); + } + ~~~~ + @param stream The stream from which to read at most `sizeof(T)` bytes. + @return An instance of `T` read from the stream + */ + template T stream_read(_In_ ISequentialStream* stream) + { + static_assert(__is_pod(T), "Read type must be POD"); + T temp{}; + stream_read(stream, &temp, sizeof(temp)); + + return temp; + } + + /** Write an exact number of bytes to a stream from a buffer. + Fails if the stream didn't read write the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + wil::stream_write(source, dataBlob, sizeof(dataBlob)); + ~~~~ + @param stream The stream to which to write at most `size` bytes. + @param data A buffer from which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline void stream_write(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size) + { + THROW_IF_FAILED(stream_write_nothrow(stream, data, size)); + } + + /** Write a POD type to a stream. + Fails if the stream didn't accept the entire size. + ~~~~ + IStream* target = // ... + + MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 }; + wil::stream_write(target, header) + + wil::stream_write(target, 16); + ~~~~ + @param stream The stream to which to write `thing` + @param thing The POD data type to write to the stream. + */ + template inline void stream_write(_In_ ISequentialStream* stream, const T& thing) + { + stream_write(stream, wistd::addressof(thing), sizeof(thing)); + } + + /** Retrieve the size of this stream, in bytes + ~~~~ + IStream* source = // ... + ULONGLONG size = wil::stream_size(source); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @return The size, in bytes, reported by `stream` + */ + inline unsigned long long stream_size(_In_ IStream* stream) + { + unsigned long long size; + THROW_IF_FAILED(stream_size_nothrow(stream, &size)); + + return size; + } + + /** Seek a stream to an absolute offset + ~~~~ + IStream* source = // ... + wil::stream_set_position(source, sizeof(HEADER)); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param offset The offset, in bytes, to seek the stream. + @return The new absolute stream position, in bytes + */ + inline unsigned long long stream_set_position(_In_ IStream* stream, unsigned long long offset) + { + unsigned long long landed; + THROW_IF_FAILED(stream_set_position_nothrow(stream, offset, &landed)); + return landed; + } + + /** Seek a relative amount in a stream + ~~~~ + IStream* source = // ... + ULONGLONG newPosition = wil::stream_seek_from_current_position(source, 16); + ~~~~ + @param stream The stream whose location is to be moved + @param amount The offset, in bytes, to seek the stream. + @return The new absolute stream position, in bytes + */ + inline unsigned long long stream_seek_from_current_position(_In_ IStream* stream, long long amount) + { + unsigned long long landed; + THROW_IF_FAILED(stream_seek_from_current_position_nothrow(stream, amount, &landed)); + + return landed; + } + + /** Determine the current byte position in the stream + ~~~~ + IStream* source = // ... + ULONGLONG currentPos = wil::stream_get_position(source); + ~~~~ + @param stream The stream whose location is to be moved + @return The current position reported by `stream` + */ + inline unsigned long long stream_get_position(_In_ IStream* stream) + { + return stream_seek_from_current_position(stream, 0); + } + + /** Moves the stream to absolute position 0 + ~~~~ + IStream* source = // ... + wil::stream_reset(source); + ASSERT(wil::stream_get_position(source) == 0); + ~~~~ + @param stream The stream whose location is to be moved + */ + inline void stream_reset(_In_ IStream* stream) + { + stream_set_position(stream, 0); + } + + /** Copy data from one stream to another + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied = ; + if (wil::stream_copy_bytes(source, target, sizeof(Header)) < sizeof(Header)) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The maximum number of bytes to copy from `source` to `target` + @return The number of bytes copied between the two streams + */ + inline unsigned long long stream_copy_bytes(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + unsigned long long copied; + THROW_IF_FAILED(stream_copy_bytes_nothrow(source, target, amount, &copied)); + + return copied; + } + + /** Copy all data from one stream to another + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied = wil::stream_copy_all(source, target); + ~~~~ + @param source The stream from which to copy all content + @param target The steam to which to copy all content + @return The number of bytes copied between the two. + */ + inline unsigned long long stream_copy_all(_In_ IStream* source, _In_ IStream* target) + { + return stream_copy_bytes(source, target, ULLONG_MAX); + } + + /** Copies an exact amount of data from one stream to another, failing otherwise + ~~~~ + IStream* source = // ... + IStream* target = // ... + wil::stream_copy_all_nothrow(source, target, sizeof(SOMETHING)); + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The number of bytes to copy from `source` to `target` + */ + inline void stream_copy_exact(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_copy_bytes(source, target, amount) != amount); + } + +#ifdef __WIL_OBJBASE_H_ + + /** Read a string from a stream and returns an allocated copy + Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format + is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc. + Returns a zero-length (but non-null) string if the stream contained a zero-length string. + ~~~~ + IStream* source = // ... + wil::unique_cotaskmem_string content = wil::stream_read_string(source); + if (wcscmp(content.get(), L"waffles") == 0) + { + // Waffles! + } + ~~~~ + @param source The stream from which to read a string + @return An non-null string (but possibly zero lengh) string read from `source` + */ + inline wil::unique_cotaskmem_string stream_read_string(_In_ ISequentialStream* source, empty_string_options options = empty_string_options::returns_empty) + { + wil::unique_cotaskmem_string result; + THROW_IF_FAILED(stream_read_string_nothrow(source, &result, options)); + + return result; + } + +#endif // __WIL_OBJBASE_H + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + wil::stream_write_string(target, L"Waffles", 3); + ~~~~ + @param target The stream to which to write a string + @param source The string to write. Can be null if `writeLength` is zero + @param writeLength The number of characters to write from source into `target` + */ + inline void stream_write_string(_In_ ISequentialStream* target, _In_reads_opt_(toWriteCch) const wchar_t* source, _In_ size_t toWriteCch) + { + THROW_IF_FAILED(stream_write_string_nothrow(target, source, toWriteCch)); + } + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written.This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + wil::stream_write_string(target, L"Waffles"); + ~~~~ + @param target The stream to which to write a string + @param source The string to write. When nullptr, a zero-length string is written. + */ + inline void stream_write_string(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source) + { + THROW_IF_FAILED(stream_write_string_nothrow(target, source, source ? wcslen(source) : 0)); + } + + /** Saves and restores the position of a stream + Useful for potentially reading data from a stream, or being able to read ahead, then reset + back to where one left off, such as conditionally reading content from a stream. + ~~~~ + void MaybeConsumeStream(IStream* stream) + { + // On error, reset the read position in the stream to where we left off + auto saver = wil::stream_position_saver(stream); + auto header = wil::stream_read(stream); + for (ULONG i = 0; i < header.Count; ++i) + { + ProcessElement(wil::stream_read(stream)); + } + } + ~~~~ + */ + class stream_position_saver + { + public: + //! Constructs a saver from the current position of this stream + //! @param stream The stream instance whose position is to be saved. + explicit stream_position_saver(_In_opt_ IStream* stream) : + m_stream(stream), + m_position(stream ? stream_get_position(stream) : 0) + { + } + + ~stream_position_saver() + { + if (m_stream) + { + LOG_IF_FAILED(stream_set_position_nothrow(m_stream.get(), m_position)); + } + } + + /** Updates the current position in the stream + ~~~~ + // Read a size marker from the stream, then advance that much. + IStream* stream1 = // ... + auto saver = wil::stream_position_saver(stream1); + auto size = wil::stream_read(stream1); + wil::stream_seek_from_current_position(stream, size); + saver.update(); + ~~~~ + */ + void update() + { + m_position = stream_get_position(m_stream.get()); + } + + //! Returns the current position being saved for the stream + //! @returns The position, in bytes, being saved for the stream + unsigned long long position() const + { + return m_position; + } + + /** Resets the position saver to manage a new stream + Reverts the position of any stream this saver is currently holding a place for. + ~~~~ + IStream* stream1 = // ... + IStream* stream2 = // ... + auto saver = wil::stream_position_saver(stream1); + if (wil::stream_read(stream1).Flags != 0) + { + saver.reset(stream2); // position in stream1 is reverted, now holding stream2 + } + ~~~~ + @param stream The stream whose position is to be saved + */ + void reset(_In_ IStream* stream) + { + reset(); + + m_stream = stream; + m_position = wil::stream_get_position(m_stream.get()); + } + + /** Resets the position of the stream + ~~~~ + IStream* stream1 = // ... + auto saver = wil::stream_position_saver(stream1); + MyType mt = wil::stream_read(stream1); + if (mt.Flags & MyTypeFlags::Extended) + { + saver.reset(); + ProcessExtended(stream1, wil::stream_read(stream1)); + } + else + { + ProcessStandard(stream1, mt); + } + ~~~~ + */ + void reset() + { + if (m_stream) + { + wil::stream_set_position(m_stream.get(), m_position); + } + } + + /** Stops saving the position of the stream + ~~~~ + // The stream has either a standard or extended header, followed by interesting content. + // Read either one, leaving the stream after the headers have been read off. On failure, + // the stream's position is restored. + std::pair get_headers(_In_ IStream* source) + { + auto saver = wil::stream_position_saver(stream1); + MyType mt = wil::stream_read(stream1); + MyTypeExtended mte{}; + if (mt.Flags & MyTypeFlags::Extended) + { + mte = wil::stream_read(stream1); + } + saver.dismiss(); + return { mt, mte }; + } + ~~~~ + */ + void dismiss() + { + m_stream.reset(); + } + + stream_position_saver(stream_position_saver&&) = default; + stream_position_saver& operator=(stream_position_saver&&) = default; + + stream_position_saver(const stream_position_saver&) = delete; + void operator=(const stream_position_saver&) = delete; + + private: + com_ptr m_stream; + unsigned long long m_position; + }; +#endif // WIL_ENABLE_EXCEPTIONS +#pragma endregion // stream helpers + +#if defined(__IObjectWithSite_INTERFACE_DEFINED__) + /// @cond + namespace details + { + inline void __stdcall SetSiteNull(IObjectWithSite* objWithSite) + { + objWithSite->SetSite(nullptr); // break the cycle + } + } // details + /// @endcond + + using unique_set_site_null_call = wil::unique_com_call; + + /** RAII support for managing the site chain. This function sets the site pointer on an object and return an object + that resets it on destruction to break the cycle. + Note, this does not preserve the existing site if there is one (an uncommon case) so only use this when that is not required. + ~~~ + auto cleanup = wil::com_set_site(execCommand.get(), serviceProvider->GetAsSite()); + ~~~ + Include ocidl.h before wil\com.h to use this. + */ + WI_NODISCARD inline unique_set_site_null_call com_set_site(_In_opt_ IUnknown* obj, _In_opt_ IUnknown* site) + { + wil::com_ptr_nothrow objWithSite; + if (site && wil::try_com_copy_to(obj, &objWithSite)) + { + objWithSite->SetSite(site); + } + return unique_set_site_null_call(objWithSite.get()); + } + + /** Iterate over each object in a site chain. Useful for debugging site issues, here is sample use. + ~~~ + void OutputDebugSiteChainWatchWindowText(IUnknown* site) + { + OutputDebugStringW(L"Copy and paste these entries into the Visual Studio Watch Window\n"); + wil::for_each_site(site, [](IUnknown* site) + { + wchar_t msg[64]; + StringCchPrintfW(msg, ARRAYSIZE(msg), L"((IUnknown*)0x%p)->__vfptr[0]\n", site); + OutputDebugStringW(msg); + }); + } + */ + + template + void for_each_site(_In_opt_ IUnknown* siteInput, TLambda&& callback) + { + wil::com_ptr_nothrow site(siteInput); + while (site) + { + callback(site.get()); + auto objWithSite = site.try_query(); + site.reset(); + if (objWithSite) + { + objWithSite->GetSite(IID_PPV_ARGS(&site)); + } + } + } + +#endif // __IObjectWithSite_INTERFACE_DEFINED__ + +} // wil + +#endif diff --git a/Externals/WIL/include/wil/common.h b/Externals/WIL/include/wil/common.h new file mode 100644 index 0000000000..88dcfeadcd --- /dev/null +++ b/Externals/WIL/include/wil/common.h @@ -0,0 +1,747 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_COMMON_INCLUDED +#define __WIL_COMMON_INCLUDED + +#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL) +// This define indicates that the WIL usage is in a kernel mode context where +// a high degree of WIL functionality is desired. +// +// Use (sparingly) to change behavior based on whether WIL is being used in kernel +// mode or user mode. +#define WIL_KERNEL_MODE +#endif + +// Defining WIL_HIDE_DEPRECATED will hide everything deprecated. +// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at +// a particular point, allowing components to avoid backslide and catch up to the current independently. +#ifdef WIL_HIDE_DEPRECATED +#define WIL_HIDE_DEPRECATED_1809 +#endif +#ifdef WIL_HIDE_DEPRECATED_1809 +#define WIL_HIDE_DEPRECATED_1612 +#endif +#ifdef WIL_HIDE_DEPRECATED_1612 +#define WIL_HIDE_DEPRECATED_1611 +#endif + +// Implementation side note: ideally the deprecation would be done with the function-level declspec +// as it allows you to utter the error text when used. The declspec works, but doing it selectively with +// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation. +#ifdef WIL_WARN_DEPRECATED +#define WIL_WARN_DEPRECATED_1809 +#endif +#ifdef WIL_WARN_DEPRECATED_1809 +#define WIL_WARN_DEPRECATED_1612 +#endif +#ifdef WIL_WARN_DEPRECATED_1612 +#define WIL_WARN_DEPRECATED_1611 +#endif +#ifdef WIL_WARN_DEPRECATED_1809 +#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) +#endif +#ifdef WIL_WARN_DEPRECATED_1611 +#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) +#endif +#ifdef WIL_WARN_DEPRECATED_1612 +#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) +#endif + +#if defined(_MSVC_LANG) +#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245)) +#define __WI_SUPPRESS_4127_E __pragma(warning(pop)) +#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504)) +#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495)) +#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439)) +#else +#define __WI_SUPPRESS_4127_S +#define __WI_SUPPRESS_4127_E +#define __WI_SUPPRESS_NULLPTR_ANALYSIS +#define __WI_SUPPRESS_NONINIT_ANALYSIS +#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS +#endif + +#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL) + +#define WI_ODR_PRAGMA(NAME, TOKEN) +#define WI_NOEXCEPT + +#else +#pragma warning(push) +#pragma warning(disable:4714) // __forceinline not honored + +// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage +#include +#include "wistd_type_traits.h" + +//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code +#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN)) + +#ifdef WIL_KERNEL_MODE +WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1") +#else +WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0") +#endif + +// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can +// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to +// basic macros without the function for common use cases. +/// @cond +#define _Success_return_ _Success_(return) +#define _Success_true_ _Success_(true) +#define __declspec_noinline_ __declspec(noinline) +#define __declspec_selectany_ __declspec(selectany) +/// @endcond + +#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS) +/** This define is automatically set when exceptions are enabled within wil. +It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in +_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil +header. All exception-based WIL methods and classes are included behind: +~~~~ +#ifdef WIL_ENABLE_EXCEPTIONS +// code +#endif +~~~~ +This enables exception-free code to directly include WIL headers without worrying about exception-based +routines suddenly becoming available. */ +#define WIL_ENABLE_EXCEPTIONS +#endif +/// @endcond + +/// @cond +#if defined(WIL_EXCEPTION_MODE) +static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode"); +#elif !defined(WIL_LOCK_EXCEPTION_MODE) +#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0") +#elif defined(WIL_ENABLE_EXCEPTIONS) +#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1") +#else +#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2") +#endif + +#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS) +#error Must enable exceptions when WIL_EXCEPTION_MODE == 1 +#endif + +// block for documentation only +#if defined(WIL_DOXYGEN) +/** This define can be explicitly set to disable exception usage within wil. +Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking +at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based +classes and methods from WIL, define this macro ahead of including the first WIL header. */ +#define WIL_SUPPRESS_EXCEPTIONS + +/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS. +Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to +do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations +when linking libraries together with different exception handling semantics. */ +#define WIL_LOCK_EXCEPTION_MODE + +/** This define explicit sets the exception mode for the process to control optimizations. +Three exception modes are available: +0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that + use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR + violations when linking libraries together with different exception handling semantics. +1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled. +2) This locks the binary to libraries built without exceptions. */ +#define WIL_EXCEPTION_MODE +#endif + +#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703) +#define WIL_HAS_CXX_17 1 +#else +#define WIL_HAS_CXX_17 0 +#endif + +// Until we'll have C++17 enabled in our code base, we're falling back to SAL +#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE + +//! @defgroup macrobuilding Macro Composition +//! The following macros are building blocks primarily intended for authoring other macros. +//! @{ + +//! Re-state a macro value (indirection for composition) +#define WI_FLATTEN(...) __VA_ARGS__ + +/// @cond +#define __WI_PASTE_imp(a, b) a##b +/// @endcond + +//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro. +#define WI_PASTE(a, b) __WI_PASTE_imp(a, b) + +/// @cond +#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T +#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0) +/// @endcond + +//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0' +#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused) + +/// @cond +#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \ + A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \ + A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \ + A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count +#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \ + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__ +/// @endcond + +//! This variadic macro returns the number of arguments passed to it (up to 99). +#if WI_HAS_VA_OPT +#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__)) +#else +#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__)) +#endif + +/// @cond +#define __WI_FOR_imp0( fn) +#define __WI_FOR_imp1( fn, arg) fn(arg) +#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__)) +#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__)) +#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__)) +#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__)) +#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__)) +#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__)) +#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__)) +#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__)) +#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__)) +#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__)) +#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__)) +#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__)) +#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__)) +#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__)) +#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__)) +#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__)) +#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__)) +#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__)) +#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__)) +#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__)) +#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__)) +#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__)) +#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__)) +#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__)) +#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__)) +#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__)) +#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__)) +#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__)) +#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__)) +#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__)) +#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__)) +#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__)) +#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__)) +#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__)) +#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__)) +#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__)) +#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__)) +#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__)) +#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__)) +#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__)) +#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__)) +#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__)) +#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__)) +#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__)) +#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__)) +#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__)) +#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__)) +#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__)) +#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__)) +#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__)) +#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__)) +#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__)) +#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__)) +#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__)) +#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__)) +#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__)) +#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__)) +#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__)) +#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__)) +#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__)) +#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__)) +#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__)) +#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__)) +#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__)) +#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__)) +#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__)) +#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__)) +#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__)) +#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__)) +#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__)) +#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__)) +#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__)) +#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__)) +#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__)) +#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__)) +#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__)) +#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__)) +#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__)) +#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__)) +#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__)) +#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__)) +#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__)) +#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__)) +#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__)) +#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__)) +#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__)) +#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__)) +#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__)) +#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__)) +#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__)) +#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__)) +#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__)) +#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__)) +#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__)) +#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__)) +#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__)) +#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__)) +#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__)) + +#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs +/// @endcond + +//! Iterates through each of the given arguments invoking the specified macro against each one. +#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__)) + +//! Dispatches a single macro name to separate macros based on the number of arguments passed to it. +#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__)) + +//! @} // Macro composition helpers + +#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 +#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 + +//! @defgroup bitwise Bitwise Inspection and Manipulation +//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations. +//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist +//! for two primary purposes: +//! +//! 1. To improve the readability of bitwise comparisons and manipulation. +//! +//! The macro names are the more concise, readable form of what's being done and do not require that any flags +//! or variables be specified multiple times for the comparisons. +//! +//! 2. To reduce the error rate associated with bitwise operations. +//! +//! The readability improvements naturally lend themselves to this by cutting down the number of concepts. +//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison +//! operator and repetition in the flag value. +//! +//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag +//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect, +//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`. +//! +//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These +//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers +//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants. +//! +//! Common example usage (manipulation of flag variables): +//! ~~~~ +//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable +//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags +//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool +//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable +//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag +//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value +//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues +//! ~~~~ +//! Common example usage (inspection of flag variables): +//! ~~~~ +//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable? +//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set? +//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear? +//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable? +//! ~~~~ +//! @{ + +//! Returns the unsigned type of the same width and numeric value as the given enum +#define WI_EnumValue(val) static_cast<::wil::integral_from_enum>(val) +//! Validates that exactly ONE bit is set in compile-time constant `flag` +#define WI_StaticAssertSingleBitSet(flag) static_cast(::wil::details::verify_single_flag_helper(WI_EnumValue(flag))>::value) + +//! @name Bitwise manipulation macros +//! @{ + +//! Set zero or more bitflags specified by `flags` in the variable `var`. +#define WI_SetAllFlags(var, flags) ((var) |= (flags)) +//! Set a single compile-time constant `flag` in the variable `var`. +#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true. +#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0) + +//! Clear zero or more bitflags specified by `flags` from the variable `var`. +#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags)) +//! Clear a single compile-time constant `flag` from the variable `var`. +#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true. +#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0) + +//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false. +#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag)) +//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`. +#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags) + +//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`. +#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags)) +//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`. +#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! @} // bitwise manipulation macros + +//! @name Bitwise inspection macros +//! @{ + +//! Evaluates as true if every bitflag specified in `flags` is set within `val`. +#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags) +//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`. +#define WI_IsAnyFlagSet(val, flags) (static_cast(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast(0)) +//! Evaluates as true if a single compile-time constant `flag` is set within `val`. +#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag)) + +//! Evaluates as true if every bitflag specified in `flags` is clear within `val`. +#define WI_AreAllFlagsClear(val, flags) (static_cast(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast(0)) +//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`. +#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags)) +//! Evaluates as true if a single compile-time constant `flag` is clear within `val`. +#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag)) + +//! Evaluates as true if exactly one bit (any bit) is set within `val`. +#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val) +//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`. +#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask)) +//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`. +#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val) +//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`. +#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask)) +//! @} + +#if defined(WIL_DOXYGEN) +/** This macro provides a C++ header with a guaranteed initialization function. +Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw +the object away if it's unreferenced (which throws away the side-effects that the initialization function +was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the +provided function to elide that optimization. +//! +This functionality is primarily provided as a building block for header-based libraries (such as WIL) +to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models +of initialization should be used whenever they are available. +~~~~ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, [] +{ + g_pfnGetModuleName = GetCurrentModuleName; + g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout; + return 1; +}); +#endif +~~~~ +The above example is used within WIL to decide whether or not the library containing WIL is allowed to use +desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas +doing it with global function pointers and header initialization allows a runtime determination. */ +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) +#elif defined(_M_IX86) +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \ + extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast(fn()); } \ + __pragma(comment(linker, "/INCLUDE:_g_header_init_" #name)) +#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64) +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \ + extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast(fn()); } \ + __pragma(comment(linker, "/INCLUDE:g_header_init_" #name)) +#else + #error linker pragma must include g_header_init variation +#endif + + +/** All Windows Implementation Library classes and functions are located within the "wil" namespace. +The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference +the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using +statement for wil to avoid introducing potential name collisions between wil and other namespaces. */ +namespace wil +{ + /// @cond + namespace details + { + template + class pointer_range + { + public: + pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {} + T begin() const { return m_begin; } + T end() const { return m_end; } + private: + T m_begin; + T m_end; + }; + } + /// @endcond + + /** Enables using range-based for between a begin and end object pointer. + ~~~~ + for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { } + ~~~~ */ + template + details::pointer_range make_range(T begin, T end) + { + return details::pointer_range(begin, end); + } + + /** Enables using range-based for on a range when given the base pointer and the number of objects in the range. + ~~~~ + for (auto& obj : make_range(objPointer, objCount)) { } + ~~~~ */ + template + details::pointer_range make_range(T begin, size_t count) + { + return details::pointer_range(begin, begin + count); + } + + + //! @defgroup outparam Output Parameters + //! Improve the conciseness of assigning values to optional output parameters. + //! @{ + + /** Assign the given value to an optional output parameter. + Makes code more concise by removing trivial `if (outParam)` blocks. */ + template + inline void assign_to_opt_param(_Out_opt_ T *outParam, T val) + { + if (outParam != nullptr) + { + *outParam = val; + } + } + + /** Assign NULL to an optional output pointer parameter. + Makes code more concise by removing trivial `if (outParam)` blocks. */ + template + inline void assign_null_to_opt_param(_Out_opt_ T *outParam) + { + if (outParam != nullptr) + { + *outParam = nullptr; + } + } + //! @} // end output parameter helpers + + /** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation. + Example usage: + ~~~~ + template + struct FeatureRequiredBy + { + static const bool enabled = wil::variadic_logical_or::enabled...>::value; + }; + ~~~~ */ + template struct variadic_logical_or; + /// @cond + template <> struct variadic_logical_or<> : wistd::false_type { }; + template struct variadic_logical_or : wistd::true_type { }; + template struct variadic_logical_or : variadic_logical_or::type { }; + /// @endcond + + /// @cond + namespace details + { + template + struct verify_single_flag_helper + { + static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found"); + static const unsigned long long value = flag; + }; + } + /// @endcond + + + //! @defgroup typesafety Type Validation + //! Helpers to validate variable types to prevent accidental, but allowed type conversions. + //! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted + //! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type + //! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper + //! macros to validate the types given to various macro parameters. + //! @{ + + /** Verify that `val` can be evaluated as a logical bool. + Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL, + boolean, BOOLEAN, and classes with an explicit bool cast. + @param val The logical bool expression + @return A C++ bool representing the evaluation of `val`. */ + template + _Post_satisfies_(return == static_cast(val)) + __forceinline constexpr bool verify_bool(const T& val) + { + return static_cast(val); + } + + template + __forceinline constexpr bool verify_bool(T /*val*/) + { + static_assert(!wistd::is_same::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected"); + return false; + } + + template <> + _Post_satisfies_(return == val) + __forceinline constexpr bool verify_bool(bool val) + { + return val; + } + + template <> + _Post_satisfies_(return == (val != 0)) + __forceinline constexpr bool verify_bool(int val) + { + return (val != 0); + } + + template <> + _Post_satisfies_(return == !!val) + __forceinline constexpr bool verify_bool(unsigned char val) + { + return !!val; + } + + /** Verify that `val` is a Win32 BOOL value. + Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will + accept any `int` value as long as that is the underlying typedef behind `BOOL`. + @param val The Win32 BOOL returning expression + @return A Win32 BOOL representing the evaluation of `val`. */ + template + _Post_satisfies_(return == val) + __forceinline constexpr int verify_BOOL(T val) + { + // Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL; + static_assert((wistd::is_same::value), "Wrong Type: BOOL expected"); + return val; + } + + /** Verify that `hr` is an HRESULT value. + Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the + underlying typedef behind HRESULT. + //! + Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as: + ~~~~ + #define UIA_E_NOTSUPPORTED 0x80040204 + ~~~~ + Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When + these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change + their definition to match the manner in which `HRESULT` constants are defined in winerror.h: + ~~~~ + #define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L) + ~~~~ + When these are encountered in the public SDK, their type should not be changed and you should use a static_cast + to use this value in a macro that utilizes `verify_hresult`, for example: + ~~~~ + RETURN_HR_IF(static_cast(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId)); + ~~~~ + @param val The HRESULT returning expression + @return An HRESULT representing the evaluation of `val`. */ + template + _Post_satisfies_(return == hr) + inline constexpr long verify_hresult(T hr) + { + // Note: Written in terms of 'int' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT + static_assert(wistd::is_same::value, "Wrong Type: HRESULT expected"); + return hr; + } + /// @} // end type validation routines + + /// @cond + // Implementation details for macros and helper functions... do not use directly. + namespace details + { + // Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value + #define __WI_MAKE_UNSIGNED(val) \ + (__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast(val) : \ + sizeof(val) == 2 ? static_cast(val) : \ + sizeof(val) == 4 ? static_cast(val) : \ + static_cast(val)) __pragma(warning(pop))) + #define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1))) + #define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val)) + + template + __forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags) + { + return ((val & flags) == static_cast(flags)); + } + + template + __forceinline constexpr bool IsSingleFlagSetHelper(TVal val) + { + return __WI_IS_SINGLE_FLAG_SET(val); + } + + template + __forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val) + { + return ((val == static_cast>(0)) || IsSingleFlagSetHelper(val)); + } + + template + __forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags) + { + val = static_cast>((val & ~mask) | (flags & mask)); + } + + template + struct variable_size; + + template <> + struct variable_size<1> + { + typedef unsigned char type; + }; + + template <> + struct variable_size<2> + { + typedef unsigned short type; + }; + + template <> + struct variable_size<4> + { + typedef unsigned long type; + }; + + template <> + struct variable_size<8> + { + typedef unsigned long long type; + }; + + template + struct variable_size_mapping + { + typedef typename variable_size::type type; + }; + } // details + /// @endcond + + /** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type. + This allows code to generically convert any enum class to it's corresponding underlying type. */ + template + using integral_from_enum = typename details::variable_size_mapping::type; +} // wil + +#pragma warning(pop) + +#endif // __cplusplus +#endif // __WIL_COMMON_INCLUDED diff --git a/Externals/WIL/include/wil/cppwinrt.h b/Externals/WIL/include/wil/cppwinrt.h new file mode 100644 index 0000000000..c7da612fec --- /dev/null +++ b/Externals/WIL/include/wil/cppwinrt.h @@ -0,0 +1,251 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_CPPWINRT_INCLUDED +#define __WIL_CPPWINRT_INCLUDED + +#include "common.h" +#include +#include +#include + +// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to +// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to +// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below - +// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global +// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and +// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined. + +/// @cond +namespace wil::details +{ + // Since the C++/WinRT version macro is a string... + inline constexpr int major_version_from_string(const char* versionString) + { + int result = 0; + auto str = versionString; + while ((*str >= '0') && (*str <= '9')) + { + result = result * 10 + (*str - '0'); + ++str; + } + + return result; + } +} +/// @endcond + +#ifdef CPPWINRT_VERSION +// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of +// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer +// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0 +static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, + "Please include wil/cppwinrt.h before including any C++/WinRT headers"); +#endif + +// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed +#ifdef WINRT_EXTERNAL_CATCH_CLAUSE +#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1 +#else +#define WINRT_EXTERNAL_CATCH_CLAUSE \ + catch (const wil::ResultException& e) \ + { \ + return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \ + } +#endif + +#include "result_macros.h" +#include + +#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE +static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, + "C++/WinRT external catch clause already defined outside of WIL"); +#endif + +// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid +// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to +// use it unless the version of C++/WinRT is high enough +extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; + +/// @cond +namespace wil::details +{ + inline void MaybeGetExceptionString( + const winrt::hresult_error& exception, + _Out_writes_opt_(debugStringChars) PWSTR debugString, + _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str()); + } + } + + inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( + _Inout_updates_opt_(debugStringChars) PWSTR debugString, + _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, + _Inout_ bool* isNormalized) noexcept + { + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const winrt::hresult_error& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.code().value; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (const std::out_of_range& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_BOUNDS; + } + catch (const std::invalid_argument& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_INVALIDARG; + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const winrt::hresult_error& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.code().value; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (const std::out_of_range& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_BOUNDS; + } + catch (const std::invalid_argument& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_INVALIDARG; + } + catch (const std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return S_OK; + } +} +/// @endcond + +namespace wil +{ + inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept + { + // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't + // have accurate file/line/etc. information + return static_cast(details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress))); + } + + inline void WilInitialize_CppWinRT() + { + details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt; + if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2) + { + WI_ASSERT(winrt_to_hresult_handler == nullptr); + winrt_to_hresult_handler = winrt_to_hresult; + } + } + + /// @cond + namespace details + { +#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS + WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0") + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, [] + { + ::wil::WilInitialize_CppWinRT(); + return 1; + }); +#else + WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") +#endif + } + /// @endcond + + // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. + inline long verify_hresult(winrt::hresult hr) noexcept + { + return hr; + } + + // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. + template + auto get_abi(T const& object) noexcept + { + return winrt::get_abi(object); + } + + inline auto get_abi(winrt::hstring const& object) noexcept + { + return static_cast(winrt::get_abi(object)); + } + + template + auto put_abi(T& object) noexcept + { + return winrt::put_abi(object); + } + + inline auto put_abi(winrt::hstring& object) noexcept + { + return reinterpret_cast(winrt::put_abi(object)); + } +} + +#endif // __WIL_CPPWINRT_INCLUDED diff --git a/Externals/WIL/include/wil/filesystem.h b/Externals/WIL/include/wil/filesystem.h new file mode 100644 index 0000000000..b74cd43086 --- /dev/null +++ b/Externals/WIL/include/wil/filesystem.h @@ -0,0 +1,908 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_FILESYSTEM_INCLUDED +#define __WIL_FILESYSTEM_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include +#include // Needed for CoTaskMemFree() used in output of some helpers. +#include // LocalAlloc +#include +#include "result.h" +#include "win32_helpers.h" +#include "resource.h" + +namespace wil +{ + //! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH. + inline bool is_extended_length_path(_In_ PCWSTR path) + { + return wcsncmp(path, L"\\\\?\\", 4) == 0; + } + + //! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW() + //! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature? + inline PCWSTR find_last_path_segment(_In_ PCWSTR path) + { + auto const pathLength = wcslen(path); + // If there is a trailing slash ignore that in the search. + auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength; + + PCWSTR result; + auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast(limitedLength), L"\\", 1, TRUE); + if (offset == -1) + { + result = path + pathLength; // null terminator + } + else + { + result = path + offset + 1; // just past the slash + } + return result; + } + + //! Determine if the file name is one of the special "." or ".." names. + inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName) + { + return ((fileName[0] == L'.') && + ((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0')))); + } + + //! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths. + inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber) + { + if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\') + { + path += 4; + } + if (path[0] && (path[1] == L':')) + { + if ((path[0] >= L'a') && (path[0] <= L'z')) + { + *driveNumber = path[0] - L'a'; + return true; + } + else if ((path[0] >= L'A') && (path[0] <= L'Z')) + { + *driveNumber = path[0] - L'A'; + return true; + } + } + *driveNumber = -1; + return false; + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + + // PathCch.h APIs are only in desktop API for now. + + // Compute the substring in the input value that is the parent folder path. + // returns: + // true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length. + // false, no parent path, the input is a root path. + inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength) + { + *parentPathLength = 0; + bool hasParent = false; + PCWSTR rootEnd; + if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0')) + { + auto const lastSegment = find_last_path_segment(path); + *parentPathLength = lastSegment - path; + hasParent = (*parentPathLength != 0); + } + return hasParent; + } + + // Creates directories for the specified path, creating parent paths + // as needed. + inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT + { + if (::CreateDirectoryW(path, nullptr) == FALSE) + { + DWORD const lastError = ::GetLastError(); + if (lastError == ERROR_PATH_NOT_FOUND) + { + size_t parentLength; + if (try_get_parent_path_range(path, &parentLength)) + { + wistd::unique_ptr parent(new (std::nothrow) wchar_t[parentLength + 1]); + RETURN_IF_NULL_ALLOC(parent.get()); + RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength)); + CreateDirectoryDeepNoThrow(parent.get()); // recurs + } + RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr)); + } + else if (lastError != ERROR_ALREADY_EXISTS) + { + RETURN_WIN32(lastError); + } + } + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline void CreateDirectoryDeep(PCWSTR path) + { + THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path)); + } +#endif // WIL_ENABLE_EXCEPTIONS + + //! A strongly typed version of the Win32 API GetFullPathNameW. + //! Return a path in an allocated buffer for handling long paths. + //! Optionally return the pointer to the file name part. + template + HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr) + { + wil::assign_null_to_opt_param(filePart); + const auto hr = AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + // Note that GetFullPathNameW() is not limited to MAX_PATH + // but it does take a fixed size buffer. + *valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast(valueLength), value, nullptr); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + if (SUCCEEDED(hr) && filePart) + { + *filePart = wil::find_last_path_segment(details::string_maker::get(path)); + } + return hr; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A strongly typed version of the Win32 API of GetFullPathNameW. + //! Return a path in an allocated buffer for handling long paths. + //! Optionally return the pointer to the file name part. + template + string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr) + { + string_type result; + THROW_IF_FAILED((GetFullPathNameW(file, result, filePart))); + return result; + } +#endif + + enum class RemoveDirectoryOptions + { + None = 0, + KeepRootDirectory = 0x1 + }; + DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions); + + // If inputPath is a non-normalized name be sure to pass an extended length form to ensure + // it can be addressed and deleted. + inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT + { + wil::unique_hlocal_string path; + PATHCCH_OPTIONS combineOptions = PATHCCH_NONE; + + if (is_extended_length_path(inputPath)) + { + path = wil::make_hlocal_string_nothrow(inputPath); + RETURN_IF_NULL_ALLOC(path); + // PathAllocCombine will convert extended length paths to regular paths if shorter than + // MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names. + combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH; + } + else + { + // For regular paths normalize here to get consistent results when searching and deleting. + RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path)); + combineOptions = PATHCCH_ALLOW_LONG_PATHS; + } + + wil::unique_hlocal_string searchPath; + RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath)); + + WIN32_FIND_DATAW fd; + wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd)); + RETURN_LAST_ERROR_IF(!findHandle); + + for (;;) + { + // skip "." and ".." + if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName))) + { + // Need to form an extended length path to provide the ability to delete paths > MAX_PATH + // and files with non-normalized names (dots or spaces at the end). + wil::unique_hlocal_string pathToDelete; + RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName, + PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete)); + if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) + { + RemoveDirectoryOptions localOptions = options; + RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory))); + } + else + { + // note: if pathToDelete is read-only this will fail, consider adding + // RemoveDirectoryOptions::RemoveReadOnly to enable this behavior. + RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get())); + } + } + + if (!::FindNextFileW(findHandle.get(), &fd)) + { + auto const err = ::GetLastError(); + if (err == ERROR_NO_MORE_FILES) + { + break; + } + RETURN_WIN32(err); + } + } + + if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory)) + { + RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get())); + } + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) + { + THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options)); + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing + // a result buffer that contains data. This is used in the following FileIO calls: + // FileStreamInfo, FILE_STREAM_INFO + // FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO + // FileFullDirectoryInfo, FILE_FULL_DIR_INFO + // FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO + // ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION + + template + struct next_entry_offset_iterator + { + // Fulfill std::iterator_traits requirements + using difference_type = ptrdiff_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; +#ifdef _XUTILITY_ + using iterator_category = ::std::forward_iterator_tag; +#endif + + next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {} + + // range based for requires operator!=, operator++ and operator* to do its work + // on the type returned from begin() and end(), provide those here. + bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; } + + next_entry_offset_iterator& operator++() + { + current_ = (current_->NextEntryOffset != 0) ? + reinterpret_cast(reinterpret_cast(current_) + current_->NextEntryOffset) : + __nullptr; + return *this; + } + + next_entry_offset_iterator operator++(int) + { + auto copy = *this; + ++(*this); + return copy; + } + + reference operator*() const WI_NOEXCEPT { return *current_; } + pointer operator->() const WI_NOEXCEPT { return current_; } + + next_entry_offset_iterator begin() { return *this; } + next_entry_offset_iterator end() { return next_entry_offset_iterator(); } + + T* current_; + }; + + template + next_entry_offset_iterator create_next_entry_offset_iterator(T* p) + { + return next_entry_offset_iterator(p); + } + +#pragma region Folder Watcher + // Example use in exception based code: + // auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []() + // { + // // respond + // }); + // + // Example use in result code based code: + // wil::unique_folder_watcher watcher; + // THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []() + // { + // // respond + // })); + + enum class FolderChangeEvent : DWORD + { + ChangesLost = 0, // requies special handling, reset state as events were lost + Added = FILE_ACTION_ADDED, + Removed = FILE_ACTION_REMOVED, + Modified = FILE_ACTION_MODIFIED, + RenameOldName = FILE_ACTION_RENAMED_OLD_NAME, + RenameNewName = FILE_ACTION_RENAMED_NEW_NAME, + }; + + enum class FolderChangeEvents : DWORD + { + None = 0, + FileName = FILE_NOTIFY_CHANGE_FILE_NAME, + DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME, + Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES, + FileSize = FILE_NOTIFY_CHANGE_SIZE, + LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE, + Security = FILE_NOTIFY_CHANGE_SECURITY, + All = FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_SECURITY + }; + DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents); + + /// @cond + namespace details + { + struct folder_watcher_state + { + folder_watcher_state(wistd::function &&callback) : m_callback(wistd::move(callback)) + { + } + wistd::function m_callback; + // Order is important, need to close the thread pool wait before the change handle. + unique_hfind_change m_findChangeHandle; + unique_threadpool_wait m_threadPoolWait; + }; + + inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; } + + typedef resource_policy folder_watcher_state_resource_policy; + } + /// @endcond + + template + class folder_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(folderToWatch, isRecursive, filter, wistd::move(callback)); + } + + result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback))); + } + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/) + { + auto watcherState = static_cast(context); + watcherState->m_callback(); + + // Rearm the wait. Should not fail with valid parameters. + FindNextChangeNotification(watcherState->m_findChangeHandle.get()); + SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr); + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + wistd::unique_ptr watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + + watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast(filter))); + RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + this->reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_folder_watcher_nothrow; + + inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) WI_NOEXCEPT + { + unique_folder_watcher_nothrow watcher; + watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_folder_watcher; + + inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS + +#pragma endregion + +#pragma region Folder Reader + + // Example use for throwing: + // auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All, + // [](wil::FolderChangeEvent event, PCWSTR fileName) + // { + // switch (event) + // { + // case wil::FolderChangeEvent::ChangesLost: break; + // case wil::FolderChangeEvent::Added: break; + // case wil::FolderChangeEvent::Removed: break; + // case wil::FolderChangeEvent::Modified: break; + // case wil::FolderChangeEvent::RenamedOldName: break; + // case wil::FolderChangeEvent::RenamedNewName: break; + // }); + // + // Example use for non throwing: + // wil::unique_folder_change_reader_nothrow reader; + // THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All, + // [](wil::FolderChangeEvent event, PCWSTR fileName) + // { + // // handle changes + // })); + // + + // @cond + namespace details + { + struct folder_change_reader_state + { + folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter) + { + } + + ~folder_change_reader_state() + { + if (m_tpIo != __nullptr) + { + TP_IO *tpIo = m_tpIo; + + // Indicate to the callback function that this object is being torn + // down. + + { + auto autoLock = m_cancelLock.lock_exclusive(); + m_tpIo = __nullptr; + } + + // Cancel IO to terminate the file system monitoring operation. + + if (m_folderHandle) + { + CancelIoEx(m_folderHandle.get(), &m_overlapped); + } + + // Wait for callbacks to complete. + // + // N.B. This is a blocking call and must not be made within a + // callback or within a lock which is taken inside the + // callback. + + WaitForThreadpoolIoCallbacks(tpIo, TRUE); + CloseThreadpoolIo(tpIo); + } + } + + HRESULT StartIo() + { + // Unfortunately we have to handle ref-counting of IOs on behalf of the + // thread pool. + StartThreadpoolIo(m_tpIo); + HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer), + m_isRecursive, static_cast(m_filter), __nullptr, &m_overlapped, __nullptr) ? + S_OK : HRESULT_FROM_WIN32(::GetLastError()); + if (FAILED(hr)) + { + // This operation does not have the usual semantic of returning + // ERROR_IO_PENDING. + // WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING)); + + // If the operation failed for whatever reason, ensure the TP + // ref counts are accurate. + + CancelThreadpoolIo(m_tpIo); + } + return hr; + } + + // void (wil::FolderChangeEvent event, PCWSTR fileName) + wistd::function m_callback; + unique_handle m_folderHandle; + BOOL m_isRecursive = FALSE; + FolderChangeEvents m_filter = FolderChangeEvents::None; + OVERLAPPED m_overlapped{}; + TP_IO *m_tpIo = __nullptr; + srwlock m_cancelLock; + char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow. + }; + + inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; } + + typedef resource_policy folder_change_reader_state_resource_policy; + } + /// @endcond + + template + class folder_change_reader_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(folderToWatch, isRecursive, filter, wistd::move(callback)); + } + + result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback))); + } + + wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; } + + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/, + ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */) + { + auto readerState = static_cast(context); + // WI_ASSERT(overlapped == &readerState->m_overlapped); + + bool requeue = true; + if (result == ERROR_SUCCESS) + { + for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast(readerState->m_readBuffer))) + { + wchar_t realtiveFileName[MAX_PATH]; + StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0])); + + readerState->m_callback(static_cast(info.Action), realtiveFileName); + } + } + else if (result == ERROR_NOTIFY_ENUM_DIR) + { + readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr); + } + else + { + requeue = false; + } + + if (requeue) + { + // If the lock is held non-shared or the TP IO is nullptr, this + // structure is being torn down. Otherwise, monitor for further + // changes. + auto autoLock = readerState->m_cancelLock.try_lock_shared(); + if (autoLock && readerState->m_tpIo) + { + readerState->StartIo(); // ignoring failure here + } + } + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + wistd::unique_ptr readerState(new(std::nothrow) details::folder_change_reader_state( + isRecursive, filter, wistd::move(callback))); + RETURN_IF_NULL_ALLOC(readerState); + + readerState->m_folderHandle.reset(CreateFileW(folderToWatch, + FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, + __nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr)); + RETURN_LAST_ERROR_IF(!readerState->m_folderHandle); + + readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr); + RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo); + RETURN_IF_FAILED(readerState->StartIo()); + this->reset(readerState.release()); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_folder_change_reader_nothrow; + + inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, + wistd::function &&callback) WI_NOEXCEPT + { + unique_folder_change_reader_nothrow watcher; + watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_folder_change_reader; + + inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, + wistd::function &&callback) + { + return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS +#pragma endregion + + //! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix. + enum class VolumePrefix + { + Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp + VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp + // The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios. + None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp + NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp + }; + enum class PathOptions + { + Normalized = FILE_NAME_NORMALIZED, + Opened = FILE_NAME_OPENED, + }; + DEFINE_ENUM_FLAG_OPERATORS(PathOptions); + + /** A strongly typed version of the Win32 API GetFinalPathNameByHandleW. + Get the full path name in different forms + Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to + get that path form. */ + template + HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path, + wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized) + { + return AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + *valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast(valueLength), + static_cast(volumePrefix) | static_cast(options)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** A strongly typed version of the Win32 API GetFinalPathNameByHandleW. + Get the full path name in different forms. Use this + VolumePrefix::None + instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */ + template + string_type GetFinalPathNameByHandleW(HANDLE fileHandle, + wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized) + { + string_type result; + THROW_IF_FAILED((GetFinalPathNameByHandleW(fileHandle, result, volumePrefix, options))); + return result; + } +#endif + + //! A strongly typed version of the Win32 API of GetCurrentDirectoryW. + //! Return a path in an allocated buffer for handling long paths. + template + HRESULT GetCurrentDirectoryW(string_type& path) + { + return AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + *valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast(valueLength), value); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A strongly typed version of the Win32 API of GetCurrentDirectoryW. + //! Return a path in an allocated buffer for handling long paths. + template + string_type GetCurrentDirectoryW() + { + string_type result; + THROW_IF_FAILED((GetCurrentDirectoryW(result))); + return result; + } +#endif + + // TODO: add support for these and other similar APIs. + // GetShortPathNameW() + // GetLongPathNameW() + // GetWindowsDirectory() + // GetTempDirectory() + + /// @cond + namespace details + { + template struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler +#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \ + template <> struct MapInfoClassToInfoStruct \ + { \ + typedef InfoStruct type; \ + static bool const isFixed = IsFixed; \ + static size_t const extraSize = Extra; \ + }; + + MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0); +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0); +#endif + + // Type unsafe version used in the implementation to avoid template bloat. + inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize, + _Outptr_result_nullonfailure_ void **result) + { + *result = nullptr; + + wistd::unique_ptr resultHolder(new(std::nothrow) char[allocationSize]); + RETURN_IF_NULL_ALLOC(resultHolder); + + for (;;) + { + if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast(allocationSize))) + { + *result = resultHolder.release(); + break; + } + else + { + DWORD const lastError = ::GetLastError(); + if (lastError == ERROR_MORE_DATA) + { + allocationSize *= 2; + resultHolder.reset(new(std::nothrow) char[allocationSize]); + RETURN_IF_NULL_ALLOC(resultHolder); + } + else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases + { + break; + } + else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system + { + return HRESULT_FROM_WIN32(lastError); + } + else + { + RETURN_WIN32(lastError); + } + } + } + return S_OK; + } + } + /// @endcond + + /** Get file information for a variable sized structure, returns an HRESULT. + ~~~ + wistd::unique_ptr fileNameInfo; + RETURN_IF_FAILED(GetFileInfoNoThrow(fileHandle, fileNameInfo)); + ~~~ + */ + template ::isFixed, int>::type = 0> + HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr::type> &result) WI_NOEXCEPT + { + void *rawResult; + HRESULT hr = details::GetFileInfo(fileHandle, infoClass, + sizeof(typename details::MapInfoClassToInfoStruct::type) + details::MapInfoClassToInfoStruct::extraSize, + &rawResult); + result.reset(static_cast::type*>(rawResult)); + RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system + RETURN_IF_FAILED(hr); + return S_OK; + } + + /** Get file information for a fixed sized structure, returns an HRESULT. + ~~~ + FILE_BASIC_INFO fileBasicInfo; + RETURN_IF_FAILED(GetFileInfoNoThrow(fileHandle, &fileBasicInfo)); + ~~~ + */ + template ::isFixed, int>::type = 0> + HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct::type *result) WI_NOEXCEPT + { + const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ? + S_OK : HRESULT_FROM_WIN32(::GetLastError()); + RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system + RETURN_IF_FAILED(hr); + return S_OK; + } + +#ifdef _CPPUNWIND + /** Get file information for a fixed sized structure, throws on failure. + ~~~ + auto fileBasicInfo = GetFileInfo(fileHandle); + ~~~ + */ + template ::isFixed, int>::type = 0> + typename details::MapInfoClassToInfoStruct::type GetFileInfo(HANDLE fileHandle) + { + typename details::MapInfoClassToInfoStruct::type result; + THROW_IF_FAILED(GetFileInfoNoThrow(fileHandle, &result)); + return result; + } + + /** Get file information for a variable sized structure, throws on failure. + ~~~ + auto fileBasicInfo = GetFileInfo(fileHandle); + ~~~ + */ + template ::isFixed, int>::type = 0> + wistd::unique_ptr::type> GetFileInfo(HANDLE fileHandle) + { + wistd::unique_ptr::type> result; + THROW_IF_FAILED(GetFileInfoNoThrow(fileHandle, result)); + return result; + } +#endif // _CPPUNWIND +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +} + +#endif // __WIL_FILESYSTEM_INCLUDED diff --git a/Externals/WIL/include/wil/registry.h b/Externals/WIL/include/wil/registry.h new file mode 100644 index 0000000000..ffb44e2821 --- /dev/null +++ b/Externals/WIL/include/wil/registry.h @@ -0,0 +1,277 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_REGISTRY_INCLUDED +#define __WIL_REGISTRY_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include +#include // new(std::nothrow) +#include "resource.h" // unique_hkey + +namespace wil +{ + //! The key name includes the absolute path of the key in the registry, always starting at a + //! base key, for example, HKEY_LOCAL_MACHINE. + size_t const max_registry_key_name_length = 255; + + //! The maximum number of characters allowed in a registry value's name. + size_t const max_registry_value_name_length = 16383; + + // unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast + // These classes make it easy to execute a provided function when a + // registry key changes (optionally recursively). Specify the key + // either as a root key + path, or an open registry handle as wil::unique_hkey + // or a raw HKEY value (that will be duplicated). + // + // Example use with exceptions base error handling: + // auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[] + // { + // if (changeKind == RegistryChangeKind::Delete) + // { + // watcher.reset(); + // } + // // invalidate cached registry data here + // }); + // + // Example use with error code base error handling: + // auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[] + // { + // // invalidate cached registry data here + // }); + // RETURN_IF_NULL_ALLOC(watcher); + + enum class RegistryChangeKind + { + Modify = 0, + Delete = 1, + }; + + /// @cond + namespace details + { + struct registry_watcher_state + { + registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive) + { + } + wistd::function m_callback; + unique_hkey m_keyToWatch; + unique_event_nothrow m_eventHandle; + + // While not strictly needed since this is ref counted the thread pool wait + // should be last to ensure that the other members are valid + // when it is destructed as it will reference them. + unique_threadpool_wait m_threadPoolWait; + bool m_isRecursive; + + volatile long m_refCount = 1; + srwlock m_lock; + + // Returns true if the refcount can be increased from a non zero value, + // false it was zero impling that the object is in or on the way to the destructor. + // In this case ReleaseFromCallback() should not be called. + bool TryAddRef() + { + return ::InterlockedIncrement(&m_refCount) > 1; + } + + void Release() + { + auto lock = m_lock.lock_exclusive(); + if (0 == ::InterlockedDecrement(&m_refCount)) + { + lock.reset(); // leave the lock before deleting it. + delete this; + } + } + + void ReleaseFromCallback(bool rearm) + { + auto lock = m_lock.lock_exclusive(); + if (0 == ::InterlockedDecrement(&m_refCount)) + { + // Destroy the thread pool wait now to avoid the wait that would occur in the + // destructor. That wait would cause a deadlock since we are doing this from the callback. + ::CloseThreadpoolWait(m_threadPoolWait.release()); + lock.reset(); // leave the lock before deleting it. + delete this; + // Sleep(1); // Enable for testing to find use after free bugs. + } + else if (rearm) + { + ::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr); + } + } + }; + + inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); } + + typedef resource_policy registry_watcher_state_resource_policy; + } + /// @endcond + + template + class registry_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } + + // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch. + result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + // Most use will want to create the key, consider adding an option for open as a future design change. + unique_hkey keyToWatch; + HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr)); + if (FAILED(hr)) + { + return err_policy::HResult(hr); + } + return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + } + + result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + } + + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT) + { +#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST +#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST +#endif + __WIL_REGISTRY_CHANGE_CALLBACK_TEST + auto watcherState = static_cast(context); + if (watcherState->TryAddRef()) + { + // using auto reset event so don't need to manually reset. + + // failure here is a programming error. + const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive, + REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, + watcherState->m_eventHandle.get(), TRUE); + + // Call the client before re-arming to ensure that multiple callbacks don't + // run concurrently. + switch (error) + { + case ERROR_SUCCESS: + case ERROR_ACCESS_DENIED: + // Normal modification: send RegistryChangeKind::Modify and re-arm. + watcherState->m_callback(RegistryChangeKind::Modify); + watcherState->ReleaseFromCallback(true); + break; + + case ERROR_KEY_DELETED: + // Key deleted, send RegistryChangeKind::Delete, do not re-arm. + watcherState->m_callback(RegistryChangeKind::Delete); + watcherState->ReleaseFromCallback(false); + break; + + case ERROR_HANDLE_REVOKED: + // Handle revoked. This can occur if the user session ends before + // the watcher shuts-down. Disarm silently since there is generally no way to respond. + watcherState->ReleaseFromCallback(false); + break; + + default: + FAIL_FAST_HR(HRESULT_FROM_WIN32(error)); + } + } + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + wistd::unique_ptr watcherState(new(std::nothrow) details::registry_watcher_state( + wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + RETURN_IF_FAILED(watcherState->m_eventHandle.create()); + RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), + watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, + watcherState->m_eventHandle.get(), TRUE)); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(®istry_watcher_t::callback, watcherState.get(), nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + storage_t::reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_registry_watcher_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_registry_watcher_failfast; + + inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT + { + unique_registry_watcher_nothrow watcher; + watcher.create(rootKey, subKey, isRecursive, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT + { + unique_registry_watcher_nothrow watcher; + watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy >> unique_registry_watcher; + + inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS +} // namespace wil + +#endif diff --git a/Externals/WIL/include/wil/resource.h b/Externals/WIL/include/wil/resource.h new file mode 100644 index 0000000000..a0e5741b09 --- /dev/null +++ b/Externals/WIL/include/wil/resource.h @@ -0,0 +1,5984 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* + +#include "result_macros.h" +#include "wistd_functional.h" +#include "wistd_memory.h" + +#pragma warning(push) +#pragma warning(disable:26135 26110) // Missing locking annotation, Caller failing to hold lock +#pragma warning(disable:4714) // __forceinline not honored + +#ifndef __WIL_RESOURCE +#define __WIL_RESOURCE + +// stdint.h and intsafe.h have conflicting definitions, so it's not safe to include either to pick up our dependencies, +// so the definitions we need are copied below +#ifdef _WIN64 +#define __WI_SIZE_MAX 0xffffffffffffffffui64 // UINT64_MAX +#else /* _WIN64 */ +#define __WI_SIZE_MAX 0xffffffffui32 // UINT32_MAX +#endif /* _WIN64 */ + +// Forward declaration +/// @cond +namespace Microsoft +{ + namespace WRL + { + template + class ComPtr; + } +} +/// @endcond + +namespace wil +{ + //! This type copies the current value of GetLastError at construction and resets the last error + //! to that value when it is destroyed. + //! + //! This is useful in library code that runs during a value's destructor. If the library code could + //! inadvertantly change the value of GetLastError (by calling a Win32 API or similar), it should + //! instantiate a value of this type before calling the library function in order to preserve the + //! GetLastError value the user would expect. + //! + //! This construct exists to hide kernel mode/user mode differences in wil library code. + //! + //! Example usage: + //! + //! if (!CreateFile(...)) + //! { + //! auto lastError = wil::last_error_context(); + //! WriteFile(g_hlog, logdata); + //! } + //! + class last_error_context + { +#ifndef WIL_KERNEL_MODE + bool m_dismissed; + DWORD m_error; + public: + last_error_context() WI_NOEXCEPT : + m_dismissed(false), + m_error(::GetLastError()) + { + } + + last_error_context(last_error_context&& other) WI_NOEXCEPT + { + operator=(wistd::move(other)); + } + + last_error_context & operator=(last_error_context&& other) WI_NOEXCEPT + { + m_dismissed = wistd::exchange(other.m_dismissed, true); + m_error = other.m_error; + + return *this; + } + + ~last_error_context() WI_NOEXCEPT + { + if (!m_dismissed) + { + ::SetLastError(m_error); + } + } + + //! last_error_context doesn't own a concrete resource, so therefore + //! it just disarms its destructor and returns void. + void release() WI_NOEXCEPT + { + WI_ASSERT(!m_dismissed); + m_dismissed = true; + } +#else + public: + void release() WI_NOEXCEPT { } +#endif // WIL_KERNEL_MODE + }; + + /// @cond + namespace details + { + typedef wistd::integral_constant pointer_access_all; // get(), release(), addressof(), and '&' are available + typedef wistd::integral_constant pointer_access_noaddress; // get() and release() are available + typedef wistd::integral_constant pointer_access_none; // the raw pointer is not available + + template // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer + struct resource_policy + { + typedef pointer_storage pointer_storage; + typedef pointer pointer; + typedef pointer_invalid pointer_invalid; + typedef pointer_access pointer_access; + __forceinline static pointer_storage invalid_value() WI_NOEXCEPT { return (pointer)invalid; } + __forceinline static bool is_valid(pointer_storage value) WI_NOEXCEPT { return (static_cast(value) != (pointer)invalid); } + __forceinline static void close(pointer_storage value) WI_NOEXCEPT { wistd::invoke(close_fn, value); } + + inline static void close_reset(pointer_storage value) WI_NOEXCEPT + { + auto preserveError = last_error_context(); + wistd::invoke(close_fn, value); + } + }; + + + // This class provides the pointer storage behind the implementation of unique_any_t utilizing the given + // resource_policy. It is separate from unique_any_t to allow a type-specific specialization class to plug + // into the inheritance chain between unique_any_t and unique_storage. This allows classes like unique_event + // to be a unique_any formed class, but also expose methods like SetEvent directly. + + template + class unique_storage + { + protected: + typedef policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef unique_storage base_storage; + + unique_storage() WI_NOEXCEPT : + m_ptr(policy::invalid_value()) + { + } + + explicit unique_storage(pointer_storage ptr) WI_NOEXCEPT : + m_ptr(ptr) + { + } + + unique_storage(unique_storage &&other) WI_NOEXCEPT : + m_ptr(wistd::move(other.m_ptr)) + { + other.m_ptr = policy::invalid_value(); + } + + ~unique_storage() WI_NOEXCEPT + { + if (policy::is_valid(m_ptr)) + { + policy::close(m_ptr); + } + } + + void replace(unique_storage &&other) WI_NOEXCEPT + { + reset(other.m_ptr); + other.m_ptr = policy::invalid_value(); + } + + public: + bool is_valid() const WI_NOEXCEPT + { + return policy::is_valid(m_ptr); + } + + void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + { + if (policy::is_valid(m_ptr)) + { + policy::close_reset(m_ptr); + } + m_ptr = ptr; + } + + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value"); + reset(); + } + + pointer get() const WI_NOEXCEPT + { + return static_cast(m_ptr); + } + + pointer_storage release() WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "release(): the raw handle value is not available for this resource class"); + auto ptr = m_ptr; + m_ptr = policy::invalid_value(); + return ptr; + } + + pointer_storage *addressof() WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "addressof(): the address of the raw handle is not available for this resource class"); + return &m_ptr; + } + + private: + pointer_storage m_ptr; + }; + } // details + /// @endcond + + + // This class when paired with unique_storage and an optional type-specific specialization class implements + // the same interface as STL's unique_ptr<> for resource handle types. It is a non-copyable, yet movable class + // supporting attach (reset), detach (release), retrieval (get()). + + template + class unique_any_t : public storage_t + { + public: + typedef typename storage_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + + unique_any_t(unique_any_t const &) = delete; + unique_any_t& operator=(unique_any_t const &) = delete; + + // Note that the default constructor really shouldn't be needed (taken care of by the forwarding constructor below), but + // the forwarding constructor causes an internal compiler error when the class is used in a C++ array. Defining the default + // constructor independent of the forwarding constructor removes the compiler limitation. + unique_any_t() = default; + + // forwarding constructor: forwards all 'explicit' and multi-arg constructors to the base class + template + explicit unique_any_t(arg1 && first, args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + storage_t(wistd::forward(first), wistd::forward(args)...) + { + static_assert(wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value, "pointer_access policy must be a known pointer_access* integral type"); + } + + unique_any_t(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value"); + } + + unique_any_t(unique_any_t &&other) WI_NOEXCEPT : + storage_t(wistd::move(other)) + { + } + + unique_any_t& operator=(unique_any_t &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + // cast to base_storage to 'skip' calling the (optional) specialization class that provides handle-specific functionality + storage_t::replace(wistd::move(static_cast(other))); + } + return (*this); + } + + unique_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value"); + storage_t::reset(); + return (*this); + } + + void swap(unique_any_t &other) WI_NOEXCEPT + { + unique_any_t self(wistd::move(*this)); + operator=(wistd::move(other)); + other = wistd::move(self); + } + + explicit operator bool() const WI_NOEXCEPT + { + return storage_t::is_valid(); + } + + //! ~~~~ + //! BOOL OpenOrCreateWaffle(PCWSTR name, HWAFFLE* handle); + //! wil::unique_any waffle; + //! RETURN_IF_WIN32_BOOL_FALSE(OpenOrCreateWaffle(L"tasty.yum", waffle.put())); + //! ~~~~ + pointer_storage *put() WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "operator & is not available for this handle"); + storage_t::reset(); + return storage_t::addressof(); + } + + pointer_storage *operator&() WI_NOEXCEPT + { + return put(); + } + + pointer get() const WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "get(): the raw handle value is not available for this resource class"); + return storage_t::get(); + } + + // The following functions are publicly exposed by their inclusion in the unique_storage base class + + // explicit unique_any_t(pointer_storage ptr) WI_NOEXCEPT + // void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + // void reset(wistd::nullptr_t) WI_NOEXCEPT + // pointer_storage release() WI_NOEXCEPT // not exposed for some resource types + // pointer_storage *addressof() WI_NOEXCEPT // not exposed for some resource types + }; + + template + void swap(unique_any_t& left, unique_any_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + bool operator==(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (left.get() == right.get()); + } + + template + bool operator==(const unique_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !left; + } + + template + bool operator==(wistd::nullptr_t, const unique_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !right; + } + + template + bool operator!=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(left.get() == right.get())); + } + + template + bool operator!=(const unique_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!left; + } + + template + bool operator!=(wistd::nullptr_t, const unique_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!right; + } + + template + bool operator<(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (left.get() < right.get()); + } + + template + bool operator>=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(left < right)); + } + + template + bool operator>(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (right < left); + } + + template + bool operator<=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(right < left)); + } + + // unique_any provides a template alias for easily building a unique_any_t from a unique_storage class with the given + // template parameters for resource_policy. + + template // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer + using unique_any = unique_any_t>>; + + /// @cond + namespace details + { + template + class lambda_call + { + public: + lambda_call(const lambda_call&) = delete; + lambda_call& operator=(const lambda_call&) = delete; + lambda_call& operator=(lambda_call&& other) = delete; + + explicit lambda_call(TLambda&& lambda) WI_NOEXCEPT : m_lambda(wistd::move(lambda)) + { + static_assert(wistd::is_same::value, "scope_exit lambdas must not have a return value"); + static_assert(!wistd::is_lvalue_reference::value && !wistd::is_rvalue_reference::value, + "scope_exit should only be directly used with a lambda"); + } + + lambda_call(lambda_call&& other) WI_NOEXCEPT : m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call) + { + other.m_call = false; + } + + ~lambda_call() WI_NOEXCEPT + { + reset(); + } + + // Ensures the scope_exit lambda will not be called + void release() WI_NOEXCEPT + { + m_call = false; + } + + // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again + void reset() WI_NOEXCEPT + { + if (m_call) + { + m_call = false; + m_lambda(); + } + } + + // Returns true if the scope_exit lambda is still going to be executed + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + protected: + TLambda m_lambda; + bool m_call = true; + }; + +#ifdef WIL_ENABLE_EXCEPTIONS + template + class lambda_call_log + { + public: + lambda_call_log(const lambda_call_log&) = delete; + lambda_call_log& operator=(const lambda_call_log&) = delete; + lambda_call_log& operator=(lambda_call_log&& other) = delete; + + explicit lambda_call_log(void* address, const DiagnosticsInfo& info, TLambda&& lambda) WI_NOEXCEPT : + m_address(address), m_info(info), m_lambda(wistd::move(lambda)) + { + static_assert(wistd::is_same::value, "scope_exit lambdas must return 'void'"); + static_assert(!wistd::is_lvalue_reference::value && !wistd::is_rvalue_reference::value, + "scope_exit should only be directly used with a lambda"); + } + + lambda_call_log(lambda_call_log&& other) WI_NOEXCEPT : + m_address(other.m_address), m_info(other.m_info), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call) + { + other.m_call = false; + } + + ~lambda_call_log() WI_NOEXCEPT + { + reset(); + } + + // Ensures the scope_exit lambda will not be called + void release() WI_NOEXCEPT + { + m_call = false; + } + + // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again + void reset() WI_NOEXCEPT + { + if (m_call) + { + m_call = false; + try + { + m_lambda(); + } + catch (...) + { + ReportFailure_CaughtException(__R_DIAGNOSTICS(m_info), m_address); + } + } + } + + // Returns true if the scope_exit lambda is still going to be executed + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + private: + void* m_address; + DiagnosticsInfo m_info; + TLambda m_lambda; + bool m_call = true; + }; +#endif // WIL_ENABLE_EXCEPTIONS + } + /// @endcond + + /** Returns an object that executes the given lambda when destroyed. + Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid + execution. Exceptions thrown in the lambda will fail-fast; use scope_exit_log to avoid. */ + template + WI_NODISCARD inline auto scope_exit(TLambda&& lambda) WI_NOEXCEPT + { + return details::lambda_call(wistd::forward(lambda)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Returns an object that executes the given lambda when destroyed; logs exceptions. + Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid + execution. Exceptions thrown in the lambda will be caught and logged without being propagated. */ + template + WI_NODISCARD inline __declspec(noinline) auto scope_exit_log(const DiagnosticsInfo& diagnostics, TLambda&& lambda) WI_NOEXCEPT + { + return details::lambda_call_log(_ReturnAddress(), diagnostics, wistd::forward(lambda)); + } +#endif + + // Forward declaration... + template + class com_ptr_t; + + //! Type traits class that identifies the inner type of any smart pointer. + template + struct smart_pointer_details + { + typedef typename Ptr::pointer pointer; + }; + + /// @cond + template + struct smart_pointer_details> + { + typedef T* pointer; + }; + /// @endcond + + /** Generically detaches a raw pointer from any smart pointer. + Caller takes ownership of the returned raw pointer; calls the correct release(), detach(), + or Detach() method based on the smart pointer type */ + template + WI_NODISCARD typename TSmartPointer::pointer detach_from_smart_pointer(TSmartPointer& smartPtr) + { + return smartPtr.release(); + } + + /// @cond + // Generically detaches a raw pointer from any smart pointer + template + WI_NODISCARD T* detach_from_smart_pointer(wil::com_ptr_t& smartPtr) + { + return smartPtr.detach(); + } + + // Generically detaches a raw pointer from any smart pointer + template + WI_NODISCARD T* detach_from_smart_pointer(Microsoft::WRL::ComPtr& smartPtr) + { + return smartPtr.Detach(); + } + + template class com_ptr_t; // forward + namespace details + { + // The first two attach_to_smart_pointer() overloads are ambiguous when passed a com_ptr_t. + // To solve that use this functions return type to elminate the reset form for com_ptr_t. + template wistd::false_type use_reset(wil::com_ptr_t*) { return wistd::false_type(); } + template wistd::true_type use_reset(T*) { return wistd::true_type(); } + } + /// @endcond + + /** Generically attach a raw pointer to a compatible smart pointer. + Calls the correct reset(), attach(), or Attach() method based on samrt pointer type. */ + template (nullptr)))::value>> + void attach_to_smart_pointer(TSmartPointer& smartPtr, typename TSmartPointer::pointer rawPtr) + { + smartPtr.reset(rawPtr); + } + + /// @cond + + // Generically attach a raw pointer to a compatible smart pointer. + template + void attach_to_smart_pointer(wil::com_ptr_t& smartPtr, T* rawPtr) + { + smartPtr.attach(rawPtr); + } + + // Generically attach a raw pointer to a compatible smart pointer. + template + void attach_to_smart_pointer(Microsoft::WRL::ComPtr& smartPtr, T* rawPtr) + { + smartPtr.Attach(rawPtr); + } + /// @endcond + + //! @ingroup outparam + /** Detach a smart pointer resource to an optional output pointer parameter. + Avoids cluttering code with nullptr tests; works generically for any smart pointer */ + template + inline void detach_to_opt_param(_Out_opt_ T* outParam, TSmartPointer&& smartPtr) + { + if (outParam) + { + *outParam = detach_from_smart_pointer(smartPtr); + } + } + + /// @cond + namespace details + { + template + struct out_param_t + { + typedef typename wil::smart_pointer_details::pointer pointer; + T &wrapper; + pointer pRaw; + bool replace = true; + + out_param_t(_Inout_ T &output) : + wrapper(output), + pRaw(nullptr) + { + } + + out_param_t(out_param_t&& other) : + wrapper(other.wrapper), + pRaw(other.pRaw) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator pointer*() + { + WI_ASSERT(replace); + return &pRaw; + } + + ~out_param_t() + { + if (replace) + { + attach_to_smart_pointer(wrapper, pRaw); + } + } + + out_param_t(out_param_t const &other) = delete; + out_param_t &operator=(out_param_t const &other) = delete; + }; + + template + struct out_param_ptr_t + { + typedef typename wil::smart_pointer_details::pointer pointer; + T &wrapper; + pointer pRaw; + bool replace = true; + + out_param_ptr_t(_Inout_ T &output) : + wrapper(output), + pRaw(nullptr) + { + } + + out_param_ptr_t(out_param_ptr_t&& other) : + wrapper(other.wrapper), + pRaw(other.pRaw) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator Tcast() + { + WI_ASSERT(replace); + return reinterpret_cast(&pRaw); + } + + ~out_param_ptr_t() + { + if (replace) + { + attach_to_smart_pointer(wrapper, pRaw); + } + } + + out_param_ptr_t(out_param_ptr_t const &other) = delete; + out_param_ptr_t &operator=(out_param_ptr_t const &other) = delete; + }; + } // details + /// @endcond + + /** Use to retrieve raw out parameter pointers into smart pointers that do not support the '&' operator. + This avoids multi-step handling of a raw resource to establish the smart pointer. + Example: `GetFoo(out_param(foo));` */ + template + details::out_param_t out_param(T& p) + { + return details::out_param_t(p); + } + + /** Use to retrieve raw out parameter pointers (with a required cast) into smart pointers that do not support the '&' operator. + Use only when the smart pointer's &handle is not equal to the output type a function requries, necessitating a cast. + Example: `wil::out_param_ptr(securityDescriptor)` */ + template + details::out_param_ptr_t out_param_ptr(T& p) + { + return details::out_param_ptr_t(p); + } + + /** Use unique_struct to define an RAII type for a trivial struct that references resources that must be cleaned up. + Unique_struct wraps a trivial struct using a custom clean up function and, optionally, custom initializer function. If no custom initialier function is defined in the template + then ZeroMemory is used. + Unique_struct is modeled off of std::unique_ptr. However, unique_struct inherits from the defined type instead of managing the struct through a private member variable. + + If the type you're wrapping is a system type, you can share the code by declaring it in this file (Resource.h). Send requests to wildisc. + Otherwise, if the type is local to your project, declare it locally. + @tparam struct_t The struct you want to manage + @tparam close_fn_t The type of the function to clean up the struct. Takes one parameter: a pointer of struct_t. Return values are ignored. + @tparam close_fn The function of type close_fn_t. This is called in the destructor and reset functions. + @tparam init_fn_t Optional:The type of the function to initialize the struct. Takes one parameter: a pointer of struct_t. Return values are ignored. + @tparam init_fn Optional:The function of type init_fn_t. This is called in the constructor, reset, and release functions. The default is ZeroMemory to initialize the struct. + + Defined using the default zero memory initializer + ~~~ + typedef wil::unique_struct unique_prop_variant_default_init; + + unique_prop_variant propvariant; + SomeFunction(&propvariant); + ~~~ + + Defined using a custom initializer + ~~~ + typedef wil::unique_struct unique_prop_variant; + + unique_prop_variant propvariant; + SomeFunction(&propvariant); + ~~~ + */ + template + class unique_struct : public struct_t + { + public: + //! Initializes the managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + unique_struct() + { + call_init(use_default_init_fn()); + } + + //! Takes ownership of the struct by doing a shallow copy. Must explicitly be type struct_t + explicit unique_struct(const struct_t& other) WI_NOEXCEPT : + struct_t(other) + {} + + //! Initializes the managed struct by taking the ownership of the other managed struct + //! Then resets the other managed struct by calling the custom close function + unique_struct(unique_struct&& other) WI_NOEXCEPT : + struct_t(other.release()) + {} + + //! Resets this managed struct by calling the custom close function and takes ownership of the other managed struct + //! Then resets the other managed struct by calling the custom close function + unique_struct & operator=(unique_struct&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(other.release()); + } + return *this; + } + + //! Calls the custom close function + ~unique_struct() WI_NOEXCEPT + { + wistd::invoke(close_fn, this); + } + + void reset(const unique_struct&) = delete; + + //! Resets this managed struct by calling the custom close function and begins management of the other struct + void reset(const struct_t& other) WI_NOEXCEPT + { + { + auto preserveError = last_error_context(); + wistd::invoke(close_fn, this); + } + struct_t::operator=(other); + } + + //! Resets this managed struct by calling the custom close function + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + void reset() WI_NOEXCEPT + { + wistd::invoke(close_fn, this); + call_init(use_default_init_fn()); + } + + void swap(struct_t&) = delete; + + //! Swaps the managed structs + void swap(unique_struct& other) WI_NOEXCEPT + { + struct_t self(*this); + struct_t::operator=(other); + *(other.addressof()) = self; + } + + //! Returns the managed struct + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + struct_t release() WI_NOEXCEPT + { + struct_t value(*this); + call_init(use_default_init_fn()); + return value; + } + + //! Returns address of the managed struct + struct_t * addressof() WI_NOEXCEPT + { + return this; + } + + //! Resets this managed struct by calling the custom close function + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + //! Returns address of the managed struct + struct_t * reset_and_addressof() WI_NOEXCEPT + { + reset(); + return this; + } + + unique_struct(const unique_struct&) = delete; + unique_struct& operator=(const unique_struct&) = delete; + unique_struct& operator=(const struct_t&) = delete; + + private: + typedef typename wistd::is_same::type use_default_init_fn; + + void call_init(wistd::true_type) + { + RtlZeroMemory(this, sizeof(*this)); + } + + void call_init(wistd::false_type) + { + init_fn(this); + } + }; + + struct empty_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T) const + { + } + }; + + /** unique_any_array_ptr is a RAII type for managing conformant arrays that need to be freed and have elements that may need to be freed. + The intented use for this RAII type would be to capture out params from API like IPropertyValue::GetStringArray. + This class also maintains the size of the array, so it can iterate over the members and deallocate them before it deallocates the base array pointer. + + If the type you're wrapping is a system type, you can share the code by declaring it in this file (Resource.h). Send requests to wildisc. + Otherwise, if the type is local to your project, declare it locally. + + @tparam ValueType: The type of array you want to manage. + @tparam ArrayDeleter: The type of the function to clean up the array. Takes one parameter of type T[] or T*. Return values are ignored. This is called in the destructor and reset functions. + @tparam ElementDeleter: The type of the function to clean up the array elements. Takes one parameter of type T. Return values are ignored. This is called in the destructor and reset functions. + + ~~~ + void GetSomeArray(_Out_ size_t*, _Out_ NOTMYTYPE**); + + struct not_my_deleter + { + void operator()(NOTMYTYPE p) const + { + destroy(p); + } + }; + + wil::unique_any_array_ptr myArray; + GetSomeArray(myArray.size_address(), &myArray); + ~~~ */ + template + class unique_any_array_ptr + { + public: + typedef ValueType value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef ValueType *pointer; + typedef const ValueType *const_pointer; + typedef ValueType& reference; + typedef const ValueType& const_reference; + + typedef ValueType* iterator; + typedef const ValueType* const_iterator; + + unique_any_array_ptr() = default; + unique_any_array_ptr(const unique_any_array_ptr&) = delete; + unique_any_array_ptr& operator=(const unique_any_array_ptr&) = delete; + + unique_any_array_ptr(wistd::nullptr_t) WI_NOEXCEPT + { + } + + unique_any_array_ptr& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + return *this; + } + + unique_any_array_ptr(pointer ptr, size_t size) WI_NOEXCEPT : m_ptr(ptr), m_size(size) + { + } + + unique_any_array_ptr(unique_any_array_ptr&& other) WI_NOEXCEPT : m_ptr(other.m_ptr), m_size(other.m_size) + { + other.m_ptr = nullptr; + other.m_size = size_type{}; + } + + unique_any_array_ptr& operator=(unique_any_array_ptr&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + swap(other); + } + return *this; + } + + ~unique_any_array_ptr() WI_NOEXCEPT + { + reset(); + } + + void swap(unique_any_array_ptr& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + auto size = m_size; + m_ptr = other.m_ptr; + m_size = other.m_size; + other.m_ptr = ptr; + other.m_size = size; + } + + iterator begin() WI_NOEXCEPT + { + return (iterator(m_ptr)); + } + + const_iterator begin() const WI_NOEXCEPT + { + return (const_iterator(m_ptr)); + } + + iterator end() WI_NOEXCEPT + { + return (iterator(m_ptr + m_size)); + } + + const_iterator end() const WI_NOEXCEPT + { + return (const_iterator(m_ptr + m_size)); + } + + const_iterator cbegin() const WI_NOEXCEPT + { + return (begin()); + } + + const_iterator cend() const WI_NOEXCEPT + { + return (end()); + } + + size_type size() const WI_NOEXCEPT + { + return (m_size); + } + + bool empty() const WI_NOEXCEPT + { + return (size() == size_type{}); + } + + reference operator[](size_type position) + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + const_reference operator[](size_type position) const + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + reference front() + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + const_reference front() const + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + reference back() + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + const_reference back() const + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + ValueType* data() WI_NOEXCEPT + { + return (m_ptr); + } + + const ValueType* data() const WI_NOEXCEPT + { + return (m_ptr); + } + + pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != pointer()); + } + + pointer release() WI_NOEXCEPT + { + auto result = m_ptr; + m_ptr = nullptr; + m_size = size_type{}; + return result; + } + + void reset() WI_NOEXCEPT + { + if (m_ptr) + { + reset_array(ElementDeleter()); + ArrayDeleter()(m_ptr); + m_ptr = nullptr; + m_size = size_type{}; + } + } + + void reset(pointer ptr, size_t size) WI_NOEXCEPT + { + reset(); + m_ptr = ptr; + m_size = size; + } + + pointer* addressof() WI_NOEXCEPT + { + return &m_ptr; + } + + pointer* put() WI_NOEXCEPT + { + reset(); + return addressof(); + } + + pointer* operator&() WI_NOEXCEPT + { + return put(); + } + + size_type* size_address() WI_NOEXCEPT + { + return &m_size; + } + + template + struct size_address_ptr + { + unique_any_array_ptr& wrapper; + TSize size{}; + bool replace = true; + + size_address_ptr(_Inout_ unique_any_array_ptr& output) : + wrapper(output) + { + } + + size_address_ptr(size_address_ptr&& other) : + wrapper(other.wrapper), + size(other.size) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator TSize*() + { + WI_ASSERT(replace); + return &size; + } + + ~size_address_ptr() + { + if (replace) + { + *wrapper.size_address() = static_cast(size); + } + } + + size_address_ptr(size_address_ptr const &other) = delete; + size_address_ptr &operator=(size_address_ptr const &other) = delete; + }; + + template + size_address_ptr size_address() WI_NOEXCEPT + { + return size_address_ptr(*this); + } + + private: + pointer m_ptr = nullptr; + size_type m_size{}; + + void reset_array(const empty_deleter&) + { + } + + template + void reset_array(const T& deleter) + { + for (auto& element : make_range(m_ptr, m_size)) + { + deleter(element); + } + } + }; + + // forward declaration + template + class com_ptr_t; + + /// @cond + namespace details + { + template + struct unique_any_array_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + UniqueAnyType::policy::close_reset(p); + } + }; + + template + struct unique_struct_array_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T& p) const + { + wistd::invoke(close_fn, &p); + } + }; + + struct com_unknown_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { + p->Release(); + } + } + }; + + template + struct element_traits + { + typedef empty_deleter deleter; + typedef T type; + }; + + template + struct element_traits> + { + typedef unique_any_array_deleter> deleter; + typedef typename unique_any_t::pointer type; + }; + + template + struct element_traits> + { + typedef com_unknown_deleter deleter; + typedef T* type; + }; + + template + struct element_traits> + { + typedef unique_struct_array_deleter deleter; + typedef struct_t type; + }; + } + /// @endcond + + template + using unique_array_ptr = unique_any_array_ptr::type, ArrayDeleter, typename details::element_traits::deleter>; + + /** Adapter for single-parameter 'free memory' for `wistd::unique_ptr`. + This struct provides a standard wrapper for calling a platform function to deallocate memory held by a + `wistd::unique_ptr`, making declaring them as easy as declaring wil::unique_any<>. + + Consider this adapter in preference to `wil::unique_any<>` when the returned type is really a pointer or an + array of items; `wistd::unique_ptr<>` exposes `operator->()` and `operator[]` for array-typed things safely. + ~~~~ + EXTERN_C VOID WINAPI MyDllFreeMemory(void* p); + EXTERN_C HRESULT MyDllGetString(_Outptr_ PWSTR* pString); + EXTERN_C HRESULT MyDllGetThing(_In_ PCWSTR pString, _Outptr_ PMYSTRUCT* ppThing); + template + using unique_mydll_ptr = wistd::unique_ptr>; + HRESULT Test() + { + unique_mydll_ptr dllString; + unique_mydll_ptr thing; + RETURN_IF_FAILED(MyDllGetString(wil::out_param(dllString))); + RETURN_IF_FAILED(MyDllGetThing(dllString.get(), wil::out_param(thing))); + if (thing->Member) + { + // ... + } + return S_OK; + } + ~~~~ */ + template struct function_deleter + { + template void operator()(_Frees_ptr_opt_ T* toFree) const + { + TDeleter(toFree); + } + }; + + /** Use unique_com_token to define an RAII type for a token-based resource that is managed by a COM interface. + By comparison, unique_any_t has the requirement that the close function must be static. This works for functions + such as CloseHandle(), but for any resource cleanup function that relies on a more complex interface, + unique_com_token can be used. + + @tparam interface_t A COM interface pointer that will manage this resource type. + @tparam token_t The token type that relates to the COM interface management functions. + @tparam close_fn_t The type of the function that is called when the resource is destroyed. + @tparam close_fn The function used to destroy the associated resource. This function should have the signature void(interface_t* source, token_t token). + @tparam invalid_token Optional:An invalid token value. Defaults to default-constructed token_t(). + + Example + ~~~ + void __stdcall MyInterfaceCloseFunction(IMyInterface* source, DWORD token) + { + source->MyCloseFunction(token); + } + using unique_my_interface_token = wil::unique_com_token; + ~~~ */ + template + class unique_com_token + { + public: + unique_com_token() = default; + + unique_com_token(_In_opt_ interface_t* source, token_t token = invalid_token) WI_NOEXCEPT + { + reset(source, token); + } + + unique_com_token(unique_com_token&& other) WI_NOEXCEPT : m_source(other.m_source), m_token(other.m_token) + { + other.m_source = nullptr; + other.m_token = invalid_token; + } + + unique_com_token& operator=(unique_com_token&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_source = other.m_source; + m_token = other.m_token; + + other.m_source = nullptr; + other.m_token = invalid_token; + } + return *this; + } + + ~unique_com_token() WI_NOEXCEPT + { + reset(); + } + + //! Determine if the underlying source and token are valid + explicit operator bool() const WI_NOEXCEPT + { + return (m_token != invalid_token) && m_source; + } + + //! Associates a new source and releases the existing token if valid + void associate(_In_opt_ interface_t* source) WI_NOEXCEPT + { + reset(source, invalid_token); + } + + //! Assigns a new source and token + void reset(_In_opt_ interface_t* source, token_t token) WI_NOEXCEPT + { + WI_ASSERT(source || (token == invalid_token)); + + // Determine if we need to call the close function on our previous token. + if (m_token != invalid_token) + { + if ((m_source != source) || (m_token != token)) + { + wistd::invoke(close_fn, m_source, m_token); + } + } + + m_token = token; + + // Assign our new source and manage the reference counts + if (m_source != source) + { + auto oldSource = m_source; + m_source = source; + + if (m_source) + { + m_source->AddRef(); + } + + if (oldSource) + { + oldSource->Release(); + } + } + } + + //! Assigns a new token without modifying the source; associate must be called first + void reset(token_t token) WI_NOEXCEPT + { + reset(m_source, token); + } + + //! Closes the token and the releases the reference to the source + void reset() WI_NOEXCEPT + { + reset(nullptr, invalid_token); + } + + //! Exchanges values with another managed token + void swap(unique_com_token& other) WI_NOEXCEPT + { + wistd::swap_wil(m_source, other.m_source); + wistd::swap_wil(m_token, other.m_token); + } + + //! Releases the held token to the caller without closing it and releases the reference to the source. + //! Requires that the associated COM interface be kept alive externally or the released token may be invalidated + token_t release() WI_NOEXCEPT + { + auto token = m_token; + m_token = invalid_token; + reset(); + return token; + } + + //! Returns address of the managed token; associate must be called first + token_t* addressof() WI_NOEXCEPT + { + WI_ASSERT(m_source); + return &m_token; + } + + //! Releases the held token and allows attaching a new token; associate must be called first + token_t* put() WI_NOEXCEPT + { + reset(invalid_token); + return addressof(); + } + + //! Releases the held token and allows attaching a new token; associate must be called first + token_t* operator&() WI_NOEXCEPT + { + return put(); + } + + //! Retrieves the token + token_t get() const WI_NOEXCEPT + { + return m_token; + } + + unique_com_token(const unique_com_token&) = delete; + unique_com_token& operator=(const unique_com_token&) = delete; + + private: + interface_t* m_source = nullptr; + token_t m_token = invalid_token; + }; + + /** Use unique_com_call to define an RAII type that demands a particular parameter-less method be called on a COM interface. + This allows implementing an RAII type that can call a Close() method (think IClosable) or a SetSite(nullptr) + method (think IObjectWithSite) or some other method when a basic interface call is required as part of the RAII contract. + see wil::com_set_site in wil\com.h for the IObjectWithSite support. + + @tparam interface_t A COM interface pointer that provides context to make the call. + @tparam close_fn_t The type of the function that is called to invoke the method. + @tparam close_fn The function used to invoke the interface method. This function should have the signature void(interface_t* source). + + Example + ~~~ + void __stdcall CloseIClosable(IClosable* source) + { + source->Close(); + } + using unique_closable_call = wil::unique_com_call; + ~~~ */ + template + class unique_com_call + { + public: + unique_com_call() = default; + + explicit unique_com_call(_In_opt_ interface_t* ptr) WI_NOEXCEPT + { + reset(ptr); + } + + unique_com_call(unique_com_call&& other) WI_NOEXCEPT + { + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + } + + unique_com_call& operator=(unique_com_call&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + } + return *this; + } + + ~unique_com_call() WI_NOEXCEPT + { + reset(); + } + + //! Assigns an interface to make a given call on + void reset(_In_opt_ interface_t* ptr = nullptr) WI_NOEXCEPT + { + if (ptr != m_ptr) + { + auto oldSource = m_ptr; + m_ptr = ptr; + if (m_ptr) + { + m_ptr->AddRef(); + } + if (oldSource) + { + wistd::invoke(close_fn, oldSource); + oldSource->Release(); + } + } + } + + //! Exchanges values with another class + void swap(unique_com_call& other) WI_NOEXCEPT + { + wistd::swap_wil(m_ptr, other.m_ptr); + } + + //! Cancel the interface call that this class was expected to make + void release() WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = nullptr; + if (ptr) + { + ptr->Release(); + } + } + + //! Returns true if the call this class was expected to make is still outstanding + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != nullptr); + } + + //! Returns address of the internal interface + interface_t** addressof() WI_NOEXCEPT + { + return &m_ptr; + } + + //! Releases the held interface (first performing the interface call if required) + //! and allows attaching a new interface + interface_t** put() WI_NOEXCEPT + { + reset(); + return addressof(); + } + + //! Releases the held interface (first performing the interface call if required) + //! and allows attaching a new interface + interface_t** operator&() WI_NOEXCEPT + { + return put(); + } + + unique_com_call(const unique_com_call&) = delete; + unique_com_call& operator=(const unique_com_call&) = delete; + + private: + interface_t* m_ptr = nullptr; + }; + + + /** Use unique_call to define an RAII type that demands a particular parameter-less global function be called. + This allows implementing a RAII types that can call methods like CoUninitialize. + + @tparam close_fn_t The type of the function that is called to invoke the call. + @tparam close_fn The function used to invoke the call. This function should have the signature void(). + @tparam default_value Determines whether the unique_call is active or inactive when default-constructed or reset. + + Example + ~~~ + void __stdcall CoUninitializeFunction() + { + ::CoUninitialize(); + } + using unique_couninitialize_call = wil::unique_call; + ~~~ */ + template + class unique_call + { + public: + unique_call() = default; + + explicit unique_call(bool call) WI_NOEXCEPT : m_call(call) + { + } + + unique_call(unique_call&& other) WI_NOEXCEPT + { + m_call = other.m_call; + other.m_call = false; + } + + unique_call& operator=(unique_call&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_call = other.m_call; + other.m_call = false; + } + return *this; + } + + ~unique_call() WI_NOEXCEPT + { + reset(); + } + + //! Assigns a new ptr and token + void reset() WI_NOEXCEPT + { + auto call = m_call; + m_call = false; + if (call) + { + wistd::invoke(close_fn); + } + } + + //! Exchanges values with raii class + void swap(unique_call& other) WI_NOEXCEPT + { + wistd::swap_wil(m_call, other.m_call); + } + + //! Make the interface call that was expected of this class + void activate() WI_NOEXCEPT + { + m_call = true; + } + + //! Do not make the interface call that was expected of this class + void release() WI_NOEXCEPT + { + m_call = false; + } + + //! Returns true if the call that was expected is still outstanding + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + unique_call(const unique_call&) = delete; + unique_call& operator=(const unique_call&) = delete; + + private: + bool m_call = default_value; + }; + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // Overloads in this file support any string that is implicitly convertible to a PCWSTR, HSTRING, and any unique_any_t + // that points to any other supported type (this covers unique_hstring, unique_cotaskmem_string, and similar). + // An overload for std::wstring is available in stl.h. + inline PCWSTR str_raw_ptr(PCWSTR str) + { + return str; + } + + template + PCWSTR str_raw_ptr(const unique_any_t& ua) + { + return str_raw_ptr(ua.get()); + } + + namespace details + { + // Forward declaration + template struct string_maker; + + // Concatenate any number of strings together and store it in an automatically allocated string. If a string is present + // in the input buffer, it is overwritten. + template + HRESULT str_build_nothrow(string_type& result, _In_reads_(strCount) PCWSTR* strList, size_t strCount) + { + size_t lengthRequiredWithoutNull{}; + for (auto& string : make_range(strList, strCount)) + { + lengthRequiredWithoutNull += string ? wcslen(string) : 0; + } + + details::string_maker maker; + RETURN_IF_FAILED(maker.make(nullptr, lengthRequiredWithoutNull)); + + auto buffer = maker.buffer(); + auto bufferEnd = buffer + lengthRequiredWithoutNull + 1; + for (auto& string : make_range(strList, strCount)) + { + if (string) + { + RETURN_IF_FAILED(StringCchCopyExW(buffer, (bufferEnd - buffer), string, &buffer, nullptr, STRSAFE_IGNORE_NULLS)); + } + } + + result = maker.release(); + return S_OK; + } + + // NOTE: 'Strings' must all be PCWSTR, or convertible to PCWSTR, but C++ doesn't allow us to express that cleanly + template + HRESULT str_build_nothrow(string_type& result, Strings... strings) + { + PCWSTR localStrings[] = { strings... }; + return str_build_nothrow(result, localStrings, sizeof...(Strings)); + } + } + + // Concatenate any number of strings together and store it in an automatically allocated string. If a string is present + // in the input buffer, the remaining strings are appended to it. + template + HRESULT str_concat_nothrow(string_type& buffer, const strings&... str) + { + static_assert(sizeof...(str) > 0, "attempting to concatenate no strings"); + return details::str_build_nothrow(buffer, details::string_maker::get(buffer), str_raw_ptr(str)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Concatenate any number of strings together and store it in an automatically allocated string. + template + string_type str_concat(arguments&&... args) + { + string_type result; + THROW_IF_FAILED(str_concat_nothrow(result, wistd::forward(args)...)); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Concatenate any number of strings together and store it in an automatically allocated string. + template + string_type str_concat_failfast(arguments&&... args) + { + string_type result; + FAIL_FAST_IF_FAILED(str_concat_nothrow(result, wistd::forward(args)...)); + return result; + } + + namespace details + { + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + HRESULT str_vprintf_nothrow(string_type& result, _Printf_format_string_ PCWSTR pszFormat, va_list& argsVL) + { + size_t lengthRequiredWithoutNull = _vscwprintf(pszFormat, argsVL); + + string_maker maker; + RETURN_IF_FAILED(maker.make(nullptr, lengthRequiredWithoutNull)); + + auto buffer = maker.buffer(); + RETURN_IF_FAILED(::StringCchVPrintfExW(buffer, lengthRequiredWithoutNull + 1, nullptr, nullptr, STRSAFE_NULL_ON_FAILURE, pszFormat, argsVL)); + + result = maker.release(); + return S_OK; + } + } + + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + HRESULT str_printf_nothrow(string_type& result, _Printf_format_string_ PCWSTR pszFormat, _In_ ...) + { + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + return hr; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + string_type str_printf(_Printf_format_string_ PCWSTR pszFormat, _In_ ...) + { + string_type result; + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + THROW_IF_FAILED(hr); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + string_type str_printf_failfast(_Printf_format_string_ PCWSTR pszFormat, _In_ ...) + { + string_type result; + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + FAIL_FAST_IF_FAILED(hr); + return result; + } + +} // namespace wil +#endif // __WIL_RESOURCE + + + // Hash deferral function for unique_any_t +#if (defined(_UNORDERED_SET_) || defined(_UNORDERED_MAP_)) && !defined(__WIL_RESOURCE_UNIQUE_HASH) +#define __WIL_RESOURCE_UNIQUE_HASH +namespace std +{ + template + struct hash> + { + size_t operator()(wil::unique_any_t const &val) const + { + return (hash::pointer>()(val.get())); + } + }; +} +#endif + +// shared_any and weak_any implementation using STL header +#if defined(_MEMORY_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(WIL_RESOURCE_STL) && !defined(RESOURCE_SUPPRESS_STL) +#define WIL_RESOURCE_STL +namespace wil { + + template + class weak_any; + + /// @cond + namespace details + { + // This class provides the pointer storage behind the implementation of shared_any_t utilizing the given + // resource_policy. It is separate from shared_any_t to allow a type-specific specialization class to plug + // into the inheritance chain between shared_any_t and shared_storage. This allows classes like shared_event + // to be a shared_any formed class, but also expose methods like SetEvent directly. + + template + class shared_storage + { + protected: + typedef unique_t unique_t; + typedef typename unique_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef shared_storage base_storage; + + shared_storage() = default; + + explicit shared_storage(pointer_storage ptr) + { + if (policy::is_valid(ptr)) + { + m_ptr = std::make_shared(unique_t(ptr)); // unique_t on the stack to prevent leak on throw + } + } + + shared_storage(unique_t &&other) + { + if (other) + { + m_ptr = std::make_shared(wistd::move(other)); + } + } + + shared_storage(const shared_storage &other) WI_NOEXCEPT : + m_ptr(other.m_ptr) + { + } + + shared_storage& operator=(const shared_storage &other) WI_NOEXCEPT + { + m_ptr = other.m_ptr; + return *this; + } + + shared_storage(shared_storage &&other) WI_NOEXCEPT : + m_ptr(wistd::move(other.m_ptr)) + { + } + + shared_storage(std::shared_ptr const &ptr) : + m_ptr(ptr) + { + } + + void replace(shared_storage &&other) WI_NOEXCEPT + { + m_ptr = wistd::move(other.m_ptr); + } + + public: + bool is_valid() const WI_NOEXCEPT + { + return (m_ptr && m_ptr->is_valid()); + } + + void reset(pointer_storage ptr = policy::invalid_value()) + { + if (policy::is_valid(ptr)) + { + m_ptr = std::make_shared(unique_t(ptr)); // unique_t on the stack to prevent leak on throw + } + else + { + m_ptr = nullptr; + } + } + + void reset(unique_t &&other) + { + m_ptr = std::make_shared(wistd::move(other)); + } + + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value"); + reset(); + } + + template ::value, int>::type = 0> + pointer get() const WI_NOEXCEPT + { + return (m_ptr ? m_ptr->get() : policy::invalid_value()); + } + + template ::value, int>::type = 0> + pointer_storage *addressof() + { + if (!m_ptr) + { + m_ptr = std::make_shared(); + } + return m_ptr->addressof(); + } + + long int use_count() const WI_NOEXCEPT + { + return m_ptr.use_count(); + } + + private: + template + friend class ::wil::weak_any; + + std::shared_ptr m_ptr; + }; + } + /// @endcond + + // This class when paired with shared_storage and an optional type-specific specialization class implements + // the same interface as STL's shared_ptr<> for resource handle types. It is both copyable and movable, supporting + // weak references and automatic closure of the handle upon release of the last shared_any. + + template + class shared_any_t : public storage_t + { + public: + typedef typename storage_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef typename storage_t::unique_t unique_t; + + // default and forwarding constructor: forwards default, all 'explicit' and multi-arg constructors to the base class + template + explicit shared_any_t(args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + storage_t(wistd::forward(args)...) + { + } + + shared_any_t(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value"); + } + + shared_any_t(shared_any_t &&other) WI_NOEXCEPT : + storage_t(wistd::move(other)) + { + } + + shared_any_t(const shared_any_t &other) WI_NOEXCEPT : + storage_t(other) + { + } + + shared_any_t& operator=(shared_any_t &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + storage_t::replace(wistd::move(static_cast(other))); + } + return (*this); + } + + shared_any_t& operator=(const shared_any_t& other) WI_NOEXCEPT + { + storage_t::operator=(other); + return (*this); + } + + shared_any_t(unique_t &&other) : + storage_t(wistd::move(other)) + { + } + + shared_any_t& operator=(unique_t &&other) + { + storage_t::reset(wistd::move(other)); + return (*this); + } + + shared_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value"); + storage_t::reset(); + return (*this); + } + + void swap(shared_any_t &other) WI_NOEXCEPT + { + shared_any_t self(wistd::move(*this)); + operator=(wistd::move(other)); + other = wistd::move(self); + } + + explicit operator bool() const WI_NOEXCEPT + { + return storage_t::is_valid(); + } + + pointer_storage *put() + { + static_assert(wistd::is_same::value, "operator & is not available for this handle"); + storage_t::reset(); + return storage_t::addressof(); + } + + pointer_storage *operator&() + { + return put(); + } + + pointer get() const WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "get(): the raw handle value is not available for this resource class"); + return storage_t::get(); + } + + // The following functions are publicly exposed by their inclusion in the base class + + // void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + // void reset(wistd::nullptr_t) WI_NOEXCEPT + // pointer_storage *addressof() WI_NOEXCEPT // (note: not exposed for opaque resource types) + }; + + template + void swap(shared_any_t& left, shared_any_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + bool operator==(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (left.get() == right.get()); + } + + template + bool operator==(const shared_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !left; + } + + template + bool operator==(wistd::nullptr_t, const shared_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !right; + } + + template + bool operator!=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(left.get() == right.get())); + } + + template + bool operator!=(const shared_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!left; + } + + template + bool operator!=(wistd::nullptr_t, const shared_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!right; + } + + template + bool operator<(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (left.get() < right.get()); + } + + template + bool operator>=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(left < right)); + } + + template + bool operator>(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (right < left); + } + + template + bool operator<=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(right < left)); + } + + + // This class provides weak_ptr<> support for shared_any<>, bringing the same weak reference counting and lock() acquire semantics + // to shared_any. + + template + class weak_any + { + public: + typedef shared_t shared_t; + + weak_any() WI_NOEXCEPT + { + } + + weak_any(const shared_t &other) WI_NOEXCEPT : + m_weakPtr(other.m_ptr) + { + } + + weak_any(const weak_any &other) WI_NOEXCEPT : + m_weakPtr(other.m_weakPtr) + { + } + + weak_any& operator=(const weak_any &right) WI_NOEXCEPT + { + m_weakPtr = right.m_weakPtr; + return (*this); + } + + weak_any& operator=(const shared_t &right) WI_NOEXCEPT + { + m_weakPtr = right.m_ptr; + return (*this); + } + + void reset() WI_NOEXCEPT + { + m_weakPtr.reset(); + } + + void swap(weak_any &other) WI_NOEXCEPT + { + m_weakPtr.swap(other.m_weakPtr); + } + + bool expired() const WI_NOEXCEPT + { + return m_weakPtr.expired(); + } + + shared_t lock() const WI_NOEXCEPT + { + return shared_t(m_weakPtr.lock()); + } + + private: + std::weak_ptr m_weakPtr; + }; + + template + void swap(weak_any& left, weak_any& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + using shared_any = shared_any_t>; + +} // namespace wil +#endif + + +#if defined(WIL_RESOURCE_STL) && (defined(_UNORDERED_SET_) || defined(_UNORDERED_MAP_)) && !defined(__WIL_RESOURCE_SHARED_HASH) +#define __WIL_RESOURCE_SHARED_HASH +namespace std +{ + template + struct hash> + { + size_t operator()(wil::shared_any_t const &val) const + { + return (hash::pointer>()(val.get())); + } + }; +} +#endif + + +namespace wil +{ + +#if defined(__NOTHROW_T_DEFINED) && !defined(__WIL__NOTHROW_T_DEFINED) +#define __WIL__NOTHROW_T_DEFINED + /** Provides `std::make_unique()` semantics for resources allocated in a context that may not throw upon allocation failure. + `wil::make_unique_nothrow()` is identical to `std::make_unique()` except for the following: + * It returns `wistd::unique_ptr`, rather than `std::unique_ptr` + * It returns an empty (null) `wistd::unique_ptr` upon allocation failure, rather than throwing an exception + + Note that `wil::make_unique_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_nothrow(fooConstructorParam1, fooConstructorParam2); + if (foo) + { + foo->Bar(); + } + ~~~ + */ + template + inline typename wistd::enable_if::value, wistd::unique_ptr<_Ty> >::type make_unique_nothrow(_Types&&... _Args) + { + return (wistd::unique_ptr<_Ty>(new(std::nothrow) _Ty(wistd::forward<_Types>(_Args)...))); + } + + /** Provides `std::make_unique()` semantics for array resources allocated in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_nothrow(size); // the default constructor will be called on each Foo object + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + elem.Bar(); + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent<_Ty>::value == 0, wistd::unique_ptr<_Ty> >::type make_unique_nothrow(size_t _Size) + { + typedef typename wistd::remove_extent<_Ty>::type _Elem; + return (wistd::unique_ptr<_Ty>(new(std::nothrow) _Elem[_Size]())); + } + + template + typename wistd::enable_if::value != 0, void>::type make_unique_nothrow(_Types&&...) = delete; + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + /** Provides `std::make_unique()` semantics for resources allocated in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_failfast(fooConstructorParam1, fooConstructorParam2); + foo->Bar(); + ~~~ + */ + template + inline typename wistd::enable_if::value, wistd::unique_ptr<_Ty> >::type make_unique_failfast(_Types&&... _Args) + { +#pragma warning(suppress: 28193) // temporary must be inspected (it is within the called function) + return (wistd::unique_ptr<_Ty>(FAIL_FAST_IF_NULL_ALLOC(new(std::nothrow) _Ty(wistd::forward<_Types>(_Args)...)))); + } + + /** Provides `std::make_unique()` semantics for array resources allocated in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_nothrow(size); // the default constructor will be called on each Foo object + for (auto& elem : wil::make_range(foos.get(), size)) + { + elem.Bar(); + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent<_Ty>::value == 0, wistd::unique_ptr<_Ty> >::type make_unique_failfast(size_t _Size) + { + typedef typename wistd::remove_extent<_Ty>::type _Elem; +#pragma warning(suppress: 28193) // temporary must be inspected (it is within the called function) + return (wistd::unique_ptr<_Ty>(FAIL_FAST_IF_NULL_ALLOC(new(std::nothrow) _Elem[_Size]()))); + } + + template + typename wistd::enable_if::value != 0, void>::type make_unique_failfast(_Types&&...) = delete; +#endif // !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#endif // __WIL__NOTHROW_T_DEFINED + +#if defined(_WINBASE_) && !defined(__WIL_WINBASE_) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINBASE_ + /// @cond + namespace details + { + inline void __stdcall SetEvent(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::SetEvent(h)); + } + + inline void __stdcall ResetEvent(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ResetEvent(h)); + } + + inline void __stdcall CloseHandle(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::CloseHandle(h)); + } + + inline void __stdcall ReleaseSemaphore(_In_ HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ReleaseSemaphore(h, 1, nullptr)); + } + + inline void __stdcall ReleaseMutex(_In_ HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ReleaseMutex(h)); + } + + inline void __stdcall CloseTokenLinkedToken(_In_ TOKEN_LINKED_TOKEN* linkedToken) WI_NOEXCEPT + { + if (linkedToken->LinkedToken && (linkedToken->LinkedToken != INVALID_HANDLE_VALUE)) + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::CloseHandle(linkedToken->LinkedToken)); + } + } + + enum class PendingCallbackCancellationBehavior + { + Cancel, + Wait, + NoWait, + }; + + template + struct DestroyThreadPoolWait + { + static void Destroy(_In_ PTP_WAIT threadPoolWait) WI_NOEXCEPT + { + ::SetThreadpoolWait(threadPoolWait, nullptr, nullptr); + ::WaitForThreadpoolWaitCallbacks(threadPoolWait, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolWait(threadPoolWait); + } + }; + + template <> + struct DestroyThreadPoolWait + { + static void Destroy(_In_ PTP_WAIT threadPoolWait) WI_NOEXCEPT + { + ::CloseThreadpoolWait(threadPoolWait); + } + }; + + template + struct DestroyThreadPoolWork + { + static void Destroy(_In_ PTP_WORK threadpoolWork) WI_NOEXCEPT + { + ::WaitForThreadpoolWorkCallbacks(threadpoolWork, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolWork(threadpoolWork); + } + }; + + template <> + struct DestroyThreadPoolWork + { + static void Destroy(_In_ PTP_WORK threadpoolWork) WI_NOEXCEPT + { + ::CloseThreadpoolWork(threadpoolWork); + } + }; + + // Non-RTL implementation for threadpool_t parameter of DestroyThreadPoolTimer<> + struct SystemThreadPoolMethods + { + static void WINAPI SetThreadpoolTimer(_Inout_ PTP_TIMER Timer, _In_opt_ PFILETIME DueTime, _In_ DWORD Period, _In_opt_ DWORD WindowLength) WI_NOEXCEPT + { + ::SetThreadpoolTimer(Timer, DueTime, Period, WindowLength); + } + static void WaitForThreadpoolTimerCallbacks(_Inout_ PTP_TIMER Timer, _In_ BOOL CancelPendingCallbacks) WI_NOEXCEPT + { + ::WaitForThreadpoolTimerCallbacks(Timer, CancelPendingCallbacks); + } + static void CloseThreadpoolTimer(_Inout_ PTP_TIMER Timer) WI_NOEXCEPT + { + ::CloseThreadpoolTimer(Timer); + } + }; + + // SetThreadpoolTimer(timer, nullptr, 0, 0) will cancel any pending callbacks, + // then CloseThreadpoolTimer will asynchronusly close the timer if a callback is running. + template + struct DestroyThreadPoolTimer + { + static void Destroy(_In_ PTP_TIMER threadpoolTimer) WI_NOEXCEPT + { + threadpool_t::SetThreadpoolTimer(threadpoolTimer, nullptr, 0, 0); +#pragma warning(suppress:4127) // conditional expression is constant + if (cancellationBehavior != PendingCallbackCancellationBehavior::NoWait) + { + threadpool_t::WaitForThreadpoolTimerCallbacks(threadpoolTimer, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + } + threadpool_t::CloseThreadpoolTimer(threadpoolTimer); + } + }; + + // PendingCallbackCancellationBehavior::NoWait explicitly does not block waiting for + // callbacks when destructing. + template + struct DestroyThreadPoolTimer + { + static void Destroy(_In_ PTP_TIMER threadpoolTimer) WI_NOEXCEPT + { + threadpool_t::CloseThreadpoolTimer(threadpoolTimer); + } + }; + + template + struct DestroyThreadPoolIo + { + static void Destroy(_In_ PTP_IO threadpoolIo) WI_NOEXCEPT + { + ::WaitForThreadpoolIoCallbacks(threadpoolIo, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolIo(threadpoolIo); + } + }; + + template <> + struct DestroyThreadPoolIo + { + static void Destroy(_In_ PTP_IO threadpoolIo) WI_NOEXCEPT + { + ::CloseThreadpoolIo(threadpoolIo); + } + }; + + template + struct handle_invalid_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return ((ptr != INVALID_HANDLE_VALUE) && (ptr != nullptr)); } + }; + + template + struct handle_null_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return ((ptr != nullptr) && (ptr != INVALID_HANDLE_VALUE)); } + }; + + template + struct handle_null_only_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return (ptr != nullptr); } + }; + + typedef resource_policy handle_resource_policy; + } + /// @endcond + + template + using unique_any_handle_invalid = unique_any_t>>; + + template + using unique_any_handle_null = unique_any_t>>; + + template + using unique_any_handle_null_only = unique_any_t>>; + + typedef unique_any_handle_invalid unique_hfile; + typedef unique_any_handle_null unique_handle; + typedef unique_any_handle_invalid unique_hfind; + typedef unique_any unique_hmodule; + typedef unique_any_handle_null_only unique_process_handle; + + typedef unique_struct unique_token_linked_token; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) + typedef unique_any unique_sid; +#endif + + using unique_tool_help_snapshot = unique_hfile; + + typedef unique_any::Destroy> unique_threadpool_wait; + typedef unique_any::Destroy> unique_threadpool_wait_nocancel; + typedef unique_any::Destroy> unique_threadpool_wait_nowait; + typedef unique_any::Destroy> unique_threadpool_work; + typedef unique_any::Destroy> unique_threadpool_work_nocancel; + typedef unique_any::Destroy> unique_threadpool_work_nowait; + typedef unique_any::Destroy> unique_threadpool_timer; + typedef unique_any::Destroy> unique_threadpool_timer_nocancel; + typedef unique_any::Destroy> unique_threadpool_timer_nowait; + typedef unique_any::Destroy> unique_threadpool_io; + typedef unique_any::Destroy> unique_threadpool_io_nocancel; + typedef unique_any::Destroy> unique_threadpool_io_nowait; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef unique_any_handle_invalid unique_hfind_change; +#endif + + typedef unique_any event_set_scope_exit; + typedef unique_any event_reset_scope_exit; + + // Guarantees a SetEvent on the given event handle when the returned object goes out of scope + // Note: call SetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline event_set_scope_exit SetEvent_scope_exit(HANDLE hEvent) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hEvent != nullptr); + return event_set_scope_exit(hEvent); + } + + // Guarantees a ResetEvent on the given event handle when the returned object goes out of scope + // Note: call ResetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline event_reset_scope_exit ResetEvent_scope_exit(HANDLE hEvent) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hEvent != nullptr); + return event_reset_scope_exit(hEvent); + } + + // Checks to see if the given *manual reset* event is currently signaled. The event must not be an auto-reset event. + // Use when the event will only be set once (cancellation-style) or will only be reset by the polling thread + inline bool event_is_signaled(HANDLE hEvent) WI_NOEXCEPT + { + auto status = ::WaitForSingleObjectEx(hEvent, 0, FALSE); + // Fast fail will trip for wait failures, auto-reset events, or when the event is being both Set and Reset + // from a thread other than the polling thread (use event_wait directly for those cases). + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || ((status == WAIT_OBJECT_0) && (WAIT_OBJECT_0 == ::WaitForSingleObjectEx(hEvent, 0, FALSE)))); + return (status == WAIT_OBJECT_0); + } + + // Waits on the given handle for the specified duration + inline bool handle_wait(HANDLE hEvent, DWORD dwMilliseconds = INFINITE) WI_NOEXCEPT + { + DWORD status = ::WaitForSingleObjectEx(hEvent, dwMilliseconds, FALSE); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0)); + return (status == WAIT_OBJECT_0); + } + + enum class EventOptions + { + None = 0x0, + ManualReset = 0x1, + Signaled = 0x2 + }; + DEFINE_ENUM_FLAG_OPERATORS(EventOptions); + + template + class event_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit event_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructor to create an unnamed event + event_t(EventOptions options) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(options); + } + + void ResetEvent() const WI_NOEXCEPT + { + details::ResetEvent(storage_t::get()); + } + + void SetEvent() const WI_NOEXCEPT + { + details::SetEvent(storage_t::get()); + } + + // Guarantees a SetEvent on the given event handle when the returned object goes out of scope + // Note: call SetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD event_set_scope_exit SetEvent_scope_exit() const WI_NOEXCEPT + { + return wil::SetEvent_scope_exit(storage_t::get()); + } + + // Guarantees a ResetEvent on the given event handle when the returned object goes out of scope + // Note: call ResetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD event_reset_scope_exit ResetEvent_scope_exit() const WI_NOEXCEPT + { + return wil::ResetEvent_scope_exit(storage_t::get()); + } + + // Checks if a *manual reset* event is currently signaled. The event must not be an auto-reset event. + // Use when the event will only be set once (cancellation-style) or will only be reset by the polling thread + bool is_signaled() const WI_NOEXCEPT + { + return wil::event_is_signaled(storage_t::get()); + } + + // Basic WaitForSingleObject on the event handle with the given timeout + bool wait(DWORD dwMilliseconds = INFINITE) const WI_NOEXCEPT + { + return wil::handle_wait(storage_t::get(), dwMilliseconds); + } + + // Tries to create a named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(EventOptions options, PCWSTR name, _In_opt_ LPSECURITY_ATTRIBUTES pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + { + auto handle = ::CreateEventExW(pSecurity, name, (WI_IsFlagSet(options, EventOptions::ManualReset) ? CREATE_EVENT_MANUAL_RESET : 0) | (WI_IsFlagSet(options, EventOptions::Signaled) ? CREATE_EVENT_INITIAL_SET : 0), EVENT_ALL_ACCESS); + if (!handle) + { + assign_to_opt_param(pAlreadyExists, false); + return false; + } + assign_to_opt_param(pAlreadyExists, (::GetLastError() == ERROR_ALREADY_EXISTS)); + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_event_nothrow, void with exceptions for shared_event and unique_event + result create(EventOptions options = EventOptions::None, PCWSTR name = nullptr, _In_opt_ LPSECURITY_ATTRIBUTES pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(options, name, pSecurity, pAlreadyExists)); + } + + // Tries to open the named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenEventW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_event_nothrow, void with exceptions for shared_event and unique_event + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_event_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_event_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_event; +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + enum class SlimEventType + { + AutoReset, + ManualReset, + }; + + /** A lean and mean event class. + This class provides a very similar API to `wil::unique_event` but doesn't require a kernel object. + + The two variants of this class are: + - `wil::slim_event_auto_reset` + - `wil::slim_event_manual_reset` + + In addition, `wil::slim_event_auto_reset` has the alias `wil::slim_event`. + + Some key differences to `wil::unique_event` include: + - There is no 'create()' function, as initialization occurs in the constructor and can't fail. + - The move functions have been deleted. + - For auto-reset events, the `is_signaled()` function doesn't reset the event. (Use `ResetEvent()` instead.) + - The `ResetEvent()` function returns the previous state of the event. + - To create a manual reset event, use `wil::slim_event_manual_reset'. + ~~~~ + wil::slim_event finished; + std::thread doStuff([&finished] () { + Sleep(10); + finished.SetEvent(); + }); + finished.wait(); + + std::shared_ptr CreateSharedEvent(bool startSignaled) + { + return std::make_shared(startSignaled); + } + ~~~~ */ + template + class slim_event_t + { + public: + slim_event_t() WI_NOEXCEPT = default; + + slim_event_t(bool isSignaled) WI_NOEXCEPT : + m_isSignaled(isSignaled ? TRUE : FALSE) + { + } + + // Cannot change memory location. + slim_event_t(const slim_event_t&) = delete; + slim_event_t(slim_event_t&&) = delete; + slim_event_t& operator=(const slim_event_t&) = delete; + slim_event_t& operator=(slim_event_t&&) = delete; + + // Returns the previous state of the event. + bool ResetEvent() WI_NOEXCEPT + { + return !!InterlockedExchange(&m_isSignaled, FALSE); + } + + void SetEvent() WI_NOEXCEPT + { + // FYI: 'WakeByAddress*' invokes a full memory barrier. + WriteRelease(&m_isSignaled, TRUE); + + #pragma warning(suppress:4127) // conditional expression is constant + if (Type == SlimEventType::AutoReset) + { + WakeByAddressSingle(&m_isSignaled); + } + else + { + WakeByAddressAll(&m_isSignaled); + } + } + + // Checks if the event is currently signaled. + // Note: Unlike Win32 auto-reset event objects, this will not reset the event. + bool is_signaled() const WI_NOEXCEPT + { + return !!ReadAcquire(&m_isSignaled); + } + + bool wait(DWORD timeoutMiliseconds) WI_NOEXCEPT + { + if (timeoutMiliseconds == 0) + { + return TryAcquireEvent(); + } + else if (timeoutMiliseconds == INFINITE) + { + return wait(); + } + + UINT64 startTime; + QueryUnbiasedInterruptTime(&startTime); + + UINT64 elapsedTimeMilliseconds = 0; + + while (!TryAcquireEvent()) + { + if (elapsedTimeMilliseconds >= timeoutMiliseconds) + { + return false; + } + + DWORD newTimeout = static_cast(timeoutMiliseconds - elapsedTimeMilliseconds); + + if (!WaitForSignal(newTimeout)) + { + return false; + } + + UINT64 currTime; + QueryUnbiasedInterruptTime(&currTime); + + elapsedTimeMilliseconds = (currTime - startTime) / (10 * 1000); + } + + return true; + } + + bool wait() WI_NOEXCEPT + { + while (!TryAcquireEvent()) + { + if (!WaitForSignal(INFINITE)) + { + return false; + } + } + + return true; + } + + private: + bool TryAcquireEvent() WI_NOEXCEPT + { + #pragma warning(suppress:4127) // conditional expression is constant + if (Type == SlimEventType::AutoReset) + { + return ResetEvent(); + } + else + { + return is_signaled(); + } + } + + bool WaitForSignal(DWORD timeoutMiliseconds) WI_NOEXCEPT + { + LONG falseValue = FALSE; + BOOL waitResult = WaitOnAddress(&m_isSignaled, &falseValue, sizeof(m_isSignaled), timeoutMiliseconds); + __FAIL_FAST_ASSERT__(waitResult || ::GetLastError() == ERROR_TIMEOUT); + return !!waitResult; + } + + LONG m_isSignaled = FALSE; + }; + + /** An event object that will atomically revert to an unsignaled state anytime a `wait()` call succeeds (i.e. returns true). */ + using slim_event_auto_reset = slim_event_t; + + /** An event object that once signaled remains that way forever, unless `ResetEvent()` is called. */ + using slim_event_manual_reset = slim_event_t; + + /** An alias for `wil::slim_event_auto_reset`. */ + using slim_event = slim_event_auto_reset; + +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + + typedef unique_any mutex_release_scope_exit; + + WI_NODISCARD inline mutex_release_scope_exit ReleaseMutex_scope_exit(_In_ HANDLE hMutex) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hMutex != nullptr); + return mutex_release_scope_exit(hMutex); + } + + // For efficiency, avoid using mutexes when an srwlock or condition variable will do. + template + class mutex_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit mutex_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructor to create a mutex (prefer unnamed (nullptr) for the name) + mutex_t(_In_opt_ PCWSTR name) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(name); + } + + void ReleaseMutex() const WI_NOEXCEPT + { + details::ReleaseMutex(storage_t::get()); + } + + WI_NODISCARD mutex_release_scope_exit ReleaseMutex_scope_exit() const WI_NOEXCEPT + { + return wil::ReleaseMutex_scope_exit(storage_t::get()); + } + + WI_NODISCARD mutex_release_scope_exit acquire(_Out_opt_ DWORD *pStatus = nullptr, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) const WI_NOEXCEPT + { + auto handle = storage_t::get(); + DWORD status = ::WaitForSingleObjectEx(handle, dwMilliseconds, bAlertable); + assign_to_opt_param(pStatus, status); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0) || (status == WAIT_ABANDONED) || (bAlertable && (status == WAIT_IO_COMPLETION))); + return mutex_release_scope_exit(((status == WAIT_OBJECT_0) || (status == WAIT_ABANDONED)) ? handle : nullptr); + } + + // Tries to create a named mutex -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(_In_opt_ PCWSTR name, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pMutexAttributes = nullptr) + { + auto handle = ::CreateMutexExW(pMutexAttributes, name, dwFlags, desiredAccess); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_mutex_nothrow, void with exceptions for shared_mutex and unique_mutex + result create(_In_opt_ PCWSTR name = nullptr, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pMutexAttributes = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(name, dwFlags, desiredAccess, pMutexAttributes)); + } + + // Tries to open a named mutex -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenMutexW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_mutex_nothrow, void with exceptions for shared_mutex and unique_mutex + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_mutex_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_mutex_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_mutex; +#endif + + typedef unique_any semaphore_release_scope_exit; + + WI_NODISCARD inline semaphore_release_scope_exit ReleaseSemaphore_scope_exit(_In_ HANDLE hSemaphore) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hSemaphore != nullptr); + return semaphore_release_scope_exit(hSemaphore); + } + + template + class semaphore_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit semaphore_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Note that for custom-constructors the type given the constructor has to match exactly as not all implicit conversions will make it through the + // forwarding constructor. This constructor, for example, uses 'int' instead of 'LONG' as the count to ease that particular issue (const numbers are int by default). + explicit semaphore_t(int initialCount, int maximumCount, _In_opt_ PCWSTR name = nullptr, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(initialCount, maximumCount, name, desiredAccess, pSemaphoreAttributes); + } + + void ReleaseSemaphore(long nReleaseCount = 1, _In_opt_ long *pnPreviousCount = nullptr) WI_NOEXCEPT + { + long nPreviousCount = 0; + __FAIL_FAST_ASSERT__(::ReleaseSemaphore(storage_t::get(), nReleaseCount, &nPreviousCount)); + assign_to_opt_param(pnPreviousCount, nPreviousCount); + } + + WI_NODISCARD semaphore_release_scope_exit ReleaseSemaphore_scope_exit() WI_NOEXCEPT + { + return wil::ReleaseSemaphore_scope_exit(storage_t::get()); + } + + WI_NODISCARD semaphore_release_scope_exit acquire(_Out_opt_ DWORD *pStatus = nullptr, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) WI_NOEXCEPT + { + auto handle = storage_t::get(); + DWORD status = ::WaitForSingleObjectEx(handle, dwMilliseconds, bAlertable); + assign_to_opt_param(pStatus, status); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0) || (bAlertable && (status == WAIT_IO_COMPLETION))); + return semaphore_release_scope_exit((status == WAIT_OBJECT_0) ? handle : nullptr); + } + + // Tries to create a named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(LONG lInitialCount, LONG lMaximumCount, _In_opt_ PCWSTR name, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + auto handle = ::CreateSemaphoreExW(pSemaphoreAttributes, lInitialCount, lMaximumCount, name, 0, desiredAccess); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_semaphore_nothrow, void with exceptions for shared_event and unique_event + result create(LONG lInitialCount, LONG lMaximumCount, _In_opt_ PCWSTR name = nullptr, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(lInitialCount, lMaximumCount, name, desiredAccess, pSemaphoreAttributes)); + } + + // Tries to open the named semaphore -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenSemaphoreW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_semaphore_nothrow, void with exceptions for shared_semaphore and unique_semaphore + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_semaphore_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_semaphore_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_semaphore; +#endif + + typedef unique_any rwlock_release_exclusive_scope_exit; + typedef unique_any rwlock_release_shared_scope_exit; + + WI_NODISCARD inline rwlock_release_exclusive_scope_exit AcquireSRWLockExclusive(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + ::AcquireSRWLockExclusive(plock); + return rwlock_release_exclusive_scope_exit(plock); + } + + WI_NODISCARD inline rwlock_release_shared_scope_exit AcquireSRWLockShared(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + ::AcquireSRWLockShared(plock); + return rwlock_release_shared_scope_exit(plock); + } + + WI_NODISCARD inline rwlock_release_exclusive_scope_exit TryAcquireSRWLockExclusive(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + return rwlock_release_exclusive_scope_exit(::TryAcquireSRWLockExclusive(plock) ? plock : nullptr); + } + + WI_NODISCARD inline rwlock_release_shared_scope_exit TryAcquireSRWLockShared(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + return rwlock_release_shared_scope_exit(::TryAcquireSRWLockShared(plock) ? plock : nullptr); + } + + class srwlock + { + public: + srwlock(const srwlock&) = delete; + srwlock(srwlock&&) = delete; + srwlock& operator=(const srwlock&) = delete; + srwlock& operator=(srwlock&&) = delete; + + srwlock() = default; + + WI_NODISCARD rwlock_release_exclusive_scope_exit lock_exclusive() WI_NOEXCEPT + { + return wil::AcquireSRWLockExclusive(&m_lock); + } + + WI_NODISCARD rwlock_release_exclusive_scope_exit try_lock_exclusive() WI_NOEXCEPT + { + return wil::TryAcquireSRWLockExclusive(&m_lock); + } + + WI_NODISCARD rwlock_release_shared_scope_exit lock_shared() WI_NOEXCEPT + { + return wil::AcquireSRWLockShared(&m_lock); + } + + WI_NODISCARD rwlock_release_shared_scope_exit try_lock_shared() WI_NOEXCEPT + { + return wil::TryAcquireSRWLockShared(&m_lock); + } + + private: + SRWLOCK m_lock = SRWLOCK_INIT; + }; + + typedef unique_any cs_leave_scope_exit; + + WI_NODISCARD inline cs_leave_scope_exit EnterCriticalSection(_Inout_ CRITICAL_SECTION *pcs) WI_NOEXCEPT + { + ::EnterCriticalSection(pcs); + return cs_leave_scope_exit(pcs); + } + + WI_NODISCARD inline cs_leave_scope_exit TryEnterCriticalSection(_Inout_ CRITICAL_SECTION *pcs) WI_NOEXCEPT + { + return cs_leave_scope_exit(::TryEnterCriticalSection(pcs) ? pcs : nullptr); + } + + // Critical sections are worse than srwlocks in performance and memory usage (their only unique attribute + // being recursive acquisition). Prefer srwlocks over critical sections when you don't need recursive acquisition. + class critical_section + { + public: + critical_section(const critical_section&) = delete; + critical_section(critical_section&&) = delete; + critical_section& operator=(const critical_section&) = delete; + critical_section& operator=(critical_section&&) = delete; + + critical_section(ULONG spincount = 0) WI_NOEXCEPT + { + // Initialization will not fail without invalid params... + ::InitializeCriticalSectionEx(&m_cs, spincount, 0); + } + + ~critical_section() WI_NOEXCEPT + { + ::DeleteCriticalSection(&m_cs); + } + + WI_NODISCARD cs_leave_scope_exit lock() WI_NOEXCEPT + { + return wil::EnterCriticalSection(&m_cs); + } + + WI_NODISCARD cs_leave_scope_exit try_lock() WI_NOEXCEPT + { + return wil::TryEnterCriticalSection(&m_cs); + } + + private: + CRITICAL_SECTION m_cs; + }; + + class condition_variable + { + public: + condition_variable(const condition_variable&) = delete; + condition_variable(condition_variable&&) = delete; + condition_variable& operator=(const condition_variable&) = delete; + condition_variable& operator=(condition_variable&&) = delete; + + condition_variable() = default; + + void notify_one() WI_NOEXCEPT + { + ::WakeConditionVariable(&m_cv); + } + + void notify_all() WI_NOEXCEPT + { + ::WakeAllConditionVariable(&m_cv); + } + + void wait(const cs_leave_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + void wait(const rwlock_release_exclusive_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + void wait(const rwlock_release_shared_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + bool wait_for(const cs_leave_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableCS(&m_cv, lock.get(), timeoutMs); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + bool wait_for(const rwlock_release_exclusive_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableSRW(&m_cv, lock.get(), timeoutMs, 0); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + bool wait_for(const rwlock_release_shared_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableSRW(&m_cv, lock.get(), timeoutMs, CONDITION_VARIABLE_LOCKMODE_SHARED); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + private: + CONDITION_VARIABLE m_cv = CONDITION_VARIABLE_INIT; + }; + + /// @cond + namespace details + { + template struct string_allocator + { + static void* allocate(size_t /*size*/) WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "This type did not provide a string_allocator, add a specialization of string_allocator to support your type."); + return nullptr; + } + }; + } + /// @endcond + + // This string helper does not support the ansi wil string helpers + template + PCWSTR string_get_not_null(const string_type& string) + { + return string ? string.get() : L""; + } + +#ifndef MAKE_UNIQUE_STRING_MAX_CCH +#define MAKE_UNIQUE_STRING_MAX_CCH 2147483647 // max buffer size, in characters, that we support (same as INT_MAX) +#endif + + /** Copies a string (up to the given length) into memory allocated with a specified allocator returning null on failure. + Use `wil::make_unique_string_nothrow()` for string resources returned from APIs that must satisfy a memory allocation contract + that requires use of a specific allocator and free function (CoTaskMemAlloc/CoTaskMemFree, LocalAlloc/LocalFree, GlobalAlloc/GlobalFree, etc.). + ~~~ + auto str = wil::make_unique_string_nothrow(L"a string of words", 8); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + + auto str = wil::make_unique_string_nothrow(L"a string"); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + + NOTE: If source is not null terminated, then length MUST be equal to or less than the size + of the buffer pointed to by source. + ~~~ + */ + template string_type make_unique_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source && (length == static_cast(-1))); + + // When the source string exists, calculate the number of characters to copy up to either + // 1) the length that is given + // 2) the length of the source string. When the source does not exist, use the given length + // for calculating both the size of allocated buffer and the number of characters to copy. + size_t lengthToCopy = length; + if (source) + { + size_t maxLength = length < MAKE_UNIQUE_STRING_MAX_CCH ? length : MAKE_UNIQUE_STRING_MAX_CCH; + PCWSTR endOfSource = source; + while (maxLength && (*endOfSource != L'\0')) + { + endOfSource++; + maxLength--; + } + lengthToCopy = endOfSource - source; + } + + if (length == static_cast(-1)) + { + length = lengthToCopy; + } + const size_t allocatedBytes = (length + 1) * sizeof(*source); + auto result = static_cast(details::string_allocator::allocate(allocatedBytes)); + + if (result) + { + if (source) + { + const size_t bytesToCopy = lengthToCopy * sizeof(*source); + memcpy_s(result, allocatedBytes, source, bytesToCopy); + result[lengthToCopy] = L'\0'; // ensure the copied string is zero terminated + } + else + { + *result = L'\0'; // ensure null terminated in the "reserve space" use case. + } + result[length] = L'\0'; // ensure the final char of the buffer is zero terminated + } + return string_type(result); + } +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source && (length == static_cast(-1))); + + if (length == static_cast(-1)) + { + length = strlen(source); + } + const size_t cb = (length + 1) * sizeof(*source); + auto result = static_cast(details::string_allocator::allocate(cb)); + if (result) + { + if (source) + { + memcpy_s(result, cb, source, cb - sizeof(*source)); + } + else + { + *result = '\0'; // ensure null terminated in the "reserve space" use case. + } + result[length] = '\0'; // ensure zero terminated + } + return string_type(result); + } +#endif // WIL_NO_ANSI_STRINGS + + /** Copies a given string into memory allocated with a specified allocator that will fail fast on failure. + The use of variadic templates parameters supports the 2 forms of make_unique_string, see those for more details. + */ + template string_type make_unique_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + auto result(make_unique_string_nothrow(source, length)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + auto result(make_unique_ansistring_nothrow(source, length)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_NO_ANSI_STRINGS + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into memory allocated with a specified allocator that will throw on failure. + The use of variadic templates parameters supports the 2 forms of make_unique_string, see those for more details. + */ + template string_type make_unique_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + auto result(make_unique_string_nothrow(source, length)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) + { + auto result(make_unique_ansistring_nothrow(source, length)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_NO_ANSI_STRINGS +#endif // WIL_ENABLE_EXCEPTIONS + + /// @cond + namespace details + { + // string_maker abstracts creating a string for common string types. This form supports the + // wil::unique_xxx_string types. Specializations of other types like HSTRING and std::wstring + // are found in wil\winrt.h and wil\stl.h. + // This design supports creating the string in a single step or using two phase construction. + + template struct string_maker + { + HRESULT make( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, + size_t length) + { + m_value = make_unique_string_nothrow(source, length); + return m_value ? S_OK : E_OUTOFMEMORY; + } + + wchar_t* buffer() { WI_ASSERT(m_value.get()); return m_value.get(); } + + string_type release() { return wistd::move(m_value); } + + // Utility to abstract access to the null terminated m_value of all string types. + static PCWSTR get(const string_type& value) { return value.get(); } + + private: + string_type m_value; // a wil::unique_xxx_string type. + }; + + struct SecureZeroData + { + void *pointer; + size_t sizeBytes; + SecureZeroData(void *pointer_, size_t sizeBytes_ = 0) WI_NOEXCEPT { pointer = pointer_; sizeBytes = sizeBytes_; } + operator void *() const WI_NOEXCEPT { return pointer; } + static void Close(SecureZeroData data) WI_NOEXCEPT { ::SecureZeroMemory(data.pointer, data.sizeBytes); } + }; + } + /// @endcond + + typedef unique_any secure_zero_memory_scope_exit; + + WI_NODISCARD inline secure_zero_memory_scope_exit SecureZeroMemory_scope_exit(_In_reads_bytes_(sizeBytes) void* pSource, size_t sizeBytes) + { + return secure_zero_memory_scope_exit(details::SecureZeroData(pSource, sizeBytes)); + } + + WI_NODISCARD inline secure_zero_memory_scope_exit SecureZeroMemory_scope_exit(_In_ PWSTR initializedString) + { + return SecureZeroMemory_scope_exit(static_cast(initializedString), wcslen(initializedString) * sizeof(initializedString[0])); + } + + /// @cond + namespace details + { + inline void __stdcall FreeProcessHeap(_Pre_opt_valid_ _Frees_ptr_opt_ void* p) + { + ::HeapFree(::GetProcessHeap(), 0, p); + } + } + /// @endcond + + struct process_heap_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + details::FreeProcessHeap(p); + } + }; + + struct virtualalloc_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + ::VirtualFree(p, 0, MEM_RELEASE); + } + }; + + struct mapview_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + ::UnmapViewOfFile(p); + } + }; + + template + using unique_process_heap_ptr = wistd::unique_ptr; + + typedef unique_any unique_process_heap_string; + + /// @cond + namespace details + { + template<> struct string_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size); + } + }; + } + /// @endcond + + /** Manages a typed pointer allocated with VirtualAlloc + A specialization of wistd::unique_ptr<> that frees via VirtualFree(p, 0, MEM_RELEASE). + */ + template + using unique_virtualalloc_ptr = wistd::unique_ptr; + + /** Manages a typed pointer allocated with MapViewOfFile + A specialization of wistd::unique_ptr<> that frees via UnmapViewOfFile(p). + */ + template + using unique_mapview_ptr = wistd::unique_ptr; + +#endif // __WIL_WINBASE_ + +#if defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED) +#define __WIL_WINBASE_NOTHROW_T_DEFINED + // unique_event_watcher, unique_event_watcher_nothrow, unique_event_watcher_failfast + // + // Clients must include or to enable use of this class as it uses new(std::nothrow). + // This is to avoid the dependency on those headers that some clients can't tolerate. + // + // These classes makes it easy to execute a provided function when an event + // is signaled. It will create the event handle for you, take ownership of one + // or duplicate a handle provided. It supports the ability to signal the + // event using SetEvent() and SetEvent_scope_exit(); + // + // This can be used to support producer-consumer pattern + // where a producer updates some state then signals the event when done. + // The consumer will consume that state in the callback provided to unique_event_watcher. + // + // Note, multiple signals may coalesce into a single callback. + // + // Example use of throwing version: + // auto globalStateWatcher = wil::make_event_watcher([] + // { + // currentState = GetGlobalState(); + // }); + // + // UpdateGlobalState(value); + // globalStateWatcher.SetEvent(); // signal observers so they can update + // + // Example use of non-throwing version: + // auto globalStateWatcher = wil::make_event_watcher_nothrow([] + // { + // currentState = GetGlobalState(); + // }); + // RETURN_IF_NULL_ALLOC(globalStateWatcher); + // + // UpdateGlobalState(value); + // globalStateWatcher.SetEvent(); // signal observers so they can update + + /// @cond + namespace details + { + struct event_watcher_state + { + event_watcher_state(unique_event_nothrow &&eventHandle, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_event(wistd::move(eventHandle)) + { + } + wistd::function m_callback; + unique_event_nothrow m_event; + // The thread pool must be last to ensure that the other members are valid + // when it is destructed as it will reference them. + unique_threadpool_wait m_threadPoolWait; + }; + + inline void delete_event_watcher_state(_In_opt_ event_watcher_state *watcherStorage) { delete watcherStorage; } + + typedef resource_policy event_watcher_state_resource_policy; + } + /// @endcond + + template + class event_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit event_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + template + event_watcher_t(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(wistd::move(eventHandle), wistd::move(callback)); + } + + event_watcher_t(_In_ HANDLE eventHandle, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(eventHandle, wistd::move(callback)); + } + + event_watcher_t(wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(wistd::move(callback)); + } + + template + result create(unique_any_t, event_err_policy>> &&eventHandle, + wistd::function &&callback) + { + return err_policy::HResult(create_take_hevent_ownership(eventHandle.release(), wistd::move(callback))); + } + + // Creates the event that you will be watching. + result create(wistd::function &&callback) + { + unique_event_nothrow eventHandle; + HRESULT hr = eventHandle.create(EventOptions::ManualReset); // auto-reset is supported too. + if (FAILED(hr)) + { + return err_policy::HResult(hr); + } + return err_policy::HResult(create_take_hevent_ownership(eventHandle.release(), wistd::move(callback))); + } + + // Input is an event handler that is duplicated into this class. + result create(_In_ HANDLE eventHandle, wistd::function &&callback) + { + unique_event_nothrow ownedHandle; + if (!DuplicateHandle(GetCurrentProcess(), eventHandle, GetCurrentProcess(), &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return err_policy::LastError(); + } + return err_policy::HResult(create_take_hevent_ownership(ownedHandle.release(), wistd::move(callback))); + } + + // Provide access to the inner event and the very common SetEvent() method on it. + unique_event_nothrow const& get_event() const WI_NOEXCEPT { return storage_t::get()->m_event; } + void SetEvent() const WI_NOEXCEPT { storage_t::get()->m_event.SetEvent(); } + + private: + + // Had to move this from a Lambda so it would compile in C++/CLI (which thought the Lambda should be a managed function for some reason). + static void CALLBACK wait_callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT) + { + auto pThis = static_cast(context); + // Manual events must be re-set to avoid missing the last notification. + pThis->m_event.ResetEvent(); + // Call the client before re-arming to ensure that multiple callbacks don't + // run concurrently. + pThis->m_callback(); + SetThreadpoolWait(pThreadPoolWait, pThis->m_event.get(), nullptr); // valid params ensure success + } + + // To avoid template expansion (if unique_event/unique_event_nothrow forms were used) this base + // create function takes a raw handle and assumes its ownership, even on failure. + HRESULT create_take_hevent_ownership(_In_ HANDLE rawHandleOwnershipTaken, wistd::function &&callback) + { + __FAIL_FAST_ASSERT__(rawHandleOwnershipTaken != nullptr); // invalid parameter + unique_event_nothrow eventHandle(rawHandleOwnershipTaken); + wistd::unique_ptr watcherState(new(std::nothrow) details::event_watcher_state(wistd::move(eventHandle), wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(wait_callback, watcherState.get(), nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + storage_t::reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_event.get(), nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_event_watcher_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_event_watcher_failfast; + + template + unique_event_watcher_nothrow make_event_watcher_nothrow(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(wistd::move(eventHandle), wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_event_watcher_nothrow make_event_watcher_nothrow(_In_ HANDLE eventHandle, wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(eventHandle, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_event_watcher_nothrow make_event_watcher_nothrow(wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + template + unique_event_watcher_failfast make_event_watcher_failfast(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + { + return unique_event_watcher_failfast(wistd::move(eventHandle), wistd::move(callback)); + } + + inline unique_event_watcher_failfast make_event_watcher_failfast(_In_ HANDLE eventHandle, wistd::function &&callback) + { + return unique_event_watcher_failfast(eventHandle, wistd::move(callback)); + } + + inline unique_event_watcher_failfast make_event_watcher_failfast(wistd::function &&callback) + { + return unique_event_watcher_failfast(wistd::move(callback)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_event_watcher; + + template + unique_event_watcher make_event_watcher(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + { + return unique_event_watcher(wistd::move(eventHandle), wistd::move(callback)); + } + + inline unique_event_watcher make_event_watcher(_In_ HANDLE eventHandle, wistd::function &&callback) + { + return unique_event_watcher(eventHandle, wistd::move(callback)); + } + + inline unique_event_watcher make_event_watcher(wistd::function &&callback) + { + return unique_event_watcher(wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_WINBASE_NOTHROW_T_DEFINED + +#if defined(__WIL_WINBASE_) && !defined(__WIL_WINBASE_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINBASE_STL + typedef shared_any_t>> shared_event; + typedef shared_any_t>> shared_mutex; + typedef shared_any_t>> shared_semaphore; + typedef shared_any shared_hfile; + typedef shared_any shared_handle; + typedef shared_any shared_hfind; + typedef shared_any shared_hmodule; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef shared_any shared_threadpool_wait; + typedef shared_any shared_threadpool_wait_nocancel; + typedef shared_any shared_threadpool_work; + typedef shared_any shared_threadpool_work_nocancel; + + typedef shared_any shared_hfind_change; +#endif + + typedef weak_any weak_event; + typedef weak_any weak_mutex; + typedef weak_any weak_semaphore; + typedef weak_any weak_hfile; + typedef weak_any weak_handle; + typedef weak_any weak_hfind; + typedef weak_any weak_hmodule; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef weak_any weak_threadpool_wait; + typedef weak_any weak_threadpool_wait_nocancel; + typedef weak_any weak_threadpool_work; + typedef weak_any weak_threadpool_work_nocancel; + + typedef weak_any weak_hfind_change; +#endif + +#endif // __WIL_WINBASE_STL + +#if defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED_STL) && defined(WIL_RESOURCE_STL) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINBASE_NOTHROW_T_DEFINED_STL + typedef shared_any_t>> shared_event_watcher; + typedef weak_any weak_event_watcher; +#endif // __WIL_WINBASE_NOTHROW_T_DEFINED_STL + +#if defined(__WIL_WINBASE_) && !defined(__WIL_WINBASE_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINBASE_DESKTOP + /// @cond + namespace details + { + inline void __stdcall DestroyPrivateObjectSecurity(_Pre_opt_valid_ _Frees_ptr_opt_ PSECURITY_DESCRIPTOR pObjectDescriptor) WI_NOEXCEPT + { + ::DestroyPrivateObjectSecurity(&pObjectDescriptor); + } + } + /// @endcond + + using hlocal_deleter = function_deleter; + + template + using unique_hlocal_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + Use `wil::make_unique_hlocal_nothrow()` for resources returned from APIs that must satisfy a memory allocation contract that requires the use of `LocalAlloc()` / `LocalFree()`. + Use `wil::make_unique_nothrow()` when `LocalAlloc()` is not required. + + Allocations are initialized with placement new and will call constructors (if present), but this does not guarantee initialization. + + Note that `wil::make_unique_hlocal_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_hlocal_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal_nothrow(Args&&... args) + { + static_assert(wistd::is_trivially_destructible::value, "T has a destructor that won't be run when used with this function; use make_unique instead"); + unique_hlocal_ptr sp(static_cast(::LocalAlloc(LMEM_FIXED, sizeof(T)))); + if (sp) + { + // use placement new to initialize memory from the previous allocation + new (sp.get()) T(wistd::forward(args)...); + } + return sp; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal_nothrow(size_t size) + { + typedef typename wistd::remove_extent::type E; + static_assert(wistd::is_trivially_destructible::value, "E has a destructor that won't be run when used with this function; use make_unique instead"); + FAIL_FAST_IF((__WI_SIZE_MAX / sizeof(E)) < size); + size_t allocSize = sizeof(E) * size; + unique_hlocal_ptr sp(static_cast(::LocalAlloc(LMEM_FIXED, allocSize))); + if (sp) + { + // use placement new to initialize memory from the previous allocation; + // note that array placement new cannot be used as the standard allows for operator new[] + // to consume overhead in the allocation for internal bookkeeping + for (auto& elem : make_range(static_cast(sp.get()), size)) + { + new (&elem) E(); + } + } + return sp; + } + + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal_failfast(Args&&... args) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal_failfast(size_t size) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal(Args&&... args) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal(size_t size) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any unique_hlocal; + typedef unique_any unique_hlocal_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_hlocal_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + struct localalloc_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::LocalAlloc(LMEM_FIXED, size); + } + }; + + template<> struct string_allocator : localalloc_allocator {}; +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : localalloc_allocator {}; +#endif // WIL_NO_ANSI_STRINGS + } + /// @endcond + + inline auto make_hlocal_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_hlocal_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifndef WIL_NO_ANSI_STRINGS + inline auto make_hlocal_ansistring_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_ansistring_nothrow(source, length); + } + + inline auto make_hlocal_ansistring_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_ansistring_failfast(source, length); + } +#endif + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_hlocal_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } + +#ifndef WIL_NO_ANSI_STRINGS + inline auto make_hlocal_ansistring( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) + { + return make_unique_ansistring(source, length); + } +#endif // WIL_NO_ANSI_STRINGS +#endif // WIL_ENABLE_EXCEPTIONS + + struct hlocal_secure_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { +#pragma warning(suppress: 26006 26007) // LocalSize() ensures proper buffer length + ::SecureZeroMemory(p, ::LocalSize(p)); // this is safe since LocalSize() returns 0 on failure + ::LocalFree(p); + } + } + }; + + template + using unique_hlocal_secure_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_nothrow(Args&&... args) + { + return unique_hlocal_secure_ptr(make_unique_hlocal_nothrow(wistd::forward(args)...).release()); + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_nothrow(size_t size) + { + return unique_hlocal_secure_ptr(make_unique_hlocal_nothrow(size).release()); + } + + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_failfast(Args&&... args) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_failfast(size_t size) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure(Args&&... args) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure(size_t size) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_hlocal_secure_ptr unique_hlocal_string_secure; + + /** Copies a given string into secure memory allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure_nothrow(L"a string"); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure_nothrow(_In_ PCWSTR source) WI_NOEXCEPT + { + return unique_hlocal_string_secure(make_hlocal_string_nothrow(source).release()); + } + + /** Copies a given string into secure memory allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure_failfast(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure_failfast(_In_ PCWSTR source) WI_NOEXCEPT + { + unique_hlocal_string_secure result(make_hlocal_string_secure_nothrow(source)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into secure memory allocated with `LocalAlloc()`. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure(_In_ PCWSTR source) + { + unique_hlocal_string_secure result(make_hlocal_string_secure_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif + + using hglobal_deleter = function_deleter; + + template + using unique_hglobal_ptr = wistd::unique_ptr; + + typedef unique_any unique_hglobal; + typedef unique_any unique_hglobal_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_hglobal_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + template<> struct string_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::GlobalAlloc(GPTR, size); + } + }; + } + /// @endcond + + inline auto make_process_heap_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_process_heap_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_process_heap_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any_handle_null unique_hheap; + typedef unique_any unique_tls; + typedef unique_any unique_hlocal_security_descriptor; + typedef unique_any unique_private_security_descriptor; + +#if defined(_WINUSER_) && !defined(__WIL__WINUSER_) +#define __WIL__WINUSER_ + typedef unique_any unique_haccel; + typedef unique_any unique_hcursor; + typedef unique_any unique_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef unique_any unique_hhook; +#endif +#if !defined(NOWINABLE) + typedef unique_any unique_hwineventhook; +#endif +#endif // __WIL__WINUSER_ + +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef unique_any unique_hdesk; + typedef unique_any unique_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + +#endif +#if defined(__WIL_WINBASE_DESKTOP) && !defined(__WIL_WINBASE_DESKTOP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINBASE_DESKTOP_STL + typedef shared_any shared_hheap; + typedef shared_any shared_hlocal; + typedef shared_any shared_tls; + typedef shared_any shared_hlocal_security_descriptor; + typedef shared_any shared_private_security_descriptor; + typedef shared_any shared_haccel; + typedef shared_any shared_hcursor; +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef shared_any shared_hdesk; + typedef shared_any shared_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + typedef shared_any shared_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef shared_any shared_hhook; +#endif +#if !defined(NOWINABLE) + typedef shared_any shared_hwineventhook; +#endif + + typedef weak_any weak_hheap; + typedef weak_any weak_hlocal; + typedef weak_any weak_tls; + typedef weak_any weak_hlocal_security_descriptor; + typedef weak_any weak_private_security_descriptor; + typedef weak_any weak_haccel; + typedef weak_any weak_hcursor; +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef weak_any weak_hdesk; + typedef weak_any weak_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + typedef weak_any weak_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef weak_any weak_hhook; +#endif +#if !defined(NOWINABLE) + typedef weak_any weak_hwineventhook; +#endif +#endif // __WIL_WINBASE_DESKTOP_STL + +#if defined(_COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL__COMBASEAPI_H_ +#if (NTDDI_VERSION >= NTDDI_WIN8) + typedef unique_any unique_mta_usage_cookie; +#endif + + typedef unique_any unique_com_class_object_cookie; + + /// @cond + namespace details + { + inline void __stdcall MultiQiCleanup(_In_ MULTI_QI* multiQi) + { + if (multiQi->pItf) + { + multiQi->pItf->Release(); + multiQi->pItf = nullptr; + } + } + } + /// @endcond + + //! A type that calls CoRevertToSelf on destruction (or reset()). + using unique_coreverttoself_call = unique_call; + + //! Calls CoImpersonateClient and fail-fasts if it fails; returns an RAII object that reverts + WI_NODISCARD inline unique_coreverttoself_call CoImpersonateClient_failfast() + { + FAIL_FAST_IF_FAILED(::CoImpersonateClient()); + return unique_coreverttoself_call(); + } + + typedef unique_struct unique_multi_qi; +#endif // __WIL__COMBASEAPI_H_ +#if defined(__WIL__COMBASEAPI_H_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__COMBASEAPI_H_EXCEPTIONAL) +#define __WIL__COMBASEAPI_H_EXCEPTIONAL + WI_NODISCARD inline unique_coreverttoself_call CoImpersonateClient() + { + THROW_IF_FAILED(::CoImpersonateClient()); + return unique_coreverttoself_call(); + } +#endif +#if defined(__WIL__COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H__STL) && defined(WIL_RESOURCE_STL) && (NTDDI_VERSION >= NTDDI_WIN8) +#define __WIL__COMBASEAPI_H__STL + typedef shared_any shared_mta_usage_cookie; + typedef weak_any weak_mta_usage_cookie; +#endif // __WIL__COMBASEAPI_H__STL + +#if defined(_COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL__COMBASEAPI_H_APP + //! A type that calls CoUninitialize on destruction (or reset()). + using unique_couninitialize_call = unique_call; + + //! Calls CoInitializeEx and fail-fasts if it fails; returns an RAII object that reverts + WI_NODISCARD inline unique_couninitialize_call CoInitializeEx_failfast(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + FAIL_FAST_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); + return unique_couninitialize_call(); + } +#endif // __WIL__COMBASEAPI_H_APP +#if defined(__WIL__COMBASEAPI_H_APP) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__COMBASEAPI_H_APPEXCEPTIONAL) +#define __WIL__COMBASEAPI_H_APPEXCEPTIONAL + WI_NODISCARD inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); + return unique_couninitialize_call(); + } +#endif + +#if defined(__ROAPI_H_) && !defined(__WIL__ROAPI_H_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && (NTDDI_VERSION >= NTDDI_WIN8) +#define __WIL__ROAPI_H_APP + + typedef unique_any unique_ro_registration_cookie; + + //! A type that calls RoUninitialize on destruction (or reset()). + //! Use as a replacement for Windows::Foundation::Uninitialize. + using unique_rouninitialize_call = unique_call; + + //! Calls RoInitialize and fail-fasts if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + WI_NODISCARD inline unique_rouninitialize_call RoInitialize_failfast(RO_INIT_TYPE initType = RO_INIT_MULTITHREADED) + { + FAIL_FAST_IF_FAILED(::RoInitialize(initType)); + return unique_rouninitialize_call(); + } +#endif // __WIL__ROAPI_H_APP +#if defined(__WIL__ROAPI_H_APP) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__ROAPI_H_APPEXCEPTIONAL) +#define __WIL__ROAPI_H_APPEXCEPTIONAL + //! Calls RoInitialize and throws an exception if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + WI_NODISCARD inline unique_rouninitialize_call RoInitialize(RO_INIT_TYPE initType = RO_INIT_MULTITHREADED) + { + THROW_IF_FAILED(::RoInitialize(initType)); + return unique_rouninitialize_call(); + } +#endif + +#if defined(__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL__WINSTRING_H_ + typedef unique_any unique_hstring; + + template<> inline unique_hstring make_unique_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length) WI_NOEXCEPT + { + WI_ASSERT(source != nullptr); // the HSTRING version of this function does not suport this case + if (length == static_cast(-1)) + { + length = wcslen(source); + } + + unique_hstring result; + ::WindowsCreateString(source, static_cast(length), &result); + return result; + } + + typedef unique_any unique_hstring_buffer; + + /** Promotes an hstring_buffer to an HSTRING. + When an HSTRING_BUFFER object is promoted to a real string it must not be passed to WindowsDeleteString. The caller owns the + HSTRING afterwards. + ~~~ + HRESULT Type::MakePath(_Out_ HSTRING* path) + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + RETURN_IF_FAILED(::WindowsPreallocateStringBuffer(65, &bufferStorage, &theBuffer)); + RETURN_IF_FAILED(::PathCchCombine(bufferStorage, 65, m_foo, m_bar)); + RETURN_IF_FAILED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), path))); + return S_OK; + } + ~~~ + */ + inline HRESULT make_hstring_from_buffer_nothrow(unique_hstring_buffer&& source, _Out_ HSTRING* promoted) + { + HRESULT hr = ::WindowsPromoteStringBuffer(source.get(), promoted); + if (SUCCEEDED(hr)) + { + source.release(); + } + return hr; + } + + //! A fail-fast variant of `make_hstring_from_buffer_nothrow` + inline unique_hstring make_hstring_from_buffer_failfast(unique_hstring_buffer&& source) + { + unique_hstring result; + FAIL_FAST_IF_FAILED(make_hstring_from_buffer_nothrow(wistd::move(source), &result)); + return result; + } + +#if defined WIL_ENABLE_EXCEPTIONS + /** Promotes an hstring_buffer to an HSTRING. + When an HSTRING_BUFFER object is promoted to a real string it must not be passed to WindowsDeleteString. The caller owns the + HSTRING afterwards. + ~~~ + wil::unique_hstring Type::Make() + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + THROW_IF_FAILED(::WindowsPreallocateStringBuffer(65, &bufferStorage, &theBuffer)); + THROW_IF_FAILED(::PathCchCombine(bufferStorage, 65, m_foo, m_bar)); + return wil::make_hstring_from_buffer(wistd::move(theBuffer)); + } + ~~~ + */ + inline unique_hstring make_hstring_from_buffer(unique_hstring_buffer&& source) + { + unique_hstring result; + THROW_IF_FAILED(make_hstring_from_buffer_nothrow(wistd::move(source), &result)); + return result; + } +#endif + + /// @cond + namespace details + { + template<> struct string_maker + { + string_maker() = default; + string_maker(const string_maker&) = delete; + void operator=(const string_maker&) = delete; + string_maker& operator=(string_maker&& source) WI_NOEXCEPT + { + m_value = wistd::move(source.m_value); + m_bufferHandle = wistd::move(source.m_bufferHandle); + m_charBuffer = wistd::exchange(source.m_charBuffer, nullptr); + return *this; + } + + HRESULT make( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, + size_t length) + { + if (source) + { + RETURN_IF_FAILED(WindowsCreateString(source, static_cast(length), &m_value)); + } + else + { + // Need to set it to the empty string to support the empty string case. + m_value.reset(); + RETURN_IF_FAILED(WindowsPreallocateStringBuffer(static_cast(length), &m_charBuffer, &m_bufferHandle)); + } + return S_OK; + } + + wchar_t* buffer() { WI_ASSERT(m_charBuffer != nullptr); return m_charBuffer; } + const wchar_t* buffer() const { return m_charBuffer; } + + unique_hstring release() + { + m_charBuffer = nullptr; + if (m_bufferHandle) + { + return make_hstring_from_buffer_failfast(wistd::move(m_bufferHandle)); + } + return wistd::move(m_value); + } + + static PCWSTR get(const wil::unique_hstring& value) { return WindowsGetStringRawBuffer(value.get(), nullptr); } + + private: + unique_hstring m_value; + unique_hstring_buffer m_bufferHandle; + wchar_t* m_charBuffer = nullptr; + }; + } + /// @endcond + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // This is the overload for HSTRING. Other overloads available above. + inline PCWSTR str_raw_ptr(HSTRING str) + { + return WindowsGetStringRawBuffer(str, nullptr); + } + + inline PCWSTR str_raw_ptr(const unique_hstring& str) + { + return str_raw_ptr(str.get()); + } + +#endif // __WIL__WINSTRING_H_ +#if defined(__WIL__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL__WINSTRING_H_STL + typedef shared_any shared_hstring; + typedef shared_any shared_hstring_buffer; + typedef weak_any weak_hstring; + typedef weak_any weak_hstring_buffer; +#endif // __WIL__WINSTRING_H_STL + + +#if defined(_WINREG_) && !defined(__WIL_WINREG_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINREG_ + typedef unique_any unique_hkey; +#endif // __WIL_WINREG_ +#if defined(__WIL_WINREG_) && !defined(__WIL_WINREG_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINREG_STL + typedef shared_any shared_hkey; + typedef weak_any weak_hkey; +#endif // __WIL_WINREG_STL + +#if defined(__propidl_h__) && !defined(_WIL__propidl_h__) && !defined(WIL_KERNEL_MODE) +#define _WIL__propidl_h__ + using unique_prop_variant = wil::unique_struct; +#endif // _WIL__propidl_h__ + +#if defined(_OLEAUTO_H_) && !defined(__WIL_OLEAUTO_H_) && !defined(WIL_KERNEL_MODE) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL_OLEAUTO_H_ + using unique_variant = wil::unique_struct; + typedef unique_any unique_bstr; + + inline wil::unique_bstr make_bstr_nothrow(PCWSTR source) WI_NOEXCEPT + { + return wil::unique_bstr(::SysAllocString(source)); + } + + inline wil::unique_bstr make_bstr_failfast(PCWSTR source) WI_NOEXCEPT + { + return wil::unique_bstr(FAIL_FAST_IF_NULL_ALLOC(::SysAllocString(source))); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline wil::unique_bstr make_bstr(PCWSTR source) + { + wil::unique_bstr result(make_bstr_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_OLEAUTO_H_ +#if defined(__WIL_OLEAUTO_H_) && !defined(__WIL_OLEAUTO_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_OLEAUTO_H_STL + typedef shared_any shared_bstr; + typedef weak_any weak_bstr; +#endif // __WIL_OLEAUTO_H_STL + + +#if (defined(_WININET_) || defined(_DUBINET_)) && !defined(__WIL_WININET_) +#define __WIL_WININET_ + typedef unique_any unique_hinternet; +#endif // __WIL_WININET_ +#if defined(__WIL_WININET_) && !defined(__WIL_WININET_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WININET_STL + typedef shared_any shared_hinternet; + typedef weak_any weak_hinternet; +#endif // __WIL_WININET_STL + + +#if defined(_WINHTTPX_) && !defined(__WIL_WINHTTP_) +#define __WIL_WINHTTP_ + typedef unique_any unique_winhttp_hinternet; +#endif // __WIL_WINHTTP_ +#if defined(__WIL_WINHTTP_) && !defined(__WIL_WINHTTP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINHTTP_STL + typedef shared_any shared_winhttp_hinternet; + typedef weak_any weak_winhttp_hinternet; +#endif // __WIL_WINHTTP_STL + + +#if defined(_WINSOCKAPI_) && !defined(__WIL_WINSOCKAPI_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINSOCKAPI_ + typedef unique_any unique_socket; +#endif // __WIL_WINSOCKAPI_ +#if defined(__WIL_WINSOCKAPI_) && !defined(__WIL_WINSOCKAPI_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINSOCKAPI_STL + typedef shared_any shared_socket; + typedef weak_any weak_socket; +#endif // __WIL_WINSOCKAPI_STL + + +#if defined(_WINGDI_) && !defined(__WIL_WINGDI_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(NOGDI) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINGDI_ + struct window_dc + { + HDC dc; + HWND hwnd; + window_dc(HDC dc_, HWND hwnd_ = nullptr) WI_NOEXCEPT { dc = dc_; hwnd = hwnd_; } + operator HDC() const WI_NOEXCEPT { return dc; } + static void close(window_dc wdc) WI_NOEXCEPT { ::ReleaseDC(wdc.hwnd, wdc.dc); } + }; + typedef unique_any unique_hdc_window; + + struct paint_dc + { + HWND hwnd; + PAINTSTRUCT ps; + paint_dc(HDC hdc = nullptr) { ::ZeroMemory(this, sizeof(*this)); ps.hdc = hdc; } + operator HDC() const WI_NOEXCEPT { return ps.hdc; } + static void close(paint_dc pdc) WI_NOEXCEPT { ::EndPaint(pdc.hwnd, &pdc.ps); } + }; + typedef unique_any unique_hdc_paint; + + struct select_result + { + HGDIOBJ hgdi; + HDC hdc; + select_result(HGDIOBJ hgdi_, HDC hdc_ = nullptr) WI_NOEXCEPT { hgdi = hgdi_; hdc = hdc_; } + operator HGDIOBJ() const WI_NOEXCEPT { return hgdi; } + static void close(select_result sr) WI_NOEXCEPT { ::SelectObject(sr.hdc, sr.hgdi); } + }; + typedef unique_any unique_select_object; + + inline unique_hdc_window GetDC(HWND hwnd) WI_NOEXCEPT + { + return unique_hdc_window(window_dc(::GetDC(hwnd), hwnd)); + } + + inline unique_hdc_window GetWindowDC(HWND hwnd) WI_NOEXCEPT + { + return unique_hdc_window(window_dc(::GetWindowDC(hwnd), hwnd)); + } + + inline unique_hdc_paint BeginPaint(HWND hwnd, _Out_opt_ PPAINTSTRUCT pPaintStruct = nullptr) WI_NOEXCEPT + { + paint_dc pdc; + pdc.hwnd = hwnd; + HDC hdc = ::BeginPaint(hwnd, &pdc.ps); + assign_to_opt_param(pPaintStruct, pdc.ps); + return (hdc == nullptr) ? unique_hdc_paint() : unique_hdc_paint(pdc); + } + + inline unique_select_object SelectObject(HDC hdc, HGDIOBJ gdiobj) WI_NOEXCEPT + { + return unique_select_object(select_result(::SelectObject(hdc, gdiobj), hdc)); + } + + typedef unique_any unique_hgdiobj; + typedef unique_any unique_hpen; + typedef unique_any unique_hbrush; + typedef unique_any unique_hfont; + typedef unique_any unique_hbitmap; + typedef unique_any unique_hrgn; + typedef unique_any unique_hpalette; + typedef unique_any unique_hdc; + typedef unique_any unique_hicon; +#if !defined(NOMENUS) + typedef unique_any unique_hmenu; +#endif // !defined(NOMENUS) +#endif // __WIL_WINGDI_ +#if defined(__WIL_WINGDI_) && !defined(__WIL_WINGDI_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINGDI_STL + typedef shared_any shared_hgdiobj; + typedef shared_any shared_hpen; + typedef shared_any shared_hbrush; + typedef shared_any shared_hfont; + typedef shared_any shared_hbitmap; + typedef shared_any shared_hrgn; + typedef shared_any shared_hpalette; + typedef shared_any shared_hdc; + typedef shared_any shared_hicon; +#if !defined(NOMENUS) + typedef shared_any shared_hmenu; +#endif // !defined(NOMENUS) + + typedef weak_any weak_hgdiobj; + typedef weak_any weak_hpen; + typedef weak_any weak_hbrush; + typedef weak_any weak_hfont; + typedef weak_any weak_hbitmap; + typedef weak_any weak_hrgn; + typedef weak_any weak_hpalette; + typedef weak_any weak_hdc; + typedef weak_any weak_hicon; +#if !defined(NOMENUS) + typedef weak_any weak_hmenu; +#endif // !defined(NOMENUS) +#endif // __WIL_WINGDI_STL + +#if defined(_INC_WTSAPI) && !defined(__WIL_WTSAPI) +#define __WIL_WTSAPI + template + using unique_wtsmem_ptr = wistd::unique_ptr>; +#endif // __WIL_WTSAPI + +#if defined(_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINSCARD_H_ + typedef unique_any unique_scardctx; +#endif // __WIL_WINSCARD_H_ +#if defined(__WIL_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINSCARD_H_STL + typedef shared_any shared_scardctx; + typedef weak_any weak_scardctx; +#endif // __WIL_WINSCARD_H_STL + + +#if defined(__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL__WINCRYPT_H__ + /// @cond + namespace details + { + inline void __stdcall CertCloseStoreNoParam(_Pre_opt_valid_ _Frees_ptr_opt_ HCERTSTORE hCertStore) WI_NOEXCEPT + { + ::CertCloseStore(hCertStore, 0); + } + + inline void __stdcall CryptReleaseContextNoParam(_Pre_opt_valid_ _Frees_ptr_opt_ HCRYPTPROV hCryptCtx) WI_NOEXCEPT + { + ::CryptReleaseContext(hCryptCtx, 0); + } + } + /// @endcond + + typedef unique_any unique_cert_context; + typedef unique_any unique_cert_chain_context; + typedef unique_any unique_hcertstore; + typedef unique_any unique_hcryptprov; + typedef unique_any unique_hcryptkey; + typedef unique_any unique_hcrypthash; +#endif // __WIL__WINCRYPT_H__ +#if defined(__WIL__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__STL) && defined(WIL_RESOURCE_STL) +#define __WIL__WINCRYPT_H__STL + typedef shared_any shared_cert_context; + typedef shared_any shared_cert_chain_context; + typedef shared_any shared_hcertstore; + typedef shared_any shared_hcryptprov; + typedef shared_any shared_hcryptkey; + typedef shared_any shared_hcrypthash; + + typedef weak_any weak_cert_context; + typedef weak_any weak_cert_chain_context; + typedef weak_any weak_hcertstore; + typedef weak_any weak_hcryptprov; + typedef weak_any weak_hcryptkey; + typedef weak_any weak_hcrypthash; +#endif // __WIL__WINCRYPT_H__STL + + +#if defined(__NCRYPT_H__) && !defined(__WIL_NCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_NCRYPT_H__ + using ncrypt_deleter = function_deleter; + + template + using unique_ncrypt_ptr = wistd::unique_ptr; + + typedef unique_any unique_ncrypt_prov; + typedef unique_any unique_ncrypt_key; + typedef unique_any unique_ncrypt_secret; +#endif // __WIL_NCRYPT_H__ +#if defined(__WIL_NCRYPT_H__) && !defined(__WIL_NCRYPT_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NCRYPT_H_STL + typedef shared_any shared_ncrypt_prov; + typedef shared_any shared_ncrypt_key; + typedef shared_any shared_ncrypt_secret; + + typedef weak_any weak_ncrypt_prov; + typedef weak_any weak_ncrypt_key; + typedef weak_any weak_ncrypt_secret; +#endif // __WIL_NCRYPT_H_STL + +#if defined(__BCRYPT_H__) && !defined(__WIL_BCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_BCRYPT_H__ + /// @cond + namespace details + { + inline void __stdcall BCryptCloseAlgorithmProviderNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ BCRYPT_ALG_HANDLE hAlgorithm) WI_NOEXCEPT + { + if (hAlgorithm) + { + ::BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + } + } + /// @endcond + + using bcrypt_deleter = function_deleter; + + template + using unique_bcrypt_ptr = wistd::unique_ptr; + + typedef unique_any unique_bcrypt_algorithm; + typedef unique_any unique_bcrypt_hash; + typedef unique_any unique_bcrypt_key; + typedef unique_any unique_bcrypt_secret; +#endif // __WIL_BCRYPT_H__ +#if defined(__WIL_BCRYPT_H__) && !defined(__WIL_BCRYPT_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_BCRYPT_H_STL + typedef shared_any shared_bcrypt_algorithm; + typedef shared_any shared_bcrypt_hash; + typedef shared_any shared_bcrypt_key; + typedef shared_any shared_bcrypt_secret; + + typedef weak_any weak_bcrypt_algorithm; + typedef weak_any weak_bcrypt_hash; + typedef weak_any weak_bcrypt_key; + typedef weak_any weak_bcrypt_secret; +#endif // __WIL_BCRYPT_H_STL + + +#if defined(__RPCNDR_H__) && !defined(__WIL__RPCNDR_H__) && !defined(WIL_KERNEL_MODE) +#define __WIL__RPCNDR_H__ + + //! Function deleter for use with pointers allocated by MIDL_user_allocate + using midl_deleter = function_deleter; + + //! Unique-ptr holding a type allocated by MIDL_user_alloc or returned from an RPC invocation + template using unique_midl_ptr = wistd::unique_ptr; + + //! Unique-ptr for strings allocated by MIDL_user_alloc + using unique_midl_string = unique_midl_ptr; +#ifndef WIL_NO_ANSI_STRINGS + using unique_midl_ansistring = unique_midl_ptr; +#endif + + namespace details + { + struct midl_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::MIDL_user_allocate(size); + } + }; + + // Specialization to support construction of unique_midl_string instances + template<> struct string_allocator : midl_allocator {}; + +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : midl_allocator {}; +#endif + } +#endif // __WIL__RPCNDR_H__ + +#if defined(_OBJBASE_H_) && !defined(__WIL_OBJBASE_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_OBJBASE_H_ + using cotaskmem_deleter = function_deleter; + + template + using unique_cotaskmem_ptr = wistd::unique_ptr; + + template + using unique_cotaskmem_array_ptr = unique_array_ptr; + + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + Use `wil::make_unique_cotaskmem_nothrow()` for resources returned from APIs that must satisfy a memory allocation contract that requires the use of `CoTaskMemAlloc()` / `CoTaskMemFree()`. + Use `wil::make_unique_nothrow()` when `CoTaskMemAlloc()` is not required. + + Allocations are initialized with placement new and will call constructors (if present), but this does not guarantee initialization. + + Note that `wil::make_unique_cotaskmem_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_cotaskmem_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem_nothrow(Args&&... args) + { + static_assert(wistd::is_trivially_destructible::value, "T has a destructor that won't be run when used with this function; use make_unique instead"); + unique_cotaskmem_ptr sp(static_cast(::CoTaskMemAlloc(sizeof(T)))); + if (sp) + { + // use placement new to initialize memory from the previous allocation + new (sp.get()) T(wistd::forward(args)...); + } + return sp; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem_nothrow(size_t size) + { + typedef typename wistd::remove_extent::type E; + static_assert(wistd::is_trivially_destructible::value, "E has a destructor that won't be run when used with this function; use make_unique instead"); + FAIL_FAST_IF((__WI_SIZE_MAX / sizeof(E)) < size); + size_t allocSize = sizeof(E) * size; + unique_cotaskmem_ptr sp(static_cast(::CoTaskMemAlloc(allocSize))); + if (sp) + { + // use placement new to initialize memory from the previous allocation; + // note that array placement new cannot be used as the standard allows for operator new[] + // to consume overhead in the allocation for internal bookkeeping + for (auto& elem : make_range(static_cast(sp.get()), size)) + { + new (&elem) E(); + } + } + return sp; + } + + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem_failfast(Args&&... args) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem_failfast(size_t size) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem(Args&&... args) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem(size_t size) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any unique_cotaskmem; + typedef unique_any unique_cotaskmem_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_cotaskmem_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + struct cotaskmem_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::CoTaskMemAlloc(size); + } + }; + + template<> struct string_allocator : cotaskmem_allocator {}; + +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : cotaskmem_allocator {}; +#endif // WIL_NO_ANSI_STRINGS + } + /// @endcond + + inline auto make_cotaskmem_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_cotaskmem_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_cotaskmem_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } + +#endif // WIL_ENABLE_EXCEPTIONS +#endif // __WIL_OBJBASE_H_ +#if defined(__WIL_OBJBASE_H_) && !defined(__WIL_OBJBASE_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_OBJBASE_H_STL + typedef shared_any shared_cotaskmem; + typedef weak_any weak_cotaskmem; + typedef shared_any shared_cotaskmem_string; + typedef weak_any weak_cotaskmem_string; +#endif // __WIL_OBJBASE_H_STL + +#if defined(__WIL_OBJBASE_H_) && defined(__WIL_WINBASE_) && !defined(__WIL_OBJBASE_AND_WINBASE_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_OBJBASE_AND_WINBASE_H_ + + struct cotaskmem_secure_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { + IMalloc* malloc; + if (SUCCEEDED(::CoGetMalloc(1, &malloc))) + { + size_t const size = malloc->GetSize(p); + if (size != static_cast(-1)) + { + ::SecureZeroMemory(p, size); + } + malloc->Release(); + } + ::CoTaskMemFree(p); + } + } + }; + + template + using unique_cotaskmem_secure_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_nothrow(Args&&... args) + { + return unique_cotaskmem_secure_ptr(make_unique_cotaskmem_nothrow(wistd::forward(args)...).release()); + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_nothrow(size_t size) + { + return unique_cotaskmem_secure_ptr(make_unique_cotaskmem_nothrow(size).release()); + } + + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_failfast(Args&&... args) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_failfast(size_t size) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure(Args&&... args) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure(size_t size) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_cotaskmem_secure_ptr unique_cotaskmem_string_secure; + + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure_nothrow(L"a string"); + if (str) + { + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + } + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure_nothrow(_In_ PCWSTR source) WI_NOEXCEPT + { + return unique_cotaskmem_string_secure(make_cotaskmem_string_nothrow(source).release()); + } + + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure_failfast(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure_failfast(_In_ PCWSTR source) WI_NOEXCEPT + { + unique_cotaskmem_string_secure result(make_cotaskmem_string_secure_nothrow(source)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure(_In_ PCWSTR source) + { + unique_cotaskmem_string_secure result(make_cotaskmem_string_secure_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif +#endif // __WIL_OBJBASE_AND_WINBASE_H_ + +#if defined(_OLE2_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_OLE2_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_OLE2_H_ + typedef unique_struct unique_stg_medium; + struct unique_hglobal_locked : public unique_any + { + unique_hglobal_locked() = delete; + + explicit unique_hglobal_locked(HGLOBAL global) : unique_any(global) + { + // GlobalLock returns a pointer to the associated global memory block and that's what callers care about. + m_globalMemory = GlobalLock(global); + if (!m_globalMemory) + { + release(); + } + } + + explicit unique_hglobal_locked(unique_hglobal& global) : unique_hglobal_locked(global.get()) + { + } + + explicit unique_hglobal_locked(STGMEDIUM& medium) : unique_hglobal_locked(medium.hGlobal) + { + } + + pointer get() const + { + return m_globalMemory; + } + + private: + pointer m_globalMemory; + }; + + //! A type that calls OleUninitialize on destruction (or reset()). + //! Use as a replacement for Windows::Foundation::Uninitialize. + using unique_oleuninitialize_call = unique_call; + + //! Calls RoInitialize and fail-fasts if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + _Check_return_ inline unique_oleuninitialize_call OleInitialize_failfast() + { + FAIL_FAST_IF_FAILED(::OleInitialize(nullptr)); + return unique_oleuninitialize_call(); + } +#endif // __WIL_OLE2_H_ + +#if defined(__WIL_OLE2_H_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL_OLE2_H_EXCEPTIONAL) +#define __WIL_OLE2_H_EXCEPTIONAL + //! Calls RoInitialize and throws an exception if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + _Check_return_ inline unique_oleuninitialize_call OleInitialize() + { + THROW_IF_FAILED(::OleInitialize(nullptr)); + return unique_oleuninitialize_call(); + } +#endif + +#if defined(_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_COMMCTRL + typedef unique_any unique_himagelist; +#endif // __WIL_INC_COMMCTRL +#if defined(__WIL_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_INC_COMMCTRL_STL + typedef shared_any shared_himagelist; + typedef weak_any weak_himagelist; +#endif // __WIL_INC_COMMCTRL_STL + +#if defined(_UXTHEME_H_) && !defined(__WIL_INC_UXTHEME) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_UXTHEME + typedef unique_any unique_htheme; +#endif // __WIL_INC_UXTHEME + +#if defined(_WINSVC_) && !defined(__WIL_HANDLE_H_WINSVC) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_HANDLE_H_WINSVC + typedef unique_any unique_schandle; +#endif // __WIL_HANDLE_H_WINSVC +#if defined(__WIL_HANDLE_H_WINSVC) && !defined(__WIL_HANDLE_H_WINSVC_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_HANDLE_H_WINSVC_STL + typedef shared_any shared_schandle; + typedef weak_any weak_schandle; +#endif // __WIL_HANDLE_H_WINSVC_STL + +#if defined(_INC_STDIO) && !defined(__WIL_INC_STDIO) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_STDIO + typedef unique_any unique_pipe; + typedef unique_any unique_file; +#endif // __WIL_INC_STDIO +#if defined(__WIL_INC_STDIO) && !defined(__WIL__INC_STDIO_STL) && defined(WIL_RESOURCE_STL) +#define __WIL__INC_STDIO_STL + typedef shared_any shared_pipe; + typedef weak_any weak_pipe; + typedef shared_any shared_file; + typedef weak_any weak_file; +#endif // __WIL__INC_STDIO_STL + +#if defined(_NTLSA_) && !defined(__WIL_NTLSA_) && !defined(WIL_KERNEL_MODE) +#define __WIL_NTLSA_ + typedef unique_any unique_hlsa; + + using lsa_freemem_deleter = function_deleter; + + template + using unique_lsamem_ptr = wistd::unique_ptr; +#endif // _NTLSA_ +#if defined(_NTLSA_) && !defined(__WIL_NTLSA_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NTLSA_STL + typedef shared_any shared_hlsa; + typedef weak_any weak_hlsa; +#endif // _NTLSA_ + +#if defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_) +#define __WIL_LSALOOKUP_ + typedef unique_any unique_hlsalookup; + + using lsalookup_freemem_deleter = function_deleter; + + template + using unique_lsalookupmem_ptr = wistd::unique_ptr; +#endif // _LSALOOKUP_ +#if defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_LSALOOKUP_STL + typedef shared_any shared_hlsalookup; + typedef weak_any weak_hlsalookup; +#endif // _LSALOOKUP_ + +#if defined(_NTLSA_IFS_) && !defined(__WIL_HANDLE_H_NTLSA_IFS_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_HANDLE_H_NTLSA_IFS_ + using lsa_deleter = function_deleter; + + template + using unique_lsa_ptr = wistd::unique_ptr; +#endif // __WIL_HANDLE_H_NTLSA_IFS_ + +#if defined(__WERAPI_H__) && !defined(__WIL_WERAPI_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WERAPI_H__ + typedef unique_any unique_wer_report; +#endif + +#if defined(__MIDLES_H__) && !defined(__WIL_MIDLES_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_MIDLES_H__ + typedef unique_any unique_rpc_pickle; +#endif +#if defined(__WIL_MIDLES_H__) && !defined(__WIL_MIDLES_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_MIDLES_H_STL + typedef shared_any shared_rpc_pickle; + typedef weak_any weak_rpc_pickle; +#endif + +#if defined(__RPCDCE_H__) && !defined(__WIL_RPCDCE_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_RPCDCE_H__ + /// @cond + namespace details + { + inline void __stdcall WpRpcBindingFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_BINDING_HANDLE binding) + { + ::RpcBindingFree(&binding); + } + + inline void __stdcall WpRpcBindingVectorFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_BINDING_VECTOR* bindingVector) + { + ::RpcBindingVectorFree(&bindingVector); + } + + inline void __stdcall WpRpcStringFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_WSTR wstr) + { + ::RpcStringFreeW(&wstr); + } + } + /// @endcond + + typedef unique_any unique_rpc_binding; + typedef unique_any unique_rpc_binding_vector; + typedef unique_any unique_rpc_wstr; +#endif +#if defined(__WIL_RPCDCE_H__) && !defined(__WIL_RPCDCE_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_RPCDCE_H_STL + typedef shared_any shared_rpc_binding; + typedef weak_any weak_rpc_binding; + typedef shared_any shared_rpc_binding_vector; + typedef weak_any weak_rpc_binding_vector; + typedef shared_any shared_rpc_wstr; + typedef weak_any weak_rpc_wstr; +#endif + +#if defined(_WCMAPI_H) && !defined(__WIL_WCMAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WCMAPI_H_ + using wcm_deleter = function_deleter; + + template + using unique_wcm_ptr = wistd::unique_ptr; +#endif + +#if defined(_NETIOAPI_H_) && defined(_WS2IPDEF_) && defined(MIB_INVALID_TEREDO_PORT_NUMBER) && !defined(__WIL_NETIOAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_NETIOAPI_H_ + typedef unique_any unique_mib_iftable; +#endif +#if defined(__WIL_NETIOAPI_H_) && !defined(__WIL_NETIOAPI_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NETIOAPI_H_STL + typedef shared_any shared_mib_iftable; + typedef weak_any weak_mib_iftable; +#endif + +#if defined(_WLAN_WLANAPI_H) && !defined(__WIL_WLAN_WLANAPI_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WLAN_WLANAPI_H + using wlan_deleter = function_deleter; + + template + using unique_wlan_ptr = wistd::unique_ptr < T, wlan_deleter >; + + /// @cond + namespace details + { + inline void __stdcall CloseWlanHandle(_Frees_ptr_ HANDLE hClientHandle) + { + ::WlanCloseHandle(hClientHandle, nullptr); + } + } + /// @endcond + + typedef unique_any unique_wlan_handle; +#endif +#if defined(__WIL_WLAN_WLANAPI_H) && !defined(__WIL_WLAN_WLANAPI_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WLAN_WLANAPI_H_STL + typedef shared_any shared_wlan_handle; + typedef weak_any weak_wlan_handle; +#endif + +#if defined(_HPOWERNOTIFY_DEF_) && !defined(__WIL_HPOWERNOTIFY_DEF_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_HPOWERNOTIFY_DEF_H_ + typedef unique_any unique_hpowernotify; +#endif + +#if defined(__WIL_WINBASE_DESKTOP) && defined(SID_DEFINED) && !defined(__WIL_PSID_DEF_H_) +#define __WIL_PSID_DEF_H_ + typedef unique_any unique_any_psid; +#if defined(_OBJBASE_H_) + typedef unique_any unique_cotaskmem_psid; +#endif +#endif + +#if defined(_PROCESSTHREADSAPI_H_) && !defined(__WIL_PROCESSTHREADSAPI_H_DESK_SYS) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL_PROCESSTHREADSAPI_H_DESK_SYS + /// @cond + namespace details + { + inline void __stdcall CloseProcessInformation(_In_ PROCESS_INFORMATION* p) + { + if (p->hProcess) + { + CloseHandle(p->hProcess); + } + + if (p->hThread) + { + CloseHandle(p->hThread); + } + } + } + /// @endcond + + /** Manages the outbound parameter containing handles returned by `CreateProcess()` and related methods. + ~~~ + unique_process_information process; + CreateProcessW(..., CREATE_SUSPENDED, ..., &process); + THROW_IF_WIN32_BOOL_FALSE(ResumeThread(process.hThread)); + THROW_LAST_ERROR_IF(WaitForSingleObject(process.hProcess, INFINITE) != WAIT_OBJECT_0); + ~~~ + */ + using unique_process_information = unique_struct; +#endif + +#if defined(_PROCESSENV_) && !defined(__WIL__PROCESSENV_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL__PROCESSENV_ + /** Manages lifecycle of an environment-strings block + ~~~ + wil::unique_environstrings_ptr env { ::GetEnvironmentStringsW() }; + const wchar_t *nextVar = env.get(); + while (nextVar && *nextVar) + { + // consume 'nextVar' + nextVar += wcslen(nextVar) + 1; + } + ~~~ + */ + using unique_environstrings_ptr = wistd::unique_ptr>; + +#ifndef WIL_NO_ANSI_STRINGS + //! ANSI equivalent to unique_environstrings_ptr; + using unique_environansistrings_ptr = wistd::unique_ptr>; +#endif +#endif + +#if defined(_APPMODEL_H_) && !defined(__WIL_APPMODEL_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_APPMODEL_H_ + typedef unique_any unique_package_info_reference; +#endif // __WIL_APPMODEL_H_ +#if defined(__WIL_APPMODEL_H_) && !defined(__WIL_APPMODEL_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_APPMODEL_H_STL + typedef shared_any shared_package_info_reference; + typedef weak_any weak_package_info_reference; +#endif // __WIL_APPMODEL_H_STL + +#if defined(WDFAPI) && !defined(__WIL_WDFAPI) +#define __WIL_WDFAPI + + namespace details + { + template + using wdf_object_resource_policy = resource_policy; + } + + template + using unique_wdf_any = unique_any_t>>; + + using unique_wdf_object = unique_wdf_any; + + using unique_wdf_timer = unique_wdf_any; + using unique_wdf_work_item = unique_wdf_any; + + using unique_wdf_memory = unique_wdf_any; + + using unique_wdf_dma_enabler = unique_wdf_any; + using unique_wdf_dma_transaction = unique_wdf_any; + using unique_wdf_common_buffer = unique_wdf_any; + + using unique_wdf_key = unique_wdf_any; + using unique_wdf_string = unique_wdf_any; + using unique_wdf_collection = unique_wdf_any; + + using wdf_wait_lock_release_scope_exit = + unique_any< + WDFWAITLOCK, + decltype(&::WdfWaitLockRelease), + ::WdfWaitLockRelease, + details::pointer_access_none>; + + inline + WI_NODISCARD + _IRQL_requires_max_(PASSIVE_LEVEL) + _Acquires_lock_(lock) + wdf_wait_lock_release_scope_exit + acquire_wdf_wait_lock(WDFWAITLOCK lock) WI_NOEXCEPT + { + ::WdfWaitLockAcquire(lock, nullptr); + return wdf_wait_lock_release_scope_exit(lock); + } + + inline + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + _When_(return, _Acquires_lock_(lock)) + wdf_wait_lock_release_scope_exit + try_acquire_wdf_wait_lock(WDFWAITLOCK lock) WI_NOEXCEPT + { + LONGLONG timeout = 0; + NTSTATUS status = ::WdfWaitLockAcquire(lock, &timeout); + if (status == STATUS_SUCCESS) + { + return wdf_wait_lock_release_scope_exit(lock); + } + else + { + return wdf_wait_lock_release_scope_exit(); + } + } + + using wdf_spin_lock_release_scope_exit = + unique_any< + WDFSPINLOCK, + decltype(&::WdfSpinLockRelease), + ::WdfSpinLockRelease, + details::pointer_access_none>; + + inline + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_raises_(DISPATCH_LEVEL) + _Acquires_lock_(lock) + wdf_spin_lock_release_scope_exit + acquire_wdf_spin_lock(WDFSPINLOCK lock) WI_NOEXCEPT + { + ::WdfSpinLockAcquire(lock); + return wdf_spin_lock_release_scope_exit(lock); + } + + namespace details + { + template + using unique_wdf_lock_storage = unique_storage>; + + class unique_wdf_spin_lock_storage : public unique_wdf_lock_storage + { + using wdf_lock_storage_t = unique_wdf_lock_storage; + + public: + + using pointer = wdf_lock_storage_t::pointer; + + // Forward all base class constructors, but have it be explicit. + template + explicit unique_wdf_spin_lock_storage(args_t&& ... args) WI_NOEXCEPT : wdf_lock_storage_t(wistd::forward(args)...) {} + + NTSTATUS create(_In_opt_ WDF_OBJECT_ATTRIBUTES* attributes = WDF_NO_OBJECT_ATTRIBUTES) + { + return ::WdfSpinLockCreate(attributes, out_param(*this)); + } + + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_raises_(DISPATCH_LEVEL) + wdf_spin_lock_release_scope_exit acquire() WI_NOEXCEPT + { + return wil::acquire_wdf_spin_lock(wdf_lock_storage_t::get()); + } + }; + + class unique_wdf_wait_lock_storage : public unique_wdf_lock_storage + { + using wdf_lock_storage_t = unique_wdf_lock_storage; + + public: + + using pointer = wdf_lock_storage_t::pointer; + + // Forward all base class constructors, but have it be explicit. + template + explicit unique_wdf_wait_lock_storage(args_t&& ... args) WI_NOEXCEPT : wdf_lock_storage_t(wistd::forward(args)...) {} + + NTSTATUS create(_In_opt_ WDF_OBJECT_ATTRIBUTES* attributes = WDF_NO_OBJECT_ATTRIBUTES) + { + return ::WdfWaitLockCreate(attributes, out_param(*this)); + } + + WI_NODISCARD + _IRQL_requires_max_(PASSIVE_LEVEL) + wdf_wait_lock_release_scope_exit acquire() WI_NOEXCEPT + { + return wil::acquire_wdf_wait_lock(wdf_lock_storage_t::get()); + } + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + wdf_wait_lock_release_scope_exit try_acquire() WI_NOEXCEPT + { + return wil::try_acquire_wdf_wait_lock(wdf_lock_storage_t::get()); + } + }; + } + + using unique_wdf_wait_lock = unique_any_t; + using unique_wdf_spin_lock = unique_any_t; + + template + struct wdf_object_reference + { + TWDFOBJECT wdfObject = WDF_NO_HANDLE; + PVOID tag = nullptr; + + wdf_object_reference() WI_NOEXCEPT = default; + + wdf_object_reference(TWDFOBJECT wdfObject, PVOID tag = nullptr) WI_NOEXCEPT + : wdfObject(wdfObject), tag(tag) + { + } + + operator TWDFOBJECT() const WI_NOEXCEPT + { + return wdfObject; + } + + static void close(const wdf_object_reference& wdfObjectReference) WI_NOEXCEPT + { + // We don't use WdfObjectDereferenceActual because there is no way to provide the + // correct __LINE__ and __FILE__, but if you use RAII all the way, you shouldn't have to + // worry about where it was released, only where it was acquired. + WdfObjectDereferenceWithTag(wdfObjectReference.wdfObject, wdfObjectReference.tag); + } + }; + + template + using unique_wdf_object_reference = unique_any::close), + &wdf_object_reference::close, details::pointer_access_noaddress, wdf_object_reference>; + + // Increment the ref-count on a WDF object a unique_wdf_object_reference for it. Use + // WI_WdfObjectReferenceIncrement to automatically use the call-site source location. Use this + // function only if the call-site source location is obtained from elsewhere (i.e., plumbed + // through other abstractions). + template + inline WI_NODISCARD unique_wdf_object_reference wdf_object_reference_increment( + TWDFOBJECT wdfObject, PVOID tag, LONG lineNumber, PCSTR fileName) WI_NOEXCEPT + { + // Parameter is incorrectly marked as non-const, so the const-cast is required. + ::WdfObjectReferenceActual(wdfObject, tag, lineNumber, const_cast(fileName)); + return unique_wdf_object_reference{ wdf_object_reference{ wdfObject, tag } }; + } + +// A macro so that we can capture __LINE__ and __FILE__. +#define WI_WdfObjectReferenceIncrement(wdfObject, tag) \ + wil::wdf_object_reference_increment(wdfObject, tag, __LINE__, __FILE__) + +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && \ + defined(_CFGMGR32_H_) && \ + (WINVER >= _WIN32_WINNT_WIN8) && \ + !defined(__WIL_CFGMGR32_H_) +#define __WIL_CFGMGR32_H_ + typedef unique_any unique_hcmnotification; +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && \ + defined(_SWDEVICE_H_) && \ + (WINVER >= _WIN32_WINNT_WIN8) && \ + !defined(__WIL_SWDEVICE_H_) +#define __WIL_SWDEVICE_H_ + typedef unique_any unique_hswdevice; +#endif + +#if defined(WIL_KERNEL_MODE) && (defined(_WDMDDK_) || defined(_NTDDK_)) && !defined(__WIL_RESOURCE_WDM) +#define __WIL_RESOURCE_WDM + + namespace details + { + struct kspin_lock_saved_irql + { + PKSPIN_LOCK spinLock = nullptr; + KIRQL savedIrql = PASSIVE_LEVEL; + + kspin_lock_saved_irql() = default; + + kspin_lock_saved_irql(PKSPIN_LOCK /* spinLock */) + { + // This constructor exists simply to allow conversion of the pointer type to + // pointer_storage type when constructing an invalid instance. The spinLock pointer + // is expected to be nullptr. + } + + // Exists to satisfy the interconvertibility requirement for pointer_storage and + // pointer. + explicit operator PKSPIN_LOCK() const + { + return spinLock; + } + + _IRQL_requires_(DISPATCH_LEVEL) + static + void Release(_In_ _IRQL_restores_ const kspin_lock_saved_irql& spinLockSavedIrql) + { + KeReleaseSpinLock(spinLockSavedIrql.spinLock, spinLockSavedIrql.savedIrql); + } + }; + + // On some architectures KeReleaseSpinLockFromDpcLevel is a macro, and we need a thunk + // function we can take the address of. + inline + _IRQL_requires_min_(DISPATCH_LEVEL) + void __stdcall ReleaseSpinLockFromDpcLevel(_Inout_ PKSPIN_LOCK spinLock) WI_NOEXCEPT + { + KeReleaseSpinLockFromDpcLevel(spinLock); + } + } + + using kspin_lock_guard = unique_any; + + using kspin_lock_at_dpc_guard = unique_any; + + inline + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) + kspin_lock_guard + acquire_kspin_lock(_In_ PKSPIN_LOCK spinLock) + { + details::kspin_lock_saved_irql spinLockSavedIrql; + KeAcquireSpinLock(spinLock, &spinLockSavedIrql.savedIrql); + spinLockSavedIrql.spinLock = spinLock; + return kspin_lock_guard(spinLockSavedIrql); + } + + inline + WI_NODISCARD + _IRQL_requires_min_(DISPATCH_LEVEL) + kspin_lock_at_dpc_guard + acquire_kspin_lock_at_dpc(_In_ PKSPIN_LOCK spinLock) + { + KeAcquireSpinLockAtDpcLevel(spinLock); + return kspin_lock_at_dpc_guard(spinLock); + } + + class kernel_spin_lock + { + public: + + kernel_spin_lock() WI_NOEXCEPT + { + ::KeInitializeSpinLock(&m_kSpinLock); + } + + ~kernel_spin_lock() = default; + + // Cannot change memory location. + kernel_spin_lock(const kernel_spin_lock&) = delete; + kernel_spin_lock& operator=(const kernel_spin_lock&) = delete; + kernel_spin_lock(kernel_spin_lock&&) = delete; + kernel_spin_lock& operator=(kernel_spin_lock&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) + kspin_lock_guard acquire() WI_NOEXCEPT + { + return acquire_kspin_lock(&m_kSpinLock); + } + + WI_NODISCARD + _IRQL_requires_min_(DISPATCH_LEVEL) + kspin_lock_at_dpc_guard acquire_at_dpc() WI_NOEXCEPT + { + return acquire_kspin_lock_at_dpc(&m_kSpinLock); + } + + private: + + KSPIN_LOCK m_kSpinLock; + }; + + namespace details + { + template + class kernel_event_t + { + public: + + explicit kernel_event_t(bool isSignaled = false) WI_NOEXCEPT + { + ::KeInitializeEvent(&m_kernelEvent, static_cast(eventType), isSignaled ? TRUE : FALSE); + } + + // Cannot change memory location. + kernel_event_t(const kernel_event_t&) = delete; + kernel_event_t(kernel_event_t&&) = delete; + kernel_event_t& operator=(const kernel_event_t&) = delete; + kernel_event_t& operator=(kernel_event_t&&) = delete; + + // Get the underlying KEVENT structure for more advanced usages like + // KeWaitForMultipleObjects or KeWaitForSingleObject with non-default parameters. + PRKEVENT get() WI_NOEXCEPT + { + return &m_kernelEvent; + } + + void clear() WI_NOEXCEPT + { + // The most common use-case is to clear the event with no interest in its previous + // value. Hence, that is the functionality we provide by default. If the previous + // value is required, one may .get() the underlying event object and call + // ::KeResetEvent(). + ::KeClearEvent(&m_kernelEvent); + } + + // Returns the previous state of the event. + bool set(KPRIORITY increment = IO_NO_INCREMENT) WI_NOEXCEPT + { + return ::KeSetEvent(&m_kernelEvent, increment, FALSE) ? true : false; + } + + // Checks if the event is currently signaled. Does not change the state of the event. + bool is_signaled() const WI_NOEXCEPT + { + return ::KeReadStateEvent(const_cast(&m_kernelEvent)) ? true : false; + } + + // Return true if the wait was satisfied. Time is specified in 100ns units, relative + // (negative) or absolute (positive). For more details, see the documentation of + // KeWaitForSingleObject. + bool wait(LONGLONG waitTime) WI_NOEXCEPT + { + LARGE_INTEGER duration; + duration.QuadPart = waitTime; + return wait_for_single_object(&duration); + } + + // Waits indefinitely for the event to be signaled. + void wait() WI_NOEXCEPT + { + wait_for_single_object(nullptr); + } + + private: + + bool wait_for_single_object(_In_opt_ LARGE_INTEGER* waitDuration) WI_NOEXCEPT + { + auto status = ::KeWaitForSingleObject(&m_kernelEvent, Executive, KernelMode, FALSE, waitDuration); + + // We specified Executive and non-alertable, which means some of the return values are + // not possible. + WI_ASSERT((status == STATUS_SUCCESS) || (status == STATUS_TIMEOUT)); + return (status == STATUS_SUCCESS); + } + + KEVENT m_kernelEvent; + }; + } + + using kernel_event_auto_reset = details::kernel_event_t; + using kernel_event_manual_reset = details::kernel_event_t; + using kernel_event = kernel_event_auto_reset; // For parity with the default for other WIL event types. + + namespace details + { + // Define a templated type for pool functions in order to satisfy overload resolution below + template + struct pool_helpers + { + static inline + _IRQL_requires_max_(DISPATCH_LEVEL) + void __stdcall FreePoolWithTag(pointer value) WI_NOEXCEPT + { + if (value) + { + ExFreePoolWithTag(value, tag); + } + } + }; + } + + template + using unique_tagged_pool_ptr = unique_any::FreePoolWithTag), &details::pool_helpers::FreePoolWithTag>; + + // For use with IRPs that need to be IoFreeIrp'ed when done, typically allocated using IoAllocateIrp. + using unique_allocated_irp = wil::unique_any; + using unique_io_workitem = wil::unique_any; + +#endif // __WIL_RESOURCE_WDM + +#if defined(WIL_KERNEL_MODE) && (defined(_WDMDDK_) || defined(_ZWAPI_)) && !defined(__WIL_RESOURCE_ZWAPI) +#define __WIL_RESOURCE_ZWAPI + + using unique_kernel_handle = wil::unique_any; + +#endif // __WIL_RESOURCE_ZWAPI + +} // namespace wil + +#pragma warning(pop) diff --git a/Externals/WIL/include/wil/result.h b/Externals/WIL/include/wil/result.h new file mode 100644 index 0000000000..f3b61e6054 --- /dev/null +++ b/Externals/WIL/include/wil/result.h @@ -0,0 +1,1275 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RESULT_INCLUDED +#define __WIL_RESULT_INCLUDED + +// Most functionality is picked up from result_macros.h. This file specifically provides higher level processing of errors when +// they are encountered by the underlying macros. +#include "result_macros.h" + +// Note that we avoid pulling in STL's memory header from Result.h through Resource.h as we have +// Result.h customers who are still on older versions of STL (without std::shared_ptr<>). +#ifndef RESOURCE_SUPPRESS_STL +#define RESOURCE_SUPPRESS_STL +#include "resource.h" +#undef RESOURCE_SUPPRESS_STL +#else +#include "resource.h" +#endif + +#ifdef WIL_KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +// The updated behavior of running init-list ctors during placement new is proper & correct, disable the warning that requests developers verify they want it +#pragma warning(push) +#pragma warning(disable : 4351) + +namespace wil +{ + // WARNING: EVERYTHING in this namespace must be handled WITH CARE as the entities defined within + // are used as an in-proc ABI contract between binaries that utilize WIL. Making changes + // that add v-tables or change the storage semantics of anything herein needs to be done + // with care and respect to versioning. + ///@cond + namespace details_abi + { + #define __WI_SEMAHPORE_VERSION L"_p0" + + // This class uses named semaphores to be able to stash a numeric value (including a pointer + // for retrieval from within any module in a process). This is a very specific need of a + // header-based library that should not be generally used. + // + // Notes for use: + // * Data members must be stable unless __WI_SEMAHPORE_VERSION is changed + // * The class must not reference module code (v-table, function pointers, etc) + // * Use of this class REQUIRES that there be a MUTEX held around the semaphore manipulation + // and tests as it doesn't attempt to handle thread contention on the semaphore while manipulating + // the count. + // * This class supports storing a 31-bit number of a single semaphore or a 62-bit number across + // two semaphores and directly supports pointers. + + class SemaphoreValue + { + public: + SemaphoreValue() = default; + SemaphoreValue(const SemaphoreValue&) = delete; + SemaphoreValue& operator=(const SemaphoreValue&) = delete; + + SemaphoreValue(SemaphoreValue&& other) WI_NOEXCEPT : + m_semaphore(wistd::move(other.m_semaphore)), + m_semaphoreHigh(wistd::move(other.m_semaphoreHigh)) + { + static_assert(sizeof(m_semaphore) == sizeof(HANDLE), "unique_any must be a direct representation of the HANDLE to be used across module"); + } + + void Destroy() + { + m_semaphore.reset(); + m_semaphoreHigh.reset(); + } + + template + HRESULT CreateFromValue(PCWSTR name, T value) + { + return CreateFromValueInternal(name, (sizeof(value) > sizeof(unsigned long)), static_cast(value)); + } + + HRESULT CreateFromPointer(PCWSTR name, void* pointer) + { + ULONG_PTR value = reinterpret_cast(pointer); + FAIL_FAST_IMMEDIATE_IF(WI_IsAnyFlagSet(value, 0x3)); + return CreateFromValue(name, value >> 2); + } + + template + static HRESULT TryGetValue(PCWSTR name, _Out_ T* value, _Out_opt_ bool *retrieved = nullptr) + { + *value = static_cast(0); + unsigned __int64 value64 = 0; + __WIL_PRIVATE_RETURN_IF_FAILED(TryGetValueInternal(name, (sizeof(T) > sizeof(unsigned long)), &value64, retrieved)); + *value = static_cast(value64); + return S_OK; + } + + static HRESULT TryGetPointer(PCWSTR name, _Outptr_result_maybenull_ void** pointer) + { + *pointer = nullptr; + ULONG_PTR value = 0; + __WIL_PRIVATE_RETURN_IF_FAILED(TryGetValue(name, &value)); + *pointer = reinterpret_cast(value << 2); + return S_OK; + } + + private: + HRESULT CreateFromValueInternal(PCWSTR name, bool is64Bit, unsigned __int64 value) + { + WI_ASSERT(!m_semaphore && !m_semaphoreHigh); // call Destroy first + + // This routine only supports 31 bits when semahporeHigh is not supplied or 62 bits when the value + // is supplied. It's a programming error to use it when either of these conditions are not true. + + FAIL_FAST_IMMEDIATE_IF((!is64Bit && WI_IsAnyFlagSet(value, 0xFFFFFFFF80000000)) || + (is64Bit && WI_IsAnyFlagSet(value, 0xC000000000000000))); + + wchar_t localName[MAX_PATH]; + WI_VERIFY_SUCCEEDED(StringCchCopyW(localName, ARRAYSIZE(localName), name)); + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), __WI_SEMAHPORE_VERSION)); + + const unsigned long highPart = static_cast(value >> 31); + const unsigned long lowPart = static_cast(value & 0x000000007FFFFFFF); + + // We set the count of the semaphore equal to the max (the value we're storing). The only exception to that + // is ZERO, where you can't create a semaphore of value ZERO, where we push the max to one and use a count of ZERO. + + __WIL_PRIVATE_RETURN_IF_FAILED(m_semaphore.create(static_cast(lowPart), static_cast((lowPart > 0) ? lowPart : 1), localName)); + if (is64Bit) + { + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), L"h")); + __WIL_PRIVATE_RETURN_IF_FAILED(m_semaphoreHigh.create(static_cast(highPart), static_cast((highPart > 0) ? highPart : 1), localName)); + } + + return S_OK; + } + + static HRESULT GetValueFromSemaphore(HANDLE semaphore, _Out_ LONG* count) + { + // First we consume a single count from the semaphore. This will work in all cases other + // than the case where the count we've recorded is ZERO which will TIMEOUT. + + DWORD result = ::WaitForSingleObject(semaphore, 0); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF(result == WAIT_FAILED); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, !((result == WAIT_OBJECT_0) || (result == WAIT_TIMEOUT))); + + LONG value = 0; + if (result == WAIT_OBJECT_0) + { + // We were able to wait. To establish our count, all we have to do is release that count + // back to the semaphore and observe the value that we released. + + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(::ReleaseSemaphore(semaphore, 1, &value)); + value++; // we waited first, so our actual value is one more than the old value + + // Make sure the value is correct by validating that we have no more posts. + BOOL expectedFailure = ::ReleaseSemaphore(semaphore, 1, nullptr); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expectedFailure || (::GetLastError() != ERROR_TOO_MANY_POSTS)); + } + else + { + WI_ASSERT(result == WAIT_TIMEOUT); + + // We know at this point that the value is ZERO. We'll do some verification to ensure that + // this address is right by validating that we have one and only one more post that we could use. + + LONG expected = 0; + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(::ReleaseSemaphore(semaphore, 1, &expected)); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expected != 0); + + const BOOL expectedFailure = ::ReleaseSemaphore(semaphore, 1, nullptr); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expectedFailure || (::GetLastError() != ERROR_TOO_MANY_POSTS)); + + result = ::WaitForSingleObject(semaphore, 0); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF(result == WAIT_FAILED); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, result != WAIT_OBJECT_0); + } + + *count = value; + return S_OK; + } + + static HRESULT TryGetValueInternal(PCWSTR name, bool is64Bit, _Out_ unsigned __int64* value, _Out_opt_ bool* retrieved) + { + assign_to_opt_param(retrieved, false); + *value = 0; + + wchar_t localName[MAX_PATH]; + WI_VERIFY_SUCCEEDED(StringCchCopyW(localName, ARRAYSIZE(localName), name)); + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), __WI_SEMAHPORE_VERSION)); + + wil::unique_semaphore_nothrow semaphoreLow(::OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, localName)); + if (!semaphoreLow) + { + __WIL_PRIVATE_RETURN_HR_IF(S_OK, (::GetLastError() == ERROR_FILE_NOT_FOUND)); + __WIL_PRIVATE_RETURN_LAST_ERROR(); + } + + LONG countLow = 0; + LONG countHigh = 0; + + __WIL_PRIVATE_RETURN_IF_FAILED(GetValueFromSemaphore(semaphoreLow.get(), &countLow)); + + if (is64Bit) + { + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), L"h")); + wil::unique_semaphore_nothrow semaphoreHigh(::OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, localName)); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(semaphoreHigh); + + __WIL_PRIVATE_RETURN_IF_FAILED(GetValueFromSemaphore(semaphoreHigh.get(), &countHigh)); + } + + WI_ASSERT((countLow >= 0) && (countHigh >= 0)); + + const unsigned __int64 newValueHigh = (static_cast(countHigh) << 31); + const unsigned __int64 newValueLow = static_cast(countLow); + + assign_to_opt_param(retrieved, true); + *value = (newValueHigh | newValueLow); + return S_OK; + } + + wil::unique_semaphore_nothrow m_semaphore; + wil::unique_semaphore_nothrow m_semaphoreHigh; + }; + + template + class ProcessLocalStorageData + { + public: + ProcessLocalStorageData(unique_mutex_nothrow&& mutex, SemaphoreValue&& value) : + m_mutex(wistd::move(mutex)), + m_value(wistd::move(value)), + m_data() + { + static_assert(sizeof(m_mutex) == sizeof(HANDLE), "unique_any must be equivalent to the handle size to safely use across module"); + } + + T* GetData() + { + WI_ASSERT(m_mutex); + return &m_data; + } + + void Release() + { + if (ProcessShutdownInProgress()) + { + // There are no other threads to contend with. + if (--m_refCount == 0) + { + m_data.ProcessShutdown(); + } + } + else + { + auto lock = m_mutex.acquire(); + if (--m_refCount == 0) + { + // We must explicitly destroy our semaphores while holding the mutex + m_value.Destroy(); + lock.reset(); + + this->~ProcessLocalStorageData(); + ::HeapFree(::GetProcessHeap(), 0, this); + } + } + } + + static HRESULT Acquire(PCSTR staticNameWithVersion, _Outptr_result_nullonfailure_ ProcessLocalStorageData** data) + { + *data = nullptr; + + // NOTE: the '0' in SM0 below is intended as the VERSION number. Changes to this class require + // that this value be revised. + + const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); + wchar_t name[MAX_PATH]; + WI_VERIFY(SUCCEEDED(StringCchPrintfW(name, ARRAYSIZE(name), L"Local\\SM0:%d:%d:%hs", ::GetCurrentProcessId(), size, staticNameWithVersion))); + + unique_mutex_nothrow mutex; + mutex.reset(::CreateMutexExW(nullptr, name, 0, MUTEX_ALL_ACCESS)); + + // This will fail in some environments and will be fixed with deliverable 12394134 + RETURN_LAST_ERROR_IF_EXPECTED(!mutex); + auto lock = mutex.acquire(); + + void* pointer = nullptr; + __WIL_PRIVATE_RETURN_IF_FAILED(SemaphoreValue::TryGetPointer(name, &pointer)); + if (pointer) + { + *data = reinterpret_cast*>(pointer); + (*data)->m_refCount++; + } + else + { + __WIL_PRIVATE_RETURN_IF_FAILED(MakeAndInitialize(name, wistd::move(mutex), data)); // Assumes mutex handle ownership on success ('lock' will still be released) + } + + return S_OK; + } + + private: + + volatile long m_refCount = 1; + unique_mutex_nothrow m_mutex; + SemaphoreValue m_value; + T m_data; + + static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, ProcessLocalStorageData** data) + { + *data = nullptr; + + const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); + + unique_process_heap_ptr> dataAlloc(static_cast*>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size))); + __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(dataAlloc); + + SemaphoreValue semaphoreValue; + __WIL_PRIVATE_RETURN_IF_FAILED(semaphoreValue.CreateFromPointer(name, dataAlloc.get())); + + new(dataAlloc.get()) ProcessLocalStorageData(wistd::move(mutex), wistd::move(semaphoreValue)); + *data = dataAlloc.release(); + + return S_OK; + } + }; + + template + class ProcessLocalStorage + { + public: + ProcessLocalStorage(PCSTR staticNameWithVersion) WI_NOEXCEPT : + m_staticNameWithVersion(staticNameWithVersion) + { + } + + ~ProcessLocalStorage() WI_NOEXCEPT + { + if (m_data) + { + m_data->Release(); + } + } + + T* GetShared() WI_NOEXCEPT + { + if (!m_data) + { + ProcessLocalStorageData* localTemp = nullptr; + if (SUCCEEDED(ProcessLocalStorageData::Acquire(m_staticNameWithVersion, &localTemp)) && !m_data) + { + m_data = localTemp; + } + } + return m_data ? m_data->GetData() : nullptr; + } + + private: + PCSTR m_staticNameWithVersion = nullptr; + ProcessLocalStorageData* m_data = nullptr; + }; + + template + class ThreadLocalStorage + { + public: + ThreadLocalStorage(const ThreadLocalStorage&) = delete; + ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; + + ThreadLocalStorage() = default; + + ~ThreadLocalStorage() WI_NOEXCEPT + { + for (auto &entry : m_hashArray) + { + Node *pNode = entry; + while (pNode != nullptr) + { + auto pCurrent = pNode; + pNode = pNode->pNext; + pCurrent->~Node(); + ::HeapFree(::GetProcessHeap(), 0, pCurrent); + } + entry = nullptr; + } + } + + // Note: Can return nullptr even when (shouldAllocate == true) upon allocation failure + T* GetLocal(bool shouldAllocate = false) WI_NOEXCEPT + { + DWORD const threadId = ::GetCurrentThreadId(); + size_t const index = (threadId % ARRAYSIZE(m_hashArray)); + for (auto pNode = m_hashArray[index]; pNode != nullptr; pNode = pNode->pNext) + { + if (pNode->threadId == threadId) + { + return &pNode->value; + } + } + + if (shouldAllocate) + { + Node *pNew = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(Node))); + if (pNew != nullptr) + { + new(pNew)Node{ threadId }; + + Node *pFirst; + do + { + pFirst = m_hashArray[index]; + pNew->pNext = pFirst; + } while (::InterlockedCompareExchangePointer(reinterpret_cast(m_hashArray + index), pNew, pFirst) != pFirst); + + return &pNew->value; + } + } + return nullptr; + } + + private: + + struct Node + { + DWORD threadId; + Node* pNext = nullptr; + T value{}; + }; + + Node * volatile m_hashArray[10]{}; + }; + + struct ThreadLocalFailureInfo + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size; + unsigned char reserved1[2]; // packing, reserved + // When this failure was seen + unsigned int sequenceId; + + // Information about the failure + HRESULT hr; + PCSTR fileName; + unsigned short lineNumber; + unsigned char failureType; // FailureType + unsigned char reserved2; // packing, reserved + PCSTR modulePath; + void* returnAddress; + void* callerReturnAddress; + PCWSTR message; + + // The allocation (LocalAlloc) where structure strings point + void* stringBuffer; + size_t stringBufferSize; + + // NOTE: Externally Managed: Must not have constructor or destructor + + void Clear() + { + ::HeapFree(::GetProcessHeap(), 0, stringBuffer); + stringBuffer = nullptr; + stringBufferSize = 0; + } + + void Set(const FailureInfo& info, unsigned int newSequenceId) + { + sequenceId = newSequenceId; + + hr = info.hr; + fileName = nullptr; + lineNumber = static_cast(info.uLineNumber); + failureType = static_cast(info.type); + modulePath = nullptr; + returnAddress = info.returnAddress; + callerReturnAddress = info.callerReturnAddress; + message = nullptr; + + size_t neededSize = details::ResultStringSize(info.pszFile) + + details::ResultStringSize(info.pszModule) + + details::ResultStringSize(info.pszMessage); + + if (!stringBuffer || (stringBufferSize < neededSize)) + { + auto newBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, neededSize); + if (newBuffer) + { + ::HeapFree(::GetProcessHeap(), 0, stringBuffer); + stringBuffer = newBuffer; + stringBufferSize = neededSize; + } + } + + if (stringBuffer) + { + unsigned char *pBuffer = static_cast(stringBuffer); + unsigned char *pBufferEnd = pBuffer + stringBufferSize; + + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszFile, &fileName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszModule, &modulePath); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszMessage, &message); + ZeroMemory(pBuffer, pBufferEnd - pBuffer); + } + } + + void Get(FailureInfo& info) + { + ::ZeroMemory(&info, sizeof(info)); + + info.failureId = sequenceId; + info.hr = hr; + info.pszFile = fileName; + info.uLineNumber = lineNumber; + info.type = static_cast(failureType); + info.pszModule = modulePath; + info.returnAddress = returnAddress; + info.callerReturnAddress = callerReturnAddress; + info.pszMessage = message; + } + }; + + struct ThreadLocalData + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size = sizeof(ThreadLocalData); + + // Subscription information + unsigned int threadId = 0; + volatile long* failureSequenceId = nullptr; // backpointer to the global ID + + // Information about thread errors + unsigned int latestSubscribedFailureSequenceId = 0; + + // The last (N) observed errors + ThreadLocalFailureInfo* errors = nullptr; + unsigned short errorAllocCount = 0; + unsigned short errorCurrentIndex = 0; + + // NOTE: Externally Managed: Must allow ZERO init construction + + ~ThreadLocalData() + { + Clear(); + } + + void Clear() + { + for (auto& error : make_range(errors, errorAllocCount)) + { + error.Clear(); + } + ::HeapFree(::GetProcessHeap(), 0, errors); + errorAllocCount = 0; + errorCurrentIndex = 0; + errors = nullptr; + } + + bool EnsureAllocated(bool create = true) + { + if (!errors && create) + { + const unsigned short errorCount = 5; + errors = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo))); + if (errors) + { + errorAllocCount = errorCount; + errorCurrentIndex = 0; + for (auto& error : make_range(errors, errorAllocCount)) + { + error.size = sizeof(ThreadLocalFailureInfo); + } + } + } + return (errors != nullptr); + } + + void SetLastError(const wil::FailureInfo& info) + { + const bool hasListener = (latestSubscribedFailureSequenceId > 0); + + if (!EnsureAllocated(hasListener)) + { + // We either couldn't allocate or we haven't yet allocated and nobody + // was listening, so we ignore. + return; + } + + if (hasListener) + { + // When we have listeners, we can throw away any updates to the last seen error + // code within the same listening context presuming it's an update of the existing + // error with the same code. + + for (auto& error : make_range(errors, errorAllocCount)) + { + if ((error.sequenceId > latestSubscribedFailureSequenceId) && (error.hr == info.hr)) + { + return; + } + } + } + + // Otherwise we create a new failure... + + errorCurrentIndex = (errorCurrentIndex + 1) % errorAllocCount; + errors[errorCurrentIndex].Set(info, ::InterlockedIncrementNoFence(failureSequenceId)); + } + + bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) + { + if (!errors) + { + return false; + } + + // If the last error we saw doesn't meet the filter requirement or if the last error was never + // set, then we couldn't return a result at all... + auto& lastFailure = errors[errorCurrentIndex]; + if (minSequenceId >= lastFailure.sequenceId) + { + return false; + } + + // With no result filter, we just go to the last error and report it + if (matchRequirement == S_OK) + { + lastFailure.Get(info); + return true; + } + + // Find the oldest result matching matchRequirement and passing minSequenceId + ThreadLocalFailureInfo* find = nullptr; + for (auto& error : make_range(errors, errorAllocCount)) + { + if ((error.hr == matchRequirement) && (error.sequenceId > minSequenceId)) + { + if (!find || (error.sequenceId < find->sequenceId)) + { + find = &error; + } + } + } + if (find) + { + find->Get(info); + return true; + } + + return false; + } + + bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, _In_opt_ const DiagnosticsInfo* diagnostics, HRESULT matchRequirement, void* returnAddress) + { + // First attempt to get the last error and then see if it matches the error returned from + // the last caught exception. If it does, then we're good to go and we return that last error. + + FailureInfo last = {}; + if (GetLastError(last, minSequenceId, matchRequirement) && (last.hr == ResultFromCaughtException())) + { + info = last; + return true; + } + + // The last error didn't match or we never had one... we need to create one -- we do so by logging + // our current request and then using the last error. + + DiagnosticsInfo source; + if (diagnostics) + { + source = *diagnostics; + } + + // NOTE: FailureType::Log as it's only informative (no action) and SupportedExceptions::All as it's not a barrier, only recognition. + wchar_t message[2048]; + message[0] = L'\0'; + const HRESULT hr = details::ReportFailure_CaughtExceptionCommon(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All); + + // Now that the exception was logged, we should be able to fetch it. + return GetLastError(info, minSequenceId, hr); + } + }; + + struct ProcessLocalData + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size = sizeof(ProcessLocalData); + + // Failure Information + volatile long failureSequenceId = 1; // process global variable + ThreadLocalStorage threads; // list of allocated threads + + void ProcessShutdown() {} + }; + + __declspec(selectany) ProcessLocalStorage* g_pProcessLocalData = nullptr; + + __declspec(noinline) inline ThreadLocalData* GetThreadLocalDataCache(bool allocate = true) + { + ThreadLocalData* result = nullptr; + if (g_pProcessLocalData) + { + auto processData = g_pProcessLocalData->GetShared(); + if (processData) + { + result = processData->threads.GetLocal(allocate); + if (result && !result->failureSequenceId) + { + result->failureSequenceId = &(processData->failureSequenceId); + } + } + } + return result; + } + + __forceinline ThreadLocalData* GetThreadLocalData(bool allocate = true) + { + return GetThreadLocalDataCache(allocate); + } + + } // details_abi + /// @endcond + + + /** Returns a sequence token that can be used with wil::GetLastError to limit errors to those that occur after this token was retrieved. + General usage pattern: use wil::GetCurrentErrorSequenceId to cache a token, execute your code, on failure use wil::GetLastError with the token + to provide information on the error that occurred while executing your code. Prefer to use wil::ThreadErrorContext over this approach when + possible. */ + inline long GetCurrentErrorSequenceId() + { + auto data = details_abi::GetThreadLocalData(); + if (data) + { + // someone is interested -- make sure we can store errors + data->EnsureAllocated(); + return *data->failureSequenceId; + } + + return 0; + } + + /** Caches failure information for later retrieval from GetLastError. + Most people will never need to do this explicitly as failure information is automatically made available per-thread across a process when + errors are encountered naturally through the WIL macros. */ + inline void SetLastError(const wil::FailureInfo& info) + { + static volatile unsigned int lastThread = 0; + auto threadId = ::GetCurrentThreadId(); + if (lastThread != threadId) + { + static volatile long depth = 0; + if (::InterlockedIncrementNoFence(&depth) < 4) + { + lastThread = threadId; + auto data = details_abi::GetThreadLocalData(false); // false = avoids allocation if not already present + if (data) + { + data->SetLastError(info); + } + lastThread = 0; + } + ::InterlockedDecrementNoFence(&depth); + } + } + + /** Retrieves failure information for the current thread with the given filters. + This API can be used to retrieve information about the last WIL failure that occurred on the current thread. + This error crosses DLL boundaries as long as the error occurred in the current process. Passing a minSequenceId + restricts the error returned to one that occurred after the given sequence ID. Passing matchRequirement also filters + the returned result to the given error code. */ + inline bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId = 0, HRESULT matchRequirement = S_OK) + { + auto data = details_abi::GetThreadLocalData(false); // false = avoids allocation if not already present + if (data) + { + return data->GetLastError(info, minSequenceId, matchRequirement); + } + return false; + } + + /** Retrieves failure information when within a catch block for the current thread with the given filters. + When unable to retrieve the exception information (when WIL hasn't yet seen it), this will attempt (best effort) to + discover information about the exception and will attribute that information to the given DiagnosticsInfo position. + See GetLastError for capabilities and filtering. */ + inline __declspec(noinline) bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId = 0, const DiagnosticsInfo* diagnostics = nullptr, HRESULT matchRequirement = S_OK) + { + auto data = details_abi::GetThreadLocalData(); + if (data) + { + return data->GetCaughtExceptionError(info, minSequenceId, diagnostics, matchRequirement, _ReturnAddress()); + } + return false; + } + + /** Use this class to manage retrieval of information about an error occurring in the requested code. + Construction of this class sets a point in time after which you can use the GetLastError class method to retrieve + the origination of the last error that occurred on this thread since the class was created. */ + class ThreadErrorContext + { + public: + ThreadErrorContext() : + m_data(details_abi::GetThreadLocalData()) + { + if (m_data) + { + m_sequenceIdLast = m_data->latestSubscribedFailureSequenceId; + m_sequenceIdStart = *m_data->failureSequenceId; + m_data->latestSubscribedFailureSequenceId = m_sequenceIdStart; + } + } + + ~ThreadErrorContext() + { + if (m_data) + { + m_data->latestSubscribedFailureSequenceId = m_sequenceIdLast; + } + } + + /** Retrieves the origination of the last error that occurred since this class was constructed. + The optional parameter allows the failure information returned to be filtered to a specific + result. */ + inline bool GetLastError(FailureInfo& info, HRESULT matchRequirement = S_OK) + { + if (m_data) + { + return m_data->GetLastError(info, m_sequenceIdStart, matchRequirement); + } + return false; + } + + /** Retrieves the origin of the current exception (within a catch block) since this class was constructed. + See @ref GetCaughtExceptionError for more information */ + inline __declspec(noinline) bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, const DiagnosticsInfo* diagnostics = nullptr, HRESULT matchRequirement = S_OK) + { + if (m_data) + { + return m_data->GetCaughtExceptionError(info, m_sequenceIdStart, diagnostics, matchRequirement, _ReturnAddress()); + } + return false; + } + + private: + details_abi::ThreadLocalData* m_data; + unsigned long m_sequenceIdStart{}; + unsigned long m_sequenceIdLast{}; + }; + + + enum class WilInitializeCommand + { + Create, + Destroy, + }; + + + /// @cond + namespace details + { + struct IFailureCallback + { + virtual bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT = 0; + }; + + class ThreadFailureCallbackHolder; + + __declspec(selectany) details_abi::ThreadLocalStorage* g_pThreadFailureCallbacks = nullptr; + + class ThreadFailureCallbackHolder + { + public: + ThreadFailureCallbackHolder(_In_ IFailureCallback *pCallbackParam, _In_opt_ CallContextInfo *pCallContext = nullptr, bool watchNow = true) WI_NOEXCEPT : + m_ppThreadList(nullptr), + m_pCallback(pCallbackParam), + m_pNext(nullptr), + m_threadId(0), + m_pCallContext(pCallContext) + { + if (watchNow) + { + StartWatching(); + } + } + + ThreadFailureCallbackHolder(ThreadFailureCallbackHolder &&other) WI_NOEXCEPT : + m_ppThreadList(nullptr), + m_pCallback(other.m_pCallback), + m_pNext(nullptr), + m_threadId(0), + m_pCallContext(other.m_pCallContext) + { + if (other.m_threadId != 0) + { + other.StopWatching(); + StartWatching(); + } + } + + ~ThreadFailureCallbackHolder() WI_NOEXCEPT + { + if (m_threadId != 0) + { + StopWatching(); + } + } + + void SetCallContext(_In_opt_ CallContextInfo *pCallContext) + { + m_pCallContext = pCallContext; + } + + CallContextInfo *CallContextInfo() + { + return m_pCallContext; + } + + void StartWatching() + { + // out-of balance Start/Stop calls? + __FAIL_FAST_IMMEDIATE_ASSERT__(m_threadId == 0); + + m_ppThreadList = g_pThreadFailureCallbacks ? g_pThreadFailureCallbacks->GetLocal(true) : nullptr; // true = allocate thread list if missing + if (m_ppThreadList) + { + m_pNext = *m_ppThreadList; + *m_ppThreadList = this; + m_threadId = ::GetCurrentThreadId(); + } + } + + void StopWatching() + { + if (m_threadId != ::GetCurrentThreadId()) + { + // The thread-specific failure holder cannot be stopped on a different thread than it was started on or the + // internal book-keeping list will be corrupted. To fix this change the telemetry pattern in the calling code + // to match one of the patterns available here: + // https://microsoft.sharepoint.com/teams/osg_development/Shared%20Documents/Windows%20TraceLogging%20Helpers.docx + + WI_USAGE_ERROR("MEMORY CORRUPTION: Calling code is leaking an activity thread-watcher and releasing it on another thread"); + } + + m_threadId = 0; + + while (*m_ppThreadList != nullptr) + { + if (*m_ppThreadList == this) + { + *m_ppThreadList = m_pNext; + break; + } + m_ppThreadList = &((*m_ppThreadList)->m_pNext); + } + m_ppThreadList = nullptr; + } + + bool IsWatching() + { + return (m_threadId != 0); + } + + void SetWatching(bool shouldWatch) + { + if (shouldWatch && !IsWatching()) + { + StartWatching(); + } + else if (!shouldWatch && IsWatching()) + { + StopWatching(); + } + } + + static bool GetThreadContext(_Inout_ FailureInfo *pFailure, _In_opt_ ThreadFailureCallbackHolder *pCallback, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) + { + *callContextString = '\0'; + bool foundContext = false; + if (pCallback != nullptr) + { + foundContext = GetThreadContext(pFailure, pCallback->m_pNext, callContextString, callContextStringLength); + + if (pCallback->m_pCallContext != nullptr) + { + auto &context = *pCallback->m_pCallContext; + + // We generate the next telemetry ID only when we've found an error (avoid always incrementing) + if (context.contextId == 0) + { + context.contextId = ::InterlockedIncrementNoFence(&s_telemetryId); + } + + if (pFailure->callContextOriginating.contextId == 0) + { + pFailure->callContextOriginating = context; + } + + pFailure->callContextCurrent = context; + + auto callContextStringEnd = callContextString + callContextStringLength; + callContextString += strlen(callContextString); + + if ((callContextStringEnd - callContextString) > 2) // room for at least the slash + null + { + *callContextString++ = '\\'; + auto nameSizeBytes = strlen(context.contextName) + 1; + size_t remainingBytes = static_cast(callContextStringEnd - callContextString); + auto copyBytes = (nameSizeBytes < remainingBytes) ? nameSizeBytes : remainingBytes; + memcpy_s(callContextString, remainingBytes, context.contextName, copyBytes); + *(callContextString + (copyBytes - 1)) = '\0'; + } + + return true; + } + } + return foundContext; + } + + static void GetContextAndNotifyFailure(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT + { + *callContextString = '\0'; + bool reportedTelemetry = false; + + ThreadFailureCallbackHolder **ppListeners = g_pThreadFailureCallbacks ? g_pThreadFailureCallbacks->GetLocal() : nullptr; + if ((ppListeners != nullptr) && (*ppListeners != nullptr)) + { + callContextString[0] = '\0'; + if (GetThreadContext(pFailure, *ppListeners, callContextString, callContextStringLength)) + { + pFailure->pszCallContext = callContextString; + } + + auto pNode = *ppListeners; + do + { + reportedTelemetry |= pNode->m_pCallback->NotifyFailure(*pFailure); + pNode = pNode->m_pNext; + } + while (pNode != nullptr); + } + + if (g_pfnTelemetryCallback != nullptr) + { + g_pfnTelemetryCallback(reportedTelemetry, *pFailure); + } + } + + ThreadFailureCallbackHolder(ThreadFailureCallbackHolder const &) = delete; + ThreadFailureCallbackHolder& operator=(ThreadFailureCallbackHolder const &) = delete; + + private: + static long volatile s_telemetryId; + + ThreadFailureCallbackHolder **m_ppThreadList; + IFailureCallback *m_pCallback; + ThreadFailureCallbackHolder *m_pNext; + DWORD m_threadId; + wil::CallContextInfo *m_pCallContext; + }; + + __declspec(selectany) long volatile ThreadFailureCallbackHolder::s_telemetryId = 1; + + template + class ThreadFailureCallbackFn final : public IFailureCallback + { + public: + explicit ThreadFailureCallbackFn(_In_opt_ CallContextInfo *pContext, _Inout_ TLambda &&errorFunction) WI_NOEXCEPT : + m_errorFunction(wistd::move(errorFunction)), + m_callbackHolder(this, pContext) + { + } + + ThreadFailureCallbackFn(_Inout_ ThreadFailureCallbackFn && other) WI_NOEXCEPT : + m_errorFunction(wistd::move(other.m_errorFunction)), + m_callbackHolder(this, other.m_callbackHolder.CallContextInfo()) + { + } + + bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + { + return m_errorFunction(failure); + } + + private: + ThreadFailureCallbackFn(_In_ ThreadFailureCallbackFn const &); + ThreadFailureCallbackFn & operator=(_In_ ThreadFailureCallbackFn const &); + + TLambda m_errorFunction; + ThreadFailureCallbackHolder m_callbackHolder; + }; + + + // returns true if telemetry was reported for this error + inline void __stdcall GetContextAndNotifyFailure(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT + { + ThreadFailureCallbackHolder::GetContextAndNotifyFailure(pFailure, callContextString, callContextStringLength); + + // Update the process-wide failure cache + wil::SetLastError(*pFailure); + } + + template void InitGlobalWithStorage(WilInitializeCommand state, void* storage, T*& global, TCtorArgs&&... args) + { + if ((state == WilInitializeCommand::Create) && !global) + { + global = ::new (storage) T(wistd::forward(args)...); + } + else if ((state == WilInitializeCommand::Destroy) && global) + { + global->~T(); + global = nullptr; + } + } + } + /// @endcond + + /** Modules that cannot use CRT-based static initialization may call this method from their entrypoint + instead. Disable the use of CRT-based initializers by defining RESULT_SUPPRESS_STATIC_INITIALIZERS + while compiling this header. Linking together libraries that disagree on this setting and calling + this method will behave correctly. It may be necessary to recompile all statically linked libraries + with the RESULT_SUPPRESS_... setting to eliminate all "LNK4201 - CRT section exists, but..." errors. + */ + inline void WilInitialize_Result(WilInitializeCommand state) + { + static unsigned char s_processLocalData[sizeof(*details_abi::g_pProcessLocalData)]; + static unsigned char s_threadFailureCallbacks[sizeof(*details::g_pThreadFailureCallbacks)]; + + details::InitGlobalWithStorage(state, s_processLocalData, details_abi::g_pProcessLocalData, "WilError_03"); + details::InitGlobalWithStorage(state, s_threadFailureCallbacks, details::g_pThreadFailureCallbacks); + + if (state == WilInitializeCommand::Create) + { + details::g_pfnGetContextAndNotifyFailure = details::GetContextAndNotifyFailure; + } + } + + /// @cond + namespace details + { +#ifndef RESULT_SUPPRESS_STATIC_INITIALIZERS + __declspec(selectany) ::wil::details_abi::ProcessLocalStorage<::wil::details_abi::ProcessLocalData> g_processLocalData("WilError_03"); + __declspec(selectany) ::wil::details_abi::ThreadLocalStorage g_threadFailureCallbacks; + + WI_HEADER_INITITALIZATION_FUNCTION(InitializeResultHeader, [] + { + g_pfnGetContextAndNotifyFailure = GetContextAndNotifyFailure; + ::wil::details_abi::g_pProcessLocalData = &g_processLocalData; + g_pThreadFailureCallbacks = &g_threadFailureCallbacks; + return 1; + }); +#endif + } + /// @endcond + + + // This helper functions much like scope_exit -- give it a lambda and get back a local object that can be used to + // catch all errors happening in your module through all WIL error handling mechanisms. The lambda will be called + // once for each error throw, error return, or error catch that is handled while the returned object is still in + // scope. Usage: + // + // auto monitor = wil::ThreadFailureCallback([](wil::FailureInfo const &failure) + // { + // // Write your code that logs or cares about failure details here... + // // It has access to HRESULT, filename, line number, etc through the failure param. + // }); + // + // As long as the returned 'monitor' object remains in scope, the lambda will continue to receive callbacks for any + // failures that occur in this module on the calling thread. Note that this will guarantee that the lambda will run + // for any failure that is through any of the WIL macros (THROW_XXX, RETURN_XXX, LOG_XXX, etc). + + template + inline wil::details::ThreadFailureCallbackFn ThreadFailureCallback(_Inout_ TLambda &&fnAtExit) WI_NOEXCEPT + { + return wil::details::ThreadFailureCallbackFn(nullptr, wistd::forward(fnAtExit)); + } + + + // Much like ThreadFailureCallback, this class will receive WIL failure notifications from the time it's instantiated + // until the time that it's destroyed. At any point during that time you can ask for the last failure that was seen + // by any of the WIL macros (RETURN_XXX, THROW_XXX, LOG_XXX, etc) on the current thread. + // + // This class is most useful when utilized as a member of an RAII class that's dedicated to providing logging or + // telemetry. In the destructor of that class, if the operation had not been completed successfully (it goes out of + // scope due to early return or exception unwind before success is acknowledged) then details about the last failure + // can be retrieved and appropriately logged. + // + // Usage: + // + // class MyLogger + // { + // public: + // MyLogger() : m_fComplete(false) {} + // ~MyLogger() + // { + // if (!m_fComplete) + // { + // FailureInfo *pFailure = m_cache.GetFailure(); + // if (pFailure != nullptr) + // { + // // Log information about pFailure (pFileure->hr, pFailure->pszFile, pFailure->uLineNumber, etc) + // } + // else + // { + // // It's possible that you get stack unwind from an exception that did NOT come through WIL + // // like (std::bad_alloc from the STL). Use a reasonable default like: HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION). + // } + // } + // } + // void Complete() { m_fComplete = true; } + // private: + // bool m_fComplete; + // ThreadFailureCache m_cache; + // }; + + class ThreadFailureCache final : + public details::IFailureCallback + { + public: + ThreadFailureCache() : + m_callbackHolder(this) + { + } + + ThreadFailureCache(ThreadFailureCache && rhs) WI_NOEXCEPT : + m_failure(wistd::move(rhs.m_failure)), + m_callbackHolder(this) + { + } + + ThreadFailureCache& operator=(ThreadFailureCache && rhs) WI_NOEXCEPT + { + m_failure = wistd::move(rhs.m_failure); + return *this; + } + + void WatchCurrentThread() + { + m_callbackHolder.StartWatching(); + } + + void IgnoreCurrentThread() + { + m_callbackHolder.StopWatching(); + } + + FailureInfo const *GetFailure() + { + return (FAILED(m_failure.GetFailureInfo().hr) ? &(m_failure.GetFailureInfo()) : nullptr); + } + + bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + { + // When we "cache" a failure, we bias towards trying to find the origin of the last HRESULT + // generated, so we ignore subsequent failures on the same error code (assuming propagation). + + if (failure.hr != m_failure.GetFailureInfo().hr) + { + m_failure.SetFailureInfo(failure); + } + return false; + } + + private: + StoredFailureInfo m_failure; + details::ThreadFailureCallbackHolder m_callbackHolder; + }; + +} // wil + +#pragma warning(pop) + +#endif diff --git a/Externals/WIL/include/wil/result_macros.h b/Externals/WIL/include/wil/result_macros.h new file mode 100644 index 0000000000..d96bf6c5b7 --- /dev/null +++ b/Externals/WIL/include/wil/result_macros.h @@ -0,0 +1,5860 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RESULTMACROS_INCLUDED +#define __WIL_RESULTMACROS_INCLUDED + +// WARNING: +// Code within this scope must satisfy both C99 and C++ + +#include "common.h" + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#include +#endif + +// Setup the debug behavior +#ifndef RESULT_DEBUG +#if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG) +#define RESULT_DEBUG +#endif +#endif + +/// @cond +#if defined(_PREFAST_) +#define __WI_ANALYSIS_ASSUME(_exp) _Analysis_assume_(_exp) +#else +#ifdef RESULT_DEBUG +#define __WI_ANALYSIS_ASSUME(_exp) ((void) 0) +#else +// NOTE: Clang does not currently handle __noop correctly and will fail to compile if the argument is not copy +// constructible. Therefore, use 'sizeof' for syntax validation. We don't do this universally for all compilers +// since lambdas are not allowed in unevaluated contexts prior to C++20, which does not appear to affect __noop +#if !defined(_MSC_VER) || defined(__clang__) +#define __WI_ANALYSIS_ASSUME(_exp) ((void)sizeof(_exp)) // Validate syntax on non-debug builds +#else +#define __WI_ANALYSIS_ASSUME(_exp) __noop(_exp) +#endif +#endif +#endif // _PREFAST_ + +//***************************************************************************** +// Assert Macros +//***************************************************************************** + +#ifdef RESULT_DEBUG +#if defined(__clang__) && defined(_WIN32) +// Clang currently mis-handles '__annotation' for 32-bit - https://bugs.llvm.org/show_bug.cgi?id=41890 +#define __WI_ASSERT_FAIL_ANNOTATION(msg) (void)0 +#else +#define __WI_ASSERT_FAIL_ANNOTATION(msg) __annotation(L"Debug", L"AssertFail", msg) +#endif + +#define WI_ASSERT(condition) (__WI_ANALYSIS_ASSUME(condition), ((!(condition)) ? (__WI_ASSERT_FAIL_ANNOTATION(L"" #condition), DbgRaiseAssertionFailure(), FALSE) : TRUE)) +#define WI_ASSERT_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), ((!(condition)) ? (__WI_ASSERT_FAIL_ANNOTATION(L##msg), DbgRaiseAssertionFailure(), FALSE) : TRUE)) +#define WI_ASSERT_NOASSUME WI_ASSERT +#define WI_ASSERT_MSG_NOASSUME WI_ASSERT_MSG +#define WI_VERIFY WI_ASSERT +#define WI_VERIFY_MSG WI_ASSERT_MSG +#define WI_VERIFY_SUCCEEDED(condition) WI_ASSERT(SUCCEEDED(condition)) +#else +#define WI_ASSERT(condition) (__WI_ANALYSIS_ASSUME(condition), 0) +#define WI_ASSERT_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), 0) +#define WI_ASSERT_NOASSUME(condition) ((void) 0) +#define WI_ASSERT_MSG_NOASSUME(condition, msg) ((void) 0) +#define WI_VERIFY(condition) (__WI_ANALYSIS_ASSUME(condition), ((condition) ? TRUE : FALSE)) +#define WI_VERIFY_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), ((condition) ? TRUE : FALSE)) +#define WI_VERIFY_SUCCEEDED(condition) (__WI_ANALYSIS_ASSUME(SUCCEEDED(condition)), ((SUCCEEDED(condition)) ? TRUE : FALSE)) +#endif // RESULT_DEBUG + +#if !defined(_NTDEF_) +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +#endif +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif +#ifndef STATUS_UNSUCCESSFUL +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) +#endif + +#ifndef WIL_AllocateMemory +#ifdef _KERNEL_MODE +#define WIL_AllocateMemory(SIZE) ExAllocatePoolWithTag(NonPagedPoolNx, SIZE, 'LIW') +WI_ODR_PRAGMA("WIL_AllocateMemory", "2") +#else +#define WIL_AllocateMemory(SIZE) HeapAlloc(GetProcessHeap(), 0, SIZE) +WI_ODR_PRAGMA("WIL_AllocateMemory", "1") +#endif +#else +WI_ODR_PRAGMA("WIL_AllocateMemory", "0") +#endif + +#ifndef WIL_FreeMemory +#ifdef _KERNEL_MODE +#define WIL_FreeMemory(MEM) ExFreePoolWithTag(MEM, 'LIW') +WI_ODR_PRAGMA("WIL_FreeMemory", "2") +#else +#define WIL_FreeMemory(MEM) HeapFree(GetProcessHeap(), 0, MEM) +WI_ODR_PRAGMA("WIL_FreeMemory", "1") +#endif +#else +WI_ODR_PRAGMA("WIL_FreeMemory", "0") +#endif + +// It would appear as though the C++17 "noexcept is part of the type system" update in MSVC has "infected" the behavior +// when compiling with C++14 (the default...), however the updated behavior for decltype understanding noexcept is _not_ +// present... So, work around it +#if __WI_LIBCPP_STD_VER >= 17 +#define WI_PFN_NOEXCEPT WI_NOEXCEPT +#else +#define WI_PFN_NOEXCEPT +#endif +/// @endcond + +#if defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + +#include +#include // provides the _ReturnAddress() intrinsic +#include // provides 'operator new', 'std::nothrow', etc. +#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(WIL_SUPPRESS_NEW) +#include // provides std::bad_alloc in the windows and public CRT headers +#endif + +#pragma warning(push) +#pragma warning(disable:4714 6262) // __forceinline not honored, stack size + +//***************************************************************************** +// Behavioral setup (error handling macro configuration) +//***************************************************************************** +// Set any of the following macros to the values given below before including Result.h to +// control the error handling macro's trade-offs between diagnostics and performance + +// RESULT_DIAGNOSTICS_LEVEL +// This define controls the level of diagnostic instrumentation that is built into the binary as a +// byproduct of using the macros. The amount of diagnostic instrumentation that is supplied is +// a trade-off between diagnosibility of issues and code size and performance. The modes are: +// 0 - No diagnostics, smallest & fastest (subject to tail-merge) +// 1 - No diagnostics, unique call sites for each macro (defeat's tail-merge) +// 2 - Line number +// 3 - Line number + source filename +// 4 - Line number + source filename + function name +// 5 - Line number + source filename + function name + code within the macro +// By default, mode 3 is used in free builds and mode 5 is used in checked builds. Note that the +// _ReturnAddress() will always be available through all modes when possible. + +// RESULT_INCLUDE_CALLER_RETURNADDRESS +// This controls whether or not the _ReturnAddress() of the function that includes the macro will +// be reported to telemetry. Note that this is in addition to the _ReturnAddress() of the actual +// macro position (which is always reported). The values are: +// 0 - The address is not included +// 1 - The address is included +// The default value is '1'. + +// RESULT_INLINE_ERROR_TESTS +// For conditional macros (other than RETURN_XXX), this controls whether branches will be evaluated +// within the call containing the macro or will be forced into the function called by the macros. +// Pushing branching into the called function reduces code size and the number of unique branches +// evaluated, but increases the instruction count executed per macro. +// 0 - Branching will not happen inline to the macros +// 1 - Branching is pushed into the calling function via __forceinline +// The default value is '1'. Note that XXX_MSG functions are always effectively mode '0' due to the +// compiler's unwillingness to inline var-arg functions. + +// RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST +// RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST +// RESULT_INLINE_ERROR_TESTS_FAIL_FAST +// These defines are identical to those above in form/function, but only applicable to fail fast error +// handling allowing a process to have different diagnostic information and performance characteristics +// for fail fast than for other error handling given the different reporting infrastructure (Watson +// vs Telemetry). + +// Set the default diagnostic mode +// Note that RESULT_DEBUG_INFO and RESULT_SUPPRESS_DEBUG_INFO are older deprecated models of controlling mode +#ifndef RESULT_DIAGNOSTICS_LEVEL +#if (defined(RESULT_DEBUG) || defined(RESULT_DEBUG_INFO)) && !defined(RESULT_SUPPRESS_DEBUG_INFO) +#define RESULT_DIAGNOSTICS_LEVEL 5 +#else +#define RESULT_DIAGNOSTICS_LEVEL 3 +#endif +#endif +#ifndef RESULT_INCLUDE_CALLER_RETURNADDRESS +#define RESULT_INCLUDE_CALLER_RETURNADDRESS 1 +#endif +#ifndef RESULT_INLINE_ERROR_TESTS +#define RESULT_INLINE_ERROR_TESTS 1 +#endif +#ifndef RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST +#define RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST RESULT_DIAGNOSTICS_LEVEL +#endif +#ifndef RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST +#define RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST RESULT_INCLUDE_CALLER_RETURNADDRESS +#endif +#ifndef RESULT_INLINE_ERROR_TESTS_FAIL_FAST +#define RESULT_INLINE_ERROR_TESTS_FAIL_FAST RESULT_INLINE_ERROR_TESTS +#endif + + +//***************************************************************************** +// Win32 specific error macros +//***************************************************************************** + +#define FAILED_WIN32(win32err) ((win32err) != 0) +#define SUCCEEDED_WIN32(win32err) ((win32err) == 0) + + +//***************************************************************************** +// NT_STATUS specific error macros +//***************************************************************************** + +#define FAILED_NTSTATUS(status) (((NTSTATUS)(status)) < 0) +#define SUCCEEDED_NTSTATUS(status) (((NTSTATUS)(status)) >= 0) + + +//***************************************************************************** +// Testing helpers - redefine to run unit tests against fail fast +//***************************************************************************** + +#ifndef RESULT_NORETURN +#define RESULT_NORETURN __declspec(noreturn) +#endif +#ifndef RESULT_NORETURN_NULL +#define RESULT_NORETURN_NULL _Ret_notnull_ +#endif + +//***************************************************************************** +// Helpers to setup the macros and functions used below... do not directly use. +//***************************************************************************** + +/// @cond +#define __R_DIAGNOSTICS(diagnostics) diagnostics.returnAddress, diagnostics.line, diagnostics.file, nullptr, nullptr +#define __R_DIAGNOSTICS_RA(diagnostics, address) diagnostics.returnAddress, diagnostics.line, diagnostics.file, nullptr, nullptr, address +#define __R_FN_PARAMS_FULL _In_opt_ void* callerReturnAddress, unsigned int lineNumber, _In_opt_ PCSTR fileName, _In_opt_ PCSTR functionName, _In_opt_ PCSTR code, void* returnAddress +#define __R_FN_LOCALS_FULL_RA void* callerReturnAddress = nullptr; unsigned int lineNumber = 0; PCSTR fileName = nullptr; PCSTR functionName = nullptr; PCSTR code = nullptr; void* returnAddress = _ReturnAddress(); +// NOTE: This BEGINs the common macro handling (__R_ prefix) for non-fail fast handled cases +// This entire section will be repeated below for fail fast (__RFF_ prefix). +#define __R_COMMA , +#define __R_FN_CALL_FULL callerReturnAddress, lineNumber, fileName, functionName, code, returnAddress +#define __R_FN_CALL_FULL_RA callerReturnAddress, lineNumber, fileName, functionName, code, _ReturnAddress() +// The following macros assemble the varying amount of data we want to collect from the macros, treating it uniformly +#if (RESULT_DIAGNOSTICS_LEVEL >= 2) // line number +#define __R_IF_LINE(term) term +#define __R_IF_NOT_LINE(term) +#define __R_IF_COMMA , +#define __R_LINE_VALUE static_cast(__LINE__) +#else +#define __R_IF_LINE(term) +#define __R_IF_NOT_LINE(term) term +#define __R_IF_COMMA +#define __R_LINE_VALUE static_cast(0) +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 3) // line number + file name +#define __R_IF_FILE(term) term +#define __R_IF_NOT_FILE(term) +#define __R_FILE_VALUE __FILE__ +#else +#define __R_IF_FILE(term) +#define __R_IF_NOT_FILE(term) term +#define __R_FILE_VALUE nullptr +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 4) // line number + file name + function name +#define __R_IF_FUNCTION(term) term +#define __R_IF_NOT_FUNCTION(term) +#else +#define __R_IF_FUNCTION(term) +#define __R_IF_NOT_FUNCTION(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 5) // line number + file name + function name + macro code +#define __R_IF_CODE(term) term +#define __R_IF_NOT_CODE(term) +#else +#define __R_IF_CODE(term) +#define __R_IF_NOT_CODE(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS == 1) +#define __R_IF_CALLERADDRESS(term) term +#define __R_IF_NOT_CALLERADDRESS(term) +#define __R_CALLERADDRESS_VALUE _ReturnAddress() +#else +#define __R_IF_CALLERADDRESS(term) +#define __R_IF_NOT_CALLERADDRESS(term) term +#define __R_CALLERADDRESS_VALUE nullptr +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS == 1) || (RESULT_DIAGNOSTICS_LEVEL >= 2) +#define __R_IF_TRAIL_COMMA , +#else +#define __R_IF_TRAIL_COMMA +#endif +// Assemble the varying amounts of data into a single macro +#define __R_INFO_ONLY(CODE) __R_IF_CALLERADDRESS(_ReturnAddress() __R_IF_COMMA) __R_IF_LINE(__R_LINE_VALUE) __R_IF_FILE(__R_COMMA __R_FILE_VALUE) __R_IF_FUNCTION(__R_COMMA __FUNCTION__) __R_IF_CODE(__R_COMMA CODE) +#define __R_INFO(CODE) __R_INFO_ONLY(CODE) __R_IF_TRAIL_COMMA +#define __R_INFO_NOFILE_ONLY(CODE) __R_IF_CALLERADDRESS(_ReturnAddress() __R_IF_COMMA) __R_IF_LINE(__R_LINE_VALUE) __R_IF_FILE(__R_COMMA "wil") __R_IF_FUNCTION(__R_COMMA __FUNCTION__) __R_IF_CODE(__R_COMMA CODE) +#define __R_INFO_NOFILE(CODE) __R_INFO_NOFILE_ONLY(CODE) __R_IF_TRAIL_COMMA +#define __R_FN_PARAMS_ONLY __R_IF_CALLERADDRESS(void* callerReturnAddress __R_IF_COMMA) __R_IF_LINE(unsigned int lineNumber) __R_IF_FILE(__R_COMMA _In_opt_ PCSTR fileName) __R_IF_FUNCTION(__R_COMMA _In_opt_ PCSTR functionName) __R_IF_CODE(__R_COMMA _In_opt_ PCSTR code) +#define __R_FN_PARAMS __R_FN_PARAMS_ONLY __R_IF_TRAIL_COMMA +#define __R_FN_CALL_ONLY __R_IF_CALLERADDRESS(callerReturnAddress __R_IF_COMMA) __R_IF_LINE(lineNumber) __R_IF_FILE(__R_COMMA fileName) __R_IF_FUNCTION(__R_COMMA functionName) __R_IF_CODE(__R_COMMA code) +#define __R_FN_CALL __R_FN_CALL_ONLY __R_IF_TRAIL_COMMA +#define __R_FN_LOCALS __R_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __R_IF_NOT_LINE(unsigned int lineNumber = 0;) __R_IF_NOT_FILE(PCSTR fileName = nullptr;) __R_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __R_IF_NOT_CODE(PCSTR code = nullptr;) +#define __R_FN_LOCALS_RA __R_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __R_IF_NOT_LINE(unsigned int lineNumber = 0;) __R_IF_NOT_FILE(PCSTR fileName = nullptr;) __R_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __R_IF_NOT_CODE(PCSTR code = nullptr;) void* returnAddress = _ReturnAddress(); +#define __R_FN_UNREFERENCED __R_IF_CALLERADDRESS((void)callerReturnAddress;) __R_IF_LINE((void)lineNumber;) __R_IF_FILE((void)fileName;) __R_IF_FUNCTION((void)functionName;) __R_IF_CODE((void)code;) +// 1) Direct Methods +// * Called Directly by Macros +// * Always noinline +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_DIRECT_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __R_DIRECT_NORET_METHOD(RetType, MethodName) template inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#else +#define __R_DIRECT_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_DIRECT_NORET_METHOD(RetType, MethodName) inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#endif +#define __R_DIRECT_FN_PARAMS __R_FN_PARAMS +#define __R_DIRECT_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +#define __R_DIRECT_FN_CALL __R_FN_CALL_FULL_RA __R_COMMA +#define __R_DIRECT_FN_CALL_ONLY __R_FN_CALL_FULL_RA +// 2) Internal Methods +// * Only called by Conditional routines +// * 'inline' when (RESULT_INLINE_ERROR_TESTS = 0 and RESULT_DIAGNOSTICS_LEVEL != 1), otherwise noinline (directly called by code when branching is forceinlined) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1 and RESULT_INLINE_ERROR_TESTS = 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_INTERNAL_NOINLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __R_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_INTERNAL_INLINE_METHOD(MethodName) template inline __declspec(noinline) void MethodName +#define __R_INTERNAL_INLINE_NORET_METHOD(MethodName) template inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#else +#define __R_INTERNAL_NOINLINE_METHOD(MethodName) inline void MethodName +#define __R_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline RESULT_NORETURN void MethodName +#define __R_INTERNAL_INLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __R_INTERNAL_INLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#endif +#define __R_CALL_INTERNAL_NOINLINE_METHOD(MethodName) MethodName +#define __R_INTERNAL_NOINLINE_FN_PARAMS __R_FN_PARAMS void* returnAddress __R_COMMA +#define __R_INTERNAL_NOINLINE_FN_PARAMS_ONLY __R_FN_PARAMS void* returnAddress +#define __R_INTERNAL_NOINLINE_FN_CALL __R_FN_CALL_FULL __R_COMMA +#define __R_INTERNAL_NOINLINE_FN_CALL_ONLY __R_FN_CALL_FULL +#define __R_INTERNAL_INLINE_FN_PARAMS __R_FN_PARAMS +#define __R_INTERNAL_INLINE_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +#define __R_INTERNAL_INLINE_FN_CALL __R_FN_CALL_FULL_RA __R_COMMA +#define __R_INTERNAL_INLINE_FN_CALL_ONLY __R_FN_CALL_FULL_RA +#if (RESULT_INLINE_ERROR_TESTS == 0) +#define __R_INTERNAL_METHOD __R_INTERNAL_NOINLINE_METHOD +#define __R_INTERNAL_NORET_METHOD __R_INTERNAL_NOINLINE_NORET_METHOD +#define __R_CALL_INTERNAL_METHOD __R_CALL_INTERNAL_NOINLINE_METHOD +#define __R_INTERNAL_FN_PARAMS __R_INTERNAL_NOINLINE_FN_PARAMS +#define __R_INTERNAL_FN_PARAMS_ONLY __R_INTERNAL_NOINLINE_FN_PARAMS_ONLY +#define __R_INTERNAL_FN_CALL __R_INTERNAL_NOINLINE_FN_CALL +#define __R_INTERNAL_FN_CALL_ONLY __R_INTERNAL_NOINLINE_FN_CALL_ONLY +#else +#define __R_INTERNAL_METHOD __R_INTERNAL_INLINE_METHOD +#define __R_INTERNAL_NORET_METHOD __R_INTERNAL_INLINE_NORET_METHOD +#define __R_CALL_INTERNAL_METHOD __R_CALL_INTERNAL_INLINE_METHOD +#define __R_INTERNAL_FN_PARAMS __R_INTERNAL_INLINE_FN_PARAMS +#define __R_INTERNAL_FN_PARAMS_ONLY __R_INTERNAL_INLINE_FN_PARAMS_ONLY +#define __R_INTERNAL_FN_CALL __R_INTERNAL_INLINE_FN_CALL +#define __R_INTERNAL_FN_CALL_ONLY __R_INTERNAL_INLINE_FN_CALL_ONLY +#endif +// 3) Conditional Methods +// * Called Directly by Macros +// * May be noinline or __forceinline depending upon (RESULT_INLINE_ERROR_TESTS) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_INLINE_METHOD(RetType, MethodName) template __forceinline RetType MethodName +#define __R_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_PARTIAL_TEMPLATE unsigned int optimizerCounter __R_COMMA +#else +#define __R_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_INLINE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_PARTIAL_TEMPLATE +#endif +#define __R_CONDITIONAL_NOINLINE_FN_CALL __R_FN_CALL _ReturnAddress() __R_COMMA +#define __R_CONDITIONAL_NOINLINE_FN_CALL_ONLY __R_FN_CALL _ReturnAddress() +#define __R_CONDITIONAL_INLINE_FN_CALL __R_FN_CALL +#define __R_CONDITIONAL_INLINE_FN_CALL_ONLY __R_FN_CALL_ONLY +#if (RESULT_INLINE_ERROR_TESTS == 0) +#define __R_CONDITIONAL_METHOD __R_CONDITIONAL_NOINLINE_METHOD +#define __R_CONDITIONAL_TEMPLATE_METHOD __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD +#define __R_CONDITIONAL_FN_CALL __R_CONDITIONAL_NOINLINE_FN_CALL +#define __R_CONDITIONAL_FN_CALL_ONLY __R_CONDITIONAL_NOINLINE_FN_CALL_ONLY +#else +#define __R_CONDITIONAL_METHOD __R_CONDITIONAL_INLINE_METHOD +#define __R_CONDITIONAL_TEMPLATE_METHOD __R_CONDITIONAL_INLINE_TEMPLATE_METHOD +#define __R_CONDITIONAL_FN_CALL __R_CONDITIONAL_INLINE_FN_CALL +#define __R_CONDITIONAL_FN_CALL_ONLY __R_CONDITIONAL_INLINE_FN_CALL_ONLY +#endif +#define __R_CONDITIONAL_FN_PARAMS __R_FN_PARAMS +#define __R_CONDITIONAL_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +// Macro call-site helpers +#define __R_NS_ASSEMBLE2(ri, rd) in##ri##diag##rd // Differing internal namespaces eliminate ODR violations between modes +#define __R_NS_ASSEMBLE(ri, rd) __R_NS_ASSEMBLE2(ri, rd) +#define __R_NS_NAME __R_NS_ASSEMBLE(RESULT_INLINE_ERROR_TESTS, RESULT_DIAGNOSTICS_LEVEL) +#define __R_NS wil::details::__R_NS_NAME +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_FN(MethodName) __R_NS:: MethodName <__COUNTER__> +#else +#define __R_FN(MethodName) __R_NS:: MethodName +#endif +// NOTE: This ENDs the common macro handling (__R_ prefix) for non-fail fast handled cases +// This entire section is repeated below for fail fast (__RFF_ prefix). For ease of editing this section, the +// process is to copy/paste, and search and replace (__R_ -> __RFF_), (RESULT_DIAGNOSTICS_LEVEL -> RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST), +// (RESULT_INLINE_ERROR_TESTS -> RESULT_INLINE_ERROR_TESTS_FAIL_FAST) and (RESULT_INCLUDE_CALLER_RETURNADDRESS -> RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST) +#define __RFF_COMMA , +#define __RFF_FN_CALL_FULL callerReturnAddress, lineNumber, fileName, functionName, code, returnAddress +#define __RFF_FN_CALL_FULL_RA callerReturnAddress, lineNumber, fileName, functionName, code, _ReturnAddress() +// The following macros assemble the varying amount of data we want to collect from the macros, treating it uniformly +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 2) // line number +#define __RFF_IF_LINE(term) term +#define __RFF_IF_NOT_LINE(term) +#define __RFF_IF_COMMA , +#else +#define __RFF_IF_LINE(term) +#define __RFF_IF_NOT_LINE(term) term +#define __RFF_IF_COMMA +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 3) // line number + file name +#define __RFF_IF_FILE(term) term +#define __RFF_IF_NOT_FILE(term) +#else +#define __RFF_IF_FILE(term) +#define __RFF_IF_NOT_FILE(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 4) // line number + file name + function name +#define __RFF_IF_FUNCTION(term) term +#define __RFF_IF_NOT_FUNCTION(term) +#else +#define __RFF_IF_FUNCTION(term) +#define __RFF_IF_NOT_FUNCTION(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 5) // line number + file name + function name + macro code +#define __RFF_IF_CODE(term) term +#define __RFF_IF_NOT_CODE(term) +#else +#define __RFF_IF_CODE(term) +#define __RFF_IF_NOT_CODE(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST == 1) +#define __RFF_IF_CALLERADDRESS(term) term +#define __RFF_IF_NOT_CALLERADDRESS(term) +#else +#define __RFF_IF_CALLERADDRESS(term) +#define __RFF_IF_NOT_CALLERADDRESS(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST == 1) || (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 2) +#define __RFF_IF_TRAIL_COMMA , +#else +#define __RFF_IF_TRAIL_COMMA +#endif +// Assemble the varying amounts of data into a single macro +#define __RFF_INFO_ONLY(CODE) __RFF_IF_CALLERADDRESS(_ReturnAddress() __RFF_IF_COMMA) __RFF_IF_LINE(__R_LINE_VALUE) __RFF_IF_FILE(__RFF_COMMA __R_FILE_VALUE) __RFF_IF_FUNCTION(__RFF_COMMA __FUNCTION__) __RFF_IF_CODE(__RFF_COMMA CODE) +#define __RFF_INFO(CODE) __RFF_INFO_ONLY(CODE) __RFF_IF_TRAIL_COMMA +#define __RFF_INFO_NOFILE_ONLY(CODE) __RFF_IF_CALLERADDRESS(_ReturnAddress() __RFF_IF_COMMA) __RFF_IF_LINE(__R_LINE_VALUE) __RFF_IF_FILE(__RFF_COMMA "wil") __RFF_IF_FUNCTION(__RFF_COMMA __FUNCTION__) __RFF_IF_CODE(__RFF_COMMA CODE) +#define __RFF_INFO_NOFILE(CODE) __RFF_INFO_NOFILE_ONLY(CODE) __RFF_IF_TRAIL_COMMA +#define __RFF_FN_PARAMS_ONLY __RFF_IF_CALLERADDRESS(void* callerReturnAddress __RFF_IF_COMMA) __RFF_IF_LINE(unsigned int lineNumber) __RFF_IF_FILE(__RFF_COMMA _In_opt_ PCSTR fileName) __RFF_IF_FUNCTION(__RFF_COMMA _In_opt_ PCSTR functionName) __RFF_IF_CODE(__RFF_COMMA _In_opt_ PCSTR code) +#define __RFF_FN_PARAMS __RFF_FN_PARAMS_ONLY __RFF_IF_TRAIL_COMMA +#define __RFF_FN_CALL_ONLY __RFF_IF_CALLERADDRESS(callerReturnAddress __RFF_IF_COMMA) __RFF_IF_LINE(lineNumber) __RFF_IF_FILE(__RFF_COMMA fileName) __RFF_IF_FUNCTION(__RFF_COMMA functionName) __RFF_IF_CODE(__RFF_COMMA code) +#define __RFF_FN_CALL __RFF_FN_CALL_ONLY __RFF_IF_TRAIL_COMMA +#define __RFF_FN_LOCALS __RFF_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __RFF_IF_NOT_LINE(unsigned int lineNumber = 0;) __RFF_IF_NOT_FILE(PCSTR fileName = nullptr;) __RFF_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __RFF_IF_NOT_CODE(PCSTR code = nullptr;) +#define __RFF_FN_UNREFERENCED __RFF_IF_CALLERADDRESS(callerReturnAddress;) __RFF_IF_LINE(lineNumber;) __RFF_IF_FILE(fileName;) __RFF_IF_FUNCTION(functionName;) __RFF_IF_CODE(code;) +// 1) Direct Methods +// * Called Directly by Macros +// * Always noinline +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_DIRECT_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __RFF_DIRECT_NORET_METHOD(RetType, MethodName) template inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#else +#define __RFF_DIRECT_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_DIRECT_NORET_METHOD(RetType, MethodName) inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#endif +#define __RFF_DIRECT_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_DIRECT_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +#define __RFF_DIRECT_FN_CALL __RFF_FN_CALL_FULL_RA __RFF_COMMA +#define __RFF_DIRECT_FN_CALL_ONLY __RFF_FN_CALL_FULL_RA +// 2) Internal Methods +// * Only called by Conditional routines +// * 'inline' when (RESULT_INLINE_ERROR_TESTS_FAIL_FAST = 0 and RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST != 1), otherwise noinline (directly called by code when branching is forceinlined) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1 and RESULT_INLINE_ERROR_TESTS_FAIL_FAST = 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_INTERNAL_NOINLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_INTERNAL_INLINE_METHOD(MethodName) template inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_INLINE_NORET_METHOD(MethodName) template inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#else +#define __RFF_INTERNAL_NOINLINE_METHOD(MethodName) inline void MethodName +#define __RFF_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline RESULT_NORETURN void MethodName +#define __RFF_INTERNAL_INLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_INLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#endif +#define __RFF_CALL_INTERNAL_NOINLINE_METHOD(MethodName) MethodName +#define __RFF_INTERNAL_NOINLINE_FN_PARAMS __RFF_FN_PARAMS void* returnAddress __RFF_COMMA +#define __RFF_INTERNAL_NOINLINE_FN_PARAMS_ONLY __RFF_FN_PARAMS void* returnAddress +#define __RFF_INTERNAL_NOINLINE_FN_CALL __RFF_FN_CALL_FULL __RFF_COMMA +#define __RFF_INTERNAL_NOINLINE_FN_CALL_ONLY __RFF_FN_CALL_FULL +#define __RFF_INTERNAL_INLINE_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_INTERNAL_INLINE_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +#define __RFF_INTERNAL_INLINE_FN_CALL __RFF_FN_CALL_FULL_RA __RFF_COMMA +#define __RFF_INTERNAL_INLINE_FN_CALL_ONLY __RFF_FN_CALL_FULL_RA +#if (RESULT_INLINE_ERROR_TESTS_FAIL_FAST == 0) +#define __RFF_INTERNAL_METHOD __RFF_INTERNAL_NOINLINE_METHOD +#define __RFF_INTERNAL_NORET_METHOD __RFF_INTERNAL_NOINLINE_NORET_METHOD +#define __RFF_CALL_INTERNAL_METHOD __RFF_CALL_INTERNAL_NOINLINE_METHOD +#define __RFF_INTERNAL_FN_PARAMS __RFF_INTERNAL_NOINLINE_FN_PARAMS +#define __RFF_INTERNAL_FN_PARAMS_ONLY __RFF_INTERNAL_NOINLINE_FN_PARAMS_ONLY +#define __RFF_INTERNAL_FN_CALL __RFF_INTERNAL_NOINLINE_FN_CALL +#define __RFF_INTERNAL_FN_CALL_ONLY __RFF_INTERNAL_NOINLINE_FN_CALL_ONLY +#else +#define __RFF_INTERNAL_METHOD __RFF_INTERNAL_INLINE_METHOD +#define __RFF_INTERNAL_NORET_METHOD __RFF_INTERNAL_INLINE_NORET_METHOD +#define __RFF_CALL_INTERNAL_METHOD __RFF_CALL_INTERNAL_INLINE_METHOD +#define __RFF_INTERNAL_FN_PARAMS __RFF_INTERNAL_INLINE_FN_PARAMS +#define __RFF_INTERNAL_FN_PARAMS_ONLY __RFF_INTERNAL_INLINE_FN_PARAMS_ONLY +#define __RFF_INTERNAL_FN_CALL __RFF_INTERNAL_INLINE_FN_CALL +#define __RFF_INTERNAL_FN_CALL_ONLY __RFF_INTERNAL_INLINE_FN_CALL_ONLY +#endif +// 3) Conditional Methods +// * Called Directly by Macros +// * May be noinline or __forceinline depending upon (RESULT_INLINE_ERROR_TESTS_FAIL_FAST) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_INLINE_METHOD(RetType, MethodName) template __forceinline RetType MethodName +#define __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_PARTIAL_TEMPLATE unsigned int optimizerCounter __RFF_COMMA +#else +#define __RFF_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_INLINE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_PARTIAL_TEMPLATE +#endif +#define __RFF_CONDITIONAL_NOINLINE_FN_CALL __RFF_FN_CALL _ReturnAddress() __RFF_COMMA +#define __RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY __RFF_FN_CALL _ReturnAddress() +#define __RFF_CONDITIONAL_INLINE_FN_CALL __RFF_FN_CALL +#define __RFF_CONDITIONAL_INLINE_FN_CALL_ONLY __RFF_FN_CALL_ONLY +#if (RESULT_INLINE_ERROR_TESTS_FAIL_FAST == 0) +#define __RFF_CONDITIONAL_METHOD __RFF_CONDITIONAL_NOINLINE_METHOD +#define __RFF_CONDITIONAL_TEMPLATE_METHOD __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD +#define __RFF_CONDITIONAL_FN_CALL __RFF_CONDITIONAL_NOINLINE_FN_CALL +#define __RFF_CONDITIONAL_FN_CALL_ONLY __RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY +#else +#define __RFF_CONDITIONAL_METHOD __RFF_CONDITIONAL_INLINE_METHOD +#define __RFF_CONDITIONAL_TEMPLATE_METHOD __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD +#define __RFF_CONDITIONAL_FN_CALL __RFF_CONDITIONAL_INLINE_FN_CALL +#define __RFF_CONDITIONAL_FN_CALL_ONLY __RFF_CONDITIONAL_INLINE_FN_CALL_ONLY +#endif +#define __RFF_CONDITIONAL_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_CONDITIONAL_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +// Macro call-site helpers +#define __RFF_NS_ASSEMBLE2(ri, rd) in##ri##diag##rd // Differing internal namespaces eliminate ODR violations between modes +#define __RFF_NS_ASSEMBLE(ri, rd) __RFF_NS_ASSEMBLE2(ri, rd) +#define __RFF_NS_NAME __RFF_NS_ASSEMBLE(RESULT_INLINE_ERROR_TESTS_FAIL_FAST, RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST) +#define __RFF_NS wil::details::__RFF_NS_NAME +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_FN(MethodName) __RFF_NS:: MethodName <__COUNTER__> +#else +#define __RFF_FN(MethodName) __RFF_NS:: MethodName +#endif +// end-of-repeated fail-fast handling macros + +// Helpers for return macros +#define __RETURN_HR_MSG(hr, str, fmt, ...) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, ##__VA_ARGS__); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, ##__VA_ARGS__); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_MSG(err, str, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, ##__VA_ARGS__); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, ##__VA_ARGS__); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_GLE_MSG_FAIL(str, fmt, ...) return __R_FN(Return_GetLastErrorMsg)(__R_INFO(str) fmt, ##__VA_ARGS__) +#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_NOFILE(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_FAIL(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_FAIL_NOFILE(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32(err, str) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_FAIL(err, str) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_GLE_FAIL(str) return __R_FN(Return_GetLastError)(__R_INFO_ONLY(str)) +#define __RETURN_GLE_FAIL_NOFILE(str) return __R_FN(Return_GetLastError)(__R_INFO_NOFILE_ONLY(str)) +#define __RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_NTSTATUS_FAIL(status, str) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } __WI_SUPPRESS_4127_E while ((void)0, 0) +/// @endcond + +//***************************************************************************** +// Macros for returning failures as HRESULTs +//***************************************************************************** + +// Always returns a known result (HRESULT) - always logs failures +#define RETURN_HR(hr) __RETURN_HR(wil::verify_hresult(hr), #hr) +#define RETURN_LAST_ERROR() __RETURN_GLE_FAIL(nullptr) +#define RETURN_WIN32(win32err) __RETURN_WIN32(win32err, #win32err) +#define RETURN_NTSTATUS(status) __RETURN_NTSTATUS(status, #status) + +// Conditionally returns failures (HRESULT) - always logs failures +#define RETURN_IF_FAILED(hr) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) __WI_SUPPRESS_4127_S do { const auto __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_ERROR(win32err) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_NULL_ALLOC(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_HR_IF(hr, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_HR_IF_NULL(hr, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) + +// Always returns a known failure (HRESULT) - always logs a var-arg message on failure +#define RETURN_HR_MSG(hr, fmt, ...) __RETURN_HR_MSG(wil::verify_hresult(hr), #hr, fmt, ##__VA_ARGS__) +#define RETURN_LAST_ERROR_MSG(fmt, ...) __RETURN_GLE_MSG_FAIL(nullptr, fmt, ##__VA_ARGS__) +#define RETURN_WIN32_MSG(win32err, fmt, ...) __RETURN_WIN32_MSG(win32err, #win32err, fmt, ##__VA_ARGS__) +#define RETURN_NTSTATUS_MSG(status, fmt, ...) __RETURN_NTSTATUS_MSG(status, #status, fmt, ##__VA_ARGS__) + +// Conditionally returns failures (HRESULT) - always logs a var-arg message on failure +#define RETURN_IF_FAILED_MSG(hr, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __WI_SUPPRESS_4127_S do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +// Conditionally returns failures (HRESULT) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern +#define RETURN_IF_FAILED_EXPECTED(hr) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) __WI_SUPPRESS_4127_S do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return __HRESULT_FROM_WIN32(__errRet); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_EXPECTED(hr, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +#define __WI_OR_IS_EXPECTED_HRESULT(e) || (__hrRet == wil::verify_hresult(e)) +#define RETURN_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) \ + do \ + { \ + const auto __hrRet = wil::verify_hresult(hr); \ + if (FAILED(__hrRet)) \ + { \ + if ((__hrRet == wil::verify_hresult(hrExpected)) WI_FOREACH(__WI_OR_IS_EXPECTED_HRESULT, ##__VA_ARGS__)) \ + { \ + return __hrRet; \ + } \ + __RETURN_HR_FAIL(__hrRet, #hr); \ + } \ + } \ + while ((void)0, 0) + +//***************************************************************************** +// Macros for logging failures (ignore or pass-through) +//***************************************************************************** + +// Always logs a known failure +#define LOG_HR(hr) __R_FN(Log_Hr)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define LOG_LAST_ERROR() __R_FN(Log_GetLastError)(__R_INFO_ONLY(nullptr)) +#define LOG_WIN32(win32err) __R_FN(Log_Win32)(__R_INFO(#win32err) win32err) +#define LOG_NTSTATUS(status) __R_FN(Log_NtStatus)(__R_INFO(#status) status) + +// Conditionally logs failures - returns parameter value +#define LOG_IF_FAILED(hr) __R_FN(Log_IfFailed)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define LOG_IF_WIN32_BOOL_FALSE(win32BOOL) __R_FN(Log_IfWin32BoolFalse)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define LOG_IF_WIN32_ERROR(win32err) __R_FN(Log_IfWin32Error)(__R_INFO(#win32err) win32err) +#define LOG_IF_NULL_ALLOC(ptr) __R_FN(Log_IfNullAlloc)(__R_INFO(#ptr) ptr) +#define LOG_HR_IF(hr, condition) __R_FN(Log_HrIf)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define LOG_HR_IF_NULL(hr, ptr) __R_FN(Log_HrIfNull)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define LOG_LAST_ERROR_IF(condition) __R_FN(Log_GetLastErrorIf)(__R_INFO(#condition) wil::verify_bool(condition)) +#define LOG_LAST_ERROR_IF_NULL(ptr) __R_FN(Log_GetLastErrorIfNull)(__R_INFO(#ptr) ptr) +#define LOG_IF_NTSTATUS_FAILED(status) __R_FN(Log_IfNtStatusFailed)(__R_INFO(#status) status) + +// Alternatives for SUCCEEDED(hr) and FAILED(hr) that conditionally log failures +#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr)) +#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr)) +#define SUCCEEDED_WIN32_LOG(win32err) SUCCEEDED_WIN32(LOG_IF_WIN32_ERROR(win32err)) +#define FAILED_WIN32_LOG(win32err) FAILED_WIN32(LOG_IF_WIN32_ERROR(win32err)) +#define SUCCEEDED_NTSTATUS_LOG(status) SUCCEEDED_NTSTATUS(LOG_IF_NTSTATUS_FAILED(status)) +#define FAILED_NTSTATUS_LOG(status) FAILED_NTSTATUS(LOG_IF_NTSTATUS_FAILED(status)) + +// Alternatives for NT_SUCCESS(x) that conditionally logs failures +#define NT_SUCCESS_LOG(status) NT_SUCCESS(LOG_IF_NTSTATUS_FAILED(status)) + +// Always logs a known failure - logs a var-arg message on failure +#define LOG_HR_MSG(hr, fmt, ...) __R_FN(Log_HrMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_MSG(fmt, ...) __R_FN(Log_GetLastErrorMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define LOG_WIN32_MSG(win32err, fmt, ...) __R_FN(Log_Win32Msg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define LOG_NTSTATUS_MSG(status, fmt, ...) __R_FN(Log_NtStatusMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally logs failures - returns parameter value - logs a var-arg message on failure +#define LOG_IF_FAILED_MSG(hr, fmt, ...) __R_FN(Log_IfFailedMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define LOG_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __R_FN(Log_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define LOG_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __R_FN(Log_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define LOG_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __R_FN(Log_IfNullAllocMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define LOG_HR_IF_MSG(hr, condition, fmt, ...) __R_FN(Log_HrIfMsg)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define LOG_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __R_FN(Log_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_IF_MSG(condition, fmt, ...) __R_FN(Log_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __R_FN(Log_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define LOG_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __R_FN(Log_IfNtStatusFailedMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +#define __WI_COMMA_EXPECTED_HRESULT(e) , wil::verify_hresult(e) +#define LOG_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) __R_FN(Log_IfFailedWithExpected)(__R_INFO(#hr) wil::verify_hresult(hr), WI_ARGS_COUNT(__VA_ARGS__) + 1, wil::verify_hresult(hrExpected) WI_FOREACH(__WI_COMMA_EXPECTED_HRESULT, ##__VA_ARGS__)) + +//***************************************************************************** +// Macros to fail fast the process on failures +//***************************************************************************** + +// Always fail fast a known failure +#define FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO(#hr) wil::verify_hresult(hr)) +#define FAIL_FAST_LAST_ERROR() __RFF_FN(FailFast_GetLastError)(__RFF_INFO_ONLY(nullptr)) +#define FAIL_FAST_WIN32(win32err) __RFF_FN(FailFast_Win32)(__RFF_INFO(#win32err) win32err) +#define FAIL_FAST_NTSTATUS(status) __RFF_FN(FailFast_NtStatus)(__RFF_INFO(#status) status) + +// Conditionally fail fast failures - returns parameter value +#define FAIL_FAST_IF_FAILED(hr) __RFF_FN(FailFast_IfFailed)(__RFF_INFO(#hr) wil::verify_hresult(hr)) +#define FAIL_FAST_IF_WIN32_BOOL_FALSE(win32BOOL) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define FAIL_FAST_IF_WIN32_ERROR(win32err) __RFF_FN(FailFast_IfWin32Error)(__RFF_INFO(#win32err) win32err) +#define FAIL_FAST_IF_NULL_ALLOC(ptr) __RFF_FN(FailFast_IfNullAlloc)(__RFF_INFO(#ptr) ptr) +#define FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define FAIL_FAST_HR_IF_NULL(hr, ptr) __RFF_FN(FailFast_HrIfNull)(__RFF_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define FAIL_FAST_LAST_ERROR_IF(condition) __RFF_FN(FailFast_GetLastErrorIf)(__RFF_INFO(#condition) wil::verify_bool(condition)) +#define FAIL_FAST_LAST_ERROR_IF_NULL(ptr) __RFF_FN(FailFast_GetLastErrorIfNull)(__RFF_INFO(#ptr) ptr) +#define FAIL_FAST_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFast_IfNtStatusFailed)(__RFF_INFO(#status) status) + +// Always fail fast a known failure - fail fast a var-arg message on failure +#define FAIL_FAST_HR_MSG(hr, fmt, ...) __RFF_FN(FailFast_HrMsg)(__RFF_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_MSG(fmt, ...) __RFF_FN(FailFast_GetLastErrorMsg)(__RFF_INFO(nullptr) fmt, ##__VA_ARGS__) +#define FAIL_FAST_WIN32_MSG(win32err, fmt, ...) __RFF_FN(FailFast_Win32Msg)(__RFF_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define FAIL_FAST_NTSTATUS_MSG(status, fmt, ...) __RFF_FN(FailFast_NtStatusMsg)(__RFF_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally fail fast failures - returns parameter value - fail fast a var-arg message on failure +#define FAIL_FAST_IF_FAILED_MSG(hr, fmt, ...) __RFF_FN(FailFast_IfFailedMsg)(__RFF_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __RFF_FN(FailFast_IfWin32BoolFalseMsg)(__RFF_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __RFF_FN(FailFast_IfWin32ErrorMsg)(__RFF_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __RFF_FN(FailFast_IfNullAllocMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_HR_IF_MSG(hr, condition, fmt, ...) __RFF_FN(FailFast_HrIfMsg)(__RFF_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __RFF_FN(FailFast_HrIfNullMsg)(__RFF_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_IF_MSG(condition, fmt, ...) __RFF_FN(FailFast_GetLastErrorIfMsg)(__RFF_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __RFF_FN(FailFast_GetLastErrorIfNullMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __RFF_FN(FailFast_IfNtStatusFailedMsg)(__RFF_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Always fail fast a known failure +#ifndef FAIL_FAST +#define FAIL_FAST() __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(nullptr)) +#endif + +// Conditionally fail fast failures - returns parameter value +#define FAIL_FAST_IF(condition) __RFF_FN(FailFast_If)(__RFF_INFO(#condition) wil::verify_bool(condition)) +#define FAIL_FAST_IF_NULL(ptr) __RFF_FN(FailFast_IfNull)(__RFF_INFO(#ptr) ptr) + +// Always fail fast a known failure - fail fast a var-arg message on failure +#define FAIL_FAST_MSG(fmt, ...) __RFF_FN(FailFast_UnexpectedMsg)(__RFF_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Conditionally fail fast failures - returns parameter value - fail fast a var-arg message on failure +#define FAIL_FAST_IF_MSG(condition, fmt, ...) __RFF_FN(FailFast_IfMsg)(__RFF_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NULL_MSG(ptr, fmt, ...) __RFF_FN(FailFast_IfNullMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) + +// Immediate fail fast (no telemetry - use rarely / only when *already* in an undefined state) +#define FAIL_FAST_IMMEDIATE() __RFF_FN(FailFastImmediate_Unexpected)() + +// Conditional immediate fail fast (no telemetry - use rarely / only when *already* in an undefined state) +#define FAIL_FAST_IMMEDIATE_IF_FAILED(hr) __RFF_FN(FailFastImmediate_IfFailed)(wil::verify_hresult(hr)) +#define FAIL_FAST_IMMEDIATE_IF(condition) __RFF_FN(FailFastImmediate_If)(wil::verify_bool(condition)) +#define FAIL_FAST_IMMEDIATE_IF_NULL(ptr) __RFF_FN(FailFastImmediate_IfNull)(ptr) +#define FAIL_FAST_IMMEDIATE_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFastImmediate_IfNtStatusFailed)(status) + +// Specializations +#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while ((void)0, 0) + + +//***************************************************************************** +// Macros to throw exceptions on failure +//***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + +// Always throw a known failure +#define THROW_HR(hr) __R_FN(Throw_Hr)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define THROW_LAST_ERROR() __R_FN(Throw_GetLastError)(__R_INFO_ONLY(nullptr)) +#define THROW_WIN32(win32err) __R_FN(Throw_Win32)(__R_INFO(#win32err) win32err) +#define THROW_EXCEPTION(exception) wil::details::ReportFailure_CustomException(__R_INFO(#exception) exception) +#define THROW_NTSTATUS(status) __R_FN(Throw_NtStatus)(__R_INFO(#status) status) + +// Conditionally throw failures - returns parameter value +#define THROW_IF_FAILED(hr) __R_FN(Throw_IfFailed)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define THROW_IF_WIN32_BOOL_FALSE(win32BOOL) __R_FN(Throw_IfWin32BoolFalse)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define THROW_IF_WIN32_ERROR(win32err) __R_FN(Throw_IfWin32Error)(__R_INFO(#win32err) win32err) +#define THROW_IF_NULL_ALLOC(ptr) __R_FN(Throw_IfNullAlloc)(__R_INFO(#ptr) ptr) +#define THROW_HR_IF(hr, condition) __R_FN(Throw_HrIf)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define THROW_HR_IF_NULL(hr, ptr) __R_FN(Throw_HrIfNull)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define THROW_LAST_ERROR_IF(condition) __R_FN(Throw_GetLastErrorIf)(__R_INFO(#condition) wil::verify_bool(condition)) +#define THROW_LAST_ERROR_IF_NULL(ptr) __R_FN(Throw_GetLastErrorIfNull)(__R_INFO(#ptr) ptr) +#define THROW_IF_NTSTATUS_FAILED(status) __R_FN(Throw_IfNtStatusFailed)(__R_INFO(#status) status) + +// Always throw a known failure - throw a var-arg message on failure +#define THROW_HR_MSG(hr, fmt, ...) __R_FN(Throw_HrMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_MSG(fmt, ...) __R_FN(Throw_GetLastErrorMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define THROW_WIN32_MSG(win32err, fmt, ...) __R_FN(Throw_Win32Msg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define THROW_EXCEPTION_MSG(exception, fmt, ...) wil::details::ReportFailure_CustomExceptionMsg(__R_INFO(#exception) exception, fmt, ##__VA_ARGS__) +#define THROW_NTSTATUS_MSG(status, fmt, ...) __R_FN(Throw_NtStatusMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally throw failures - returns parameter value - throw a var-arg message on failure +#define THROW_IF_FAILED_MSG(hr, fmt, ...) __R_FN(Throw_IfFailedMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define THROW_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __R_FN(Throw_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define THROW_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __R_FN(Throw_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define THROW_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __R_FN(Throw_IfNullAllocMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define THROW_HR_IF_MSG(hr, condition, fmt, ...) __R_FN(Throw_HrIfMsg)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define THROW_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __R_FN(Throw_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_IF_MSG(condition, fmt, ...) __R_FN(Throw_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __R_FN(Throw_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define THROW_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __R_FN(Throw_IfNtStatusFailedMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + + +//***************************************************************************** +// Macros to catch and convert exceptions on failure +//***************************************************************************** + +// Use these macros *within* a catch (...) block to handle exceptions +#define RETURN_CAUGHT_EXCEPTION() return __R_FN(Return_CaughtException)(__R_INFO_ONLY(nullptr)) +#define RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define RETURN_CAUGHT_EXCEPTION_EXPECTED() return wil::ResultFromCaughtException() +#define LOG_CAUGHT_EXCEPTION() __R_FN(Log_CaughtException)(__R_INFO_ONLY(nullptr)) +#define LOG_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(Log_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define FAIL_FAST_CAUGHT_EXCEPTION() __R_FN(FailFast_CaughtException)(__R_INFO_ONLY(nullptr)) +#define FAIL_FAST_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(FailFast_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define THROW_NORMALIZED_CAUGHT_EXCEPTION() __R_FN(Throw_CaughtException)(__R_INFO_ONLY(nullptr)) +#define THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(Throw_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Use these macros in place of a catch block to handle exceptions +#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); } +#define CATCH_RETURN_MSG(fmt, ...) catch (...) { RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_RETURN_EXPECTED() catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } +#define CATCH_LOG() catch (...) { LOG_CAUGHT_EXCEPTION(); } +// Use CATCH_LOG_RETURN instead of CATCH_LOG in a function-try block around a destructor. CATCH_LOG in this specific case has an implicit throw at the end of scope. +// Due to a bug (DevDiv 441931), Warning 4297 (function marked noexcept throws exception) is detected even when the throwing code is unreachable, such as the end of scope after a return, in function-level catch. +#define CATCH_LOG_RETURN() catch (...) { __pragma(warning(suppress : 4297)); LOG_CAUGHT_EXCEPTION(); return; } +#define CATCH_LOG_MSG(fmt, ...) catch (...) { LOG_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +// Likewise use CATCH_LOG_RETURN_MSG instead of CATCH_LOG_MSG in function-try blocks around destructors. +#define CATCH_LOG_RETURN_MSG(fmt, ...) catch (...) { __pragma(warning(suppress : 4297)); LOG_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); return; } +#define CATCH_FAIL_FAST() catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); } +#define CATCH_FAIL_FAST_MSG(fmt, ...) catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_THROW_NORMALIZED() catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } +#define CATCH_THROW_NORMALIZED_MSG(fmt, ...) catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_LOG_RETURN_HR(hr) catch (...) { LOG_CAUGHT_EXCEPTION(); return hr; } + +#endif // WIL_ENABLE_EXCEPTIONS + +// Use this macro to supply diagnostics information to wil::ResultFromException +#define WI_DIAGNOSTICS_INFO wil::DiagnosticsInfo(__R_CALLERADDRESS_VALUE, __R_LINE_VALUE, __R_FILE_VALUE) +#define WI_DIAGNOSTICS_NAME(name) wil::DiagnosticsInfo(__R_CALLERADDRESS_VALUE, __R_LINE_VALUE, __R_FILE_VALUE, name) + + + +//***************************************************************************** +// Usage Error Macros +//***************************************************************************** + +#ifndef WI_USAGE_ASSERT_STOP +#define WI_USAGE_ASSERT_STOP(condition) WI_ASSERT(condition) +#endif +#ifdef RESULT_DEBUG +#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, ##__VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, ##__VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#else +#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#endif +#define WI_USAGE_VERIFY(condition, msg, ...) do { const auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, ##__VA_ARGS__); }} while ((void)0, 0) +#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { const auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, ##__VA_ARGS__); }} while ((void)0, 0) +#ifdef RESULT_DEBUG +#define WI_USAGE_ASSERT(condition, msg, ...) WI_USAGE_VERIFY(condition, msg, ##__VA_ARGS__) +#else +#define WI_USAGE_ASSERT(condition, msg, ...) +#endif + +//***************************************************************************** +// Internal Error Macros - DO NOT USE - these are for internal WIL use only to reduce sizes of binaries that use WIL +//***************************************************************************** +#ifdef RESULT_DEBUG +#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) RETURN_IF_FAILED(hr) +#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) RETURN_HR_IF(hr, cond) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) RETURN_LAST_ERROR_IF(cond) +#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) RETURN_LAST_ERROR_IF_NULL(ptr) +#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) RETURN_IF_NULL_ALLOC(ptr) +#define __WIL_PRIVATE_RETURN_LAST_ERROR() RETURN_LAST_ERROR() +#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) FAIL_FAST_HR_IF(hr, condition) +#define __WIL_PRIVATE_FAIL_FAST_HR(hr) FAIL_FAST_HR(hr) +#define __WIL_PRIVATE_LOG_HR(hr) LOG_HR(hr) +#else +#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { const BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR() __RETURN_GLE_FAIL_NOFILE(nullptr) +#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO_NOFILE(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define __WIL_PRIVATE_FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO_NOFILE(#hr) wil::verify_hresult(hr)) +#define __WIL_PRIVATE_LOG_HR(hr) __R_FN(Log_Hr)(__R_INFO_NOFILE(#hr) wil::verify_hresult(hr)) +#endif + +namespace wil +{ + // Indicates the kind of message / failure type that was used to produce a given error + enum class FailureType + { + Exception, // THROW_... + Return, // RETURN_..._LOG or RETURN_..._MSG + Log, // LOG_... + FailFast // FAIL_FAST_... + }; + + /** Use with functions and macros that allow customizing which kinds of exceptions are handled. + This is used with methods like wil::ResultFromException and wil::ResultFromExceptionDebug. */ + enum class SupportedExceptions + { + Default, //!< [Default] all well known exceptions (honors g_fResultFailFastUnknownExceptions). + Known, //!< [Known] all well known exceptions (including std::exception). + All, //!< [All] all exceptions, known or otherwise. + None, //!< [None] no exceptions at all, an exception will fail-fast where thrown. + Thrown, //!< [Thrown] exceptions thrown by wil only (Platform::Exception^ or ResultException). + ThrownOrAlloc //!< [ThrownOrAlloc] exceptions thrown by wil (Platform::Exception^ or ResultException) or std::bad_alloc. + }; + + // Represents the call context information about a given failure + // No constructors, destructors or virtual members should be contained within + struct CallContextInfo + { + long contextId; // incrementing ID for this call context (unique across an individual module load within process) + PCSTR contextName; // the explicit name given to this context + PCWSTR contextMessage; // [optional] Message that can be associated with the call context + }; + + // Represents all context information about a given failure + // No constructors, destructors or virtual members should be contained within + struct FailureInfo + { + FailureType type; + HRESULT hr; + long failureId; // incrementing ID for this specific failure (unique across an individual module load within process) + PCWSTR pszMessage; // Message is only present for _MSG logging (it's the Sprintf message) + DWORD threadId; // the thread this failure was originally encountered on + PCSTR pszCode; // [debug only] Capture code from the macro + PCSTR pszFunction; // [debug only] The function name + PCSTR pszFile; + unsigned int uLineNumber; + int cFailureCount; // How many failures of 'type' have been reported in this module so far + PCSTR pszCallContext; // General breakdown of the call context stack that generated this failure + CallContextInfo callContextOriginating; // The outermost (first seen) call context + CallContextInfo callContextCurrent; // The most recently seen call context + PCSTR pszModule; // The module where the failure originated + void* returnAddress; // The return address to the point that called the macro + void* callerReturnAddress; // The return address of the function that includes the macro + }; + + //! Created automatically from using WI_DIAGNOSTICS_INFO to provide diagnostics to functions. + //! Note that typically wil hides diagnostics from users under the covers by passing them automatically to functions as + //! parameters hidden behind a macro. In some cases, the user needs to directly supply these, so this class provides + //! the mechanism for that. We only use this for user-passed content as it can't be directly controlled by RESULT_DIAGNOSTICS_LEVEL + //! to ensure there are no ODR violations (though that variable still controls what parameters within this structure would be available). + struct DiagnosticsInfo + { + void* returnAddress = nullptr; + PCSTR file = nullptr; + PCSTR name = nullptr; + unsigned short line = 0; + + DiagnosticsInfo() = default; + + __forceinline DiagnosticsInfo(void* returnAddress_, unsigned short line_, PCSTR file_) : + returnAddress(returnAddress_), + file(file_), + line(line_) + { + } + + __forceinline DiagnosticsInfo(void* returnAddress_, unsigned short line_, PCSTR file_, PCSTR name_) : + returnAddress(returnAddress_), + file(file_), + name(name_), + line(line_) + { + } + }; + + enum class ErrorReturn + { + Auto, + None + }; + + // [optionally] Plug in error logging + // Note: This callback is deprecated. Please use SetResultTelemetryFallback for telemetry or + // SetResultLoggingCallback for observation. + extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_PFN_NOEXCEPT = nullptr; + + // [optional] + // This can be explicitly set to control whether or not error messages will be output to OutputDebugString. It can also + // be set directly from within the debugger to force console logging for debugging purposes. + __declspec(selectany) bool g_fResultOutputDebugString = true; + + // [optionally] Allows application to specify a debugger to detect whether a debugger is present. + // Useful for processes that can only be debugged under kernel debuggers where IsDebuggerPresent returns + // false. + __declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_PFN_NOEXCEPT = nullptr; + + // [optionally] Allows forcing WIL to believe a debugger is present. Useful for when a kernel debugger is attached and ::IsDebuggerPresent returns false + __declspec(selectany) bool g_fIsDebuggerPresent = false; + + // [optionally] Plug in additional exception-type support (return S_OK when *unable* to remap the exception) + __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_PFN_NOEXCEPT = nullptr; + + // [optionally] Use to configure fast fail of unknown exceptions (turn them off). + __declspec(selectany) bool g_fResultFailFastUnknownExceptions = true; + + // [optionally] Set to false to a configure all THROW_XXX macros in C++/CX to throw ResultException rather than Platform::Exception^ + __declspec(selectany) bool g_fResultThrowPlatformException = true; + + // [optionally] Set to false to a configure all CATCH_ and CAUGHT_ macros to NOT support (fail-fast) std::exception based exceptions (other than std::bad_alloc and wil::ResultException) + __declspec(selectany) bool g_fResultSupportStdException = true; + + // [optionally] Set to true to cause a debug break to occur on a result failure + __declspec(selectany) bool g_fBreakOnFailure = false; + + // [optionally] customize failfast behavior + __declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_PFN_NOEXCEPT = nullptr; + + /// @cond + namespace details + { + // True if g_pfnResultLoggingCallback is set (allows cutting off backwards compat calls to the function) + __declspec(selectany) bool g_resultMessageCallbackSet = false; + + _Success_(true) _Ret_range_(dest, destEnd) + inline PWSTR LogStringPrintf(_Out_writes_to_ptr_(destEnd) _Always_(_Post_z_) PWSTR dest, _Pre_satisfies_(destEnd >= dest) PCWSTR destEnd, _In_ _Printf_format_string_ PCWSTR format, ...) + { + va_list argList; + va_start(argList, format); + StringCchVPrintfW(dest, (destEnd - dest), format, argList); + return (destEnd == dest) ? dest : (dest + wcslen(dest)); + } + } + /// @endcond + + // This call generates the default logging string that makes its way to OutputDebugString for + // any particular failure. This string is also used to associate a failure with a PlatformException^ which + // only allows a single string to be associated with the exception. + inline HRESULT GetFailureLogString(_Out_writes_(cchDest) _Always_(_Post_z_) PWSTR pszDest, _Pre_satisfies_(cchDest > 0) _In_ size_t cchDest, _In_ FailureInfo const &failure) WI_NOEXCEPT + { + // This function was lenient to empty strings at one point and some callers became dependent on this beahvior + if ((cchDest == 0) || (pszDest == nullptr)) + { + return S_OK; + } + + pszDest[0] = L'\0'; + + // Call the logging callback (if present) to allow them to generate the debug string that will be pushed to the console + // or the platform exception object if the caller desires it. + if ((g_pfnResultLoggingCallback != nullptr) && details::g_resultMessageCallbackSet) + { + // older-form callback was a non-const FailureInfo*; conceptually this is const as callers should not be modifying + g_pfnResultLoggingCallback(const_cast(&failure), pszDest, cchDest); + } + + // The callback only optionally needs to supply the debug string -- if the callback didn't populate it, yet we still want + // it for OutputDebugString or exception message, then generate the default string. + if (pszDest[0] == L'\0') + { + PCSTR pszType = ""; + switch (failure.type) + { + case FailureType::Exception: + pszType = "Exception"; + break; + case FailureType::Return: + pszType = "ReturnHr"; + break; + case FailureType::Log: + pszType = "LogHr"; + break; + case FailureType::FailFast: + pszType = "FailFast"; + break; + } + + wchar_t szErrorText[256]; + szErrorText[0] = L'\0'; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, failure.hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorText, ARRAYSIZE(szErrorText), nullptr); + + // %FILENAME(%LINE): %TYPE(%count) tid(%threadid) %HRESULT %SystemMessage + // %Caller_MSG [%CODE(%FUNCTION)] + + PWSTR dest = pszDest; + PCWSTR destEnd = (pszDest + cchDest); + + if (failure.pszFile != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"%hs(%u)\\%hs!%p: ", failure.pszFile, failure.uLineNumber, failure.pszModule, failure.returnAddress); + } + else + { + dest = details::LogStringPrintf(dest, destEnd, L"%hs!%p: ", failure.pszModule, failure.returnAddress); + } + + if (failure.callerReturnAddress != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"(caller: %p) ", failure.callerReturnAddress); + } + + dest = details::LogStringPrintf(dest, destEnd, L"%hs(%d) tid(%x) %08X %ws", pszType, failure.cFailureCount, ::GetCurrentThreadId(), failure.hr, szErrorText); + + if ((failure.pszMessage != nullptr) || (failure.pszCallContext != nullptr) || (failure.pszFunction != nullptr)) + { + dest = details::LogStringPrintf(dest, destEnd, L" "); + if (failure.pszMessage != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"Msg:[%ws] ", failure.pszMessage); + } + if (failure.pszCallContext != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"CallContext:[%hs] ", failure.pszCallContext); + } + + if (failure.pszCode != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"[%hs(%hs)]\n", failure.pszFunction, failure.pszCode); + } + else if (failure.pszFunction != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"[%hs]\n", failure.pszFunction); + } + else + { + dest = details::LogStringPrintf(dest, destEnd, L"\n"); + } + } + } + + // Explicitly choosing to return success in the event of truncation... Current callers + // depend upon it or it would be eliminated. + return S_OK; + } + + /// @cond + namespace details + { + //! Interface used to wrap up code (generally a lambda or other functor) to run in an exception-managed context where + //! exceptions or errors can be observed and logged. + struct IFunctor + { + virtual HRESULT Run() = 0; + }; + + //! Used to provide custom behavior when an exception is encountered while executing IFunctor + struct IFunctorHost + { + virtual HRESULT Run(IFunctor& functor) = 0; + virtual HRESULT ExceptionThrown(void* returnAddress) = 0; + }; + + // Fallback telemetry provider callback (set with wil::SetResultTelemetryFallback) + __declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr; + + // Result.h plug-in (WIL use only) + __declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_PFN_NOEXCEPT = nullptr; + + // Observe all errors flowing through the system with this callback (set with wil::SetResultLoggingCallback); use with custom logging + __declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Module fetch function (automatically setup) + __declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Retrieve address offset and modulename + __declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_PFN_NOEXCEPT = nullptr; + + // Called with the expectation that the program will terminate when called inside of a loader callout. + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_PFN_NOEXCEPT = nullptr; + + // Called to translate an NTSTATUS value to a Win32 error code + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Call to DebugBreak + __declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_PFN_NOEXCEPT = nullptr; + + // Called to determine whether or not termination is happening + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) BOOLEAN(__stdcall *g_pfnDllShutdownInProgress)() WI_PFN_NOEXCEPT = nullptr; + __declspec(selectany) bool g_processShutdownInProgress = false; + + // On Desktop/System WINAPI family: dynalink RaiseFailFastException because we may encounter modules + // that do not have RaiseFailFastException in kernelbase. UWP apps will directly link. + __declspec(selectany) void (__stdcall *g_pfnRaiseFailFastException)(PEXCEPTION_RECORD,PCONTEXT,DWORD) = nullptr; + + // Exception-based compiled additions + __declspec(selectany) HRESULT(__stdcall *g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr; + __declspec(selectany) void(__stdcall *g_pfnRethrow)() = nullptr; + __declspec(selectany) void(__stdcall *g_pfnThrowResultException)(const FailureInfo& failure) = nullptr; + extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + + // C++/WinRT additions + extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException_CppWinRt)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + + // C++/cx compiled additions + extern "C" __declspec(selectany) void(__stdcall *g_pfnThrowPlatformException)(FailureInfo const &failure, PCWSTR debugString) = nullptr; + extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromKnownExceptions_WinRt)(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) = nullptr; + + // Plugin to call RoOriginateError (WIL use only) + __declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_PFN_NOEXCEPT = nullptr; + + enum class ReportFailureOptions + { + None = 0x00, + ForcePlatformException = 0x01, + MayRethrow = 0x02, + }; + DEFINE_ENUM_FLAG_OPERATORS(ReportFailureOptions); + + template + using functor_return_type = decltype((*static_cast(nullptr))()); + + template + struct functor_wrapper_void : public IFunctor + { + TFunctor&& functor; + functor_wrapper_void(TFunctor&& functor_) : functor(wistd::forward(functor_)) { } + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + HRESULT Run() override + { + functor(); + return S_OK; + } + #pragma warning(pop) + }; + + template + struct functor_wrapper_HRESULT : public IFunctor + { + TFunctor&& functor; + functor_wrapper_HRESULT(TFunctor& functor_) : functor(wistd::forward(functor_)) { } + HRESULT Run() override + { + return functor(); + } + }; + + template + struct functor_wrapper_other : public IFunctor + { + TFunctor&& functor; + TReturn& retVal; + functor_wrapper_other(TFunctor& functor_, TReturn& retval_) : functor(wistd::forward(functor_)), retVal(retval_) { } + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + HRESULT Run() override + { + retVal = functor(); + return S_OK; + } + #pragma warning(pop) + }; + + struct tag_return_void : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_void; + }; + + struct tag_return_HRESULT : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_HRESULT; + }; + + struct tag_return_other : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_other; + }; + + // type-trait to help discover the return type of a functor for tag/dispatch. + + template + struct return_type + { + typedef tag_return_other type; + }; + + template <> + struct return_type + { + typedef tag_return_HRESULT type; + }; + + template <> + struct return_type + { + typedef tag_return_void type; + }; + + template <> + struct return_type + { + typedef tag_return_void type; + }; + + template + using functor_tag = typename return_type>::type; + + // Forward declarations to enable use of fail fast and reporting internally... + namespace __R_NS_NAME + { + _Post_satisfies_(return == hr) __R_DIRECT_METHOD(HRESULT, Log_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT; + _Post_satisfies_(return == hr) __R_DIRECT_METHOD(HRESULT, Log_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT; + _Post_satisfies_(return == err) __R_DIRECT_METHOD(DWORD, Log_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT; + } + namespace __RFF_NS_NAME + { + __RFF_DIRECT_NORET_METHOD(void, FailFast_Unexpected)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_If)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIf)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_IfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_If)(bool condition) WI_NOEXCEPT; + } + + __declspec(noreturn) inline void __stdcall WilFailFast(const FailureInfo& info); + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, + bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, + _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, + _Out_ FailureInfo *failure) WI_NOEXCEPT; + + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + template + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + template + inline void ReportFailure_ReplaceMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, ...); + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr); + template + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr); + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + + //***************************************************************************** + // Fail fast helpers (for use only internally to WIL) + //***************************************************************************** + + /// @cond + #define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while ((void)0, 0) + #define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while ((void)0, 0) + #define __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(condition) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#condition) wil::verify_BOOL(condition)) + + // A simple ref-counted buffer class. The interface is very similar to shared_ptr<>, only it manages + // an allocated buffer and maintains the size. + + class shared_buffer + { + public: + shared_buffer() WI_NOEXCEPT : m_pCopy(nullptr), m_size(0) + { + } + + shared_buffer(shared_buffer const &other) WI_NOEXCEPT : m_pCopy(nullptr), m_size(0) + { + assign(other.m_pCopy, other.m_size); + } + + shared_buffer(shared_buffer &&other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy), + m_size(other.m_size) + { + other.m_pCopy = nullptr; + other.m_size = 0; + } + + ~shared_buffer() WI_NOEXCEPT + { + reset(); + } + + shared_buffer& operator=(shared_buffer const &other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + assign(other.m_pCopy, other.m_size); + } + return *this; + } + + shared_buffer& operator=(shared_buffer &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + m_size = other.m_size; + other.m_pCopy = nullptr; + other.m_size = 0; + } + return *this; + } + + void reset() WI_NOEXCEPT + { + if (m_pCopy != nullptr) + { + if (0 == ::InterlockedDecrementRelease(m_pCopy)) + { + WIL_FreeMemory(m_pCopy); + } + m_pCopy = nullptr; + m_size = 0; + } + } + + bool create(_In_reads_bytes_opt_(cbData) void const *pData, size_t cbData) WI_NOEXCEPT + { + if (cbData == 0) + { + reset(); + return true; + } + + long *pCopyRefCount = reinterpret_cast(WIL_AllocateMemory(sizeof(long)+cbData)); + if (pCopyRefCount == nullptr) + { + return false; + } + + *pCopyRefCount = 0; + if (pData != nullptr) + { + memcpy_s(pCopyRefCount + 1, cbData, pData, cbData); // +1 to advance past sizeof(long) counter + } + assign(pCopyRefCount, cbData); + return true; + } + + bool create(size_t cbData) WI_NOEXCEPT + { + return create(nullptr, cbData); + } + + void* get(_Out_opt_ size_t *pSize = nullptr) const WI_NOEXCEPT + { + if (pSize != nullptr) + { + *pSize = m_size; + } + return (m_pCopy == nullptr) ? nullptr : (m_pCopy + 1); + } + + size_t size() const WI_NOEXCEPT + { + return m_size; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + bool unique() const WI_NOEXCEPT + { + return ((m_pCopy != nullptr) && (*m_pCopy == 1)); + } + + private: + long *m_pCopy; // pointer to allocation: refcount + data + size_t m_size; // size of the data from m_pCopy + + void assign(_In_opt_ long *pCopy, size_t cbSize) WI_NOEXCEPT + { + reset(); + if (pCopy != nullptr) + { + m_pCopy = pCopy; + m_size = cbSize; + ::InterlockedIncrementNoFence(m_pCopy); + } + } + }; + + inline shared_buffer make_shared_buffer_nothrow(_In_reads_bytes_opt_(countBytes) void *pData, size_t countBytes) WI_NOEXCEPT + { + shared_buffer buffer; + buffer.create(pData, countBytes); + return buffer; + } + + inline shared_buffer make_shared_buffer_nothrow(size_t countBytes) WI_NOEXCEPT + { + shared_buffer buffer; + buffer.create(countBytes); + return buffer; + } + + // A small mimic of the STL shared_ptr class, but unlike shared_ptr, a pointer is not attached to the class, but is + // always simply contained within (it cannot be attached or detached). + + template + class shared_object + { + public: + shared_object() WI_NOEXCEPT : m_pCopy(nullptr) + { + } + + shared_object(shared_object const &other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy) + { + if (m_pCopy != nullptr) + { + ::InterlockedIncrementNoFence(&m_pCopy->m_refCount); + } + } + + shared_object(shared_object &&other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy) + { + other.m_pCopy = nullptr; + } + + ~shared_object() WI_NOEXCEPT + { + reset(); + } + + shared_object& operator=(shared_object const &other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + if (m_pCopy != nullptr) + { + ::InterlockedIncrementNoFence(&m_pCopy->m_refCount); + } + } + return *this; + } + + shared_object& operator=(shared_object &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + other.m_pCopy = nullptr; + } + return *this; + } + + void reset() WI_NOEXCEPT + { + if (m_pCopy != nullptr) + { + if (0 == ::InterlockedDecrementRelease(&m_pCopy->m_refCount)) + { + delete m_pCopy; + } + m_pCopy = nullptr; + } + } + + bool create() + { + RefAndObject *pObject = new(std::nothrow) RefAndObject(); + if (pObject == nullptr) + { + return false; + } + reset(); + m_pCopy = pObject; + return true; + } + + template + bool create(param_t &¶m1) + { + RefAndObject *pObject = new(std::nothrow) RefAndObject(wistd::forward(param1)); + if (pObject == nullptr) + { + return false; + } + reset(); + m_pCopy = pObject; + return true; + } + + object_t* get() const WI_NOEXCEPT + { + return (m_pCopy == nullptr) ? nullptr : &m_pCopy->m_object; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + bool unique() const WI_NOEXCEPT + { + return ((m_pCopy != nullptr) && (m_pCopy->m_refCount == 1)); + } + + object_t *operator->() const WI_NOEXCEPT + { + return get(); + } + + private: + struct RefAndObject + { + long m_refCount; + object_t m_object; + + RefAndObject() : + m_refCount(1), + m_object() + { + } + + template + RefAndObject(param_t &¶m1) : + m_refCount(1), + m_object(wistd::forward(param1)) + { + } + }; + + RefAndObject *m_pCopy; + }; + + // The following functions are basically the same, but are kept separated to: + // 1) Provide a unique count and last error code per-type + // 2) Avoid merging the types to allow easy debugging (breakpoints, conditional breakpoints based + // upon count of errors from a particular type, etc) + + __declspec(noinline) inline int RecordException(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordReturn(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordLog(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordFailFast(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + s_hrErrorLast = hr; + return 1; + } + + inline __declspec(noreturn) void __stdcall WilRaiseFailFastException(_In_ PEXCEPTION_RECORD er, _In_opt_ PCONTEXT cr, _In_ DWORD flags) + { + // if we managed to load the pointer either through WilDynamicRaiseFailFastException (PARTITION_DESKTOP etc.) + // or via direct linkage (e.g. UWP apps), then use it. + if (g_pfnRaiseFailFastException) + { + g_pfnRaiseFailFastException(er, cr, flags); + } + // if not, as a best effort, we are just going to call the intrinsic. + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + inline bool __stdcall GetModuleInformation(_In_opt_ void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_NOEXCEPT + { + HMODULE hModule = nullptr; + if (address && !GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(address), &hModule)) + { + assign_to_opt_param(addressOffset, 0U); + return false; + } + if (addressOffset) + { + *addressOffset = address ? static_cast(static_cast(address) - reinterpret_cast(hModule)) : 0; + } + if (name) + { + char modulePath[MAX_PATH]; + if (!GetModuleFileNameA(hModule, modulePath, ARRAYSIZE(modulePath))) + { + return false; + } + + PCSTR start = modulePath + strlen(modulePath); + while ((start > modulePath) && (*(start - 1) != '\\')) + { + start--; + } + StringCchCopyA(name, size, start); + } + return true; + } + + inline PCSTR __stdcall GetCurrentModuleName() WI_NOEXCEPT + { + static char s_szModule[64] = {}; + static volatile bool s_fModuleValid = false; + if (!s_fModuleValid) // Races are acceptable + { + GetModuleInformation(reinterpret_cast(&RecordFailFast), nullptr, s_szModule, ARRAYSIZE(s_szModule)); + s_fModuleValid = true; + } + return s_szModule; + } + + inline void __stdcall DebugBreak() WI_NOEXCEPT + { + ::DebugBreak(); + } + + inline void __stdcall WilDynamicLoadRaiseFailFastException(_In_ PEXCEPTION_RECORD er, _In_ PCONTEXT cr, _In_ DWORD flags) + { + auto k32handle = GetModuleHandleW(L"kernelbase.dll"); + _Analysis_assume_(k32handle != nullptr); + auto pfnRaiseFailFastException = reinterpret_cast(GetProcAddress(k32handle, "RaiseFailFastException")); + if (pfnRaiseFailFastException) + { + pfnRaiseFailFastException(er, cr, flags); + } + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + + inline bool __stdcall GetModuleInformationFromAddress(_In_opt_ void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* buffer, size_t size) WI_NOEXCEPT + { + if (size > 0) + { + assign_to_opt_param(buffer, '\0'); + } + if (addressOffset) + { + *addressOffset = 0; + } + if (g_pfnGetModuleInformation) + { + return g_pfnGetModuleInformation(address, addressOffset, buffer, size); + } + return false; + } + + __declspec(noinline) inline HRESULT NtStatusToHr(NTSTATUS status) WI_NOEXCEPT + { + // The following conversions are the only known incorrect mappings in RtlNtStatusToDosErrorNoTeb + if (SUCCEEDED_NTSTATUS(status)) + { + // All successful status codes have only one hresult equivalent, S_OK + return S_OK; + } + if (status == static_cast(STATUS_NO_MEMORY)) + { + // RtlNtStatusToDosErrorNoTeb maps STATUS_NO_MEMORY to the less popular of two Win32 no memory error codes resulting in an unexpected mapping + return E_OUTOFMEMORY; + } + + if (g_pfnRtlNtStatusToDosErrorNoTeb != nullptr) + { + DWORD err = g_pfnRtlNtStatusToDosErrorNoTeb(status); + + // ERROR_MR_MID_NOT_FOUND indicates a bug in the originator of the error (failure to add a mapping to the Win32 error codes). + // There are known instances of this bug which are unlikely to be fixed soon, and it's always possible that additional instances + // could be added in the future. In these cases, it's better to use HRESULT_FROM_NT rather than returning a meaningless error. + if ((err != 0) && (err != ERROR_MR_MID_NOT_FOUND)) + { + return __HRESULT_FROM_WIN32(err); + } + } + + return HRESULT_FROM_NT(status); + } + + // The following set of functions all differ only based upon number of arguments. They are unified in their handling + // of data from each of the various error-handling types (fast fail, exceptions, etc.). + _Post_equals_last_error_ + inline DWORD GetLastErrorFail(__R_FN_PARAMS_FULL) WI_NOEXCEPT + { + __R_FN_UNREFERENCED; + auto err = ::GetLastError(); + if (SUCCEEDED_WIN32(err)) + { + // This function should only be called when GetLastError() is set to a FAILURE. + // If you hit this assert (or are reviewing this failure telemetry), then there are one of three issues: + // 1) Your code is using a macro (such as RETURN_IF_WIN32_BOOL_FALSE()) on a function that does not actually + // set the last error (consult MSDN). + // 2) Your macro check against the error is not immediately after the API call. Pushing it later can result + // in another API call between the previous one and the check resetting the last error. + // 3) The API you're calling has a bug in it and does not accurately set the last error (there are a few + // examples here, such as SendMessageTimeout() that don't accurately set the last error). For these, + // please send mail to 'wildisc' when found and work-around with win32errorhelpers. + + WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. GetLastError() does not have an error."); + return ERROR_ASSERTION_FAILURE; + } + return err; + } + + _Translates_last_error_to_HRESULT_ + inline HRESULT GetLastErrorFailHr(__R_FN_PARAMS_FULL) WI_NOEXCEPT + { + return HRESULT_FROM_WIN32(GetLastErrorFail(__R_FN_CALL_FULL)); + } + + _Translates_last_error_to_HRESULT_ + inline __declspec(noinline) HRESULT GetLastErrorFailHr() WI_NOEXCEPT + { + __R_FN_LOCALS_FULL_RA; + return GetLastErrorFailHr(__R_FN_CALL_FULL); + } + + inline void PrintLoggingMessage(_Out_writes_(cchDest) _Post_z_ PWSTR pszDest, _Pre_satisfies_(cchDest > 0) size_t cchDest, _In_opt_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) WI_NOEXCEPT + { + if (formatString == nullptr) + { + pszDest[0] = L'\0'; + } + else if (argList == nullptr) + { + StringCchPrintfW(pszDest, cchDest, L"%hs", formatString); + } + else + { + wchar_t szFormatWide[2048]; + StringCchPrintfW(szFormatWide, ARRAYSIZE(szFormatWide), L"%hs", formatString); + StringCchVPrintfW(pszDest, cchDest, szFormatWide, argList); + } + } + +#pragma warning(push) +#pragma warning(disable:__WARNING_RETURNING_BAD_RESULT) + // NOTE: The following two functions are unfortunate copies of strsafe.h functions that have been copied to reduce the friction associated with using + // Result.h and ResultException.h in a build that does not have WINAPI_PARTITION_DESKTOP defined (where these are conditionally enabled). + + static STRSAFEAPI WilStringLengthWorkerA(_In_reads_or_z_(cchMax) STRSAFE_PCNZCH psz, _In_ _In_range_(<= , STRSAFE_MAX_CCH) size_t cchMax, _Out_opt_ _Deref_out_range_(< , cchMax) _Deref_out_range_(<= , _String_length_(psz)) size_t* pcchLength) + { + HRESULT hr = S_OK; + size_t cchOriginalMax = cchMax; + while (cchMax && (*psz != '\0')) + { + psz++; + cchMax--; + } + if (cchMax == 0) + { + // the string is longer than cchMax + hr = STRSAFE_E_INVALID_PARAMETER; + } + if (pcchLength) + { + if (SUCCEEDED(hr)) + { + *pcchLength = cchOriginalMax - cchMax; + } + else + { + *pcchLength = 0; + } + } + return hr; + } + + _Must_inspect_result_ STRSAFEAPI StringCchLengthA(_In_reads_or_z_(cchMax) STRSAFE_PCNZCH psz, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax, _Out_opt_ _Deref_out_range_(<, cchMax) _Deref_out_range_(<= , _String_length_(psz)) size_t* pcchLength) + { + HRESULT hr; + if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = WilStringLengthWorkerA(psz, cchMax, pcchLength); + } + if (FAILED(hr) && pcchLength) + { + *pcchLength = 0; + } + return hr; + } +#pragma warning(pop) + + _Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH /*pszDest*/, _In_ size_t cchDest, _In_ const size_t cchMax) + { + HRESULT hr = S_OK; + if ((cchDest == 0) || (cchDest > cchMax)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + return hr; + } + + static STRSAFEAPI WilStringVPrintfWorkerA(_Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchDest, _Always_(_Out_opt_ _Deref_out_range_(<=, cchDest - 1)) size_t* pcchNewDestLength, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, _In_ va_list argList) + { + HRESULT hr = S_OK; + int iRet; + size_t cchMax; + size_t cchNewDestLength = 0; + + // leave the last space for the null terminator + cchMax = cchDest - 1; +#undef STRSAFE_USE_SECURE_CRT +#define STRSAFE_USE_SECURE_CRT 1 + #if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL) + iRet = _vsnprintf_s(pszDest, cchDest, cchMax, pszFormat, argList); + #else + #pragma warning(push) + #pragma warning(disable: __WARNING_BANNED_API_USAGE)// "STRSAFE not included" + iRet = _vsnprintf(pszDest, cchMax, pszFormat, argList); + #pragma warning(pop) + #endif + // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax)); + + if ((iRet < 0) || (((size_t)iRet) > cchMax)) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + + cchNewDestLength = cchMax; + + // we have truncated pszDest + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + else if (((size_t)iRet) == cchMax) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + + cchNewDestLength = cchMax; + } + else + { + cchNewDestLength = (size_t)iRet; + } + + if (pcchNewDestLength) + { + *pcchNewDestLength = cchNewDestLength; + } + + return hr; + } + + __inline HRESULT StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...) + { + HRESULT hr; + hr = wil::details::WilStringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH); + if (SUCCEEDED(hr)) + { + va_list argList; + va_start(argList, pszFormat); + hr = wil::details::WilStringVPrintfWorkerA(pszDest, cchDest, NULL, pszFormat, argList); + va_end(argList); + } + else if (cchDest > 0) + { + *pszDest = '\0'; + } + return hr; + } + + _Ret_range_(sizeof(char), (psz == nullptr) ? sizeof(char) : (_String_length_(psz) + sizeof(char))) + inline size_t ResultStringSize(_In_opt_ PCSTR psz) + { return (psz == nullptr) ? sizeof(char) : (strlen(psz) + sizeof(char)); } + + _Ret_range_(sizeof(wchar_t), (psz == nullptr) ? sizeof(wchar_t) : ((_String_length_(psz) + 1) * sizeof(wchar_t))) + inline size_t ResultStringSize(_In_opt_ PCWSTR psz) + { return (psz == nullptr) ? sizeof(wchar_t) : (wcslen(psz) + 1) * sizeof(wchar_t); } + + template + _Ret_range_(pStart, pEnd) inline unsigned char* WriteResultString( + _Pre_satisfies_(pStart <= pEnd) + _When_((pStart == pEnd) || (pszString == nullptr) || (pszString[0] == 0), _In_opt_) + _When_((pStart != pEnd) && (pszString != nullptr) && (pszString[0] != 0), _Out_writes_bytes_opt_(_String_length_(pszString) * sizeof(pszString[0]))) + unsigned char* pStart, _Pre_satisfies_(pEnd >= pStart) unsigned char* pEnd, _In_opt_z_ TString pszString, _Outptr_result_maybenull_z_ TString* ppszBufferString) + { + // No space? Null string? Do nothing. + if ((pStart == pEnd) || !pszString || !*pszString) + { + assign_null_to_opt_param(ppszBufferString); + return pStart; + } + + // Treats the range pStart--pEnd as a memory buffer into which pszString is copied. A pointer to + // the start of the copied string is placed into ppszStringBuffer. If the buffer isn't big enough, + // do nothing, and tell the caller nothing was written. + size_t const stringSize = ResultStringSize(pszString); + size_t const bufferSize = pEnd - pStart; + if (bufferSize < stringSize) + { + assign_null_to_opt_param(ppszBufferString); + return pStart; + } + + memcpy_s(pStart, bufferSize, pszString, stringSize); + assign_to_opt_param(ppszBufferString, reinterpret_cast(pStart)); + return pStart + stringSize; + } + + _Ret_range_(0, (cchMax > 0) ? cchMax - 1 : 0) inline size_t UntrustedStringLength(_In_ PCSTR psz, _In_ size_t cchMax) { size_t cbLength; return SUCCEEDED(wil::details::StringCchLengthA(psz, cchMax, &cbLength)) ? cbLength : 0; } + _Ret_range_(0, (cchMax > 0) ? cchMax - 1 : 0) inline size_t UntrustedStringLength(_In_ PCWSTR psz, _In_ size_t cchMax) { size_t cbLength; return SUCCEEDED(::StringCchLengthW(psz, cchMax, &cbLength)) ? cbLength : 0; } + + template + _Ret_range_(pStart, pEnd) inline unsigned char *GetResultString(_In_reads_to_ptr_opt_(pEnd) unsigned char *pStart, _Pre_satisfies_(pEnd >= pStart) unsigned char *pEnd, _Out_ TString *ppszBufferString) + { + size_t cchLen = UntrustedStringLength(reinterpret_cast(pStart), (pEnd - pStart) / sizeof((*ppszBufferString)[0])); + *ppszBufferString = (cchLen > 0) ? reinterpret_cast(pStart) : nullptr; + auto pReturn = min(pEnd, pStart + ((cchLen + 1) * sizeof((*ppszBufferString)[0]))); + __analysis_assume((pReturn >= pStart) && (pReturn <= pEnd)); + return pReturn; + } + } // details namespace + /// @endcond + + //***************************************************************************** + // WIL result handling initializers + // + // Generally, callers do not need to manually initialize WIL. This header creates + // the appropriate .CRT init section pieces through global objects to ensure that + // WilInitialize... is called before DllMain or main(). + // + // Certain binaries do not link with the CRT or do not support .CRT-section based + // initializers. Those binaries must link only with other static libraries that + // also set RESULT_SUPPRESS_STATIC_INITIALIZERS to ensure no .CRT inits are left, + // and they should call one of the WilInitialize_ResultMacros_??? methods during + // their initialization phase. Skipping this initialization path is OK as well, + // but results in a slightly degraded experience with result reporting. + // + // Calling WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse provides: + // - The name of the current module in wil::FailureInfo::pszModule + // - The name of the returning-to module during wil\staging.h failures + //***************************************************************************** + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + //! Call this method to initialize WIL manually in a module where RESULT_SUPPRESS_STATIC_INITIALIZERS is required. WIL will + //! only use publicly documented APIs. + inline void WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse() + { + details::g_pfnGetModuleName = details::GetCurrentModuleName; + details::g_pfnGetModuleInformation = details::GetModuleInformation; + details::g_pfnDebugBreak = details::DebugBreak; + details::g_pfnRaiseFailFastException = wil::details::WilDynamicLoadRaiseFailFastException; + } + + /// @cond + namespace details + { +#ifndef RESULT_SUPPRESS_STATIC_INITIALIZERS +#if !defined(BUILD_WINDOWS) || defined(WIL_SUPPRESS_PRIVATE_API_USE) + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse, [] + { + ::wil::WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse(); + return 1; + }); +#endif +#endif + } + /// @endcond +#else // !WINAPI_PARTITION_DESKTOP, !WINAPI_PARTITION_SYSTEM, explicitly assume these modules can direct link + namespace details + { + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_ResultMacros_AppOnly, [] + { + g_pfnRaiseFailFastException = ::RaiseFailFastException; + return 1; + }); + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + + //***************************************************************************** + // Public Error Handling Helpers + //***************************************************************************** + + //! Call this method to determine if process shutdown is in progress (allows avoiding work during dll unload). + inline bool ProcessShutdownInProgress() + { + return (details::g_processShutdownInProgress || (details::g_pfnDllShutdownInProgress ? details::g_pfnDllShutdownInProgress() : false)); + } + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down, + but the hosting DLL doesn't support CRT initializers (such as kernelbase.dll). The hosting DLL is responsible for calling + Construct() and Destroy() to manually run the constructor and destructor during DLL load & unload. + Upon process shutdown a method (ProcessShutdown()) is called that must be implemented on the object, otherwise the destructor is + called as is typical. */ + template + class manually_managed_shutdown_aware_object + { + public: + void construct() + { + void* var = &m_raw; + ::new(var) T(); + } + + void destroy() + { + if (ProcessShutdownInProgress()) + { + get().ProcessShutdown(); + } + else + { + (&get())->~T(); + } + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return *reinterpret_cast(&m_raw); + } + + private: + alignas(T) unsigned char m_raw[sizeof(T)]; + }; + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down. + Upon process shutdown a method (ProcessShutdown()) is called that must be implemented on the object, otherwise the destructor is + called as is typical. */ + template + class shutdown_aware_object + { + public: + shutdown_aware_object() + { + m_object.construct(); + } + + ~shutdown_aware_object() + { + m_object.destroy(); + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return m_object.get(); + } + + private: + manually_managed_shutdown_aware_object m_object; + }; + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down. */ + template + class object_without_destructor_on_shutdown + { + public: + object_without_destructor_on_shutdown() + { + void* var = &m_raw; + ::new(var) T(); + } + + ~object_without_destructor_on_shutdown() + { + if (!ProcessShutdownInProgress()) + { + get().~T(); + } + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return *reinterpret_cast(&m_raw); + } + + private: + alignas(T) unsigned char m_raw[sizeof(T)]{}; + }; + + /** Forward your DLLMain to this function so that WIL can have visibility into whether a DLL unload is because + of termination or normal unload. Note that when g_pfnDllShutdownInProgress is set, WIL attempts to make this + determination on its own without this callback. Suppressing private APIs requires use of this. */ + inline void DLLMain(HINSTANCE, DWORD reason, _In_opt_ LPVOID reserved) + { + if (!details::g_processShutdownInProgress) + { + if ((reason == DLL_PROCESS_DETACH) && (reserved != nullptr)) + { + details::g_processShutdownInProgress = true; + } + } + } + + // [optionally] Plug in fallback telemetry reporting + // Normally, the callback is owned by including ResultLogging.h in the including module. Alternatively a module + // could re-route fallback telemetry to any ONE specific provider by calling this method. + inline void SetResultTelemetryFallback(_In_opt_ decltype(details::g_pfnTelemetryCallback) callbackFunction) + { + // Only ONE telemetry provider can own the fallback telemetry callback. + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnTelemetryCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnTelemetryCallback == callbackFunction)); + details::g_pfnTelemetryCallback = callbackFunction; + } + + // [optionally] Plug in result logging (do not use for telemetry) + // This provides the ability for a module to hook all failures flowing through the system for inspection + // and/or logging. + inline void SetResultLoggingCallback(_In_opt_ decltype(details::g_pfnLoggingCallback) callbackFunction) + { + // Only ONE function can own the result logging callback + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnLoggingCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnLoggingCallback == callbackFunction)); + details::g_pfnLoggingCallback = callbackFunction; + } + + // [optionally] Plug in custom result messages + // There are some purposes that require translating the full information that is known about a failure + // into a message to be logged (either through the console for debugging OR as the message attached + // to a Platform::Exception^). This callback allows a module to format the string itself away from the + // default. + inline void SetResultMessageCallback(_In_opt_ decltype(wil::g_pfnResultLoggingCallback) callbackFunction) + { + // Only ONE function can own the result message callback + __FAIL_FAST_IMMEDIATE_ASSERT__((g_pfnResultLoggingCallback == nullptr) || (callbackFunction == nullptr) || (g_pfnResultLoggingCallback == callbackFunction)); + details::g_resultMessageCallbackSet = true; + g_pfnResultLoggingCallback = callbackFunction; + } + + // [optionally] Plug in exception remapping + // A module can plug a callback in using this function to setup custom exception handling to allow any + // exception type to be converted into an HRESULT from exception barriers. + inline void SetResultFromCaughtExceptionCallback(_In_opt_ decltype(wil::g_pfnResultFromCaughtException) callbackFunction) + { + // Only ONE function can own the exception conversion + __FAIL_FAST_IMMEDIATE_ASSERT__((g_pfnResultFromCaughtException == nullptr) || (callbackFunction == nullptr) || (g_pfnResultFromCaughtException == callbackFunction)); + g_pfnResultFromCaughtException = callbackFunction; + } + + // [optionally] Plug in exception remapping + // This provides the ability for a module to call RoOriginateError in case of a failure. + // Normally, the callback is owned by including result_originate.h in the including module. Alternatively a module + // could re-route error origination callback to its own implementation. + inline void SetOriginateErrorCallback(_In_opt_ decltype(details::g_pfnOriginateCallback) callbackFunction) + { + // Only ONE function can own the error origination callback + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnOriginateCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnOriginateCallback == callbackFunction)); + details::g_pfnOriginateCallback = callbackFunction; + } + + // A RAII wrapper around the storage of a FailureInfo struct (which is normally meant to be consumed + // on the stack or from the caller). The storage of FailureInfo needs to copy some data internally + // for lifetime purposes. + + class StoredFailureInfo + { + public: + StoredFailureInfo() WI_NOEXCEPT + { + ::ZeroMemory(&m_failureInfo, sizeof(m_failureInfo)); + } + + StoredFailureInfo(FailureInfo const &other) WI_NOEXCEPT + { + SetFailureInfo(other); + } + + FailureInfo const & GetFailureInfo() const WI_NOEXCEPT + { + return m_failureInfo; + } + + void SetFailureInfo(FailureInfo const &failure) WI_NOEXCEPT + { + m_failureInfo = failure; + + size_t const cbNeed = details::ResultStringSize(failure.pszMessage) + + details::ResultStringSize(failure.pszCode) + + details::ResultStringSize(failure.pszFunction) + + details::ResultStringSize(failure.pszFile) + + details::ResultStringSize(failure.pszCallContext) + + details::ResultStringSize(failure.pszModule) + + details::ResultStringSize(failure.callContextCurrent.contextName) + + details::ResultStringSize(failure.callContextCurrent.contextMessage) + + details::ResultStringSize(failure.callContextOriginating.contextName) + + details::ResultStringSize(failure.callContextOriginating.contextMessage); + + if (!m_spStrings.unique() || (m_spStrings.size() < cbNeed)) + { + m_spStrings.reset(); + m_spStrings.create(cbNeed); + } + + size_t cbAlloc; + unsigned char *pBuffer = static_cast(m_spStrings.get(&cbAlloc)); + unsigned char *pBufferEnd = (pBuffer != nullptr) ? pBuffer + cbAlloc : nullptr; + + if (pBuffer) + { + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszMessage, &m_failureInfo.pszMessage); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszCode, &m_failureInfo.pszCode); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszFunction, &m_failureInfo.pszFunction); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszFile, &m_failureInfo.pszFile); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszCallContext, &m_failureInfo.pszCallContext); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszModule, &m_failureInfo.pszModule); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextCurrent.contextName, &m_failureInfo.callContextCurrent.contextName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextCurrent.contextMessage, &m_failureInfo.callContextCurrent.contextMessage); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextOriginating.contextName, &m_failureInfo.callContextOriginating.contextName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextOriginating.contextMessage, &m_failureInfo.callContextOriginating.contextMessage); + ZeroMemory(pBuffer, pBufferEnd - pBuffer); + } + } + + // Relies upon generated copy constructor and assignment operator + + protected: + FailureInfo m_failureInfo; + details::shared_buffer m_spStrings; + }; + +#if defined(WIL_ENABLE_EXCEPTIONS) || defined(WIL_FORCE_INCLUDE_RESULT_EXCEPTION) + + //! This is WIL's default exception class thrown from all THROW_XXX macros (outside of c++/cx). + //! This class stores all of the FailureInfo context that is available when the exception is thrown. It's also caught by + //! exception guards for automatic conversion to HRESULT. + //! + //! In c++/cx, Platform::Exception^ is used instead of this class (unless @ref wil::g_fResultThrowPlatformException has been changed). + class ResultException : public std::exception + { + public: + //! Constructs a new ResultException from an existing FailureInfo. + ResultException(const FailureInfo& failure) WI_NOEXCEPT : + m_failure(failure) + { + } + + //! Constructs a new exception type from a given HRESULT (use only for constructing custom exception types). + ResultException(_Pre_satisfies_(hr < 0) HRESULT hr) WI_NOEXCEPT : + m_failure(CustomExceptionFailureInfo(hr)) + { + } + + //! Returns the failed HRESULT that this exception represents. + _Always_(_Post_satisfies_(return < 0)) HRESULT GetErrorCode() const WI_NOEXCEPT + { + HRESULT const hr = m_failure.GetFailureInfo().hr; + __analysis_assume(hr < 0); + return hr; + } + + //! Get a reference to the stored FailureInfo. + FailureInfo const & GetFailureInfo() const WI_NOEXCEPT + { + return m_failure.GetFailureInfo(); + } + + //! Sets the stored FailureInfo (use primarily only when constructing custom exception types). + void SetFailureInfo(FailureInfo const &failure) WI_NOEXCEPT + { + m_failure.SetFailureInfo(failure); + } + + //! Provides a string representing the FailureInfo from this exception. + inline const char * __CLR_OR_THIS_CALL what() const WI_NOEXCEPT override + { + if (!m_what) + { + wchar_t message[2048]; + GetFailureLogString(message, ARRAYSIZE(message), m_failure.GetFailureInfo()); + + char messageA[1024]; + wil::details::StringCchPrintfA(messageA, ARRAYSIZE(messageA), "%ws", message); + m_what.create(messageA, strlen(messageA) + sizeof(*messageA)); + } + return static_cast(m_what.get()); + } + + // Relies upon auto-generated copy constructor and assignment operator + protected: + StoredFailureInfo m_failure; //!< The failure information for this exception + mutable details::shared_buffer m_what; //!< The on-demand generated what() string + + //! Use to produce a custom FailureInfo from an HRESULT (use only when constructing custom exception types). + static FailureInfo CustomExceptionFailureInfo(HRESULT hr) WI_NOEXCEPT + { + FailureInfo fi = {}; + fi.type = FailureType::Exception; + fi.hr = hr; + return fi; + } + }; +#endif + + + //***************************************************************************** + // Public Helpers that catch -- mostly only enabled when exceptions are enabled + //***************************************************************************** + + // ResultFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally + // it re-throws and catches the exception to convert it to an HRESULT. If an exception is of an unrecognized type + // the function will fail fast. + // + // try + // { + // // Code + // } + // catch (...) + // { + // hr = wil::ResultFromCaughtException(); + // } + _Always_(_Post_satisfies_(return < 0)) + __declspec(noinline) inline HRESULT ResultFromCaughtException() WI_NOEXCEPT + { + bool isNormalized = false; + HRESULT hr = S_OK; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + hr = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized); + } + if (FAILED(hr)) + { + return hr; + } + + // Caller bug: an unknown exception was thrown + __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); + return __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + + //! Identical to 'throw;', but can be called from error-code neutral code to rethrow in code that *may* be running under an exception context + inline void RethrowCaughtException() + { + // We always want to rethrow the exception under normal circumstances. Ordinarily, we could actually guarantee + // this as we should be able to rethrow if we caught an exception, but if we got here in the middle of running + // dynamic initializers, then it's possible that we haven't yet setup the rethrow function pointer, thus the + // runtime check without the noreturn annotation. + + if (details::g_pfnRethrow) + { + details::g_pfnRethrow(); + } + } + + //! Identical to 'throw ResultException(failure);', but can be referenced from error-code neutral code + inline void ThrowResultException(const FailureInfo& failure) + { + if (details::g_pfnThrowResultException) + { + details::g_pfnThrowResultException(failure); + } + } + + //! @cond + namespace details + { +#ifdef WIL_ENABLE_EXCEPTIONS + //***************************************************************************** + // Private helpers to catch and propagate exceptions + //***************************************************************************** + + __declspec(noreturn) inline void TerminateAndReportError(_In_opt_ PEXCEPTION_POINTERS) + { + // This is an intentional fail-fast that was caught by an exception guard with WIL. Look back up the callstack to determine + // the source of the actual exception being thrown. The exception guard used by the calling code did not expect this + // exception type to be thrown or is specifically requesting fail-fast for this class of exception. + + FailureInfo failure{}; + WilFailFast(failure); + } + + inline void MaybeGetExceptionString(const ResultException& exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + GetFailureLogString(debugString, debugStringChars, exception.GetFailureInfo()); + } + } + + inline void MaybeGetExceptionString(const std::exception& exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + StringCchPrintfW(debugString, debugStringChars, L"std::exception: %hs", exception.what()); + } + } + + inline HRESULT ResultFromKnownException(const ResultException& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + auto hr = exception.GetErrorCode(); + wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + return hr; + } + + inline HRESULT ResultFromKnownException(const std::bad_alloc& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + constexpr auto hr = E_OUTOFMEMORY; + wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + return hr; + } + + inline HRESULT ResultFromKnownException(const std::exception& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + constexpr auto hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + return hr; + } + + inline HRESULT ResultFromKnownException_CppWinRT(const DiagnosticsInfo& diagnostics, void* returnAddress) + { + if (g_pfnResultFromCaughtException_CppWinRt) + { + wchar_t message[2048]; + message[0] = L'\0'; + bool ignored; + auto hr = g_pfnResultFromCaughtException_CppWinRt(message, ARRAYSIZE(message), &ignored); + if (FAILED(hr)) + { + ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + return hr; + } + } + + // Indicate that this either isn't a C++/WinRT exception or a handler isn't configured by returning success + return S_OK; + } + + inline HRESULT RecognizeCaughtExceptionFromCallback(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + HRESULT hr = g_pfnResultFromCaughtException(); + + // If we still don't know the error -- or we would like to get the debug string for the error (if possible) we + // rethrow and catch std::exception. + + if (SUCCEEDED(hr) || debugString) + { + try + { + throw; + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + if (SUCCEEDED(hr)) + { + hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + } + catch (...) + { + } + } + + return hr; + } + +#ifdef __cplusplus_winrt + inline Platform::String^ GetPlatformExceptionMessage(Platform::Exception^ exception) + { + struct RawExceptionData_Partial + { + PCWSTR description; + PCWSTR restrictedErrorString; + }; + + auto exceptionPtr = reinterpret_cast(static_cast<::Platform::Object^>(exception)); + auto exceptionInfoPtr = reinterpret_cast(exceptionPtr) - 1; + auto partial = reinterpret_cast(*exceptionInfoPtr); + + Platform::String^ message = exception->Message; + + PCWSTR errorString = partial->restrictedErrorString; + PCWSTR messageString = reinterpret_cast(message ? message->Data() : nullptr); + + // An old Platform::Exception^ bug that did not actually expose the error string out of the exception + // message. We do it by hand here if the message associated with the strong does not contain the + // message that was originally attached to the string (in the fixed version it will). + + if ((errorString && *errorString && messageString) && + (wcsstr(messageString, errorString) == nullptr)) + { + return ref new Platform::String(reinterpret_cast<_Null_terminated_ const __wchar_t *>(errorString)); + } + return message; + } + + inline void MaybeGetExceptionString(_In_ Platform::Exception^ exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + auto message = GetPlatformExceptionMessage(exception); + auto messageString = !message ? L"(null Message)" : reinterpret_cast(message->Data()); + StringCchPrintfW(debugString, debugStringChars, L"Platform::Exception^: %ws", messageString); + } + } + + inline HRESULT ResultFromKnownException(Platform::Exception^ exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + auto hr = exception->HResult; + wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + return hr; + } + + inline HRESULT __stdcall ResultFromCaughtException_WinRt(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Inout_ bool* isNormalized) WI_NOEXCEPT + { + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (Platform::Exception^ exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception->HResult; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (Platform::Exception^ exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception->HResult; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return S_OK; + } + + // WinRT supporting version to execute a functor and catch known exceptions. + inline HRESULT __stdcall ResultFromKnownExceptions_WinRt(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) + { + WI_ASSERT(supported != SupportedExceptions::Default); + + switch (supported) + { + case SupportedExceptions::Known: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (std::exception& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (...) + { + auto hr = ResultFromKnownException_CppWinRT(diagnostics, returnAddress); + if (FAILED(hr)) + { + return hr; + } + + // Unknown exception + throw; + } + break; + + case SupportedExceptions::ThrownOrAlloc: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + break; + + case SupportedExceptions::Thrown: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + break; + } + + WI_ASSERT(false); + return S_OK; + } + + inline void __stdcall ThrowPlatformException(FailureInfo const &failure, LPCWSTR debugString) + { + throw Platform::Exception::CreateException(failure.hr, ref new Platform::String(reinterpret_cast<_Null_terminated_ const __wchar_t *>(debugString))); + } + +#if !defined(RESULT_SUPPRESS_STATIC_INITIALIZERS) + WI_HEADER_INITITALIZATION_FUNCTION(InitializeWinRt, [] + { + g_pfnResultFromCaughtException_WinRt = ResultFromCaughtException_WinRt; + g_pfnResultFromKnownExceptions_WinRt = ResultFromKnownExceptions_WinRt; + g_pfnThrowPlatformException = ThrowPlatformException; + return 1; + }); +#endif +#endif + + inline void __stdcall Rethrow() + { + throw; + } + + inline void __stdcall ThrowResultExceptionInternal(const FailureInfo& failure) + { + throw ResultException(failure); + } + + __declspec(noinline) inline HRESULT __stdcall ResultFromCaughtExceptionInternal(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT + { + if (debugString) + { + *debugString = L'\0'; + } + *isNormalized = false; + + if (details::g_pfnResultFromCaughtException_CppWinRt != nullptr) + { + RETURN_IF_FAILED_EXPECTED(details::g_pfnResultFromCaughtException_CppWinRt(debugString, debugStringChars, isNormalized)); + } + + if (details::g_pfnResultFromCaughtException_WinRt != nullptr) + { + return details::g_pfnResultFromCaughtException_WinRt(debugString, debugStringChars, isNormalized); + } + + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return S_OK; + } + + // Runs the given functor, converting any exceptions of the supported types that are known to HRESULTs and returning + // that HRESULT. Does NOT attempt to catch unknown exceptions (which propagate). Primarily used by SEH exception + // handling techniques to stop at the point the exception is thrown. + inline HRESULT ResultFromKnownExceptions(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) + { + if (supported == SupportedExceptions::Default) + { + supported = g_fResultSupportStdException ? SupportedExceptions::Known : SupportedExceptions::ThrownOrAlloc; + } + + if ((details::g_pfnResultFromKnownExceptions_WinRt != nullptr) && + ((supported == SupportedExceptions::Known) || (supported == SupportedExceptions::Thrown) || (supported == SupportedExceptions::ThrownOrAlloc))) + { + return details::g_pfnResultFromKnownExceptions_WinRt(diagnostics, returnAddress, supported, functor); + } + + switch (supported) + { + case SupportedExceptions::Known: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (std::exception& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (...) + { + auto hr = ResultFromKnownException_CppWinRT(diagnostics, returnAddress); + if (FAILED(hr)) + { + return hr; + } + + // Unknown exception + throw; + } + + case SupportedExceptions::ThrownOrAlloc: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + + case SupportedExceptions::Thrown: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + + case SupportedExceptions::All: + try + { + return functor.Run(); + } + catch (...) + { + return wil::details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), supported); + } + + case SupportedExceptions::None: + return functor.Run(); + + case SupportedExceptions::Default: + WI_ASSERT(false); + } + + WI_ASSERT(false); + return S_OK; + } + + inline HRESULT ResultFromExceptionSeh(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { + __try + { + return wil::details::ResultFromKnownExceptions(diagnostics, returnAddress, supported, functor); + } + __except (wil::details::TerminateAndReportError(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) + { + WI_ASSERT(false); + } + } + + __declspec(noinline) inline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { +#ifdef RESULT_DEBUG + // We can't do debug SEH handling if the caller also wants a shot at mapping the exceptions + // themselves or if the caller doesn't want to fail-fast unknown exceptions + if ((g_pfnResultFromCaughtException == nullptr) && g_fResultFailFastUnknownExceptions) + { + return wil::details::ResultFromExceptionSeh(diagnostics, _ReturnAddress(), supported, functor); + } +#endif + try + { + return functor.Run(); + } + catch (...) + { + return wil::details::ReportFailure_CaughtException(__R_DIAGNOSTICS(diagnostics), _ReturnAddress(), supported); + } + } + + __declspec(noinline) inline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { + return wil::details::ResultFromExceptionSeh(diagnostics, _ReturnAddress(), supported, functor); + } + + // Exception guard -- catch exceptions and log them (or handle them with a custom callback) + // WARNING: may throw an exception... + inline HRESULT __stdcall RunFunctorWithExceptionFilter(IFunctor& functor, IFunctorHost& host, void* returnAddress) + { + try + { + return host.Run(functor); + } + catch (...) + { + // Note that the host may choose to re-throw, throw a normalized exception, return S_OK and eat the exception or + // return the remapped failure. + return host.ExceptionThrown(returnAddress); + } + } + + WI_HEADER_INITITALIZATION_FUNCTION(InitializeResultExceptions, [] + { + g_pfnRunFunctorWithExceptionFilter = RunFunctorWithExceptionFilter; + g_pfnRethrow = Rethrow; + g_pfnThrowResultException = ThrowResultExceptionInternal; + g_pfnResultFromCaughtExceptionInternal = ResultFromCaughtExceptionInternal; + return 1; + }); + + } + + //! A lambda-based exception guard that can vary the supported exception types. + //! This function accepts a lambda and diagnostics information as its parameters and executes that lambda + //! under a try/catch(...) block. All exceptions are caught and the function reports the exception information + //! and diagnostics to telemetry on failure. An HRESULT is returned that maps to the exception. + //! + //! Note that an overload exists that does not report failures to telemetry at all. This version should be preferred + //! to that version. Also note that neither of these versions are preferred over using try catch blocks to accomplish + //! the same thing as they will be more efficient. + //! + //! See @ref page_exception_guards for more information and examples on exception guards. + //! ~~~~ + //! return wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] + //! { + //! // exception-based code + //! // telemetry is reported with full exception information + //! }); + //! ~~~~ + //! @param diagnostics Always pass WI_DIAGNOSTICS_INFO as the first parameter + //! @param supported What kind of exceptions you want to support + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + __forceinline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value != details::tag_return_other::value, "Functor must return void or HRESULT"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromException(diagnostics, supported, functorObject); + } + + //! A lambda-based exception guard. + //! This overload uses SupportedExceptions::Known by default. See @ref ResultFromException for more detailed information. + template + __forceinline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + return ResultFromException(diagnostics, SupportedExceptions::Known, wistd::forward(functor)); + } + + //! A lambda-based exception guard that does not report failures to telemetry. + //! This function accepts a lambda as it's only parameter and executes that lambda under a try/catch(...) block. + //! All exceptions are caught and the function returns an HRESULT mapping to the exception. + //! + //! This version (taking only a lambda) does not report failures to telemetry. An overload with the same name + //! can be utilized by passing `WI_DIAGNOSTICS_INFO` as the first parameter and the lambda as the second parameter + //! to report failure information to telemetry. + //! + //! See @ref page_exception_guards for more information and examples on exception guards. + //! ~~~~ + //! hr = wil::ResultFromException([&] + //! { + //! // exception-based code + //! // the conversion of exception to HRESULT doesn't report telemetry + //! }); + //! + //! hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] + //! { + //! // exception-based code + //! // telemetry is reported with full exception information + //! }); + //! ~~~~ + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + inline HRESULT ResultFromException(Functor&& functor) WI_NOEXCEPT try + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + functorObject.Run(); + return S_OK; + } + catch (...) + { + return ResultFromCaughtException(); + } + + + //! A lambda-based exception guard that can identify the origin of unknown exceptions and can vary the supported exception types. + //! Functionally this is nearly identical to the corresponding @ref ResultFromException function with the exception + //! that it utilizes structured exception handling internally to be able to terminate at the point where a unknown + //! exception is thrown, rather than after that unknown exception has been unwound. Though less efficient, this leads + //! to a better debugging experience when analyzing unknown exceptions. + //! + //! For example: + //! ~~~~ + //! hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&] + //! { + //! FunctionWhichMayThrow(); + //! }); + //! ~~~~ + //! Assume FunctionWhichMayThrow() has a bug in it where it accidentally does a `throw E_INVALIDARG;`. This ends up + //! throwing a `long` as an exception object which is not what the caller intended. The normal @ref ResultFromException + //! would fail-fast when this is encountered, but it would do so AFTER FunctionWhichMayThrow() is already off of the + //! stack and has been unwound. Because SEH is used for ResultFromExceptionDebug, the fail-fast occurs with everything + //! leading up to and including the `throw INVALIDARG;` still on the stack (and easily debuggable). + //! + //! The penalty paid for using this, however, is efficiency. It's far less efficient as a general pattern than either + //! using ResultFromException directly or especially using try with CATCH_ macros directly. Still it's helpful to deploy + //! selectively to isolate issues a component may be having with unknown/unhandled exceptions. + //! + //! The ability to vary the SupportedExceptions that this routine provides adds the ability to track down unexpected + //! exceptions not falling into the supported category easily through fail-fast. For example, by not supporting any + //! exception, you can use this function to quickly add an exception guard that will fail-fast any exception at the point + //! the exception occurs (the throw) in a codepath where the origination of unknown exceptions need to be tracked down. + //! + //! Also see @ref ResultFromExceptionDebugNoStdException. It functions almost identically, but also will fail-fast and stop + //! on std::exception based exceptions (but not Platform::Exception^ or wil::ResultException). Using this can help isolate + //! where an unexpected exception is being generated from. + //! @param diagnostics Always pass WI_DIAGNOSTICS_INFO as the first parameter + //! @param supported What kind of exceptions you want to support + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + __forceinline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromExceptionDebug(diagnostics, supported, functorObject); + } + + //! A lambda-based exception guard that can identify the origin of unknown exceptions. + //! This overload uses SupportedExceptions::Known by default. See @ref ResultFromExceptionDebug for more detailed information. + template + __forceinline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromExceptionDebug(diagnostics, SupportedExceptions::Known, functorObject); + } + + //! A fail-fast based exception guard. + //! Technically this is an overload of @ref ResultFromExceptionDebug that uses SupportedExceptions::None by default. Any uncaught + //! exception that makes it back to this guard would result in a fail-fast at the point the exception is thrown. + template + __forceinline void FailFastException(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + wil::details::ResultFromExceptionDebug(diagnostics, SupportedExceptions::None, functorObject); + } + + namespace details { + +#endif // WIL_ENABLE_EXCEPTIONS + + // Exception guard -- catch exceptions and log them (or handle them with a custom callback) + // WARNING: may throw an exception... + inline __declspec(noinline) HRESULT RunFunctor(IFunctor& functor, IFunctorHost& host) + { + if (g_pfnRunFunctorWithExceptionFilter) + { + return g_pfnRunFunctorWithExceptionFilter(functor, host, _ReturnAddress()); + } + + return host.Run(functor); + } + + // Returns true if a debugger should be considered to be connected. + // Modules can force this on through setting g_fIsDebuggerPresent explicitly (useful for live debugging), + // they can provide a callback function by setting g_pfnIsDebuggerPresent (useful for kernel debbugging), + // and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call + inline bool IsDebuggerPresent() + { + return g_fIsDebuggerPresent || ((g_pfnIsDebuggerPresent != nullptr) ? g_pfnIsDebuggerPresent() : (::IsDebuggerPresent() != FALSE)); + } + + //***************************************************************************** + // Shared Reporting -- all reporting macros bubble up through this codepath + //***************************************************************************** + + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, + bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, + _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, + _Out_ FailureInfo *failure) WI_NOEXCEPT + { + debugString[0] = L'\0'; + callContextString[0] = L'\0'; + + static long volatile s_failureId = 0; + + int failureCount = 0; + switch (type) + { + case FailureType::Exception: + failureCount = RecordException(hr); + break; + case FailureType::Return: + failureCount = RecordReturn(hr); + break; + case FailureType::Log: + if (SUCCEEDED(hr)) + { + // If you hit this assert (or are reviewing this failure telemetry), then most likely you are trying to log success + // using one of the WIL macros. Example: + // LOG_HR(S_OK); + // Instead, use one of the forms that conditionally logs based upon the error condition: + // LOG_IF_FAILED(hr); + + WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. Do not LOG_XXX success."); + hr = __HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE); + } + failureCount = RecordLog(hr); + break; + case FailureType::FailFast: + failureCount = RecordFailFast(hr); + break; + }; + + failure->type = type; + failure->hr = hr; + failure->failureId = ::InterlockedIncrementNoFence(&s_failureId); + failure->pszMessage = ((message != nullptr) && (message[0] != L'\0')) ? message : nullptr; + failure->threadId = ::GetCurrentThreadId(); + failure->pszFile = fileName; + failure->uLineNumber = lineNumber; + failure->cFailureCount = failureCount; + failure->pszCode = code; + failure->pszFunction = functionName; + failure->returnAddress = returnAddress; + failure->callerReturnAddress = callerReturnAddress; + failure->pszCallContext = nullptr; + ::ZeroMemory(&failure->callContextCurrent, sizeof(failure->callContextCurrent)); + ::ZeroMemory(&failure->callContextOriginating, sizeof(failure->callContextOriginating)); + failure->pszModule = (g_pfnGetModuleName != nullptr) ? g_pfnGetModuleName() : nullptr; + + // Completes filling out failure, notifies thread-based callbacks and the telemetry callback + if (details::g_pfnGetContextAndNotifyFailure) + { + details::g_pfnGetContextAndNotifyFailure(failure, callContextString, callContextStringSizeChars); + } + + // Allow hooks to inspect the failure before acting upon it + if (details::g_pfnLoggingCallback) + { + details::g_pfnLoggingCallback(*failure); + } + + // If the hook is enabled then it will be given the opportunity to call RoOriginateError to greatly improve the diagnostic experience + // for uncaught exceptions. + if (details::g_pfnOriginateCallback) + { + details::g_pfnOriginateCallback(*failure); + } + + if (SUCCEEDED(failure->hr)) + { + // Caller bug: Leaking a success code into a failure-only function + FAIL_FAST_IMMEDIATE_IF(type != FailureType::FailFast); + failure->hr = E_UNEXPECTED; + } + + bool const fUseOutputDebugString = IsDebuggerPresent() && g_fResultOutputDebugString; + + // We need to generate the logging message if: + // * We're logging to OutputDebugString + // * OR the caller asked us to (generally for attaching to a C++/CX exception) + if (fWantDebugString || fUseOutputDebugString) + { + // Call the logging callback (if present) to allow them to generate the debug string that will be pushed to the console + // or the platform exception object if the caller desires it. + if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet) + { + g_pfnResultLoggingCallback(failure, debugString, debugStringSizeChars); + } + + // The callback only optionally needs to supply the debug string -- if the callback didn't populate it, yet we still want + // it for OutputDebugString or exception message, then generate the default string. + if (debugString[0] == L'\0') + { + GetFailureLogString(debugString, debugStringSizeChars, *failure); + } + + if (fUseOutputDebugString) + { + ::OutputDebugStringW(debugString); + } + } + else + { + // [deprecated behavior] + // This callback was at one point *always* called for all failures, so we continue to call it for failures even when we don't + // need to generate the debug string information (when the callback was supplied directly). We can avoid this if the caller + // used the explicit function (through g_resultMessageCallbackSet) + if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet) + { + g_pfnResultLoggingCallback(failure, nullptr, 0); + } + } + + if (g_fBreakOnFailure && (g_pfnDebugBreak != nullptr)) + { + g_pfnDebugBreak(); + } + } + + inline __declspec(noreturn) void __stdcall WilFailFast(const wil::FailureInfo& failure) + { + if (g_pfnWilFailFast) + { + g_pfnWilFailFast(failure); + } + +#ifdef RESULT_RAISE_FAST_FAIL_EXCEPTION + // Use of this macro is an ODR violation - use the callback instead. This will be removed soon. + RESULT_RAISE_FAST_FAIL_EXCEPTION; +#endif + + // parameter 0 is the !analyze code (FAST_FAIL_FATAL_APP_EXIT) + EXCEPTION_RECORD er{}; + er.NumberParameters = 1; // default to be safe, see below + er.ExceptionCode = static_cast(STATUS_STACK_BUFFER_OVERRUN); // 0xC0000409 + er.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + er.ExceptionInformation[0] = FAST_FAIL_FATAL_APP_EXIT; // see winnt.h, generated from minkernel\published\base\ntrtl_x.w + if (failure.returnAddress == 0) // FailureInfo does not have _ReturnAddress, have RaiseFailFastException generate it + { + // passing ExceptionCode 0xC0000409 and one param with FAST_FAIL_APP_EXIT will use existing + // !analyze functionality to crawl the stack looking for the HRESULT + // don't pass a 0 HRESULT in param 1 because that will result in worse bucketing. + WilRaiseFailFastException(&er, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + } + else // use FailureInfo caller address + { + // parameter 1 is the failing HRESULT + // parameter 2 is the line number. This is never used for bucketing (due to code churn causing re-bucketing) but is available in the dump's + // exception record to aid in failure locality. Putting it here prevents it from being poisoned in triage dumps. + er.NumberParameters = 3; + er.ExceptionInformation[1] = failure.hr; + er.ExceptionInformation[2] = failure.uLineNumber; + er.ExceptionAddress = failure.returnAddress; + WilRaiseFailFastException(&er, nullptr, 0 /* do not generate exception address */); + } + } + + template + inline __declspec(noinline) void ReportFailureBaseReturn(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + { + bool needPlatformException = ((T == FailureType::Exception) && + WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && + (g_pfnThrowPlatformException != nullptr) && + (g_fResultThrowPlatformException || WI_IsFlagSet(options, ReportFailureOptions::ForcePlatformException))); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, T, hr, message, needPlatformException, + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + } + + template + inline __declspec(noinline) void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + { + ReportFailureBaseReturn(__R_FN_CALL_FULL, hr, message, options); + } + + template + inline __declspec(noinline) RESULT_NORETURN void ReportFailureBaseNoReturn(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + { + bool needPlatformException = ((T == FailureType::Exception) && + WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && + (g_pfnThrowPlatformException != nullptr) && + (g_fResultThrowPlatformException || WI_IsFlagSet(options, ReportFailureOptions::ForcePlatformException))); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, T, hr, message, needPlatformException, + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); +__WI_SUPPRESS_4127_S + if (T == FailureType::FailFast) + { + WilFailFast(const_cast(failure)); + } + else + { + if (needPlatformException) + { + g_pfnThrowPlatformException(failure, debugString); + } + + if (WI_IsFlagSet(options, ReportFailureOptions::MayRethrow)) + { + RethrowCaughtException(); + } + + ThrowResultException(failure); + + // Wil was instructed to throw, but doesn't have any capability to do so (global function pointers are not setup) + WilFailFast(const_cast(failure)); + } +__WI_SUPPRESS_4127_E + } + + template<> + inline __declspec(noinline) RESULT_NORETURN void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + { + ReportFailureBaseNoReturn(__R_FN_CALL_FULL, hr, message, options); + } + + template<> + inline __declspec(noinline) RESULT_NORETURN void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + { + ReportFailureBaseNoReturn(__R_FN_CALL_FULL, hr, message, options); + } + + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, ReportFailureOptions options) + { + switch(type) + { + case FailureType::Exception: + ReportFailure(__R_FN_CALL_FULL, hr, message, options); + break; + case FailureType::FailFast: + ReportFailure(__R_FN_CALL_FULL, hr, message, options); + break; + case FailureType::Log: + ReportFailure(__R_FN_CALL_FULL, hr, message, options); + break; + case FailureType::Return: + ReportFailure(__R_FN_CALL_FULL, hr, message, options); + break; + } + } + + template + inline HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + bool isNormalized = false; + auto length = wcslen(debugString); + WI_ASSERT(length < debugStringChars); + HRESULT hr = S_OK; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + hr = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + } + + const bool known = (FAILED(hr)); + if (!known) + { + hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + + ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; + WI_SetFlagIf(options, ReportFailureOptions::MayRethrow, isNormalized); + + if ((supported == SupportedExceptions::None) || + ((supported == SupportedExceptions::Known) && !known) || + ((supported == SupportedExceptions::Thrown) && !isNormalized) || + ((supported == SupportedExceptions::Default) && !known && g_fResultFailFastUnknownExceptions)) + { + // By default WIL will issue a fail fast for unrecognized exception types. Wil recognizes any std::exception or wil::ResultException based + // types and Platform::Exception^, so there aren't too many valid exception types which could cause this. Those that are valid, should be handled + // by remapping the exception callback. Those that are not valid should be found and fixed (meaningless accidents like 'throw hr;'). + // The caller may also be requesting non-default behavior to fail-fast more frequently (primarily for debugging unknown exceptions). + ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + } + else + { + ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + } + + return hr; + } + + template + inline HRESULT RESULT_NORETURN ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + bool isNormalized = false; + const auto length = wcslen(debugString); + WI_ASSERT(length < debugStringChars); + HRESULT hr = S_OK; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + hr = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + } + + const bool known = (FAILED(hr)); + if (!known) + { + hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + + ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; + WI_SetFlagIf(options, ReportFailureOptions::MayRethrow, isNormalized); + + if ((supported == SupportedExceptions::None) || + ((supported == SupportedExceptions::Known) && !known) || + ((supported == SupportedExceptions::Thrown) && !isNormalized) || + ((supported == SupportedExceptions::Default) && !known && g_fResultFailFastUnknownExceptions)) + { + // By default WIL will issue a fail fast for unrecognized exception types. Wil recognizes any std::exception or wil::ResultException based + // types and Platform::Exception^, so there aren't too many valid exception types which could cause this. Those that are valid, should be handled + // by remapping the exception callback. Those that are not valid should be found and fixed (meaningless accidents like 'throw hr;'). + // The caller may also be requesting non-default behavior to fail-fast more frequently (primarily for debugging unknown exceptions). + ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + } + else + { + ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + } + } + + template<> + inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported); + } + + template<> + inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported); + } + + template + inline void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure(__R_FN_CALL_FULL, hr, message); + } + + template<> + inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure(__R_FN_CALL_FULL, hr, message); + } + + template<> + inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure(__R_FN_CALL_FULL, hr, message); + } + + template + inline void ReportFailure_ReplaceMsg(__R_FN_PARAMS_FULL, HRESULT hr, PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure(__R_FN_CALL_FULL, hr); + } + + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr) + { + switch(type) + { + case FailureType::Exception: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::FailFast: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::Log: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::Return: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + } + } + + template + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + return hr; + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template + __declspec(noinline) inline DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + return err; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure(__R_FN_CALL_FULL, hr); + return hr; + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto hr = wil::details::NtStatusToHr(status); + ReportFailure(__R_FN_CALL_FULL, hr); + return hr; + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto hr = wil::details::NtStatusToHr(status); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto hr = wil::details::NtStatusToHr(status); + ReportFailure(__R_FN_CALL_FULL, hr); + } + + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + } + + template + __declspec(noinline) inline void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + return hr; + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + __declspec(noinline) inline DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + return err; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + return hr; + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = wil::details::NtStatusToHr(status); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + return hr; + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = wil::details::NtStatusToHr(status); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = wil::details::NtStatusToHr(status); + ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + } + + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default); + } + + + //***************************************************************************** + // Support for throwing custom exception types + //***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + inline HRESULT GetErrorCode(_In_ ResultException &exception) WI_NOEXCEPT + { + return exception.GetErrorCode(); + } + + inline void SetFailureInfo(_In_ FailureInfo const &failure, _Inout_ ResultException &exception) WI_NOEXCEPT + { + return exception.SetFailureInfo(failure); + } + +#ifdef __cplusplus_winrt + inline HRESULT GetErrorCode(_In_ Platform::Exception^ exception) WI_NOEXCEPT + { + return exception->HResult; + } + + inline void SetFailureInfo(_In_ FailureInfo const &, _Inout_ Platform::Exception^ exception) WI_NOEXCEPT + { + // no-op -- once a PlatformException^ is created, we can't modify the message, but this function must + // exist to distinguish this from ResultException + } +#endif + + template + __declspec(noreturn) inline void ReportFailure_CustomExceptionHelper(_Inout_ T &exception, __R_FN_PARAMS_FULL, _In_opt_ PCWSTR message = nullptr) + { + // When seeing the error: "cannot convert parameter 1 from 'XXX' to 'wil::ResultException &'" + // Custom exceptions must be based upon either ResultException or Platform::Exception^ to be used with ResultException.h. + // This compilation error indicates an attempt to throw an incompatible exception type. + const HRESULT hr = GetErrorCode(exception); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, FailureType::Exception, hr, message, false, // false = does not need debug string + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + + // push the failure info context into the custom exception class + SetFailureInfo(failure, exception); + + throw exception; + } + + template + __declspec(noreturn, noinline) inline void ReportFailure_CustomException(__R_FN_PARAMS _In_ T exception) + { + __R_FN_LOCALS_RA; + ReportFailure_CustomExceptionHelper(exception, __R_FN_CALL_FULL); + } + + template + __declspec(noreturn, noinline) inline void ReportFailure_CustomExceptionMsg(__R_FN_PARAMS _In_ T exception, _In_ _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + + __R_FN_LOCALS_RA; + ReportFailure_CustomExceptionHelper(exception, __R_FN_CALL_FULL, message); + } +#endif + + namespace __R_NS_NAME + { + //***************************************************************************** + // Return Macros + //***************************************************************************** + + __R_DIRECT_METHOD(void, Return_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + } + + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __R_DIRECT_METHOD(HRESULT, Return_Win32)(__R_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + } + + _Success_(true) + _Translates_last_error_to_HRESULT_ + __R_DIRECT_METHOD(HRESULT, Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorHr(__R_DIRECT_FN_CALL_ONLY); + } + + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __R_DIRECT_METHOD(HRESULT, Return_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } +#endif + + __R_DIRECT_METHOD(void, Return_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + } + + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __R_DIRECT_METHOD(HRESULT, Return_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + } + + _Success_(true) + _Translates_last_error_to_HRESULT_ + __R_DIRECT_METHOD(HRESULT, Return_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorHrMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __R_DIRECT_METHOD(HRESULT, Return_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + + //***************************************************************************** + // Log Macros + //***************************************************************************** + + _Post_satisfies_(return == hr) + __R_DIRECT_METHOD(HRESULT, Log_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + return hr; + } + + _Post_satisfies_(return == err) + __R_DIRECT_METHOD(DWORD, Log_Win32)(__R_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + return err; + } + + __R_DIRECT_METHOD(DWORD, Log_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastError(__R_DIRECT_FN_CALL_ONLY); + } + + _Post_satisfies_(return == status) + __R_DIRECT_METHOD(NTSTATUS, Log_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + return status; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Log_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } +#endif + + __R_INTERNAL_METHOD(_Log_Hr)(__R_INTERNAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL hr); + } + + __R_INTERNAL_METHOD(_Log_GetLastError)(__R_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_INTERNAL_FN_CALL_ONLY); + } + + __R_INTERNAL_METHOD(_Log_Win32)(__R_INTERNAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_INTERNAL_FN_CALL err); + } + + __R_INTERNAL_METHOD(_Log_NullAlloc)(__R_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __R_INTERNAL_METHOD(_Log_NtStatus)(__R_INTERNAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_INTERNAL_FN_CALL status); + } + + _Post_satisfies_(return == hr) + __R_CONDITIONAL_METHOD(HRESULT, Log_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr)) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == hr) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Log_IfFailedWithExpected)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, unsigned int expectedCount, ...) WI_NOEXCEPT + { + va_list args; + va_start(args, expectedCount); + + if (FAILED(hr)) + { + unsigned int expectedIndex; + for (expectedIndex = 0; expectedIndex < expectedCount; ++expectedIndex) + { + if (hr == va_arg(args, HRESULT)) + { + break; + } + } + + if (expectedIndex == expectedCount) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + va_end(args); + return hr; + } + + _Post_satisfies_(return == ret) + __R_CONDITIONAL_METHOD(BOOL, Log_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) WI_NOEXCEPT + { + if (!ret) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + _Post_satisfies_(return == err) + __R_CONDITIONAL_METHOD(DWORD, Log_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + __R_CALL_INTERNAL_METHOD(_Log_Win32)(__R_CONDITIONAL_FN_CALL err); + } + return err; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == status) + __R_CONDITIONAL_METHOD(NTSTATUS, Log_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + __R_CALL_INTERNAL_METHOD(_Log_NtStatus)(__R_CONDITIONAL_FN_CALL status); + } + return status; + } + + _Post_satisfies_(return == hr) + __R_DIRECT_METHOD(HRESULT, Log_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + return hr; + } + + _Post_satisfies_(return == err) + __R_DIRECT_METHOD(DWORD, Log_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + return err; + } + + __R_DIRECT_METHOD(DWORD, Log_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + _Post_satisfies_(return == status) + __R_DIRECT_METHOD(NTSTATUS, Log_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + return status; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Log_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + + __R_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_Win32Msg)(__R_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_NtStatusMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Log_IfFailedMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) + __R_CONDITIONAL_NOINLINE_METHOD(BOOL, Log_IfWin32BoolFalseMsg)(__R_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) + __R_CONDITIONAL_NOINLINE_METHOD(DWORD, Log_IfWin32ErrorMsg)(__R_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_Win32Msg)(__R_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Log_IfHandleInvalidMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Log_IfHandleNullMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_HrIfMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_HrIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_GetLastErrorIfMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_GetLastErrorIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) + __R_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, Log_IfNtStatusFailedMsg)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NtStatusMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } + } // namespace __R_NS_NAME + + namespace __RFF_NS_NAME + { + //***************************************************************************** + // FailFast Macros + //***************************************************************************** + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Hr)(__RFF_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_DIRECT_FN_CALL hr); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Win32)(__RFF_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32(__RFF_DIRECT_FN_CALL err); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_GetLastError)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__RFF_DIRECT_FN_CALL_ONLY); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_NtStatus)(__RFF_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__RFF_DIRECT_FN_CALL status); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __RFF_DIRECT_NORET_METHOD(void, FailFast_CaughtException)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_CaughtException(__RFF_DIRECT_FN_CALL_ONLY); + } +#endif + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Hr)(__RFF_INTERNAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL hr); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_GetLastError)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__RFF_INTERNAL_FN_CALL_ONLY); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Win32)(__RFF_INTERNAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32(__RFF_INTERNAL_FN_CALL err); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_NullAlloc)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_NtStatus)(__RFF_INTERNAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__RFF_INTERNAL_FN_CALL status); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HRESULT, FailFast_IfFailed)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(BOOL, FailFast_IfWin32BoolFalse)(__RFF_CONDITIONAL_FN_PARAMS BOOL ret) WI_NOEXCEPT + { + if (!ret) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(DWORD, FailFast_IfWin32Error)(__RFF_CONDITIONAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Win32)(__RFF_CONDITIONAL_FN_CALL err); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HANDLE, FailFast_IfHandleInvalid)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(RESULT_NORETURN_NULL HANDLE, FailFast_IfHandleNull)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NullAlloc)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NullAlloc)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIf)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIfFalse)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_GetLastErrorIf)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_GetLastErrorIfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(NTSTATUS, FailFast_IfNtStatusFailed)(__RFF_CONDITIONAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NtStatus)(__RFF_CONDITIONAL_FN_CALL status); + } + return status; + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_HrMsg)(__RFF_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_DIRECT_FN_CALL hr, formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Win32Msg)(__RFF_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__RFF_DIRECT_FN_CALL err, formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_GetLastErrorMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__RFF_DIRECT_FN_CALL formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_NtStatusMsg)(__RFF_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__RFF_DIRECT_FN_CALL status, formatString, argList); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __RFF_DIRECT_NORET_METHOD(void, FailFast_CaughtExceptionMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_CaughtExceptionMsg(__RFF_DIRECT_FN_CALL formatString, argList); + } +#endif + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_HrMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_GetLastErrorMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__RFF_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_Win32Msg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__RFF_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_NullAllocMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_NtStatusMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__RFF_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(HRESULT, FailFast_IfFailedMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(BOOL, FailFast_IfWin32BoolFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(DWORD, FailFast_IfWin32ErrorMsg)(__RFF_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_Win32Msg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(HANDLE, FailFast_IfHandleInvalidMsg)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(RESULT_NORETURN_NULL HANDLE, FailFast_IfHandleNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAllocMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NullAllocMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullAllocMsg)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NullAllocMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_HrIfMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_HrIfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_HrIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_GetLastErrorIfMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_GetLastErrorIfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, FailFast_IfNtStatusFailedMsg)(__RFF_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NtStatusMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Unexpected)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_DIRECT_FN_CALL E_UNEXPECTED); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Unexpected)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL E_UNEXPECTED); + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_If)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_IfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_UnexpectedMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_DIRECT_FN_CALL E_UNEXPECTED, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_UnexpectedMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL E_UNEXPECTED, formatString, argList); + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_IfMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_IfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + //***************************************************************************** + // FailFast Immediate Macros + //***************************************************************************** + + __RFF_DIRECT_NORET_METHOD(void, FailFastImmediate_Unexpected)() WI_NOEXCEPT + { + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFastImmediate_Unexpected)() WI_NOEXCEPT + { + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HRESULT, FailFastImmediate_IfFailed)(HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return hr; + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_If)(bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_IfFalse)(bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFastImmediate_IfNull)(_Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(NTSTATUS, FailFastImmediate_IfNtStatusFailed)(NTSTATUS status) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return status; + } + } // namespace __RFF_NS_NAME + + namespace __R_NS_NAME + { + //***************************************************************************** + // Exception Macros + //***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_NORET_METHOD(void, Throw_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + } + + __R_DIRECT_NORET_METHOD(void, Throw_Win32)(__R_DIRECT_FN_PARAMS DWORD err) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + } + + __R_DIRECT_NORET_METHOD(void, Throw_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_DIRECT_FN_CALL_ONLY); + } + + __R_DIRECT_NORET_METHOD(void, Throw_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + } + + __R_DIRECT_NORET_METHOD(void, Throw_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_Hr)(__R_INTERNAL_FN_PARAMS HRESULT hr) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL hr); + } + + __R_INTERNAL_NORET_METHOD(_Throw_GetLastError)(__R_INTERNAL_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_INTERNAL_FN_CALL_ONLY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_Win32)(__R_INTERNAL_FN_PARAMS DWORD err) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_INTERNAL_FN_CALL err); + } + + __R_INTERNAL_NORET_METHOD(_Throw_NullAlloc)(__R_INTERNAL_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_NtStatus)(__R_INTERNAL_FN_PARAMS NTSTATUS status) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_INTERNAL_FN_CALL status); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(HRESULT, Throw_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) + { + if (FAILED(hr)) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(BOOL, Throw_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) + { + if (!ret) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(DWORD, Throw_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) + { + if (FAILED_WIN32(err)) + { + __R_CALL_INTERNAL_METHOD(_Throw_Win32)(__R_CONDITIONAL_FN_CALL err); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(HANDLE, Throw_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == INVALID_HANDLE_VALUE) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(RESULT_NORETURN_NULL HANDLE, Throw_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == condition) + _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + _Post_satisfies_(return == condition) + _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == status) + _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(NTSTATUS, Throw_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) + { + if (FAILED_NTSTATUS(status)) + { + __R_CALL_INTERNAL_METHOD(_Throw_NtStatus)(__R_CONDITIONAL_FN_CALL status); + } + return status; + } + + __R_DIRECT_NORET_METHOD(void, Throw_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_HrMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_GetLastErrorMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_Win32Msg)(__R_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_NullAllocMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_NtStatusMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Throw_IfFailedMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(BOOL, Throw_IfWin32BoolFalseMsg)(__R_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(DWORD, Throw_IfWin32ErrorMsg)(__R_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_Win32Msg)(__R_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Throw_IfHandleInvalidMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == 0, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(RESULT_NORETURN_NULL HANDLE, Throw_IfHandleNullMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_HrIfMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_HrIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_GetLastErrorIfMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_GetLastErrorIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, Throw_IfNtStatusFailedMsg)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NtStatusMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } +#endif // WIL_ENABLE_EXCEPTIONS + + } // __R_NS_NAME namespace + } // details namespace + /// @endcond + + + //***************************************************************************** + // Error Handling Policies to switch between error-handling style + //***************************************************************************** + // The following policies are used as template policies for components that can support exception, fail-fast, and + // error-code based modes. + + // Use for classes which should return HRESULTs as their error-handling policy + // Intentionally removed logging from this policy as logging is more useful at the caller. + struct err_returncode_policy + { + typedef HRESULT result; + + __forceinline static HRESULT Win32BOOL(BOOL fReturn) { RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fReturn); return S_OK; } + __forceinline static HRESULT Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; RETURN_LAST_ERROR_IF_NULL_EXPECTED(h); return S_OK; } + _Post_satisfies_(return == hr) + __forceinline static HRESULT HResult(HRESULT hr) { return hr; } + __forceinline static HRESULT LastError() { return wil::details::GetLastErrorFailHr(); } + __forceinline static HRESULT LastErrorIfFalse(bool condition) { RETURN_LAST_ERROR_IF_EXPECTED(!condition); return S_OK; } + _Post_satisfies_(return == S_OK) + __forceinline static HRESULT OK() { return S_OK; } + }; + + // Use for classes which fail-fast on errors + struct err_failfast_policy + { + typedef _Return_type_success_(true) void result; + __forceinline static result Win32BOOL(BOOL fReturn) { FAIL_FAST_IF_WIN32_BOOL_FALSE(fReturn); } + __forceinline static result Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; FAIL_FAST_LAST_ERROR_IF_NULL(h); } + _When_(FAILED(hr), _Analysis_noreturn_) + __forceinline static result HResult(HRESULT hr) { FAIL_FAST_IF_FAILED(hr); } + __forceinline static result LastError() { FAIL_FAST_LAST_ERROR(); } + __forceinline static result LastErrorIfFalse(bool condition) { if (!condition) { FAIL_FAST_LAST_ERROR(); } } + __forceinline static result OK() {} + }; + +#ifdef WIL_ENABLE_EXCEPTIONS + // Use for classes which should return through exceptions as their error-handling policy + struct err_exception_policy + { + typedef _Return_type_success_(true) void result; + __forceinline static result Win32BOOL(BOOL fReturn) { THROW_IF_WIN32_BOOL_FALSE(fReturn); } + __forceinline static result Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; THROW_LAST_ERROR_IF_NULL(h); } + _When_(FAILED(hr), _Analysis_noreturn_) + __forceinline static result HResult(HRESULT hr) { THROW_IF_FAILED(hr); } + __forceinline static result LastError() { THROW_LAST_ERROR(); } + __forceinline static result LastErrorIfFalse(bool condition) { if (!condition) { THROW_LAST_ERROR(); } } + __forceinline static result OK() {} + }; +#else + // NOTE: A lot of types use 'err_exception_policy' as a default template argument and therefore it must be defined + // (MSVC is permissive about this, but other compilers are not). This will still cause compilation errors at + // template instantiation time since this type lacks required member functions. An alternative would be to have some + // 'default_err_policy' alias that would be something like 'err_failfast_policy' when exceptions are not available, + // but that may have unexpected side effects when compiling code that expects to be using exceptions + struct err_exception_policy + { + }; +#endif + +} // namespace wil + +#pragma warning(pop) + +#endif // defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#endif // __WIL_RESULTMACROS_INCLUDED diff --git a/Externals/WIL/include/wil/result_originate.h b/Externals/WIL/include/wil/result_originate.h new file mode 100644 index 0000000000..328a817778 --- /dev/null +++ b/Externals/WIL/include/wil/result_originate.h @@ -0,0 +1,96 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* + +// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating +// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match, +// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the +// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors +// simply because the HRESULTs match. +// +// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is +// caught and re-thrown. +// +// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions +// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must +// capture the entire stack and some additional data. + +#ifndef __WIL_RESULT_ORIGINATE_INCLUDED +#define __WIL_RESULT_ORIGINATE_INCLUDED + +#include "result.h" +#include // RestrictedErrorInfo uses BSTRs :( +#include "resource.h" +#include "com.h" +#include + +#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them. +namespace wil +{ + namespace details + { + // Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame. + inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT + { + if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception)) + { + bool shouldOriginate = true; + + wil::com_ptr_nothrow restrictedErrorInformation; + if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) + { + // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are + // observing right now. + wil::unique_bstr descriptionUnused; + HRESULT existingHr = failure.hr; + wil::unique_bstr restrictedDescriptionUnused; + wil::unique_bstr capabilitySidUnused; + if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused))) + { + shouldOriginate = (failure.hr != existingHr); + } + } + + if (shouldOriginate) + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + wil::unique_hmodule errorModule; + if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule)) + { + auto pfn = reinterpret_cast(GetProcAddress(errorModule.get(), "RoOriginateError")); + if (pfn != nullptr) + { + pfn(failure.hr, nullptr); + } + } +#else // DESKTOP | SYSTEM + ::RoOriginateError(failure.hr, nullptr); +#endif // DESKTOP | SYSTEM + } + else if (restrictedErrorInformation) + { + // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present, + // then we need to restore the error information for later observation. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + } + } + } + } // namespace details +} // namespace wil + +// Automatically call RoOriginateError upon error origination by including this file +WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] +{ + ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); + return 1; +}); +#endif // __cplusplus_winrt + +#endif // __WIL_RESULT_ORIGINATE_INCLUDED diff --git a/Externals/WIL/include/wil/rpc_helpers.h b/Externals/WIL/include/wil/rpc_helpers.h new file mode 100644 index 0000000000..4337437929 --- /dev/null +++ b/Externals/WIL/include/wil/rpc_helpers.h @@ -0,0 +1,206 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RPC_HELPERS_INCLUDED +#define __WIL_RPC_HELPERS_INCLUDED + +#include "result.h" +#include "resource.h" +#include "wistd_functional.h" +#include "wistd_type_traits.h" + +namespace wil +{ + + /// @cond + namespace details + { + // This call-adapter template converts a void-returning 'wistd::invoke' into + // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated + // with 'if constexpr' when C++17 is in wide use. + template struct call_adapter + { + template static HRESULT call(TArgs&& ... args) + { + return wistd::invoke(wistd::forward(args)...); + } + }; + + template<> struct call_adapter + { + template static HRESULT call(TArgs&& ... args) + { + wistd::invoke(wistd::forward(args)...); + return S_OK; + } + }; + + // Some RPC exceptions are already HRESULTs. Others are in the regular Win32 + // error space. If the incoming exception code isn't an HRESULT, wrap it. + constexpr HRESULT map_rpc_exception(DWORD code) + { + return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code); + } + } + /// @endcond + + /** Invokes an RPC method, mapping structured exceptions to HRESULTs + Failures encountered by the RPC infrastructure (such as server crashes, authentication + errors, client parameter issues, etc.) are emitted by raising a structured exception from + within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, + RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual + flow control machinery to use. + + Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates + the result of the _work_. HRESULTs returned by a successful completion of the _call_ are + returned as-is. + + RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_ + completes successfully. + + For example, consider an RPC interface method defined in idl as: + ~~~ + HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state); + ~~~ + To call this method, use: + ~~~ + wil::unique_rpc_binding binding = // typically gotten elsewhere; + wil::unique_midl_ptr state; + HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put()); + RETURN_IF_FAILED(hr); + ~~~ + */ + template HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT + { + RpcTryExcept + { + // Note: this helper type can be removed with C++17 enabled via + // 'if constexpr(wistd::is_same_v)' + using result_t = typename wistd::__invoke_of::type; + RETURN_IF_FAILED(details::call_adapter::call(wistd::forward(args)...)); + return S_OK; + } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); + } + RpcEndExcept + } + + /** Invokes an RPC method, mapping structured exceptions to HRESULTs + Failures encountered by the RPC infrastructure (such as server crashes, authentication + errors, client parameter issues, etc.) are emitted by raising a structured exception from + within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, + RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual + flow control machinery to use. + + Some RPC methods return results (such as a state enumeration or other value) directly in + their signature. This adapter writes that result into a caller-provided object then + returns S_OK. + + For example, consider an RPC interface method defined in idl as: + ~~~ + GUID GetKittenId([in, ref, string] const wchar_t* name); + ~~~ + To call this method, use: + ~~~ + wil::unique_rpc_binding binding = // typically gotten elsewhere; + GUID id; + HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy"); + RETURN_IF_FAILED(hr); + ~~~ + */ + template HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT + { + RpcTryExcept + { + result = wistd::invoke(wistd::forward(args)...); + return S_OK; + } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); + } + RpcEndExcept + } + + namespace details + { + // Provides an adapter around calling the context-handle-close method on an + // RPC interface, which itself is an RPC call. + template + struct rpc_closer_t + { + static void Close(TStorage arg) WI_NOEXCEPT + { + LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg)); + } + }; + } + + /** Manages explicit RPC context handles + Explicit RPC context handles are used in many RPC interfaces. Most interfaces with + context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets + the server close out the context handle. As the close method itself is an RPC call, + it can fail and raise a structured exception. + + This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow` + helper, ensuring correct cleanup and lifecycle management. + ~~~ + // Assume the interface has two methods: + // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*); + // HRESULT UseFoo([in] FOO_CONTEXT context; + // void CloseFoo([in, out] PFOO_CONTEXT); + using unique_foo_context = wil::unique_rpc_context_handle; + unique_foo_context context; + RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put())); + RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get())); + context.reset(); + ~~~ + */ + template + using unique_rpc_context_handle = unique_any::Close), details::rpc_closer_t::Close>; + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Invokes an RPC method, mapping structured exceptions to C++ exceptions + See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_ + and those returned by the _method_ are mapped to HRESULTs and thrown inside a + wil::ResultException. Using the example RPC method provided above: + ~~~ + wil::unique_midl_ptr state; + wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); + // use 'state' + ~~~ + */ + template void invoke_rpc(TCall&& ... args) + { + THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward(args)...)); + } + + /** Invokes an RPC method, mapping structured exceptions to C++ exceptions + See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the + _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the + example RPC method provided above: + ~~~ + GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); + // use 'id' + ~~~ + */ + template auto invoke_rpc_result(TCall&& ... args) + { + using result_t = typename wistd::__invoke_of::type; + result_t result{}; + THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward(args)...)); + return result; + } +#endif +} + +#endif diff --git a/Externals/WIL/include/wil/safecast.h b/Externals/WIL/include/wil/safecast.h new file mode 100644 index 0000000000..8d477698ed --- /dev/null +++ b/Externals/WIL/include/wil/safecast.h @@ -0,0 +1,369 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_SAFECAST_INCLUDED +#define __WIL_SAFECAST_INCLUDED + +#include "result_macros.h" +#include +#include "wistd_config.h" +#include "wistd_type_traits.h" + +namespace wil +{ + namespace details + { + // Default error case for undefined conversions in intsafe.h + template constexpr wistd::nullptr_t intsafe_conversion = nullptr; + + // is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known + // safe conversions can be handled by static_cast, this includes conversions between the same + // type, when the new type is larger than the old type but is not a signed to unsigned + // conversion, and when the two types are the same size and signed/unsigned. All other + // conversions will be assumed to be potentially unsafe, and the conversion must be handled + // by intsafe and checked. + template + constexpr bool is_known_safe_static_cast_v = + (sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v && wistd::is_unsigned_v)) || + (sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v && wistd::is_signed_v) || (wistd::is_unsigned_v && wistd::is_unsigned_v))); + + // Helper template to determine that NewT and OldT are both integral types. The safe_cast + // operation only supports conversions between integral types. + template + constexpr bool both_integral_v = wistd::is_integral::value && wistd::is_integral::value; + + // Note on native wchar_t (__wchar_t): + // Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is + // typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of + // support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an + // unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and + // share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast + // to a native wchar_t. + + // Intsafe does not have a defined conversion for native wchar_t + template + constexpr bool neither_native_wchar_v = !wistd::is_same::value && !wistd::is_same::value; + + // Check to see if the cast is a conversion to native wchar_t + template + constexpr bool is_cast_to_wchar_v = wistd::is_same::value && !wistd::is_same::value; + + // Check to see if the cast is a conversion from native wchar_t + template + constexpr bool is_cast_from_wchar_v = !wistd::is_same::value && wistd::is_same::value; + + // Validate the conversion to be performed has a defined mapping to an intsafe conversion + template + constexpr bool is_supported_intsafe_cast_v = intsafe_conversion != nullptr; + + // True when the conversion is between integral types and can be handled by static_cast + template + constexpr bool is_supported_safe_static_cast_v = both_integral_v && is_known_safe_static_cast_v; + + // True when the conversion is between integral types, does not involve native wchar, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_no_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + neither_native_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is between integral types, is a cast to native wchar_t, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_to_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + is_cast_to_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is between integral types, is a cast from native wchar_t, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_from_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + is_cast_from_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is supported and unsafe, and may or may not involve + // native wchar_t. + template + constexpr bool is_supported_unsafe_cast_v = + is_supported_unsafe_cast_no_wchar_v || + is_supported_unsafe_cast_to_wchar_v || + is_supported_unsafe_cast_from_wchar_v; + + // True when T is any one of the primitive types that the variably sized types are defined as. + template + constexpr bool is_potentially_variably_sized_type_v = + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value; + + // True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t) + template + constexpr bool is_potentially_variably_sized_cast_v = + is_potentially_variably_sized_type_v || + is_potentially_variably_sized_type_v; + + // Mappings of all conversions defined in intsafe.h to intsafe_conversion + // Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve + // to the base types. The base types are used since they do not vary based on architecture. + template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar; + template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt; + template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong; + template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort; + template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8; + template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong; + template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar; + template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt; + template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong; + template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort; + template<> constexpr auto intsafe_conversion = IntToChar; + template<> constexpr auto intsafe_conversion = IntToShort; + template<> constexpr auto intsafe_conversion = IntToInt8; + template<> constexpr auto intsafe_conversion = IntToULongLong; + template<> constexpr auto intsafe_conversion = IntToUChar; + template<> constexpr auto intsafe_conversion = IntToUInt; + template<> constexpr auto intsafe_conversion = IntToULong; + template<> constexpr auto intsafe_conversion = IntToUShort; + template<> constexpr auto intsafe_conversion = LongToChar; + template<> constexpr auto intsafe_conversion = LongToInt; + template<> constexpr auto intsafe_conversion = LongToShort; + template<> constexpr auto intsafe_conversion = LongToInt8; + template<> constexpr auto intsafe_conversion = LongToULongLong; + template<> constexpr auto intsafe_conversion = LongToUChar; + template<> constexpr auto intsafe_conversion = LongToUInt; + template<> constexpr auto intsafe_conversion = LongToULong; + template<> constexpr auto intsafe_conversion = LongToUShort; + template<> constexpr auto intsafe_conversion = ShortToChar; + template<> constexpr auto intsafe_conversion = ShortToInt8; + template<> constexpr auto intsafe_conversion = ShortToULongLong; + template<> constexpr auto intsafe_conversion = ShortToUChar; + template<> constexpr auto intsafe_conversion = ShortToUInt; + template<> constexpr auto intsafe_conversion = ShortToULong; + template<> constexpr auto intsafe_conversion = ShortToUShort; + template<> constexpr auto intsafe_conversion = Int8ToULongLong; + template<> constexpr auto intsafe_conversion = Int8ToUChar; + template<> constexpr auto intsafe_conversion = Int8ToUInt; + template<> constexpr auto intsafe_conversion = Int8ToULong; + template<> constexpr auto intsafe_conversion = Int8ToUShort; + template<> constexpr auto intsafe_conversion = ULongLongToLongLong; + template<> constexpr auto intsafe_conversion = ULongLongToChar; + template<> constexpr auto intsafe_conversion = ULongLongToInt; + template<> constexpr auto intsafe_conversion = ULongLongToLong; + template<> constexpr auto intsafe_conversion = ULongLongToShort; + template<> constexpr auto intsafe_conversion = ULongLongToInt8; + template<> constexpr auto intsafe_conversion = ULongLongToUChar; + template<> constexpr auto intsafe_conversion = ULongLongToUInt; + template<> constexpr auto intsafe_conversion = ULongLongToULong; + template<> constexpr auto intsafe_conversion = ULongLongToUShort; + template<> constexpr auto intsafe_conversion = UInt8ToChar; + template<> constexpr auto intsafe_conversion = UIntToInt8; + template<> constexpr auto intsafe_conversion = UIntToChar; + template<> constexpr auto intsafe_conversion = UIntToInt; + template<> constexpr auto intsafe_conversion = UIntToLong; + template<> constexpr auto intsafe_conversion = UIntToShort; + template<> constexpr auto intsafe_conversion = UIntToInt8; + template<> constexpr auto intsafe_conversion = UIntToUChar; + template<> constexpr auto intsafe_conversion = UIntToUShort; + template<> constexpr auto intsafe_conversion = ULongToChar; + template<> constexpr auto intsafe_conversion = ULongToInt; + template<> constexpr auto intsafe_conversion = ULongToLong; + template<> constexpr auto intsafe_conversion = ULongToShort; + template<> constexpr auto intsafe_conversion = ULongToInt8; + template<> constexpr auto intsafe_conversion = ULongToUChar; + template<> constexpr auto intsafe_conversion = ULongToUInt; + template<> constexpr auto intsafe_conversion = ULongToUShort; + template<> constexpr auto intsafe_conversion = UShortToChar; + template<> constexpr auto intsafe_conversion = UShortToShort; + template<> constexpr auto intsafe_conversion = UShortToInt8; + template<> constexpr auto intsafe_conversion = UShortToUChar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + NewT newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + NewT newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(static_cast(var), &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + unsigned short newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return static_cast<__wchar_t>(newVar); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + return static_cast(var); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + NewT newVar; + THROW_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + NewT newVar; + THROW_IF_FAILED((details::intsafe_conversion(static_cast(var), &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + unsigned short newVar; + THROW_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return static_cast<__wchar_t>(newVar); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + return static_cast(var); + } +#endif + + // This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_nothrow(const OldT /*var*/) + { + static_assert(!wistd::is_same_v, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead"); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_nothrow(const OldT var) + { + return static_cast(var); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(var, newTResult); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(static_cast(var), newTResult); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(var, reinterpret_cast(newTResult)); + } + + // This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion + // does not involve a variably sized type, then the compilation will fail and say the single parameter version + // of safe_cast_nothrow should be used instead. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + static_assert(details::is_potentially_variably_sized_cast_v, "This cast is always safe; use safe_cast_nothrow(value) to avoid unnecessary error handling."); + *newTResult = static_cast(var); + return S_OK; + } +} + +#endif // __WIL_SAFECAST_INCLUDED diff --git a/Externals/WIL/include/wil/stl.h b/Externals/WIL/include/wil/stl.h new file mode 100644 index 0000000000..56e95f4544 --- /dev/null +++ b/Externals/WIL/include/wil/stl.h @@ -0,0 +1,124 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_STL_INCLUDED +#define __WIL_STL_INCLUDED + +#include "common.h" +#include "resource.h" +#include +#include + +#if defined(WIL_ENABLE_EXCEPTIONS) +namespace std +{ + template + class vector; + + template + struct char_traits; + + template + class basic_string; +} // namespace std + +namespace wil +{ + /** Secure allocator for STL containers. + The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating + memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`, + `wil::secure_string`, and `wil::secure_wstring`. */ + template + struct secure_allocator + : public std::allocator + { + template + struct rebind + { + typedef secure_allocator other; + }; + + secure_allocator() + : std::allocator() + { + } + + ~secure_allocator() = default; + + secure_allocator(const secure_allocator& a) + : std::allocator(a) + { + } + + template + secure_allocator(const secure_allocator& a) + : std::allocator(a) + { + } + + T* allocate(size_t n) + { + return std::allocator::allocate(n); + } + + void deallocate(T* p, size_t n) + { + SecureZeroMemory(p, sizeof(T) * n); + std::allocator::deallocate(p, n); + } + }; + + //! `wil::secure_vector` will be securely zeroed before deallocation. + template + using secure_vector = std::vector>; + //! `wil::secure_wstring` will be securely zeroed before deallocation. + using secure_wstring = std::basic_string, wil::secure_allocator>; + //! `wil::secure_string` will be securely zeroed before deallocation. + using secure_string = std::basic_string, wil::secure_allocator>; + + /// @cond + namespace details + { + template<> struct string_maker + { + HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try + { + m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0'); + return S_OK; + } + catch (...) + { + return E_OUTOFMEMORY; + } + + wchar_t* buffer() { return &m_value[0]; } + + std::wstring release() { return std::wstring(std::move(m_value)); } + + static PCWSTR get(const std::wstring& value) { return value.c_str(); } + + private: + std::wstring m_value; + }; + } + /// @endcond + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // This is the overload for std::wstring. Other overloads available in resource.h. + inline PCWSTR str_raw_ptr(const std::wstring& str) + { + return str.c_str(); + } + +} // namespace wil + +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_STL_INCLUDED diff --git a/Externals/WIL/include/wil/token_helpers.h b/Externals/WIL/include/wil/token_helpers.h new file mode 100644 index 0000000000..746a13ef7a --- /dev/null +++ b/Externals/WIL/include/wil/token_helpers.h @@ -0,0 +1,597 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_TOKEN_HELPERS_INCLUDED +#define __WIL_TOKEN_HELPERS_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include "resource.h" +#include +#include // for UNLEN and DNLEN +#include + +// for GetUserNameEx() +#define SECURITY_WIN32 +#include + +namespace wil +{ + /// @cond + namespace details + { + // Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed + // TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may + // be an info class value that uses the same structure. That is the case for the file + // system information. + template struct MapTokenStructToInfoClass; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; }; + + // fixed size cases + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; }; + } + /// @endcond + + enum class OpenThreadTokenAs + { + Current, + Self + }; + + /** Open the active token. + Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller + can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the + effective user. + + Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information. + This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle + and much easier to manage. + ~~~~ + wil::unique_handle theToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken)); + ~~~~ + Callers who want more access to the token (such as to duplicate or modify the token) can pass + any mask of the token rights. + ~~~~ + wil::unique_handle theToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)); + ~~~~ + Services impersonating their clients may need to request that the active token is opened on the + behalf of the service process to perform certain operations. Opening a token for impersonation access + or privilege-adjustment are examples of uses. + ~~~~ + wil::unique_handle callerToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true)); + ~~~~ + @param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or + (preferably) stored in a wil::unique_handle + @param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken + @param asSelf When true, and if the thread is impersonating, the thread token is opened using the + process token's rights. + */ + inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); + if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN)) + { + hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); + } + return hr; + } + + //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. + inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HANDLE rawTokenHandle; + FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); + return wil::unique_handle(rawTokenHandle); + } + +// Exception based function to open current thread/process access token and acquire pointer to it +#ifdef WIL_ENABLE_EXCEPTIONS + //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. + inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HANDLE rawTokenHandle; + THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); + return wil::unique_handle(rawTokenHandle); + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Returns tokenHandle or the effective thread token if tokenHandle is null. + // Note, this returns an token handle who's lifetime is managed independently + // and it may be a pseudo token, don't free it! + inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle) + { + return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken(); + } + + /** Fetches information about a token. + See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information + is returned to the caller as a wistd::unique_ptr (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For + fixed sized, the struct is returned directly. + The caller must have access to read the information from the provided token. This method works with both real + (e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles. + ~~~~ + // Retrieve the TOKEN_USER structure for the current process + wistd::unique_ptr user; + RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken())); + RETURN_IF_FAILED(ConsumeSid(user->User.Sid)); + ~~~~ + Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token. + ~~~~ + wistd::unique_ptr privileges; + RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges)); + for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount)) + { + RETURN_IF_FAILED(ConsumePrivilege(privilege)); + } + ~~~~ + @param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested + type. The type of selects which TOKEN_INFORMATION_CLASS will be used. + @param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used. + @return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise. + */ + + template ::FixedSize>* = nullptr> + inline HRESULT get_token_information_nothrow(wistd::unique_ptr& tokenInfo, HANDLE tokenHandle = nullptr) + { + tokenInfo.reset(); + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = 0; + const auto infoClass = details::MapTokenStructToInfoClass::infoClass; + RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) && + (::GetLastError() == ERROR_INSUFFICIENT_BUFFER))); + wistd::unique_ptr tokenInfoClose( + static_cast(operator new(tokenInfoSize, std::nothrow))); + RETURN_IF_NULL_ALLOC(tokenInfoClose.get()); + RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize)); + tokenInfo.reset(reinterpret_cast(tokenInfoClose.release())); + + return S_OK; + } + + template ::FixedSize>* = nullptr> + inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr) + { + *tokenInfo = {}; + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = sizeof(T); + const auto infoClass = details::MapTokenStructToInfoClass::infoClass; + RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize)); + + return S_OK; + } + + namespace details + { + template::FixedSize>* = nullptr> + wistd::unique_ptr GetTokenInfoWrap(HANDLE token = nullptr) + { + wistd::unique_ptr temp; + policy::HResult(get_token_information_nothrow(temp, token)); + return temp; + } + + template::FixedSize>* = nullptr> + T GetTokenInfoWrap(HANDLE token = nullptr) + { + T temp{}; + policy::HResult(get_token_information_nothrow(&temp, token)); + return temp; + } + } + + //! A variant of get_token_information that fails-fast on errors retrieving the token + template + inline auto get_token_information_failfast(HANDLE token = nullptr) + { + return details::GetTokenInfoWrap(token); + } + + //! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token + inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr) + { + static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch"); + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = 0; + RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken, + tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize)); + return S_OK; + } + + /** Retrieves the linked-token information for a token. + Fails-fast if the link information cannot be retrieved. + ~~~~ + auto link = get_linked_token_information_failfast(GetCurrentThreadToken()); + auto tokenUser = get_token_information(link.LinkedToken); + ~~~~ + @param token Specifies the token to query. Pass nullptr to use the current effective thread token + @return unique_token_linked_token containing a handle to the linked token + */ + inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr) + { + unique_token_linked_token tokenInfo; + FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); + return tokenInfo; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Fetches information about a token. + See get_token_information_nothrow for full details. + ~~~~ + auto user = wil::get_token_information(GetCurrentProcessToken()); + ConsumeSid(user->User.Sid); + ~~~~ + Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token. + ~~~~ + auto privs = wil::get_token_information(privileges); + for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount)) + { + if (priv.Attributes & SE_PRIVILEGE_ENABLED) + { + // ... + } + } + ~~~~ + @return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of + selects which TOKEN_INFORMATION_CLASS will be used. + @param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used. + */ + template + inline auto get_token_information(HANDLE token = nullptr) + { + return details::GetTokenInfoWrap(token); + } + + /** Retrieves the linked-token information for a token. + Throws an exception if the link information cannot be retrieved. + ~~~~ + auto link = get_linked_token_information(GetCurrentThreadToken()); + auto tokenUser = get_token_information(link.LinkedToken); + ~~~~ + @param token Specifies the token to query. Pass nullptr to use the current effective thread token + @return unique_token_linked_token containing a handle to the linked token + */ + inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr) + { + unique_token_linked_token tokenInfo; + THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); + return tokenInfo; + } +#endif + + /// @cond + namespace details + { + inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken) + { + FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken)); + + if (oldToken) + { + ::CloseHandle(oldToken); + } + } + } + /// @endcond + + using unique_token_reverter = wil::unique_any< + HANDLE, + decltype(&details::RevertImpersonateToken), + details::RevertImpersonateToken, + details::pointer_access_none, + HANDLE, + INT_PTR, + -1, + HANDLE>; + + /** Temporarily impersonates a token on this thread. + This method sets a new token on a thread, restoring the current token when the returned object + is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. + ~~~~ + HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened) + { + wil::unique_handle userToken; + RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); + + wil::unique_token_reverter reverter; + RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter)); + + wil::unique_hfile userFile(::CreateFile(filePath, ...)); + RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND)); + + *opened = userFile.release(); + return S_OK; + } + ~~~~ + @param token A token to impersonate, or 'nullptr' to run as the process identity. + */ + inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter) + { + wil::unique_handle currentToken; + + // Get the token for the current thread. If there wasn't one, the reset will clear it as well + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, ¤tToken)) + { + RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN); + } + + // Update the current token + RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token)); + + reverter.reset(currentToken.release()); // Ownership passed + return S_OK; + } + + /** Temporarily clears any impersonation on this thread. + This method resets the current thread's token to nullptr, indicating that it is not impersonating + any user. Useful for elevating to whatever identity a service or higher-privilege process might + be capable of running under. + ~~~~ + HRESULT DeleteFileRetryAsSelf(PCWSTR filePath) + { + if (!::DeleteFile(filePath)) + { + RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED); + wil::unique_token_reverter reverter; + RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter)); + RETURN_IF_FAILED(TakeOwnershipOfFile(filePath)); + RETURN_IF_FAILED(GrantDeleteAccess(filePath)); + RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath)); + } + return S_OK; + } + ~~~~ + */ + inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter) + { + return impersonate_token_nothrow(nullptr, reverter); + } + + inline unique_token_reverter impersonate_token_failfast(HANDLE token) + { + unique_token_reverter oldToken; + FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken)); + return oldToken; + } + + inline unique_token_reverter run_as_self_failfast() + { + return impersonate_token_failfast(nullptr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Temporarily impersonates a token on this thread. + This method sets a new token on a thread, restoring the current token when the returned object + is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. + ~~~~ + wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session) + { + wil::unique_handle userToken; + THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); + + auto priorToken = wil::impersonate_token(userToken.get()); + + wil::unique_hfile userFile(::CreateFile(filePath, ...)); + THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND); + + return userFile; + } + ~~~~ + @param token A token to impersonate, or 'nullptr' to run as the process identity. + */ + inline unique_token_reverter impersonate_token(HANDLE token = nullptr) + { + unique_token_reverter oldToken; + THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken)); + return oldToken; + } + + /** Temporarily clears any impersonation on this thread. + This method resets the current thread's token to nullptr, indicating that it is not impersonating + any user. Useful for elevating to whatever identity a service or higher-privilege process might + be capable of running under. + ~~~~ + void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath) + { + if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED)) + { + auto priorToken = wil::run_as_self(); + TakeOwnershipOfFile(filePath); + GrantDeleteAccess(filePath); + ::DeleteFile(filePath); + } + } + ~~~~ + */ + inline unique_token_reverter run_as_self() + { + return impersonate_token(nullptr); + } +#endif // WIL_ENABLE_EXCEPTIONS + + namespace details + { + template struct static_sid_t + { + BYTE Revision; + BYTE SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + DWORD SubAuthority[AuthorityCount]; + + PSID get() + { + return reinterpret_cast(this); + } + + template static_sid_t& operator=(const static_sid_t& source) + { + static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one"); + + if (&this->Revision != &source.Revision) + { + memcpy(this, &source, sizeof(source)); + } + + return *this; + } + }; + } + + /** Returns a structure containing a Revision 1 SID initialized with the authorities provided + Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but + returned like a value. The resulting object is suitable for use with any method taking PSID, + passed by "&the_sid" or via "the_sid.get()" + ~~~~ + // Change the owner of the key to administrators + auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); + RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr)); + ~~~~ + */ + template constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities) + { + using sid_t = details::static_sid_t; + + static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities"); + static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch"); + static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch"); + static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch"); + static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch"); + + return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast(subAuthorities)... } }; + } + + //! Variant of static_sid that defaults to the NT authority + template constexpr auto make_static_nt_sid(Ts&& ... subAuthorities) + { + return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward(subAuthorities)...); + } + + /** Determines whether a specified security identifier (SID) is enabled in an access token. + This function determines whether a security identifier, described by a given set of subauthorities, is enabled + in the given access token. Note that only up to eight subauthorities can be passed to this function. + ~~~~ + bool IsGuest() + { + return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS)); + } + ~~~~ + @param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token. + @param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership + uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token. + @param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID. + @param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit) + @return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise. + */ + template HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token, + const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) + { + *result = false; + auto tempSid = make_static_sid(sidAuthority, wistd::forward(subAuthorities)...); + BOOL isMember; + RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember)); + + *result = (isMember != FALSE); + + return S_OK; + } + + /** Determine whether a token represents an app container + This method uses the passed in token and emits a boolean indicating that + whether TokenIsAppContainer is true. + ~~~~ + HRESULT OnlyIfAppContainer() + { + bool isAppContainer; + RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer)); + RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer); + RETURN_HR(...); + } + ~~~~ + @param token A token to get info about, or 'nullptr' to run as the current thread. + */ + inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value) + { + DWORD isAppContainer = 0; + DWORD returnLength = 0; + RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation( + token ? token : GetCurrentThreadEffectiveToken(), + TokenIsAppContainer, + &isAppContainer, + sizeof(isAppContainer), + &returnLength)); + + value = (isAppContainer != 0); + + return S_OK; + } + + //! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information + inline bool get_token_is_app_container_failfast(HANDLE token = nullptr) + { + bool value = false; + FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value)); + + return value; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information + inline bool get_token_is_app_container(HANDLE token = nullptr) + { + bool value = false; + THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value)); + + return value; + } +#endif // WIL_ENABLE_EXCEPTIONS + + template bool test_token_membership_failfast(_In_opt_ HANDLE token, + const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) + { + bool result; + FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward(subAuthorities)...)); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority, + Ts&&... subAuthorities) + { + bool result; + THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward(subAuthorities)...)); + return result; + } +#endif + +} //namespace wil + +#endif // __WIL_TOKEN_HELPERS_INCLUDED diff --git a/Externals/WIL/include/wil/win32_helpers.h b/Externals/WIL/include/wil/win32_helpers.h new file mode 100644 index 0000000000..0184cf2acc --- /dev/null +++ b/Externals/WIL/include/wil/win32_helpers.h @@ -0,0 +1,563 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WIN32_HELPERS_INCLUDED +#define __WIL_WIN32_HELPERS_INCLUDED + +#include // FILETIME, HINSTANCE +#include // GetSystemTimeAsFileTime +#include // GetProcAddress +#include // GetModuleFileNameExW (macro), K32GetModuleFileNameExW +#include +#include + +#include "result.h" +#include "resource.h" +#include "wistd_functional.h" +#include "wistd_type_traits.h" + +namespace wil +{ + //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT. + //! CDFs has a limit of 254. + size_t const max_path_segment_length = 255; + + //! Character length not including the null, MAX_PATH (260) includes the null. + size_t const max_path_length = 259; + + //! 32743 Character length not including the null. This is a system defined limit. + //! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4" + //! It will be 25 when there are more than 9 disks. + size_t const max_extended_path_length = 0x7FFF - 24; + + //! For {guid} string form. Includes space for the null terminator. + size_t const guid_string_buffer_length = 39; + + //! For {guid} string form. Not including the null terminator. + size_t const guid_string_length = 38; + +#pragma region FILETIME helpers + // FILETIME duration values. FILETIME is in 100 nanosecond units. + namespace filetime_duration + { + long long const one_millisecond = 10000LL; + long long const one_second = 10000000LL; + long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL + long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL + long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL + }; + + namespace filetime + { + inline unsigned long long to_int64(const FILETIME &ft) + { + // Cannot reinterpret_cast FILETIME* to unsigned long long* + // due to alignment differences. + return (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; + } + + inline FILETIME from_int64(unsigned long long i64) + { + static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match"); + static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun"); + return *reinterpret_cast(&i64); + } + + inline FILETIME add(_In_ FILETIME const &ft, long long delta) + { + return from_int64(to_int64(ft) + delta); + } + + inline bool is_empty(const FILETIME &ft) + { + return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0); + } + + inline FILETIME get_system_time() + { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return ft; + } + } +#pragma endregion + + // Use to adapt Win32 APIs that take a fixed size buffer into forms that return + // an allocated buffer. Supports many types of string representation. + // See comments below on the expected behavior of the callback. + // Adjust stackBufferLength based on typical result sizes to optimize use and + // to test the boundary cases. + template + HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function callback) + { + details::string_maker maker; + + wchar_t value[stackBufferLength]; + value[0] = L'\0'; + size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator. + RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull)); + WI_ASSERT(valueLengthNeededWithNull > 0); + if (valueLengthNeededWithNull <= ARRAYSIZE(value)) + { + // Success case as described above, make() adds the space for the null. + RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1)); + } + else + { + // Did not fit in the stack allocated buffer, need to do 2 phase construction. + // valueLengthNeededWithNull includes the null so subtract that as make() will add space for it. + RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1)); + + size_t secondLength{}; + RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength)); + + // Ensure callback produces consistent result. + FAIL_FAST_IF(valueLengthNeededWithNull != secondLength); + } + result = maker.release(); + return S_OK; + } + + /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */ + template + HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0); + return S_OK; + }); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) + /** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */ + template + HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast(valueLength), value, nullptr); + + if (*valueLengthNeededWithNul == 0) + { + // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW + const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError()); + RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + RETURN_IF_FAILED(searchResult); + } + + // AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL. + // If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL. + // If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul. + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // It fit, account for the null. + } + return S_OK; + }); + } + + // This function does not work beyond the default stack buffer size (255). + // Needs to to retry in a loop similar to wil::GetModuleFileNameExW + // These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3 + template + HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + DWORD lengthToUse = static_cast(valueLength); + BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse); + RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)); + // On both success or insufficient buffer case, add +1 for the null-terminating character + *valueLengthNeededWithNul = lengthToUse + 1; + return S_OK; + }); + } + + /** Expands environment strings and checks path existence with SearchPathW */ + template + HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT + { + wil::unique_cotaskmem_string expandedName; + RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW(input, expandedName))); + + // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW + const HRESULT searchResult = (wil::SearchPathW(nullptr, expandedName.get(), nullptr, result)); + RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + RETURN_IF_FAILED(searchResult); + + return S_OK; + } +#endif + + /** Looks up the environment variable 'key' and fails if it is not found. + 'key' should not have '%' prefix and suffix. + Dangerous since environment variable generally are optional. */ + template + inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + // If the function succeeds, the return value is the number of characters stored in the buffer + // pointed to by lpBuffer, not including the terminating null character. + // + // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in + // characters, required to hold the string and its terminating null character and the contents of + // lpBuffer are undefined. + // + // If the function fails, the return value is zero. If the specified environment variable was not + // found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND. + + ::SetLastError(ERROR_SUCCESS); + + *valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS)); + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // It fit, account for the null. + } + return S_OK; + }); + } + + /** Looks up the environment variable 'key' and returns null if it is not found. + 'key' should not have '%' prefix and suffix. */ + template + HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT + { + const auto hr = wil::GetEnvironmentVariableW(key, result); + RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND))); + return S_OK; + } + + /** Retrieves the fully qualified path for the file containing the specified module loaded + by a given process. Note GetModuleFileNameExW is a macro.*/ + template + HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) + { + // initialBufferLength is a template parameter to allow for testing. It creates some waste for + // shorter paths, but avoids iteration through the loop in common cases where paths are less + // than 128 characters. + // wil::max_extended_path_length + 1 (for the null char) + // + 1 (to be certain GetModuleFileNameExW didn't truncate) + size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0; + size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation; + + details::string_maker maker; + + for (size_t lengthWithNull = initialBufferLength; + lengthWithNull <= maxExtendedPathLengthWithNull; + lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull)) + { + // make() adds space for the trailing null + RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1)); + + DWORD copiedCount; + bool copyFailed; + bool copySucceededWithNoTruncation; + + if (process != nullptr) + { + // GetModuleFileNameExW truncates and provides no error or other indication it has done so. + // The only way to be sure it didn't truncate is if it didn't need the whole buffer. + copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast(lengthWithNull)); + copyFailed = (0 == copiedCount); + copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1); + } + else + { + // In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull + // and set the last error to ERROR_INSUFFICIENT_BUFFER. + copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast(lengthWithNull)); + copyFailed = (0 == copiedCount); + copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull); + } + + if (copyFailed) + { + RETURN_LAST_ERROR(); + } + else if (copySucceededWithNoTruncation) + { + path = maker.release(); + return S_OK; + } + + WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)); + + if (lengthWithNull == maxExtendedPathLengthWithNull) + { + // If we've reached this point, there's no point in trying a larger buffer size. + break; + } + } + + // Any path should fit into the maximum max_extended_path_length. If we reached here, something went + // terribly wrong. + FAIL_FAST(); + } + + /** Retrieves the fully qualified path for the file that contains the specified module. + The module must have been loaded by the current process. The path returned will use the + same format that was specified when the module was loaded. Therefore, the path can be a + long or short file name, and can have the prefix '\\?\'. */ + template + HRESULT GetModuleFileNameW(HMODULE module, string_type& path) + { + return wil::GetModuleFileNameExW(nullptr, module, path); + } + + template + HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0); + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */ + template + string_type ExpandEnvironmentStringsW(_In_ PCWSTR input) + { + string_type result; + THROW_IF_FAILED((wil::ExpandEnvironmentStringsW(input, result))); + return result; + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) + /** Searches for a specified file in a specified path using SearchPathW*/ + template + string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension) + { + string_type result; + HRESULT searchHR = wil::SearchPathW(path, fileName, extension, result); + THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))); + return result; + } +#endif + + /** Looks up the environment variable 'key' and fails if it is not found. + 'key' should not have '%' prefix and suffix. + Dangerous since environment variable generally are optional. */ + template + string_type GetEnvironmentVariableW(_In_ PCWSTR key) + { + string_type result; + THROW_IF_FAILED(wil::GetEnvironmentVariableW(key, result)); + return result; + } + + /** Looks up the environment variable 'key' and returns null if it is not found. + 'key' should not have '%' prefix and suffix. */ + template + string_type TryGetEnvironmentVariableW(_In_ PCWSTR key) + { + string_type result; + THROW_IF_FAILED(wil::TryGetEnvironmentVariableW(key, result)); + return result; + } + + template + string_type GetModuleFileNameW(HMODULE module) + { + string_type result; + THROW_IF_FAILED(wil::GetModuleFileNameW(module, result)); + return result; + } + + template + string_type GetModuleFileNameExW(HANDLE process, HMODULE module) + { + string_type result; + THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result)); + return result; + } + +#endif + + /** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that + the linker provides for every module. This avoids the need for a global HINSTANCE variable + and provides access to this value for static libraries. */ + EXTERN_C IMAGE_DOS_HEADER __ImageBase; + inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast(&__ImageBase); } + + /// @cond + namespace details + { + class init_once_completer + { + INIT_ONCE& m_once; + unsigned long m_flags = INIT_ONCE_INIT_FAILED; + public: + init_once_completer(_In_ INIT_ONCE& once) : m_once(once) + { + } + + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + void success() + { + m_flags = 0; + } + #pragma warning(pop) + + ~init_once_completer() + { + ::InitOnceComplete(&m_once, m_flags, nullptr); + } + }; + } + /// @endcond + + /** Performs one-time initialization + Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked + at most once. + ~~~~ + INIT_ONCE g_init{}; + ComPtr g_foo; + HRESULT MyMethod() + { + bool winner = false; + RETURN_IF_FAILED(wil::init_once_nothrow(g_init, [] + { + ComPtr foo; + RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo)); + RETURN_IF_FAILED(foo->Startup()); + g_foo = foo; + }, &winner); + if (winner) + { + RETURN_IF_FAILED(g_foo->Another()); + } + return S_OK; + } + ~~~~ + See MSDN for more information on `InitOnceExecuteOnce`. + @param initOnce The INIT_ONCE structure to use as context for initialization. + @param func A function that will be invoked to perform initialization. If this fails, the init call + fails and the once-init is not marked as initialized. A later caller could attempt to + initialize it a second time. + @param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise. + */ + template HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT + { + BOOL pending = FALSE; + wil::assign_to_opt_param(callerCompleted, false); + + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr)); + + if (pending) + { + details::init_once_completer completion(initOnce); + __WIL_PRIVATE_RETURN_IF_FAILED(func()); + completion.success(); + wil::assign_to_opt_param(callerCompleted, true); + } + + return S_OK; + } + + //! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is + //! returned to the caller instead of being an out-parameter. + template bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT + { + bool callerCompleted; + + FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward(func), &callerCompleted)); + + return callerCompleted; + }; + + //! Returns 'true' if this `init_once` structure has finished initialization, false otherwise. + inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT + { + BOOL pending = FALSE; + return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Performs one-time initialization + Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked + at most once. + ~~~~ + INIT_ONCE g_init{}; + ComPtr g_foo; + void MyMethod() + { + bool winner = wil::init_once(g_init, [] + { + ComPtr foo; + THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo)); + THROW_IF_FAILED(foo->Startup()); + g_foo = foo; + }); + if (winner) + { + THROW_IF_FAILED(g_foo->Another()); + } + } + ~~~~ + See MSDN for more information on `InitOnceExecuteOnce`. + @param initOnce The INIT_ONCE structure to use as context for initialization. + @param func A function that will be invoked to perform initialization. If this fails, the init call + fails and the once-init is not marked as initialized. A later caller could attempt to + initialize it a second time. + @returns 'true' if this was the call that caused initialization, false otherwise. + */ + template bool init_once(_Inout_ INIT_ONCE& initOnce, T func) + { + BOOL pending = FALSE; + + THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr)); + + if (pending) + { + details::init_once_completer completion(initOnce); + func(); + completion.success(); + return true; + } + else + { + return false; + } + } +#endif // WIL_ENABLE_EXCEPTIONS +} + +// Macro for calling GetProcAddress(), with type safety for C++ clients +// using the type information from the specified function. +// The return value is automatically cast to match the function prototype of the input function. +// +// Sample usage: +// +// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW); +// if (sendMail) +// { +// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0); +// } +// Declaration +#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast(GetProcAddress(hinst, #fn)) + +#endif // __WIL_WIN32_HELPERS_INCLUDED diff --git a/Externals/WIL/include/wil/winrt.h b/Externals/WIL/include/wil/winrt.h new file mode 100644 index 0000000000..c2456b27a5 --- /dev/null +++ b/Externals/WIL/include/wil/winrt.h @@ -0,0 +1,2231 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WINRT_INCLUDED +#define __WIL_WINRT_INCLUDED + +#include +#include +#include +#include +#include +#include "result.h" +#include "com.h" +#include "resource.h" +#include + +#ifdef __cplusplus_winrt +#include // bring in the CRT iterator for support for C++ CX code +#endif + +#ifdef WIL_ENABLE_EXCEPTIONS +/// @cond +namespace std +{ + template + class basic_string; + + template + struct less; +} +/// @endcond +#endif + +// This enables this code to be used in code that uses the ABI prefix or not. +// Code using the public SDK and C++ CX code has the ABI prefix, windows internal +// is built in a way that does not. +#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case +#pragma push_macro("ABI") +#undef ABI +#define ABI +#endif + +namespace wil +{ +#ifdef _INC_TIME + // time_t is the number of 1 - second intervals since January 1, 1970. + long long const SecondsToStartOf1970 = 0x2b6109100; + long long const HundredNanoSecondsInSecond = 10000000LL; + + inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime) + { + // DateTime is the number of 100 - nanosecond intervals since January 1, 1601. + return (dateTime.UniversalTime / HundredNanoSecondsInSecond - SecondsToStartOf1970); + } + + inline ABI::Windows::Foundation::DateTime time_t_to_DateTime(__time64_t timeT) + { + ABI::Windows::Foundation::DateTime dateTime; + dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond; + return dateTime; + } +#endif // _INC_TIME + +#pragma region HSTRING Helpers + /// @cond + namespace details + { + // hstring_compare is used to assist in HSTRING comparison of two potentially non-similar string types. E.g. + // comparing a raw HSTRING with WRL's HString/HStringReference/etc. The consumer can optionally inhibit the + // deduction of array sizes by providing 'true' for the 'InhibitStringArrays' template argument. This is + // generally done in scenarios where the consumer cannot guarantee that the input argument types are perfectly + // preserved from end-to-end. E.g. if a single function in the execution path captures an array as const T&, + // then it is impossible to differentiate const arrays (where we generally do want to deduce length) from + // non-const arrays (where we generally do not want to deduce length). The consumer can also optionally choose + // to perform case-insensitive comparison by providing 'true' for the 'IgnoreCase' template argument. + template + struct hstring_compare + { + // get_buffer returns the string buffer and length for the supported string types + static const wchar_t* get_buffer(HSTRING hstr, UINT32* length) WI_NOEXCEPT + { + return ::WindowsGetStringRawBuffer(hstr, length); + } + + static const wchar_t* get_buffer(const Microsoft::WRL::Wrappers::HString& hstr, UINT32* length) WI_NOEXCEPT + { + return hstr.GetRawBuffer(length); + } + + static const wchar_t* get_buffer( + const Microsoft::WRL::Wrappers::HStringReference& hstr, + UINT32* length) WI_NOEXCEPT + { + return hstr.GetRawBuffer(length); + } + + static const wchar_t* get_buffer(const unique_hstring& str, UINT32* length) WI_NOEXCEPT + { + return ::WindowsGetStringRawBuffer(str.get(), length); + } + + template + static wistd::enable_if_t get_buffer(const wchar_t* str, UINT32* length) WI_NOEXCEPT + { + str = (str != nullptr) ? str : L""; + *length = static_cast(wcslen(str)); + return str; + } + + template + static wistd::enable_if_t< + wistd::conjunction< + wistd::is_pointer, + wistd::is_same>, wchar_t>, + wistd::bool_constant + >::value, + const wchar_t*> get_buffer(StringT str, UINT32* length) WI_NOEXCEPT + { + str = (str != nullptr) ? str : L""; + *length = static_cast(wcslen(str)); + return str; + } + + template + static wistd::enable_if_t get_buffer( + const wchar_t (&str)[Size], + UINT32* length) WI_NOEXCEPT + { + *length = Size - 1; + return str; + } + + template + static wistd::enable_if_t get_buffer(wchar_t (&str)[Size], UINT32* length) WI_NOEXCEPT + { + *length = static_cast(wcslen(str)); + return str; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template + static const wchar_t* get_buffer( + const std::basic_string& str, + UINT32* length) WI_NOEXCEPT + { + *length = static_cast(str.length()); + return str.c_str(); + } +#endif + + template + static auto compare(LhsT&& lhs, RhsT&& rhs) -> + decltype(get_buffer(lhs, wistd::declval()), get_buffer(rhs, wistd::declval()), int()) + { + UINT32 lhsLength; + UINT32 rhsLength; + auto lhsBuffer = get_buffer(wistd::forward(lhs), &lhsLength); + auto rhsBuffer = get_buffer(wistd::forward(rhs), &rhsLength); + + const auto result = ::CompareStringOrdinal( + lhsBuffer, + lhsLength, + rhsBuffer, + rhsLength, + IgnoreCase ? TRUE : FALSE); + WI_ASSERT(result != 0); + + return result; + } + + template + static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_EQUAL; + } + + template + static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_EQUAL; + } + + template + static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_LESS_THAN; + } + + template + static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_GREATER_THAN; + } + + template + static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_GREATER_THAN; + } + + template + static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_LESS_THAN; + } + }; + } + /// @endcond + + //! Detects if one or more embedded null is present in an HSTRING. + inline bool HasEmbeddedNull(_In_opt_ HSTRING value) + { + BOOL hasEmbeddedNull; + WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull); + return hasEmbeddedNull != FALSE; + } + + /** TwoPhaseHStringConstructor help using the 2 phase constructor pattern for HSTRINGs. + ~~~ + auto stringConstructor = wil::TwoPhaseHStringConstructor::Preallocate(size); + RETURN_IF_NULL_ALLOC(stringConstructor.Get()); + + RETURN_IF_FAILED(stream->Read(stringConstructor.Get(), stringConstructor.ByteSize(), &bytesRead)); + + // Validate stream contents, sizes must match, string must be null terminated. + RETURN_IF_FAILED(stringConstructor.Validate(bytesRead)); + + wil::unique_hstring string { stringConstructor.Promote() }; + ~~~ + + See also wil::unique_hstring_buffer. + */ + struct TwoPhaseHStringConstructor + { + TwoPhaseHStringConstructor() = delete; + TwoPhaseHStringConstructor(const TwoPhaseHStringConstructor&) = delete; + void operator=(const TwoPhaseHStringConstructor&) = delete; + + TwoPhaseHStringConstructor(TwoPhaseHStringConstructor&& other) WI_NOEXCEPT + { + m_characterLength = other.m_characterLength; + other.m_characterLength = 0; + m_maker = wistd::move(other.m_maker); + } + + static TwoPhaseHStringConstructor Preallocate(UINT32 characterLength) + { + return TwoPhaseHStringConstructor{ characterLength }; + } + + //! Returns the HSTRING after it has been populated like Detatch() or release(); be sure to put this in a RAII type to manage its lifetime. + HSTRING Promote() + { + m_characterLength = 0; + return m_maker.release().release(); + } + + ~TwoPhaseHStringConstructor() = default; + + explicit operator PCWSTR() const + { + // This is set by WindowsPromoteStringBuffer() which must be called to + // construct this object via the static method Preallocate(). + return m_maker.buffer(); + } + + //! Returns a pointer for the buffer so it can be populated + wchar_t* Get() const { return const_cast(m_maker.buffer()); } + //! Used to validate range of buffer when populating. + ULONG ByteSize() const { return m_characterLength * sizeof(wchar_t); } + + /** Ensure that the size of the data provided is consistent with the pre-allocated buffer. + It seems that WindowsPreallocateStringBuffer() provides the null terminator in the buffer + (based on testing) so this can be called before populating the buffer. + */ + HRESULT Validate(ULONG bytesRead) const + { + // Null termination is required for the buffer before calling WindowsPromoteStringBuffer(). + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), + (bytesRead != ByteSize()) || + (Get()[m_characterLength] != L'\0')); + return S_OK; + } + + private: + TwoPhaseHStringConstructor(UINT32 characterLength) : m_characterLength(characterLength) + { + (void)m_maker.make(nullptr, characterLength); + } + + UINT32 m_characterLength; + details::string_maker m_maker; + }; + + //! A transparent less-than comparison function object that enables comparison of various string types intended for + //! use with associative containers (such as `std::set`, `std::map`, etc.) that use + //! `Microsoft::WRL::Wrappers::HString` as the key type. This removes the need for the consumer to explicitly + //! create an `HString` object when using lookup functions such as `find`, `lower_bound`, etc. For example, the + //! following scenarios would all work exactly as you would expect them to: + //! ~~~ + //! std::map map; + //! const wchar_t constArray[] = L"foo"; + //! wchar_t nonConstArray[MAX_PATH] = L"foo"; + //! + //! HString key; + //! THROW_IF_FAILED(key.Set(constArray)); + //! map.emplace(std::move(key), 42); + //! + //! HString str; + //! wil::unique_hstring uniqueStr; + //! THROW_IF_FAILED(str.Set(L"foo")); + //! THROW_IF_FAILED(str.CopyTo(&uniqueStr)); + //! + //! // All of the following return an iterator to the pair { L"foo", 42 } + //! map.find(str); + //! map.find(str.Get()); + //! map.find(HStringReference(constArray)); + //! map.find(uniqueStr); + //! map.find(std::wstring(constArray)); + //! map.find(constArray); + //! map.find(nonConstArray); + //! map.find(static_cast(constArray)); + //! ~~~ + //! The first four calls in the example above use `WindowsGetStringRawBuffer` (or equivalent) to get the string + //! buffer and length for the comparison. The fifth example uses `std::wstring::c_str` and `std::wstring::length` + //! for getting these two values. The remaining three examples use only the string buffer and call `wcslen` for the + //! length. That is, the length is *not* deduced for either array. This is because argument types are not always + //! perfectly preserved by container functions and in fact are often captured as const references making it + //! impossible to differentiate const arrays - where we can safely deduce length - from non const arrays - where we + //! cannot safely deduce length since the buffer may be larger than actually needed (e.g. creating a + //! `char[MAX_PATH]` array, but only filling it with 10 characters). The implications of this behavior is that + //! string literals that contain embedded null characters will only include the part of the buffer up to the first + //! null character. For example, the following example will result in all calls to `find` returning an end + //! iterator. + //! ~~~ + //! std::map map; + //! const wchar_t constArray[] = L"foo\0bar"; + //! wchar_t nonConstArray[MAX_PATH] = L"foo\0bar"; + //! + //! // Create the key with the embedded null character + //! HString key; + //! THROW_IF_FAILED(key.Set(constArray)); + //! map.emplace(std::move(key), 42); + //! + //! // All of the following return map.end() since they look for the string "foo" + //! map.find(constArray); + //! map.find(nonConstArray); + //! map.find(static_cast(constArray)); + //! ~~~ + //! In order to search using a string literal that contains embedded null characters, a simple alternative is to + //! first create an `HStringReference` and use that for the function call: + //! ~~~ + //! // HStringReference's constructor *will* deduce the length of const arrays + //! map.find(HStringReference(constArray)); + //! ~~~ + struct hstring_less + { + using is_transparent = void; + + template + auto operator()(const LhsT& lhs, const RhsT& rhs) const WI_NOEXCEPT -> + decltype(details::hstring_compare::less(lhs, rhs)) + { + return details::hstring_compare::less(lhs, rhs); + } + }; + + //! A transparent less-than comparison function object whose behavior is equivalent to that of @ref hstring_less + //! with the one difference that comparisons are case-insensitive. That is, the following example will correctly + //! find the inserted value: + //! ~~~ + //! std::map map; + //! + //! HString key; + //! THROW_IF_FAILED(key.Set(L"foo")); + //! map.emplace(std::move(key), 42); + //! + //! // All of the following return an iterator to the pair { L"foo", 42 } + //! map.find(L"FOo"); + //! map.find(HStringReference(L"fOo")); + //! map.find(HStringReference(L"fOO").Get()); + //! ~~~ + struct hstring_insensitive_less + { + using is_transparent = void; + + template + auto operator()(const LhsT& lhs, const RhsT& rhs) const WI_NOEXCEPT -> + decltype(details::hstring_compare::less(lhs, rhs)) + { + return details::hstring_compare::less(lhs, rhs); + } + }; + +#pragma endregion + + /// @cond + namespace details + { + // MapToSmartType::type is used to map a raw type into an RAII expression + // of it. This is needed when lifetime management of the type is needed, for example + // when holding them as a value produced in an iterator. + // This type has a common set of methods used to abstract the access to the value + // that is similar to ComPtr<> and the WRL Wrappers: Get(), GetAddressOf() and other operators. + // Clients of the smart type must use those to access the value. + + // TODO: Having the base definition defined will result in creating leaks if a type + // that needs resource management (e.g. PROPVARIANT) that has not specialized is used. + // + // One fix is to use std::is_enum to cover that case and leave the base definition undefined. + // That base should use static_assert to inform clients how to fix the lack of specialization. + template struct MapToSmartType + { + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + struct type // T holder + { + type() {}; + type(T&& value) : m_value(wistd::forward(value)) {}; + operator T() const { return m_value; } + type& operator=(T&& value) { m_value = wistd::forward(value); return *this; } + T Get() const { return m_value; } + + // Returning T&& to support move only types + // In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen + T&& Get() { return wistd::move(m_value); } + + HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; } + T* GetAddressOf() { return &m_value; } + T* ReleaseAndGetAddressOf() { return &m_value; } + T* operator&() { return &m_value; } + T m_value{}; + }; + #pragma warning(pop) + }; + + // IUnknown * derived -> Microsoft::WRL::ComPtr<> + template + struct MapToSmartType::type>::value>::type> + { + typedef Microsoft::WRL::ComPtr::type> type; + }; + + // HSTRING -> Microsoft::WRL::Wrappers::HString + template <> struct MapToSmartType + { + class HStringWithRelease : public Microsoft::WRL::Wrappers::HString + { + public: + // Unlike all other WRL types HString does not have ReleaseAndGetAddressOf and + // GetAddressOf() has non-standard behavior, calling Release(). + HSTRING* ReleaseAndGetAddressOf() WI_NOEXCEPT + { + Release(); + return &hstr_; + } + }; + typedef HStringWithRelease type; + }; + + // WinRT interfaces like IVector<>, IAsyncOperation<> and IIterable<> can be templated + // on a runtime class (instead of an interface or primitive type). In these cases the objects + // produced by those interfaces implement an interface defined by the runtime class default interface. + // + // These templates deduce the type of the produced interface or pass through + // the type unmodified in the non runtime class case. + // + // for example: + // IAsyncOperation -> IAsyncOperation + + // For IVector, IVectorView. + template struct MapVectorResultType + { + template + static TResult PeekGetAtType(HRESULT(STDMETHODCALLTYPE TVector::*)(unsigned, TResult*)); + typedef decltype(PeekGetAtType(&VectorType::GetAt)) type; + }; + + // For IIterator. + template struct MapIteratorResultType + { + template + static TResult PeekCurrentType(HRESULT(STDMETHODCALLTYPE TIterable::*)(TResult*)); + typedef decltype(PeekCurrentType(&ABI::Windows::Foundation::Collections::IIterator::get_Current)) type; + }; + + // For IAsyncOperation. + template struct MapAsyncOpResultType + { + template + static TResult PeekGetResultsType(HRESULT(STDMETHODCALLTYPE TAsyncOperation::*)(TResult*)); + typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperation::GetResults)) type; + }; + + // For IAsyncOperationWithProgress. + template struct MapAsyncOpProgressResultType + { + template + static TResult PeekGetResultsType(HRESULT(STDMETHODCALLTYPE TAsyncOperation::*)(TResult*)); + typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperationWithProgress::GetResults)) type; + }; + + // No support for IAsyncActionWithProgress

none of these (currently) use + // a runtime class for the progress type. + } + /// @endcond +#pragma region C++ iterators for WinRT collections for use with range based for and STL algorithms + + /** Range base for and STL algorithms support for WinRT ABI collection types, IVector, IVectorView, IIterable + similar to support provided by for C++ CX. Three error handling policies are supported. + ~~~ + ComPtr collection = GetCollection(); // can be IVector, IVectorView or IIterable + + for (auto const& element : wil::get_range(collection.Get())) // exceptions + for (auto const& element : wil::get_range_nothrow(collection.Get(), &hr)) // error code + for (auto const& element : wil::get_range_failfast(collection.Get())) // fail fast + { + // use element + } + ~~~ + Standard algorithm example: + ~~~ + ComPtr> files = GetFiles(); + auto fileRange = wil::get_range_nothrow(files.Get()); + auto itFound = std::find_if(fileRange.begin(), fileRange.end(), [](ComPtr file) -> bool + { + return true; // first element in range + }); + ~~~ + */ +#pragma region exception and fail fast based IVector<>/IVectorView<> + + template + class vector_range + { + public: + typedef typename details::MapVectorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + vector_range() = delete; + + explicit vector_range(_In_ VectorType *vector) : m_v(vector) + { + } + + class vector_iterator + { + public: +#ifdef _XUTILITY_ + // could be random_access_iterator_tag but missing some features + typedef ::std::bidirectional_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + // for begin() + vector_iterator(VectorType* v, unsigned int pos) + : m_v(v), m_i(pos) + { + } + + // for end() + vector_iterator() : m_v(nullptr), m_i(-1) {} + + vector_iterator(const vector_iterator& other) + { + m_v = other.m_v; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf())); + } + + vector_iterator& operator=(const vector_iterator& other) + { + if (this != wistd::addressof(other)) + { + m_v = other.m_v; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf())); + } + return *this; + } + + reference operator*() + { + err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf())); + return m_element; + } + + pointer operator->() + { + err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf())); + return wistd::addressof(m_element); + } + + vector_iterator& operator++() + { + ++m_i; + return *this; + } + + vector_iterator& operator--() + { + --m_i; + return *this; + } + + vector_iterator operator++(int) + { + vector_iterator old(*this); + ++*this; + return old; + } + + vector_iterator operator--(int) + { + vector_iterator old(*this); + --*this; + return old; + } + + vector_iterator& operator+=(int n) + { + m_i += n; + return *this; + } + + vector_iterator& operator-=(int n) + { + m_i -= n; + return *this; + } + + vector_iterator operator+(int n) const + { + vector_iterator ret(*this); + ret += n; + return ret; + } + + vector_iterator operator-(int n) const + { + vector_iterator ret(*this); + ret -= n; + return ret; + } + + ptrdiff_t operator-(const vector_iterator& other) const + { + return m_i - other.m_i; + } + + bool operator==(const vector_iterator& other) const + { + return m_i == other.m_i; + } + + bool operator!=(const vector_iterator& other) const + { + return m_i != other.m_i; + } + + bool operator<(const vector_iterator& other) const + { + return m_i < other.m_i; + } + + bool operator>(const vector_iterator& other) const + { + return m_i > other.m_i; + } + + bool operator<=(const vector_iterator& other) const + { + return m_i <= other.m_i; + } + + bool operator>=(const vector_iterator& other) const + { + return m_i >= other.m_i; + } + + private: + VectorType* m_v; // weak, collection must outlive iterators. + unsigned int m_i; + TSmart m_element; + }; + + vector_iterator begin() + { + return vector_iterator(m_v, 0); + } + + vector_iterator end() + { + unsigned int size; + err_policy::HResult(m_v->get_Size(&size)); + return vector_iterator(m_v, size); + } + private: + VectorType* m_v; // weak, collection must outlive iterators. + }; +#pragma endregion + +#pragma region error code based IVector<>/IVectorView<> + + template + class vector_range_nothrow + { + public: + typedef typename details::MapVectorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + vector_range_nothrow() = delete; + vector_range_nothrow(const vector_range_nothrow&) = delete; + vector_range_nothrow& operator=(const vector_range_nothrow&) = delete; + + vector_range_nothrow(vector_range_nothrow&& other) : + m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage), + m_currentElement(wistd::move(other.m_currentElement)) + { + } + + vector_range_nothrow(_In_ VectorType *vector, HRESULT* result = nullptr) + : m_v(vector), m_result(result ? result : &m_resultStorage) + { + *m_result = m_v->get_Size(&m_size); + } + + class vector_iterator_nothrow + { + public: +#ifdef _XUTILITY_ + // must be input_iterator_tag as use (via ++, --, etc.) of one invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + vector_iterator_nothrow() = delete; + vector_iterator_nothrow(vector_range_nothrow* range, unsigned int pos) + : m_range(range), m_i(pos) + { + } + + reference operator*() const + { + return m_range->m_currentElement; + } + + pointer operator->() const + { + return wistd::addressof(m_range->m_currentElement); + } + + vector_iterator_nothrow& operator++() + { + ++m_i; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow& operator--() + { + --m_i; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow operator++(int) + { + vector_iterator_nothrow old(*this); + ++*this; + return old; + } + + vector_iterator_nothrow operator--(int) + { + vector_iterator_nothrow old(*this); + --*this; + return old; + } + + vector_iterator_nothrow& operator+=(int n) + { + m_i += n; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow& operator-=(int n) + { + m_i -= n; + m_range->get_at_current(m_i); + return *this; + } + + bool operator==(vector_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + bool operator!=(vector_iterator_nothrow const& other) const + { + return !operator==(other); + } + + private: + vector_range_nothrow* m_range; + unsigned int m_i = 0; + }; + + vector_iterator_nothrow begin() + { + get_at_current(0); + return vector_iterator_nothrow(this, 0); + } + + vector_iterator_nothrow end() + { + return vector_iterator_nothrow(this, m_size); + } + + // Note, the error code is observed in operator!= and operator==, it always + // returns "equal" in the failed state to force the compare to the end + // iterator to return false and stop the loop. + // + // Is this ok for the general case? + void get_at_current(unsigned int i) + { + if (SUCCEEDED(*m_result) && (i < m_size)) + { + *m_result = m_v->GetAt(i, m_currentElement.ReleaseAndGetAddressOf()); + } + } + + private: + VectorType* m_v; // weak, collection must outlive iterators. + unsigned int m_size; + + // This state is shared by vector_iterator_nothrow instances. this means + // use of one iterator invalidates the other. + HRESULT* m_result; + HRESULT m_resultStorage = S_OK; // for the case where the caller does not provide the location to store the result + TSmart m_currentElement; + }; + +#pragma endregion + +#pragma region exception and fail fast based IIterable<> + + template + class iterable_range + { + public: + typedef typename details::MapIteratorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + explicit iterable_range(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable) + : m_iterable(iterable) + { + } + + class iterable_iterator + { + public: +#ifdef _XUTILITY_ + typedef ::std::forward_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + iterable_iterator() : m_i(-1) {} + + // for begin() + explicit iterable_iterator(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable) + { + err_policy::HResult(iterable->First(&m_iterator)); + boolean hasCurrent; + err_policy::HResult(m_iterator->get_HasCurrent(&hasCurrent)); + m_i = hasCurrent ? 0 : -1; + } + + // for end() + iterable_iterator(int /*currentIndex*/) : m_i(-1) + { + } + + iterable_iterator(const iterable_iterator& other) + { + m_iterator = other.m_iterator; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf())); + } + + iterable_iterator& operator=(const iterable_iterator& other) + { + m_iterator = other.m_iterator; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf())); + return *this; + } + + bool operator==(iterable_iterator const& other) const + { + return m_i == other.m_i; + } + + bool operator!=(iterable_iterator const& other) const + { + return !operator==(other); + } + + reference operator*() + { + err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf())); + return m_element; + } + + pointer operator->() + { + err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf())); + return wistd::addressof(m_element); + } + + iterable_iterator& operator++() + { + boolean hasCurrent; + err_policy::HResult(m_iterator->MoveNext(&hasCurrent)); + if (hasCurrent) + { + m_i++; + } + else + { + m_i = -1; + } + return *this; + } + + iterable_iterator operator++(int) + { + iterable_iterator old(*this); + ++*this; + return old; + } + + private: + Microsoft::WRL::ComPtr> m_iterator; + int m_i; + TSmart m_element; + }; + + iterable_iterator begin() + { + return iterable_iterator(m_iterable); + } + + iterable_iterator end() + { + return iterable_iterator(); + } + private: + // weak, collection must outlive iterators. + ABI::Windows::Foundation::Collections::IIterable* m_iterable; + }; +#pragma endregion + +#pragma region error code base IIterable<> + template + class iterable_range_nothrow + { + public: + typedef typename details::MapIteratorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + iterable_range_nothrow() = delete; + iterable_range_nothrow(const iterable_range_nothrow&) = delete; + iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete; + iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete; + + iterable_range_nothrow(iterable_range_nothrow&& other) : + m_iterator(wistd::move(other.m_iterator)), m_element(wistd::move(other.m_element)), + m_resultStorage(other.m_resultStorage) + { + if (other.m_result == &other.m_resultStorage) + { + m_result = &m_resultStorage; + } + else + { + m_result = other.m_result; + } + } + + iterable_range_nothrow(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable, HRESULT* result = nullptr) + : m_result(result ? result : &m_resultStorage) + { + *m_result = iterable->First(&m_iterator); + if (SUCCEEDED(*m_result)) + { + boolean hasCurrent; + *m_result = m_iterator->get_HasCurrent(&hasCurrent); + if (SUCCEEDED(*m_result) && hasCurrent) + { + *m_result = m_iterator->get_Current(m_element.ReleaseAndGetAddressOf()); + if (FAILED(*m_result)) + { + m_iterator = nullptr; // release the iterator if no elements are found + } + } + else + { + m_iterator = nullptr; // release the iterator if no elements are found + } + } + } + + class iterable_iterator_nothrow + { + public: +#ifdef _XUTILITY_ + // muse be input_iterator_tag as use of one instance invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + iterable_iterator_nothrow(_In_ iterable_range_nothrow* range, int currentIndex) : + m_range(range), m_i(currentIndex) + { + } + + bool operator==(iterable_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + bool operator!=(iterable_iterator_nothrow const& other) const + { + return !operator==(other); + } + + reference operator*() const WI_NOEXCEPT + { + return m_range->m_element; + } + + pointer operator->() const WI_NOEXCEPT + { + return wistd::addressof(m_range->m_element); + } + + iterable_iterator_nothrow& operator++() + { + boolean hasCurrent; + *m_range->m_result = m_range->m_iterator->MoveNext(&hasCurrent); + if (SUCCEEDED(*m_range->m_result) && hasCurrent) + { + *m_range->m_result = m_range->m_iterator->get_Current(m_range->m_element.ReleaseAndGetAddressOf()); + if (SUCCEEDED(*m_range->m_result)) + { + m_i++; + } + else + { + m_i = -1; + } + } + else + { + m_i = -1; + } + return *this; + } + + iterable_range_nothrow operator++(int) + { + iterable_range_nothrow old(*this); + ++*this; + return old; + } + + private: + iterable_range_nothrow* m_range; + int m_i; + }; + + iterable_iterator_nothrow begin() + { + return iterable_iterator_nothrow(this, this->m_iterator ? 0 : -1); + } + + iterable_iterator_nothrow end() + { + return iterable_iterator_nothrow(this, -1); + } + + private: + Microsoft::WRL::ComPtr> m_iterator; + // This state is shared by all iterator instances + // so use of one iterator can invalidate another's ability to dereference + // that is allowed for input iterators. + TSmart m_element; + HRESULT* m_result; + HRESULT m_resultStorage = S_OK; + }; + +#pragma endregion + +#ifdef WIL_ENABLE_EXCEPTIONS + template vector_range> get_range(ABI::Windows::Foundation::Collections::IVector *v) + { + return vector_range>(v); + } + + template vector_range> get_range(ABI::Windows::Foundation::Collections::IVectorView *v) + { + return vector_range>(v); + } +#endif // WIL_ENABLE_EXCEPTIONS + + template vector_range, err_failfast_policy> get_range_failfast(ABI::Windows::Foundation::Collections::IVector *v) + { + return vector_range, err_failfast_policy>(v); + } + + template vector_range, err_failfast_policy> get_range_failfast(ABI::Windows::Foundation::Collections::IVectorView *v) + { + return vector_range, err_failfast_policy>(v); + } + + template vector_range_nothrow> get_range_nothrow(ABI::Windows::Foundation::Collections::IVector *v, HRESULT* result = nullptr) + { + return vector_range_nothrow>(v, result); + } + + template vector_range_nothrow> get_range_nothrow(ABI::Windows::Foundation::Collections::IVectorView *v, HRESULT* result = nullptr) + { + return vector_range_nothrow>(v, result); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template iterable_range get_range(ABI::Windows::Foundation::Collections::IIterable *v) + { + return iterable_range(v); + } +#endif // WIL_ENABLE_EXCEPTIONS + + template iterable_range get_range_failfast(ABI::Windows::Foundation::Collections::IIterable *v) + { + return iterable_range(v); + } + + template iterable_range_nothrow get_range_nothrow(ABI::Windows::Foundation::Collections::IIterable *v, HRESULT* result = nullptr) + { + return iterable_range_nothrow(v, result); + } +} + +#pragma endregion + +#ifdef WIL_ENABLE_EXCEPTIONS + +#pragma region Global operator functions +#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +namespace ABI { +#endif + namespace Windows { + namespace Foundation { + namespace Collections { + template typename wil::vector_range>::vector_iterator begin(IVector* v) + { + return typename wil::vector_range>::vector_iterator(v, 0); + } + + template typename wil::vector_range>::vector_iterator end(IVector* v) + { + unsigned int size; + THROW_IF_FAILED(v->get_Size(&size)); + return typename wil::vector_range>::vector_iterator(v, size); + } + + template typename wil::vector_range>::vector_iterator begin(IVectorView* v) + { + return typename wil::vector_range>::vector_iterator(v, 0); + } + + template typename wil::vector_range>::vector_iterator end(IVectorView* v) + { + unsigned int size; + THROW_IF_FAILED(v->get_Size(&size)); + return typename wil::vector_range>::vector_iterator(v, size); + } + + template typename wil::iterable_range::iterable_iterator begin(IIterable* i) + { + return typename wil::iterable_range::iterable_iterator(i); + } + + template typename wil::iterable_range::iterable_iterator end(IIterable*) + { + return typename wil::iterable_range::iterable_iterator(); + } + } // namespace Collections + } // namespace Foundation + } // namespace Windows +#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +} // namespace ABI +#endif + +#endif // WIL_ENABLE_EXCEPTIONS + +#pragma endregion + +namespace wil +{ +#pragma region WinRT Async API helpers + +/// @cond +namespace details +{ + template ::value, int>::type = 0> + HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args) + { + return wistd::forward(func)(wistd::forward(args)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template ::value, int>::type = 0> + HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args) + { + try + { + wistd::forward(func)(wistd::forward(args)...); + } + CATCH_RETURN(); + return S_OK; + } +#endif + + template + HRESULT CallAndHandleErrors(TFunc&& func, Args&&... args) + { + return CallAndHandleErrorsWithReturnType(func)(wistd::forward(args)...))>( + wistd::forward(func), wistd::forward(args)...); + } + + // Get the last type of a template parameter pack. + // usage: + // LastType::type boolValue; + template struct LastType + { + template struct LastTypeOfTs + { + typedef typename LastTypeOfTs::type type; + }; + + template struct LastTypeOfTs + { + typedef T type; + }; + + template + static typename LastTypeOfTs::type LastTypeOfTsFunc() {} + typedef decltype(LastTypeOfTsFunc()) type; + }; + + // Takes a member function that has an out param like F(..., IAsyncAction**) or F(..., IAsyncOperation**) + // and returns IAsyncAction* or IAsyncOperation*. + template + typename wistd::remove_pointer::type>::type GetReturnParamPointerType(HRESULT(STDMETHODCALLTYPE I::*)(P...)); + + // Use to determine the result type of the async action/operation interfaces or example + // decltype(GetAsyncResultType(action.get())) returns void + void GetAsyncResultType(ABI::Windows::Foundation::IAsyncAction*); + template void GetAsyncResultType(ABI::Windows::Foundation::IAsyncActionWithProgress

*); + template typename wil::details::MapAsyncOpResultType::type GetAsyncResultType(ABI::Windows::Foundation::IAsyncOperation*); + template typename wil::details::MapAsyncOpProgressResultType::type GetAsyncResultType(ABI::Windows::Foundation::IAsyncOperationWithProgress*); + + // Use to determine the result type of the async action/operation interfaces or example + // decltype(GetAsyncDelegateType(action.get())) returns void + ABI::Windows::Foundation::IAsyncActionCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncAction*); + template ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler

* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncActionWithProgress

*); + template ABI::Windows::Foundation::IAsyncOperationCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncOperation*); + template ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncOperationWithProgress*); + + template + HRESULT RunWhenCompleteAction(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + typedef wistd::remove_pointer_t TIDelegate; + + auto callback = Callback, TIDelegate, TBaseAgility>>( + [func = wistd::forward(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT + { + HRESULT hr = S_OK; + if (status != AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully + { + ComPtr asyncInfo; + operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // All must implement IAsyncInfo + asyncInfo->get_ErrorCode(&hr); + } + + return CallAndHandleErrors(func, hr); + }); + RETURN_IF_NULL_ALLOC(callback); + return operation->put_Completed(callback.Get()); + } + + template + HRESULT RunWhenComplete(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Foundation::Internal; + + typedef wistd::remove_pointer_t TIDelegate; + + auto callback = Callback, TIDelegate, TBaseAgility>>( + [func = wistd::forward(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT + { + typename details::MapToSmartType::type::TResult_complex>::type>::type result; + + HRESULT hr = S_OK; + if (status == AsyncStatus::Completed) + { + hr = operation->GetResults(result.GetAddressOf()); + } + else + { + // avoid a potentially costly marshaled QI / call if we completed successfully + ComPtr asyncInfo; + operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this + asyncInfo->get_ErrorCode(&hr); + } + + return CallAndHandleErrors(func, hr, result.Get()); + }); + RETURN_IF_NULL_ALLOC(callback); + return operation->put_Completed(callback.Get()); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + template + HRESULT WaitForCompletion(_In_ TIOperation operation, COWAIT_FLAGS flags, DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT + { + typedef wistd::remove_pointer_t TIDelegate; + + class CompletionDelegate : public Microsoft::WRL::RuntimeClass, + TIDelegate, Microsoft::WRL::FtmBase> + { + public: + HRESULT RuntimeClassInitialize() + { + RETURN_HR(m_completedEventHandle.create()); + } + + HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, AsyncStatus status) override + { + m_status = status; + m_completedEventHandle.SetEvent(); + return S_OK; + } + + HANDLE GetEvent() const + { + return m_completedEventHandle.get(); + } + + AsyncStatus GetStatus() const + { + return m_status; + } + + private: + volatile AsyncStatus m_status = AsyncStatus::Started; + wil::unique_event_nothrow m_completedEventHandle; + }; + + WI_ASSERT(timedOut || (timeoutValue == INFINITE)); + assign_to_opt_param(timedOut, false); + + Microsoft::WRL::ComPtr completedDelegate; + RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&completedDelegate)); + RETURN_IF_FAILED(operation->put_Completed(completedDelegate.Get())); + + HANDLE handles[] = { completedDelegate->GetEvent() }; + DWORD dwHandleIndex; + HRESULT hr = CoWaitForMultipleHandles(flags, timeoutValue, ARRAYSIZE(handles), handles, &dwHandleIndex); + + // If the caller is listening for timedOut, and we actually timed out, set the bool and return S_OK. Otherwise, fail. + if (timedOut && (hr == RPC_S_CALLPENDING)) + { + *timedOut = true; + return S_OK; + } + RETURN_IF_FAILED(hr); + + if (completedDelegate->GetStatus() != AsyncStatus::Completed) + { + Microsoft::WRL::ComPtr asyncInfo; + operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this + hr = E_UNEXPECTED; + asyncInfo->get_ErrorCode(&hr); // error return ignored, ok? + return hr; // leave it to the caller to log failures. + } + return S_OK; + } + + template + HRESULT WaitForCompletion(_In_ TIOperation operation, _Out_ TIResults result, COWAIT_FLAGS flags, + DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT + { + RETURN_IF_FAILED_EXPECTED(details::WaitForCompletion(operation, flags, timeoutValue, timedOut)); + return operation->GetResults(result); + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +} +/// @endcond + +/** Set the completion callback for an async operation to run a caller provided function. +Once complete the function is called with the error code result of the operation +and the async operation result (if applicable). +The function parameter list must be (HRESULT hr) for actions, +(HRESULT hr, IResultInterface* object) for operations that produce interfaces, +and (HRESULT hr, TResult value) for operations that produce value types. +~~~ +run_when_complete(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void +{ + +}); +~~~ +for an agile callback use Microsoft::WRL::FtmBase +~~~ +run_when_complete(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void +{ + +}); +~~~ +Using the non throwing form: +~~~ +hr = run_when_complete_nothrow(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> HRESULT +{ + +}); +~~~ +*/ + +//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile and run on the async thread. +template +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenCompleteAction(operation, wistd::forward(func)); +} + +template::type> +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenComplete(operation, wistd::forward(func)); +} + +template::type> +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenComplete(operation, wistd::forward(func)); +} + +template +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenCompleteAction(operation, wistd::forward(func)); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile and run on the async thread. +template +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenCompleteAction(operation, wistd::forward(func)))); +} + +template::type> +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenComplete(operation, wistd::forward(func)))); +} + +template::type> +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenComplete(operation, wistd::forward(func)))); +} + +template +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenCompleteAction(operation, wistd::forward(func)))); +} +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +/** Wait for an asynchronous operation to complete (or be canceled). +Use to synchronously wait on async operations on background threads. +Do not call from UI threads or STA threads as reentrancy will result. +~~~ +ComPtr> op; +THROW_IF_FAILED(storageFileStatics->GetFileFromPathAsync(HStringReference(path).Get(), &op)); +auto file = wil::wait_for_completion(op.Get()); +~~~ +*/ +template +inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, flags, INFINITE, nullptr); +} + +// These forms return the result from the async operation + +template +HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + _Out_ typename wil::details::MapAsyncOpResultType::type* result, + COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr); +} + +template +HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr); +} + +// Same as above, but allows caller to specify a timeout value. +// On timeout, S_OK is returned, with timedOut set to true. + +template +inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, flags, timeoutValue, timedOut); +} + +template +HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + _Out_ typename wil::details::MapAsyncOpResultType::type* result, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut); +} + +template +HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Wait for an asynchronous operation to complete (or be canceled). +template +inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + THROW_IF_FAILED(details::WaitForCompletion(operation, flags, INFINITE, nullptr)); +} + +template ::type>::type> +TReturn +wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + TReturn result; + THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr)); + return result; +} + +template ::type>::type> +TReturn +wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + TReturn result; + THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr)); + return result; +} + +/** Similar to WaitForCompletion but this function encapsulates the creation of the async operation +making usage simpler. +~~~ +ComPtr launcher; // inited somewhere +auto result = call_and_wait_for_completion(launcher.Get(), &ILauncherStatics::LaunchUriAsync, uri.Get()); +~~~ +*/ +template +auto call_and_wait_for_completion(I* object, HRESULT(STDMETHODCALLTYPE I::*func)(P...), Args&&... args) +{ + Microsoft::WRL::ComPtr::type>::type>::type> op; + THROW_IF_FAILED((object->*func)(wistd::forward(args)..., &op)); + return wil::wait_for_completion(op.Get()); +} +#endif +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +#pragma endregion + +#pragma region WinRT object construction +#ifdef WIL_ENABLE_EXCEPTIONS +//! Get a WinRT activation factory object, usually using a IXXXStatics interface. +template +com_ptr GetActivationFactory(PCWSTR runtimeClass) +{ + com_ptr result; + THROW_IF_FAILED(RoGetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), IID_PPV_ARGS(&result))); + return result; +} + +//! Get a WinRT object. +template +com_ptr ActivateInstance(PCWSTR runtimeClass) +{ + com_ptr result; + THROW_IF_FAILED(RoActivateInstance(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), &result)); + return result.query(); +} +#endif +#pragma endregion + +#pragma region Async production helpers + +/// @cond +namespace details +{ + template + class SyncAsyncOp WrlFinal : public Microsoft::WRL::RuntimeClass, + Microsoft::WRL::AsyncBase>> + { + // typedef typename MapToSmartType::type TSmart; + using RuntimeClassT = typename SyncAsyncOp::RuntimeClassT; + InspectableClass(__super::z_get_rc_name_impl(), TrustLevel::BaseTrust); + public: + HRESULT RuntimeClassInitialize(const TResult& op) + { + m_result = op; + return S_OK; + } + + IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler* competed) override + { + competed->Invoke(this, AsyncStatus::Completed); + return S_OK; + } + + IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler** competed) override + { + *competed = nullptr; + return S_OK; + } + + IFACEMETHODIMP GetResults(TResult* result) override + { + *result = m_result; + return S_OK; + } + + HRESULT OnStart() override { return S_OK; } + void OnClose() override { } + void OnCancel() override { } + private: + // needs to be MapToSmartType::type to hold non trial types + TResult m_result; + }; + + extern const __declspec(selectany) wchar_t SyncAsyncActionName[] = L"SyncActionAction"; + + class SyncAsyncActionOp WrlFinal : public Microsoft::WRL::RuntimeClass +#endif + >> + { + InspectableClass(InterfaceName_Windows_Foundation_IAsyncAction, TrustLevel::BaseTrust); + public: + IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override + { + competed->Invoke(this, AsyncStatus::Completed); + return S_OK; + } + + IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler** competed) override + { + *competed = nullptr; + return S_OK; + } + + IFACEMETHODIMP GetResults() override + { + return S_OK; + } + + HRESULT OnStart() override { return S_OK; } + void OnClose() override { } + void OnCancel() override { } + }; +} + +/// @endcond +//! Creates a WinRT async operation object that implements IAsyncOperation. Use mostly for testing and for mocking APIs. +template +HRESULT make_synchronous_async_operation_nothrow(ABI::Windows::Foundation::IAsyncOperation** result, const TResult& value) +{ + return Microsoft::WRL::MakeAndInitialize>(result, value); +} + +//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs. +inline HRESULT make_synchronous_async_action_nothrow(ABI::Windows::Foundation::IAsyncAction** result) +{ + return Microsoft::WRL::MakeAndInitialize(result); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Creates a WinRT async operation object that implements IAsyncOperation. Use mostly for testing and for mocking APIs. +// TODO: map TRealResult and TSmartResult into SyncAsyncOp. +template ::type, typename TSmartResult = typename details::MapToSmartType::type> +void make_synchronous_async_operation(ABI::Windows::Foundation::IAsyncOperation** result, const TResult& value) +{ + THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize>(result, value))); +} + +//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs. +inline void make_synchronous_async_action(ABI::Windows::Foundation::IAsyncAction** result) +{ + THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize(result))); +} +#endif +#pragma endregion + +#pragma region EventRegistrationToken RAII wrapper + +// unique_winrt_event_token[_cx] is an RAII wrapper around EventRegistrationToken. When the unique_winrt_event_token[_cx] is +// destroyed, the event is automatically unregistered. Declare a wil::unique_winrt_event_token[_cx] at the scope the event +// should be registered for (often they are tied to object lifetime), where T is the type of the event sender +// wil::unique_winrt_event_token_cx m_token; +// +// Macros have been defined to register for handling the event and then returning an unique_winrt_event_token[_cx]. These +// macros simply hide the function references for adding and removing the event. +// C++/CX m_token = WI_MakeUniqueWinRtEventTokenCx(ExampleEventName, sender, handler); +// ABI m_token = WI_MakeUniqueWinRtEventToken(ExampleEventName, sender, handler, &m_token); // Exception and failfast +// ABI RETURN_IF_FAILED(WI_MakeUniqueWinRtEventTokenNoThrow(ExampleEventName, sender, handler, &m_token)); // No throw variant +// +// When the wrapper is destroyed, the handler will be unregistered. You can explicitly unregister the handler prior. +// m_token.reset(); +// +// You can release the EventRegistrationToken from being managed by the wrapper by calling .release() +// m_token.release(); // DANGER: no longer being managed +// +// If you just need the value of the EventRegistrationToken you can call .get() +// m_token.get(); +// +// See "onecore\shell\tests\wil\UniqueWinRTEventTokenTests.cpp" for more examples of usage in ABI and C++/CX. + +#ifdef __cplusplus_winrt +namespace details +{ + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; +} + +template +class unique_winrt_event_token_cx +{ + using removal_func = void(T::*)(Windows::Foundation::EventRegistrationToken); + using static_removal_func = void(__cdecl *)(Windows::Foundation::EventRegistrationToken); + +public: + unique_winrt_event_token_cx() = default; + + unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, T^ sender, removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_weakSender(sender), + m_removalFunction(removalFunction) + {} + + unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, static_removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_staticRemovalFunction(removalFunction) + {} + + unique_winrt_event_token_cx(const unique_winrt_event_token_cx&) = delete; + unique_winrt_event_token_cx& operator=(const unique_winrt_event_token_cx&) = delete; + + unique_winrt_event_token_cx(unique_winrt_event_token_cx&& other) WI_NOEXCEPT : + m_token(other.m_token), + m_weakSender(wistd::move(other.m_weakSender)), + m_removalFunction(other.m_removalFunction), + m_staticRemovalFunction(other.m_staticRemovalFunction) + { + other.m_token = {}; + other.m_weakSender = nullptr; + other.m_removalFunction = nullptr; + other.m_staticRemovalFunction = nullptr; + } + + unique_winrt_event_token_cx& operator=(unique_winrt_event_token_cx&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + + wistd::swap_wil(m_token, other.m_token); + wistd::swap_wil(m_weakSender, other.m_weakSender); + wistd::swap_wil(m_removalFunction, other.m_removalFunction); + wistd::swap_wil(m_staticRemovalFunction, other.m_staticRemovalFunction); + } + + return *this; + } + + ~unique_winrt_event_token_cx() WI_NOEXCEPT + { + reset(); + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_token.Value != 0); + } + + Windows::Foundation::EventRegistrationToken get() const WI_NOEXCEPT + { + return m_token; + } + + void reset() noexcept + { + if (m_token.Value != 0) + { + if (m_staticRemovalFunction) + { + (*m_staticRemovalFunction)(m_token); + } + else + { + auto resolvedSender = m_weakSender.Resolve(); + if (resolvedSender) + { + (resolvedSender->*m_removalFunction)(m_token); + } + } + release(); + } + } + + // Stops the wrapper from managing resource and returns the EventRegistrationToken. + Windows::Foundation::EventRegistrationToken release() WI_NOEXCEPT + { + auto token = m_token; + m_token = {}; + m_weakSender = nullptr; + m_removalFunction = nullptr; + m_staticRemovalFunction = nullptr; + return token; + } + +private: + Windows::Foundation::EventRegistrationToken m_token = {}; + Platform::WeakReference m_weakSender; + removal_func m_removalFunction = nullptr; + static_removal_func m_staticRemovalFunction = nullptr; +}; + +#endif + +template +class unique_winrt_event_token +{ + using removal_func = HRESULT(__stdcall T::*)(::EventRegistrationToken); + +public: + unique_winrt_event_token() = default; + + unique_winrt_event_token(::EventRegistrationToken token, T* sender, removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_removalFunction(removalFunction) + { + m_weakSender = wil::com_weak_query_failfast(sender); + } + + unique_winrt_event_token(const unique_winrt_event_token&) = delete; + unique_winrt_event_token& operator=(const unique_winrt_event_token&) = delete; + + unique_winrt_event_token(unique_winrt_event_token&& other) WI_NOEXCEPT : + m_token(other.m_token), + m_weakSender(wistd::move(other.m_weakSender)), + m_removalFunction(other.m_removalFunction) + { + other.m_token = {}; + other.m_removalFunction = nullptr; + } + + unique_winrt_event_token& operator=(unique_winrt_event_token&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + + wistd::swap_wil(m_token, other.m_token); + wistd::swap_wil(m_weakSender, other.m_weakSender); + wistd::swap_wil(m_removalFunction, other.m_removalFunction); + } + + return *this; + } + + ~unique_winrt_event_token() WI_NOEXCEPT + { + reset(); + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_token.value != 0); + } + + ::EventRegistrationToken get() const WI_NOEXCEPT + { + return m_token; + } + + void reset() WI_NOEXCEPT + { + if (m_token.value != 0) + { + // If T cannot be QI'ed from the weak object then T is not a COM interface. + auto resolvedSender = m_weakSender.try_query(); + if (resolvedSender) + { + FAIL_FAST_IF_FAILED((resolvedSender.get()->*m_removalFunction)(m_token)); + } + release(); + } + } + + // Stops the wrapper from managing resource and returns the EventRegistrationToken. + ::EventRegistrationToken release() WI_NOEXCEPT + { + auto token = m_token; + m_token = {}; + m_weakSender = nullptr; + m_removalFunction = nullptr; + return token; + } + +private: + ::EventRegistrationToken m_token = {}; + wil::com_weak_ref_failfast m_weakSender; + removal_func m_removalFunction = nullptr; +}; + +namespace details +{ +#ifdef __cplusplus_winrt + + // Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token. + // Not intended to be directly called. Use the WI_MakeUniqueWinRtEventTokenCx macro to abstract away specifying the functions that handle addition and removal. + template + inline wil::unique_winrt_event_token_cx make_unique_winrt_event_token_cx(T^ sender, addition_func additionFunc, removal_func removalFunc, handler^ h) + { + auto rawToken = (sender->*additionFunc)(h); + wil::unique_winrt_event_token_cx temp(rawToken, sender, removalFunc); + return temp; + } + + template + inline wil::unique_winrt_event_token_cx make_unique_winrt_static_event_token_cx(addition_func additionFunc, removal_func removalFunc, handler^ h) + { + auto rawToken = (*additionFunc)(h); + wil::unique_winrt_event_token_cx temp(rawToken, removalFunc); + return temp; + } + +#endif // __cplusplus_winrt + + // Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token. + // Not intended to be directly called. Use the WI_MakeUniqueWinRtEventToken macro to abstract away specifying the functions that handle addition and removal. + template + inline auto make_unique_winrt_event_token(T* sender, addition_func additionFunc, removal_func removalFunc, handler h, wil::unique_winrt_event_token* token_reference) + { + ::EventRegistrationToken rawToken; + err_policy::HResult((sender->*additionFunc)(h, &rawToken)); + *token_reference = wil::unique_winrt_event_token(rawToken, sender, removalFunc); + return err_policy::OK(); + } + + // Overload make function to allow for returning the constructed object when not using HRESULT based code. + template + inline typename wistd::enable_if::value, wil::unique_winrt_event_token>::type + make_unique_winrt_event_token(T* sender, addition_func additionFunc, removal_func removalFunc, handler h) + { + ::EventRegistrationToken rawToken; + err_policy::HResult((sender->*additionFunc)(h, &rawToken)); + return wil::unique_winrt_event_token(rawToken, sender, removalFunc); + } + +} // namespace details + +// Helper macros to abstract function names for event addition and removal. +#ifdef __cplusplus_winrt + +#define WI_MakeUniqueWinRtEventTokenCx(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token_cx( \ + _object, \ + &wil::details::remove_reference::type::##_event##::add, \ + &wil::details::remove_reference::type::##_event##::remove, \ + _handler) + +#define WI_MakeUniqueWinRtStaticEventTokenCx(_event, _baseType, _handler) \ + wil::details::make_unique_winrt_static_event_token_cx<_baseType>( \ + &##_baseType##::##_event##::add, \ + &##_baseType##::##_event##::remove, \ + _handler) + +#endif // __cplusplus_winrt + +#ifdef WIL_ENABLE_EXCEPTIONS + +#define WI_MakeUniqueWinRtEventToken(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler) + +#endif // WIL_ENABLE_EXCEPTIONS + +#define WI_MakeUniqueWinRtEventTokenNoThrow(_event, _object, _handler, _token_reference) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler, \ + _token_reference) + +#define WI_MakeUniqueWinRtEventTokenFailFast(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler) + +#pragma endregion // EventRegistrationToken RAII wrapper + +} // namespace wil + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + +template <> +struct ABI::Windows::Foundation::IAsyncOperation : + ABI::Windows::Foundation::IAsyncOperation_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperation*> : + ABI::Windows::Foundation::IAsyncOperation_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress*, P> : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl*, P> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress*,P>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperation*> : + ABI::Windows::Foundation::IAsyncOperation_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress*, Z> : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl*, Z> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress*,Z>"; + } +}; + +template <> +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler*> : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler*, P> : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl*, P> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler*,P>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler*> : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler*, Z> : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl*, Z> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler*,Z>"; + } +}; +#endif // NTDDI_VERSION >= NTDDI_WINBLUE + +#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case +#pragma pop_macro("ABI") +#endif + +#ifdef WIL_ENABLE_EXCEPTIONS + +namespace std +{ + //! Specialization of `std::less` for `Microsoft::WRL::Wrappers::HString` that uses `hstring_less` for the + //! comparison function object. + template <> + struct less : + public wil::hstring_less + { + }; + + //! Specialization of `std::less` for `wil::unique_hstring` that uses `hstring_less` for the comparison function + //! object. + template <> + struct less : + public wil::hstring_less + { + }; +} + +#endif + +#endif // __WIL_WINRT_INCLUDED diff --git a/Externals/WIL/include/wil/wistd_config.h b/Externals/WIL/include/wil/wistd_config.h new file mode 100644 index 0000000000..b408c24814 --- /dev/null +++ b/Externals/WIL/include/wil/wistd_config.h @@ -0,0 +1,548 @@ +// -*- C++ -*- +//===--------------------------- __config ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note +// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind + +#ifndef _WISTD_CONFIG_H_ +#define _WISTD_CONFIG_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include // For size_t and other necessary types + +/// @cond +#if defined(_MSC_VER) && !defined(__clang__) +# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +# endif +#endif + +#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +#pragma GCC system_header +#endif + +#ifdef __GNUC__ +# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__) +// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme +// introduced in GCC 5.0. +# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__) +#else +# define __WI_GNUC_VER 0 +# define __WI_GNUC_VER_NEW 0 +#endif + +// _MSVC_LANG is the more accurate way to get the C++ version in MSVC +#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus) +#define __WI_CPLUSPLUS _MSVC_LANG +#else +#define __WI_CPLUSPLUS __cplusplus +#endif + +#ifndef __WI_LIBCPP_STD_VER +# if __WI_CPLUSPLUS <= 201103L +# define __WI_LIBCPP_STD_VER 11 +# elif __WI_CPLUSPLUS <= 201402L +# define __WI_LIBCPP_STD_VER 14 +# elif __WI_CPLUSPLUS <= 201703L +# define __WI_LIBCPP_STD_VER 17 +# else +# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification +# endif +#endif // __WI_LIBCPP_STD_VER + +#if __WI_CPLUSPLUS < 201103L +#define __WI_LIBCPP_CXX03_LANG +#endif + +#if defined(__ELF__) +# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1 +#elif defined(__MACH__) +# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1 +#elif defined(_WIN32) +# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1 +#elif defined(__wasm__) +# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1 +#else +# error Unknown object file format +#endif + +#if defined(__clang__) +# define __WI_LIBCPP_COMPILER_CLANG +#elif defined(__GNUC__) +# define __WI_LIBCPP_COMPILER_GCC +#elif defined(_MSC_VER) +# define __WI_LIBCPP_COMPILER_MSVC +#elif defined(__IBMCPP__) +# define __WI_LIBCPP_COMPILER_IBM +#endif + +// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as +// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we +// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls +// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC +// so that we don't accidentally use the incorrect behavior +#ifndef __WI_LIBCPP_COMPILER_MSVC + +#ifndef __has_feature +#define __has_feature(__x) 0 +#endif + +// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by +// the compiler and '1' otherwise. +#ifndef __is_identifier +#define __is_identifier(__x) 1 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(__x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(__x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(__x) 0 +#endif + +#if __has_feature(cxx_alignas) +# define __WI_ALIGNAS_TYPE(x) alignas(x) +# define __WI_ALIGNAS(x) alignas(x) +#else +# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x)))) +# define __WI_ALIGNAS(x) __attribute__((__aligned__(x))) +#endif + +#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \ + (!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions +# define __WI_LIBCPP_EXPLICIT explicit +#else +# define __WI_LIBCPP_EXPLICIT +#endif + +#if __has_feature(cxx_attributes) +# define __WI_LIBCPP_NORETURN [[noreturn]] +#else +# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn)) +#endif + +#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS +#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS + +// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other +// NODISCARD macros to the correct attribute. +#if __has_cpp_attribute(nodiscard) +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]] +#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG) +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]] +#else +// We can't use GCC's [[gnu::warn_unused_result]] and +// __attribute__((warn_unused_result)), because GCC does not silence them via +// (void) cast. +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE +#endif + +#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union) +#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class) +#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum) +#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to) +#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty) +#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic) +#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor) +#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions) +#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable) +#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor) +#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept) +#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod) +#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable) +#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial) +#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403) + +#if !(__has_feature(cxx_noexcept)) +#define __WI_LIBCPP_HAS_NO_NOEXCEPT +#endif + +#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700 +#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS +#endif + +#if !(__has_feature(cxx_variadic_templates)) +#define __WI_LIBCPP_HAS_NO_VARIADICS +#endif + +#if __has_feature(is_literal) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T) +#endif + +#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T) +#endif + +#if __has_feature(is_final) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_HAS_IS_FINAL +#endif + +#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403 +#define __WI_LIBCPP_HAS_IS_BASE_OF +#endif + +#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001) +#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE +#endif + +#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI) +#define __WI_LIBCPP_NO_RTTI +#endif + +#if !(__has_feature(cxx_variable_templates)) +#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES +#endif + +#if !(__has_feature(cxx_relaxed_constexpr)) +#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR +#endif + +#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700 +#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF +#endif + +#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC) +# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi"))) +#else +# define __WI_LIBCPP_NO_CFI +#endif + +#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__)) + +#if __has_attribute(internal_linkage) +# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage)) +#else +# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE +#endif + +#else + +// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need +// to be updated to contain the proper _MSC_VER check +#define __WI_ALIGNAS_TYPE(x) alignas(x) +#define __WI_ALIGNAS(x) alignas(x) +#define __alignof__ __alignof + +#define __WI_LIBCPP_EXPLICIT explicit +#define __WI_LIBCPP_NORETURN [[noreturn]] +#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495)) +#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439)) + + +#if __WI_LIBCPP_STD_VER > 14 +#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]] +#else +#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_ +#endif + +#define __WI_HAS_FEATURE_IS_UNION 1 +#define __WI_HAS_FEATURE_IS_CLASS 1 +#define __WI_HAS_FEATURE_IS_ENUM 1 +#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1 +#define __WI_HAS_FEATURE_IS_EMPTY 1 +#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1 +#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1 +#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1 +#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1 +#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1 +#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1 +#define __WI_HAS_FEATURE_NOEXCEPT 1 +#define __WI_HAS_FEATURE_IS_POD 1 +#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIAL 1 +#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1 +#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1 + +#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI) +#define __WI_LIBCPP_NO_RTTI +#endif + +#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T) +#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T) +#define __WI_LIBCPP_HAS_IS_FINAL +#define __WI_LIBCPP_HAS_IS_BASE_OF + +#if __WI_LIBCPP_STD_VER < 14 +#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES +#endif + +#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF +#define __WI_LIBCPP_NO_CFI + +#define __WI_LIBCPP_ALWAYS_INLINE __forceinline +#define __WI_LIBCPP_INTERNAL_LINKAGE + +#endif + +#ifndef _WIN32 + +#ifdef __LITTLE_ENDIAN__ +# if __LITTLE_ENDIAN__ +# define __WI_LIBCPP_LITTLE_ENDIAN +# endif // __LITTLE_ENDIAN__ +#endif // __LITTLE_ENDIAN__ + +#ifdef __BIG_ENDIAN__ +# if __BIG_ENDIAN__ +# define __WI_LIBCPP_BIG_ENDIAN +# endif // __BIG_ENDIAN__ +#endif // __BIG_ENDIAN__ + +#ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __WI_LIBCPP_LITTLE_ENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define __WI_LIBCPP_BIG_ENDIAN +# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#endif // __BYTE_ORDER__ + +#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN) +# include +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define __WI_LIBCPP_LITTLE_ENDIAN +# elif __BYTE_ORDER == __BIG_ENDIAN +# define __WI_LIBCPP_BIG_ENDIAN +# else // __BYTE_ORDER == __BIG_ENDIAN +# error unable to determine endian +# endif +#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN) + +#else // _WIN32 + +#define __WI_LIBCPP_LITTLE_ENDIAN + +#endif // _WIN32 + +#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR +# define __WI_LIBCPP_CONSTEXPR +#else +# define __WI_LIBCPP_CONSTEXPR constexpr +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 +#endif + +#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 +#endif + +#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 +#endif + +#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \ + (__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD)) +# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE +#else +# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 +#endif + +#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L) +# define __WI_LIBCPP_INLINE_VAR inline +#else +# define __WI_LIBCPP_INLINE_VAR +#endif + +#ifdef __WI_LIBCPP_CXX03_LANG +#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS +#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES +#endif + +#ifndef __SIZEOF_INT128__ +#define __WI_LIBCPP_HAS_NO_INT128 +#endif + +#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) +#define __WI_LIBCPP_HAS_NO_NOEXCEPT +#endif + +#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT +# define WI_NOEXCEPT noexcept +# define __WI_NOEXCEPT_(x) noexcept(x) +#else +# define WI_NOEXCEPT throw() +# define __WI_NOEXCEPT_(x) +#endif + +#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) +#define __WI_LIBCPP_HIDDEN +#define __WI_LIBCPP_TEMPLATE_VIS +#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + +#ifndef __WI_LIBCPP_HIDDEN +# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) +# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden"))) +# else +# define __WI_LIBCPP_HIDDEN +# endif +#endif + +#ifndef __WI_LIBCPP_TEMPLATE_VIS +# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC) +# if __has_attribute(__type_visibility__) +# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default"))) +# else +# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default"))) +# endif +# else +# define __WI_LIBCPP_TEMPLATE_VIS +# endif +#endif + +#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE + +namespace wistd // ("Windows Implementation" std) +{ + typedef decltype(__nullptr) nullptr_t; + + template + struct __less + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;} + }; + + template + struct __less<_T1, _T1> + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + template + struct __less + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + template + struct __less<_T1, const _T1> + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + // These are added to wistd to enable use of min/max without having to use the windows.h min/max + // macros that some clients might not have access to. Note: the STL versions of these have debug + // checking for the less than operator and support for iterators that these implementations lack. + // Use the STL versions when you require use of those features. + + // min + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (min)(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + return __comp(__b, __a) ? __b : __a; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (min)(const _Tp& __a, const _Tp& __b) + { + return (min)(__a, __b, __less<_Tp>()); + } + + // max + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (max)(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + return __comp(__a, __b) ? __b : __a; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (max)(const _Tp& __a, const _Tp& __b) + { + return (max)(__a, __b, __less<_Tp>()); + } + + template + struct __WI_LIBCPP_TEMPLATE_VIS unary_function + { + typedef _Arg argument_type; + typedef _Result result_type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS binary_function + { + typedef _Arg1 first_argument_type; + typedef _Arg2 second_argument_type; + typedef _Result result_type; + }; +} +/// @endcond + +#endif _WISTD_CONFIG_H_ diff --git a/Externals/WIL/include/wil/wistd_functional.h b/Externals/WIL/include/wil/wistd_functional.h new file mode 100644 index 0000000000..f664385e64 --- /dev/null +++ b/Externals/WIL/include/wil/wistd_functional.h @@ -0,0 +1,543 @@ +// -*- C++ -*- +//===------------------------ functional ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_FUNCTIONAL_H_ +#define _WISTD_FUNCTIONAL_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_memory.h" +#include // For __fastfail +#include // For placement new + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#pragma warning(push) +#pragma warning(disable: 4324) + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + // wistd::function + // + // All of the code below is in direct support of wistd::function. This class is identical to std::function + // with the following exceptions: + // + // 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported) + // 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit) + // 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation) + + template + struct __invoke_void_return_wrapper + { +#ifndef __WI_LIBCPP_CXX03_LANG + template + static _Ret __call(_Args&&... __args) { + return __invoke(wistd::forward<_Args>(__args)...); + } +#else + template + static _Ret __call(_Fn __f) { + return __invoke(__f); + } + + template + static _Ret __call(_Fn __f, _A0& __a0) { + return __invoke(__f, __a0); + } + + template + static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) { + return __invoke(__f, __a0, __a1); + } + + template + static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){ + return __invoke(__f, __a0, __a1, __a2); + } +#endif + }; + + template <> + struct __invoke_void_return_wrapper + { +#ifndef __WI_LIBCPP_CXX03_LANG + template + static void __call(_Args&&... __args) { + (void)__invoke(wistd::forward<_Args>(__args)...); + } +#else + template + static void __call(_Fn __f) { + __invoke(__f); + } + + template + static void __call(_Fn __f, _A0& __a0) { + __invoke(__f, __a0); + } + + template + static void __call(_Fn __f, _A0& __a0, _A1& __a1) { + __invoke(__f, __a0, __a1); + } + + template + static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) { + __invoke(__f, __a0, __a1, __a2); + } +#endif + }; + + //////////////////////////////////////////////////////////////////////////////// + // FUNCTION + //============================================================================== + + // bad_function_call + + __WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY + void __throw_bad_function_call() + { + __fastfail(7); // FAST_FAIL_FATAL_APP_EXIT + } + + template class __WI_LIBCPP_TEMPLATE_VIS function; // undefined + + namespace __function + { + + template + struct __maybe_derive_from_unary_function + { + }; + + template + struct __maybe_derive_from_unary_function<_Rp(_A1)> + : public unary_function<_A1, _Rp> + { + }; + + template + struct __maybe_derive_from_binary_function + { + }; + + template + struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)> + : public binary_function<_A1, _A2, _Rp> + { + }; + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Fp const&) { return true; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Fp* __ptr) { return __ptr; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Ret _Class::*__ptr) { return __ptr; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(function<_Fp> const& __f) { return !!__f; } + + } // namespace __function + +#ifndef __WI_LIBCPP_CXX03_LANG + + namespace __function { + + template class __base; + + template + class __base<_Rp(_ArgTypes...)> + { + __base(const __base&); + __base& operator=(const __base&); + public: + __WI_LIBCPP_INLINE_VISIBILITY __base() {} + __WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {} + virtual void __clone(__base*) const = 0; + virtual void __move(__base*) = 0; + virtual void destroy() WI_NOEXCEPT = 0; + virtual _Rp operator()(_ArgTypes&& ...) = 0; + }; + + template class __func; + + template + class __func<_Fp, _Rp(_ArgTypes...)> + : public __base<_Rp(_ArgTypes...)> + { + _Fp __f_; + public: + __WI_LIBCPP_INLINE_VISIBILITY + explicit __func(_Fp&& __f) + : __f_(wistd::move(__f)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + explicit __func(const _Fp& __f) + : __f_(__f) {} + + virtual void __clone(__base<_Rp(_ArgTypes...)>*) const; + virtual void __move(__base<_Rp(_ArgTypes...)>*); + virtual void destroy() WI_NOEXCEPT; + virtual _Rp operator()(_ArgTypes&& ... __arg); + }; + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const + { + ::new (__p) __func(__f_); + } + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p) + { + ::new (__p) __func(wistd::move(__f_)); + } + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT + { + __f_.~_Fp(); + } + + template + _Rp + __func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg) + { + typedef __invoke_void_return_wrapper<_Rp> _Invoker; + return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...); + } + + } // __function + + template + class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> + : public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>, + public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)> + { + // 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects + // that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12 + // pointers (__base vtable takes an additional one). + static constexpr size_t __buffer_size = 13 * sizeof(void*); + + typedef __function::__base<_Rp(_ArgTypes...)> __base; + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + typename aligned_storage<__buffer_size>::type __buf_; + __base* __f_; + + __WI_LIBCPP_NO_CFI static __base *__as_base(void *p) { + return reinterpret_cast<__base*>(p); + } + + template + struct __callable_imp + { + static const bool value = is_same::value || + is_convertible::type, + _Rp>::value; + }; + + template + struct __callable_imp<_Fp, false> + { + static const bool value = false; + }; + + template + struct __callable + { + static const bool value = __callable_imp<_Fp, __lazy_and< + integral_constant, function>::value>, + __invokable<_Fp&, _ArgTypes...> + >::value>::value; + }; + + template + using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; + public: + typedef _Rp result_type; + + // construct/copy/destroy: + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function() WI_NOEXCEPT : __f_(0) {} + __WI_LIBCPP_INLINE_VISIBILITY + function(nullptr_t) WI_NOEXCEPT : __f_(0) {} + function(const function&); + function(function&&); + template> + function(_Fp); + + function& operator=(const function&); + function& operator=(function&&); + function& operator=(nullptr_t) WI_NOEXCEPT; + template> + function& operator=(_Fp&&); + + ~function(); + + // function modifiers: + void swap(function&); + + // function capacity: + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;} + + // deleted overloads close possible hole in the type system + template + bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete; + template + bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete; + public: + // function invocation: + _Rp operator()(_ArgTypes...) const; + + // NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than + // 'std' so all functions requiring RTTI have been removed + }; + + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(const function& __f) + { + if (__f.__f_ == 0) + __f_ = 0; + else + { + __f_ = __as_base(&__buf_); + __f.__f_->__clone(__f_); + } + } + + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(function&& __f) + { + if (__f.__f_ == 0) + __f_ = 0; + else + { + __f_ = __as_base(&__buf_); + __f.__f_->__move(__f_); + __f.__f_->destroy(); + __f.__f_ = 0; + } + } + + template + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(_Fp __f) + : __f_(0) + { + if (__function::__not_null(__f)) + { + typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF; + static_assert(sizeof(_FF) <= sizeof(__buf_), + "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); + __f_ = ::new((void*)&__buf_) _FF(wistd::move(__f)); + } + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(const function& __f) + { + *this = nullptr; + if (__f.__f_) + { + __f_ = __as_base(&__buf_); + __f.__f_->__clone(__f_); + } + return *this; + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(function&& __f) + { + *this = nullptr; + if (__f.__f_) + { + __f_ = __as_base(&__buf_); + __f.__f_->__move(__f_); + __f.__f_->destroy(); + __f.__f_ = 0; + } + return *this; + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT + { + __base* __t = __f_; + __f_ = 0; + if (__t) + __t->destroy(); + return *this; + } + + template + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) + { + *this = nullptr; + if (__function::__not_null(__f)) + { + typedef __function::__func::type, _Rp(_ArgTypes...)> _FF; + static_assert(sizeof(_FF) <= sizeof(__buf_), + "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); + __f_ = ::new((void*)&__buf_) _FF(wistd::move(__f)); + } + + return *this; + } + + template + function<_Rp(_ArgTypes...)>::~function() + { + if (__f_) + __f_->destroy(); + } + + template + void + function<_Rp(_ArgTypes...)>::swap(function& __f) + { + if (wistd::addressof(__f) == this) + return; + if (__f_ && __f.__f_) + { + typename aligned_storage::type __tempbuf; + __base* __t = __as_base(&__tempbuf); + __f_->__move(__t); + __f_->destroy(); + __f_ = 0; + __f.__f_->__move(__as_base(&__buf_)); + __f.__f_->destroy(); + __f.__f_ = 0; + __f_ = __as_base(&__buf_); + __t->__move(__as_base(&__f.__buf_)); + __t->destroy(); + __f.__f_ = __as_base(&__f.__buf_); + } + else if (__f_) + { + __f_->__move(__as_base(&__f.__buf_)); + __f_->destroy(); + __f_ = 0; + __f.__f_ = __as_base(&__f.__buf_); + } + else if (__f.__f_) + { + __f.__f_->__move(__as_base(&__buf_)); + __f.__f_->destroy(); + __f.__f_ = 0; + __f_ = __as_base(&__buf_); + } + } + + template + _Rp + function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const + { + if (__f_ == 0) + __throw_bad_function_call(); + return (*__f_)(wistd::forward<_ArgTypes>(__arg)...); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;} + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) + {return __x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) + {return __x.swap(__y);} + + // std::invoke + template + typename __invoke_of<_Fn, _Args...>::type + invoke(_Fn&& __f, _Args&&... __args) + __WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value)) + { + return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...); + } + +#else // __WI_LIBCPP_CXX03_LANG + +#error wistd::function and wistd::invoke not implemented for pre-C++11 + +#endif +} +/// @endcond + +#pragma warning(pop) + +#endif // _WISTD_FUNCTIONAL_H_ diff --git a/Externals/WIL/include/wil/wistd_memory.h b/Externals/WIL/include/wil/wistd_memory.h new file mode 100644 index 0000000000..48031c5760 --- /dev/null +++ b/Externals/WIL/include/wil/wistd_memory.h @@ -0,0 +1,1038 @@ +// -*- C++ -*- +//===-------------------------- memory ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_MEMORY_H_ +#define _WISTD_MEMORY_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_type_traits.h" + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + // allocator_traits + + template + struct __has_pointer_type : false_type {}; + + template + struct __has_pointer_type<_Tp, + typename __void_t::type> : true_type {}; + + namespace __pointer_type_imp + { + + template ::value> + struct __pointer_type + { + typedef typename _Dp::pointer type; + }; + + template + struct __pointer_type<_Tp, _Dp, false> + { + typedef _Tp* type; + }; + + } // __pointer_type_imp + + template + struct __pointer_type + { + typedef typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type type; + }; + + template ::value && !__libcpp_is_final<_Tp>::value> + struct __compressed_pair_elem { + typedef _Tp _ParamT; + typedef _Tp& reference; + typedef const _Tp& const_reference; + +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {} + + template ::type>::value + >::type> + __WI_LIBCPP_INLINE_VISIBILITY + constexpr explicit + __compressed_pair_elem(_Up&& __u) + : __value_(wistd::forward<_Up>(__u)) + { + } + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY __compressed_pair_elem() : __value_() {} + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair_elem(_ParamT __p) : __value_(wistd::forward<_ParamT>(__p)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY reference __get() WI_NOEXCEPT { return __value_; } + __WI_LIBCPP_INLINE_VISIBILITY + const_reference __get() const WI_NOEXCEPT { return __value_; } + + private: + _Tp __value_; + }; + + template + struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp { + typedef _Tp _ParamT; + typedef _Tp& reference; + typedef const _Tp& const_reference; + typedef _Tp __value_type; + +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default; + + template ::type>::value + >::type> + __WI_LIBCPP_INLINE_VISIBILITY + constexpr explicit + __compressed_pair_elem(_Up&& __u) + : __value_type(wistd::forward<_Up>(__u)) + {} + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY __compressed_pair_elem() : __value_type() {} + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair_elem(_ParamT __p) + : __value_type(wistd::forward<_ParamT>(__p)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY reference __get() WI_NOEXCEPT { return *this; } + __WI_LIBCPP_INLINE_VISIBILITY + const_reference __get() const WI_NOEXCEPT { return *this; } + }; + + // Tag used to construct the second element of the compressed pair. + struct __second_tag {}; + + template + class __compressed_pair : private __compressed_pair_elem<_T1, 0>, + private __compressed_pair_elem<_T2, 1> { + typedef __compressed_pair_elem<_T1, 0> _Base1; + typedef __compressed_pair_elem<_T2, 1> _Base2; + + // NOTE: This static assert should never fire because __compressed_pair + // is *almost never* used in a scenario where it's possible for T1 == T2. + // (The exception is wistd::function where it is possible that the function + // object and the allocator have the same type). + static_assert((!is_same<_T1, _T2>::value), + "__compressed_pair cannot be instantated when T1 and T2 are the same type; " + "The current implementation is NOT ABI-compatible with the previous " + "implementation for this configuration"); + + public: +#ifndef __WI_LIBCPP_CXX03_LANG + template , _Dummy>::value && + __dependent_type, _Dummy>::value + >::type + > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr __compressed_pair() {} + + template ::type, + __compressed_pair>::value, + bool>::type = true> + __WI_LIBCPP_INLINE_VISIBILITY constexpr explicit + __compressed_pair(_Tp&& __t) + : _Base1(wistd::forward<_Tp>(__t)), _Base2() {} + + template + __WI_LIBCPP_INLINE_VISIBILITY constexpr + __compressed_pair(__second_tag, _Tp&& __t) + : _Base1(), _Base2(wistd::forward<_Tp>(__t)) {} + + template + __WI_LIBCPP_INLINE_VISIBILITY constexpr + __compressed_pair(_U1&& __t1, _U2&& __t2) + : _Base1(wistd::forward<_U1>(__t1)), _Base2(wistd::forward<_U2>(__t2)) {} + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair() {} + + __WI_LIBCPP_INLINE_VISIBILITY explicit + __compressed_pair(_T1 __t1) : _Base1(wistd::forward<_T1>(__t1)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair(__second_tag, _T2 __t2) + : _Base1(), _Base2(wistd::forward<_T2>(__t2)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair(_T1 __t1, _T2 __t2) + : _Base1(wistd::forward<_T1>(__t1)), _Base2(wistd::forward<_T2>(__t2)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base1::reference first() WI_NOEXCEPT { + return static_cast<_Base1&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base1::const_reference first() const WI_NOEXCEPT { + return static_cast<_Base1 const&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base2::reference second() WI_NOEXCEPT { + return static_cast<_Base2&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base2::const_reference second() const WI_NOEXCEPT { + return static_cast<_Base2 const&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(__compressed_pair& __x) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) + { + using wistd::swap_wil; + swap_wil(first(), __x.first()); + swap_wil(second(), __x.second()); + } + }; + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void swap(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) { + __x.swap(__y); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void swap_wil(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) { + __x.swap(__y); + } + + // default_delete + + template + struct __WI_LIBCPP_TEMPLATE_VIS default_delete { + static_assert(!is_function<_Tp>::value, + "default_delete cannot be instantiated for function types"); +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr default_delete() WI_NOEXCEPT = default; +#else + __WI_LIBCPP_INLINE_VISIBILITY default_delete() {} +#endif + template + __WI_LIBCPP_INLINE_VISIBILITY + default_delete(const default_delete<_Up>&, + typename enable_if::value>::type* = + 0) WI_NOEXCEPT {} + + __WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT { + static_assert(sizeof(_Tp) > 0, + "default_delete can not delete incomplete type"); + static_assert(!is_void<_Tp>::value, + "default_delete can not delete incomplete type"); + delete __ptr; + } + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS default_delete<_Tp[]> { + private: + template + struct _EnableIfConvertible + : enable_if::value> {}; + + public: +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr default_delete() WI_NOEXCEPT = default; +#else + __WI_LIBCPP_INLINE_VISIBILITY default_delete() {} +#endif + + template + __WI_LIBCPP_INLINE_VISIBILITY + default_delete(const default_delete<_Up[]>&, + typename _EnableIfConvertible<_Up>::type* = 0) WI_NOEXCEPT {} + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename _EnableIfConvertible<_Up>::type + operator()(_Up* __ptr) const WI_NOEXCEPT { + static_assert(sizeof(_Tp) > 0, + "default_delete can not delete incomplete type"); + static_assert(!is_void<_Tp>::value, + "default_delete can not delete void type"); + delete[] __ptr; + } + }; + + + +#ifndef __WI_LIBCPP_CXX03_LANG + template + struct __unique_ptr_deleter_sfinae { + static_assert(!is_reference<_Deleter>::value, "incorrect specialization"); + typedef const _Deleter& __lval_ref_type; + typedef _Deleter&& __good_rval_ref_type; + typedef true_type __enable_rval_overload; + }; + + template + struct __unique_ptr_deleter_sfinae<_Deleter const&> { + typedef const _Deleter& __lval_ref_type; + typedef const _Deleter&& __bad_rval_ref_type; + typedef false_type __enable_rval_overload; + }; + + template + struct __unique_ptr_deleter_sfinae<_Deleter&> { + typedef _Deleter& __lval_ref_type; + typedef _Deleter&& __bad_rval_ref_type; + typedef false_type __enable_rval_overload; + }; +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + template > + class __WI_LIBCPP_TEMPLATE_VIS unique_ptr { + public: + typedef _Tp element_type; + typedef _Dp deleter_type; + typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + + static_assert(!is_rvalue_reference::value, + "the specified deleter type cannot be an rvalue reference"); + + private: + __compressed_pair __ptr_; + + struct __nat { int __for_bool_; }; + +#ifndef __WI_LIBCPP_CXX03_LANG + typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + + template + using _LValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type; + + template + using _GoodRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type; + + template + using _BadRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type; + + template , _Dummy>::type> + using _EnableIfDeleterDefaultConstructible = + typename enable_if::value && + !is_pointer<_Deleter>::value>::type; + + template + using _EnableIfDeleterConstructible = + typename enable_if::value>::type; + + template + using _EnableIfMoveConvertible = typename enable_if< + is_convertible::value && + !is_array<_Up>::value + >::type; + + template + using _EnableIfDeleterConvertible = typename enable_if< + (is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) || + (!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value) + >::type; + + template + using _EnableIfDeleterAssignable = typename enable_if< + is_assignable<_Dp&, _UDel&&>::value + >::type; + + public: + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) WI_NOEXCEPT : __ptr_(__p) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, __d) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _BadRValRefType<_Dummy> __d) = delete; + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward(__u.get_deleter())) { + } + + template , _Up>, + class = _EnableIfDeleterConvertible<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward<_Ep>(__u.get_deleter())) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward(__u.get_deleter()); + return *this; + } + + template , _Up>, + class = _EnableIfDeleterAssignable<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + +#else // __WI_LIBCPP_CXX03_LANG + private: + unique_ptr(unique_ptr&); + template unique_ptr(unique_ptr<_Up, _Ep>&); + + unique_ptr& operator=(unique_ptr&); + template unique_ptr& operator=(unique_ptr<_Up, _Ep>&); + + public: + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr() : __ptr_(pointer()) + { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + static_assert(is_default_constructible::value, + "unique_ptr::deleter_type is not default constructible"); + } + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t) : __ptr_(pointer()) + { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) + : __ptr_(wistd::move(__p)) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + operator __rv() { + return __rv(*this); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(__rv __u) + : __ptr_(__u->release(), + wistd::forward(__u->get_deleter())) {} + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + !is_array<_Up>::value && + is_convertible::pointer, + pointer>::value && + is_assignable::value, + unique_ptr&>::type + operator=(unique_ptr<_Up, _Ep> __u) { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, deleter_type __d) + : __ptr_(wistd::move(__p), wistd::move(__d)) {} +#endif // __WI_LIBCPP_CXX03_LANG + + __WI_LIBCPP_INLINE_VISIBILITY + ~unique_ptr() { reset(); } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(nullptr_t) WI_NOEXCEPT { + reset(); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename add_lvalue_reference<_Tp>::type + operator*() const { + return *__ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer operator->() const WI_NOEXCEPT { + return __ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer get() const WI_NOEXCEPT { + return __ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + deleter_type& get_deleter() WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + const deleter_type& get_deleter() const WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT { + return __ptr_.first() != nullptr; + } + + __WI_LIBCPP_INLINE_VISIBILITY + pointer release() WI_NOEXCEPT { + pointer __t = __ptr_.first(); + __ptr_.first() = pointer(); + return __t; + } + + __WI_LIBCPP_INLINE_VISIBILITY + void reset(pointer __p = pointer()) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = __p; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(unique_ptr& __u) WI_NOEXCEPT { + __ptr_.swap(__u.__ptr_); + } + }; + + + template + class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> { + public: + typedef _Tp element_type; + typedef _Dp deleter_type; + typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + + private: + __compressed_pair __ptr_; + + template + struct _CheckArrayPointerConversion : is_same<_From, pointer> {}; + + template + struct _CheckArrayPointerConversion<_FromElem*> + : integral_constant::value || + (is_same::value && + is_convertible<_FromElem(*)[], element_type(*)[]>::value) + > + {}; + +#ifndef __WI_LIBCPP_CXX03_LANG + typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + + template + using _LValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type; + + template + using _GoodRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type; + + template + using _BadRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type; + + template , _Dummy>::type> + using _EnableIfDeleterDefaultConstructible = + typename enable_if::value && + !is_pointer<_Deleter>::value>::type; + + template + using _EnableIfDeleterConstructible = + typename enable_if::value>::type; + + template + using _EnableIfPointerConvertible = typename enable_if< + _CheckArrayPointerConversion<_Pp>::value + >::type; + + template + using _EnableIfMoveConvertible = typename enable_if< + is_array<_Up>::value && + is_same::value && + is_same::value && + is_convertible<_ElemT(*)[], element_type(*)[]>::value + >::type; + + template + using _EnableIfDeleterConvertible = typename enable_if< + (is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) || + (!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value) + >::type; + + template + using _EnableIfDeleterAssignable = typename enable_if< + is_assignable<_Dp&, _UDel&&>::value + >::type; + + public: + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer()) {} + + template , + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(_Pp __p) WI_NOEXCEPT + : __ptr_(__p) {} + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, __d) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(nullptr, __d) {} + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(nullptr, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _BadRValRefType<_Dummy> __d) = delete; + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward(__u.get_deleter())) { + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward(__u.get_deleter()); + return *this; + } + + template , _Up>, + class = _EnableIfDeleterConvertible<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward<_Ep>(__u.get_deleter())) { + } + + template , _Up>, + class = _EnableIfDeleterAssignable<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& + operator=(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + +#else // __WI_LIBCPP_CXX03_LANG + private: + template explicit unique_ptr(_Up); + + unique_ptr(unique_ptr&); + template unique_ptr(unique_ptr<_Up>&); + + unique_ptr& operator=(unique_ptr&); + template unique_ptr& operator=(unique_ptr<_Up>&); + + template + unique_ptr(_Up __u, + typename conditional< + is_reference::value, deleter_type, + typename add_lvalue_reference::type>::type, + typename enable_if::value, + __nat>::type = __nat()); + public: + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr() : __ptr_(pointer()) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t) : __ptr_(pointer()) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) : __ptr_(__p) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, deleter_type __d) + : __ptr_(__p, wistd::forward(__d)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, deleter_type __d) + : __ptr_(pointer(), wistd::forward(__d)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + operator __rv() { + return __rv(*this); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(__rv __u) + : __ptr_(__u->release(), + wistd::forward(__u->get_deleter())) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(__rv __u) { + reset(__u->release()); + __ptr_.second() = wistd::forward(__u->get_deleter()); + return *this; + } + +#endif // __WI_LIBCPP_CXX03_LANG + + public: + __WI_LIBCPP_INLINE_VISIBILITY + ~unique_ptr() { reset(); } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(nullptr_t) WI_NOEXCEPT { + reset(); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename add_lvalue_reference<_Tp>::type + operator[](size_t __i) const { + return __ptr_.first()[__i]; + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer get() const WI_NOEXCEPT { + return __ptr_.first(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + deleter_type& get_deleter() WI_NOEXCEPT { + return __ptr_.second(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + const deleter_type& get_deleter() const WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT { + return __ptr_.first() != nullptr; + } + + __WI_LIBCPP_INLINE_VISIBILITY + pointer release() WI_NOEXCEPT { + pointer __t = __ptr_.first(); + __ptr_.first() = pointer(); + return __t; + } + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + _CheckArrayPointerConversion<_Pp>::value + >::type + reset(_Pp __p) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = __p; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void reset(nullptr_t = nullptr) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = nullptr; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(unique_ptr& __u) WI_NOEXCEPT { + __ptr_.swap(__u.__ptr_); + } + + }; + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Dp>::value, + void + >::type + swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) WI_NOEXCEPT {__x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Dp>::value, + void + >::type + swap_wil(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) WI_NOEXCEPT {__x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return __x.get() == __y.get();} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__x == __y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator< (const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + typedef typename unique_ptr<_T2, _D2>::pointer _P2; + typedef typename common_type<_P1, _P2>::type _Vp; + return less<_Vp>()(__x.get(), __y.get()); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator> (const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return __y < __x;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__y < __x);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__x < __y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT + { + return !__x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT + { + return !__x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT + { + return static_cast(__x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT + { + return static_cast(__x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(__x.get(), nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(nullptr, __x.get()); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return nullptr < __x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return __x < nullptr; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return !(nullptr < __x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return !(__x < nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return !(__x < nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return !(nullptr < __x); + } + +#ifdef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr<_Tp, _Dp> + move(unique_ptr<_Tp, _Dp>& __t) + { + return unique_ptr<_Tp, _Dp>(__rv >(__t)); + } + +#endif +} +/// @endcond + +#endif // _WISTD_MEMORY_H_ diff --git a/Externals/WIL/include/wil/wistd_type_traits.h b/Externals/WIL/include/wil/wistd_type_traits.h new file mode 100644 index 0000000000..8f757ed64d --- /dev/null +++ b/Externals/WIL/include/wil/wistd_type_traits.h @@ -0,0 +1,4504 @@ +// -*- C++ -*- +//===------------------------ type_traits ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_TYPE_TRAITS_H_ +#define _WISTD_TYPE_TRAITS_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_config.h" + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + template struct __WI_LIBCPP_TEMPLATE_VIS pair; + template class __WI_LIBCPP_TEMPLATE_VIS reference_wrapper; + template struct __WI_LIBCPP_TEMPLATE_VIS hash; + + template + struct __void_t { typedef void type; }; + + template + struct __identity { typedef _Tp type; }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS __dependent_type : public _Tp {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS conditional {typedef _If type;}; + template + struct __WI_LIBCPP_TEMPLATE_VIS conditional {typedef _Then type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using conditional_t = typename conditional<_Bp, _If, _Then>::type; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if {}; + template struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if {typedef typename _Tp::type type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS enable_if {}; + template struct __WI_LIBCPP_TEMPLATE_VIS enable_if {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using enable_if_t = typename enable_if<_Bp, _Tp>::type; +#endif + + // addressof +#ifndef __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF + + template + inline __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 + __WI_LIBCPP_NO_CFI __WI_LIBCPP_INLINE_VISIBILITY + _Tp* + addressof(_Tp& __x) WI_NOEXCEPT + { + return __builtin_addressof(__x); + } + +#else + + template + inline __WI_LIBCPP_NO_CFI __WI_LIBCPP_INLINE_VISIBILITY + _Tp* + addressof(_Tp& __x) WI_NOEXCEPT + { + return reinterpret_cast<_Tp *>( + const_cast(&reinterpret_cast(__x))); + } + +#endif // __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF + +#if !defined(__WI_LIBCPP_CXX03_LANG) + template _Tp* addressof(const _Tp&&) WI_NOEXCEPT = delete; +#endif + + struct __two {char __lx[2];}; + + // helper class: + + template + struct __WI_LIBCPP_TEMPLATE_VIS integral_constant + { + static __WI_LIBCPP_CONSTEXPR const _Tp value = __v; + typedef _Tp value_type; + typedef integral_constant type; + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR operator value_type() const WI_NOEXCEPT {return value;} +#if __WI_LIBCPP_STD_VER > 11 + __WI_LIBCPP_INLINE_VISIBILITY + constexpr value_type operator ()() const WI_NOEXCEPT {return value;} +#endif + }; + + template + __WI_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value; + +#if !defined(__WI_LIBCPP_CXX03_LANG) + template + using bool_constant = integral_constant; +#define __WI_LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)> +#else +#define __WI_LIBCPP_BOOL_CONSTANT(__b) integral_constant +#endif + + typedef __WI_LIBCPP_BOOL_CONSTANT(true) true_type; + typedef __WI_LIBCPP_BOOL_CONSTANT(false) false_type; + +#if !defined(__WI_LIBCPP_CXX03_LANG) + + // __lazy_and + + template + struct __lazy_and_impl; + + template + struct __lazy_and_impl : false_type {}; + + template <> + struct __lazy_and_impl : true_type {}; + + template + struct __lazy_and_impl : integral_constant {}; + + template + struct __lazy_and_impl : __lazy_and_impl<_Hp::type::value, _Tp...> {}; + + template + struct __lazy_and : __lazy_and_impl<_P1::type::value, _Pr...> {}; + + // __lazy_or + + template + struct __lazy_or_impl; + + template + struct __lazy_or_impl : true_type {}; + + template <> + struct __lazy_or_impl : false_type {}; + + template + struct __lazy_or_impl + : __lazy_or_impl<_Hp::type::value, _Tp...> {}; + + template + struct __lazy_or : __lazy_or_impl<_P1::type::value, _Pr...> {}; + + // __lazy_not + + template + struct __lazy_not : integral_constant {}; + + // __and_ + template struct __and_; + template<> struct __and_<> : true_type {}; + + template struct __and_<_B0> : _B0 {}; + + template + struct __and_<_B0, _B1> : conditional<_B0::value, _B1, _B0>::type {}; + + template + struct __and_<_B0, _B1, _B2, _Bn...> + : conditional<_B0::value, __and_<_B1, _B2, _Bn...>, _B0>::type {}; + + // __or_ + template struct __or_; + template<> struct __or_<> : false_type {}; + + template struct __or_<_B0> : _B0 {}; + + template + struct __or_<_B0, _B1> : conditional<_B0::value, _B0, _B1>::type {}; + + template + struct __or_<_B0, _B1, _B2, _Bn...> + : conditional<_B0::value, _B0, __or_<_B1, _B2, _Bn...> >::type {}; + + // __not_ + template + struct __not_ : conditional<_Tp::value, false_type, true_type>::type {}; + +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + // is_const + + template struct __WI_LIBCPP_TEMPLATE_VIS is_const : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_const<_Tp const> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_const_v + = is_const<_Tp>::value; +#endif + + // is_volatile + + template struct __WI_LIBCPP_TEMPLATE_VIS is_volatile : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_volatile<_Tp volatile> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_volatile_v + = is_volatile<_Tp>::value; +#endif + + // remove_const + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_const {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_const {typedef _Tp type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_const_t = typename remove_const<_Tp>::type; +#endif + + // remove_volatile + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_volatile {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_volatile {typedef _Tp type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_volatile_t = typename remove_volatile<_Tp>::type; +#endif + + // remove_cv + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_cv + {typedef typename remove_volatile::type>::type type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_cv_t = typename remove_cv<_Tp>::type; +#endif + + // is_void + + template struct __libcpp_is_void : public false_type {}; + template <> struct __libcpp_is_void : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_void + : public __libcpp_is_void::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_void_v + = is_void<_Tp>::value; +#endif + + // __is_nullptr_t + + template struct __is_nullptr_t_impl : public false_type {}; + template <> struct __is_nullptr_t_impl : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS __is_nullptr_t + : public __is_nullptr_t_impl::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 + template struct __WI_LIBCPP_TEMPLATE_VIS is_null_pointer + : public __is_nullptr_t_impl::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_null_pointer_v + = is_null_pointer<_Tp>::value; +#endif +#endif + + // is_integral + + template struct __libcpp_is_integral : public false_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#ifdef _MSC_VER + template <> struct __libcpp_is_integral<__wchar_t> : public true_type {}; +#else + template <> struct __libcpp_is_integral : public true_type {}; +#endif +#ifndef __WI_LIBCPP_HAS_NO_UNICODE_CHARS + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#endif // __WI_LIBCPP_HAS_NO_UNICODE_CHARS + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __libcpp_is_integral<__int128_t> : public true_type {}; + template <> struct __libcpp_is_integral<__uint128_t> : public true_type {}; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS is_integral + : public __libcpp_is_integral::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_integral_v + = is_integral<_Tp>::value; +#endif + + // is_floating_point + + template struct __libcpp_is_floating_point : public false_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_floating_point + : public __libcpp_is_floating_point::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_floating_point_v + = is_floating_point<_Tp>::value; +#endif + + // is_array + + template struct __WI_LIBCPP_TEMPLATE_VIS is_array + : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_array<_Tp[]> + : public true_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_array<_Tp[_Np]> + : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_array_v + = is_array<_Tp>::value; +#endif + + // is_pointer + + template struct __libcpp_is_pointer : public false_type {}; + template struct __libcpp_is_pointer<_Tp*> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pointer + : public __libcpp_is_pointer::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_pointer_v + = is_pointer<_Tp>::value; +#endif + + // is_reference + + template struct __WI_LIBCPP_TEMPLATE_VIS is_lvalue_reference : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_lvalue_reference<_Tp&> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_rvalue_reference : public false_type {}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS is_rvalue_reference<_Tp&&> : public true_type {}; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference<_Tp&> : public true_type {}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference<_Tp&&> : public true_type {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_reference_v + = is_reference<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_lvalue_reference_v + = is_lvalue_reference<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_rvalue_reference_v + = is_rvalue_reference<_Tp>::value; +#endif + // is_union + +#if __WI_HAS_FEATURE_IS_UNION || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_union + : public integral_constant {}; + +#else + + template struct __libcpp_union : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_union + : public __libcpp_union::type> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_union_v + = is_union<_Tp>::value; +#endif + + // is_class + +#if __WI_HAS_FEATURE_IS_CLASS || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_class + : public integral_constant {}; + +#else + + namespace __is_class_imp + { + template char __test(int _Tp::*); + template __two __test(...); + } + + template struct __WI_LIBCPP_TEMPLATE_VIS is_class + : public integral_constant(0)) == 1 && !is_union<_Tp>::value> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_class_v + = is_class<_Tp>::value; +#endif + + // is_same + + template struct __WI_LIBCPP_TEMPLATE_VIS is_same : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_same<_Tp, _Tp> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_same_v + = is_same<_Tp, _Up>::value; +#endif + + // is_function + + namespace __libcpp_is_function_imp + { + struct __dummy_type {}; + template char __test(_Tp*); + template char __test(__dummy_type); + template __two __test(...); + template _Tp& __source(int); + template __dummy_type __source(...); + } + + template ::value || + is_union<_Tp>::value || + is_void<_Tp>::value || + is_reference<_Tp>::value || + __is_nullptr_t<_Tp>::value > + struct __libcpp_is_function + : public integral_constant(__libcpp_is_function_imp::__source<_Tp>(0))) == 1> + {}; + template struct __libcpp_is_function<_Tp, true> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_function + : public __libcpp_is_function<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_function_v + = is_function<_Tp>::value; +#endif + + // is_member_function_pointer + + // template struct __libcpp_is_member_function_pointer : public false_type {}; + // template struct __libcpp_is_member_function_pointer<_Tp _Up::*> : public is_function<_Tp> {}; + // + + template + struct __member_pointer_traits_imp + { // forward declaration; specializations later + }; + + + template struct __libcpp_is_member_function_pointer + : public false_type {}; + + template + struct __libcpp_is_member_function_pointer<_Ret _Class::*> + : public is_function<_Ret> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_function_pointer + : public __libcpp_is_member_function_pointer::type>::type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_function_pointer_v + = is_member_function_pointer<_Tp>::value; +#endif + + // is_member_pointer + + template struct __libcpp_is_member_pointer : public false_type {}; + template struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_pointer + : public __libcpp_is_member_pointer::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_pointer_v + = is_member_pointer<_Tp>::value; +#endif + + // is_member_object_pointer + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_object_pointer + : public integral_constant::value && + !is_member_function_pointer<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_object_pointer_v + = is_member_object_pointer<_Tp>::value; +#endif + + // is_enum + +#if __WI_HAS_FEATURE_IS_ENUM || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_enum + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS is_enum + : public integral_constant::value && + !is_integral<_Tp>::value && + !is_floating_point<_Tp>::value && + !is_array<_Tp>::value && + !is_pointer<_Tp>::value && + !is_reference<_Tp>::value && + !is_member_pointer<_Tp>::value && + !is_union<_Tp>::value && + !is_class<_Tp>::value && + !is_function<_Tp>::value > {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_enum_v + = is_enum<_Tp>::value; +#endif + + // is_arithmetic + + template struct __WI_LIBCPP_TEMPLATE_VIS is_arithmetic + : public integral_constant::value || + is_floating_point<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_arithmetic_v + = is_arithmetic<_Tp>::value; +#endif + + // is_fundamental + + template struct __WI_LIBCPP_TEMPLATE_VIS is_fundamental + : public integral_constant::value || + __is_nullptr_t<_Tp>::value || + is_arithmetic<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_fundamental_v + = is_fundamental<_Tp>::value; +#endif + + // is_scalar + + template struct __WI_LIBCPP_TEMPLATE_VIS is_scalar + : public integral_constant::value || + is_member_pointer<_Tp>::value || + is_pointer<_Tp>::value || + __is_nullptr_t<_Tp>::value || + is_enum<_Tp>::value > {}; + + template <> struct __WI_LIBCPP_TEMPLATE_VIS is_scalar : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_scalar_v + = is_scalar<_Tp>::value; +#endif + + // is_object + + template struct __WI_LIBCPP_TEMPLATE_VIS is_object + : public integral_constant::value || + is_array<_Tp>::value || + is_union<_Tp>::value || + is_class<_Tp>::value > {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_object_v + = is_object<_Tp>::value; +#endif + + // is_compound + + template struct __WI_LIBCPP_TEMPLATE_VIS is_compound + : public integral_constant::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_compound_v + = is_compound<_Tp>::value; +#endif + + + // __is_referenceable [defns.referenceable] + + struct __is_referenceable_impl { + template static _Tp& __test(int); + template static __two __test(...); + }; + + template + struct __is_referenceable : integral_constant(0)), __two>::value> {}; + + + // add_const + + template ::value || + is_function<_Tp>::value || + is_const<_Tp>::value > + struct __add_const {typedef _Tp type;}; + + template + struct __add_const<_Tp, false> {typedef const _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_const + {typedef typename __add_const<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_const_t = typename add_const<_Tp>::type; +#endif + + // add_volatile + + template ::value || + is_function<_Tp>::value || + is_volatile<_Tp>::value > + struct __add_volatile {typedef _Tp type;}; + + template + struct __add_volatile<_Tp, false> {typedef volatile _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_volatile + {typedef typename __add_volatile<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_volatile_t = typename add_volatile<_Tp>::type; +#endif + + // add_cv + + template struct __WI_LIBCPP_TEMPLATE_VIS add_cv + {typedef typename add_const::type>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_cv_t = typename add_cv<_Tp>::type; +#endif + + // remove_reference + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference<_Tp&> {typedef _Tp type;}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference<_Tp&&> {typedef _Tp type;}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_reference_t = typename remove_reference<_Tp>::type; +#endif + + // add_lvalue_reference + + template ::value> struct __add_lvalue_reference_impl { typedef _Tp type; }; + template struct __add_lvalue_reference_impl<_Tp, true> { typedef _Tp& type; }; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_lvalue_reference + {typedef typename __add_lvalue_reference_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_lvalue_reference_t = typename add_lvalue_reference<_Tp>::type; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template ::value> struct __add_rvalue_reference_impl { typedef _Tp type; }; + template struct __add_rvalue_reference_impl<_Tp, true> { typedef _Tp&& type; }; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_rvalue_reference + {typedef typename __add_rvalue_reference_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_rvalue_reference_t = typename add_rvalue_reference<_Tp>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + // MSVC has issues compiling some source code that uses the libc++ definition of 'declval' +#ifdef _MSC_VER + template + typename add_rvalue_reference<_Tp>::type declval() WI_NOEXCEPT; +#else + template _Tp&& __declval(int); + template _Tp __declval(long); + + template + decltype(__declval<_Tp>(0)) + declval() WI_NOEXCEPT; +#endif + +#else // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + typename add_lvalue_reference<_Tp>::type + declval(); + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + // __uncvref + + template + struct __uncvref { + typedef typename remove_cv::type>::type type; + }; + + template + struct __unconstref { + typedef typename remove_const::type>::type type; + }; + +#ifndef __WI_LIBCPP_CXX03_LANG + template + using __uncvref_t = typename __uncvref<_Tp>::type; +#endif + + // __is_same_uncvref + + template + struct __is_same_uncvref : is_same::type, + typename __uncvref<_Up>::type> {}; + +#if __WI_LIBCPP_STD_VER > 17 + // remove_cvref - same as __uncvref + template + struct remove_cvref : public __uncvref<_Tp> {}; + + template using remove_cvref_t = typename remove_cvref<_Tp>::type; +#endif + + + struct __any + { + __any(...); + }; + + // remove_pointer + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp*> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* const> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* volatile> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* const volatile> {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_pointer_t = typename remove_pointer<_Tp>::type; +#endif + + // add_pointer + + template ::value || + is_same::type, void>::value> + struct __add_pointer_impl + {typedef typename remove_reference<_Tp>::type* type;}; + template struct __add_pointer_impl<_Tp, false> + {typedef _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_pointer + {typedef typename __add_pointer_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_pointer_t = typename add_pointer<_Tp>::type; +#endif + + // type_identity +#if __WI_LIBCPP_STD_VER > 17 + template struct type_identity { typedef _Tp type; }; + template using type_identity_t = typename type_identity<_Tp>::type; +#endif + + // is_signed + + template ::value> + struct __libcpp_is_signed_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(-1) < _Tp(0)) {}; + + template + struct __libcpp_is_signed_impl<_Tp, false> : public true_type {}; // floating point + + template ::value> + struct __libcpp_is_signed : public __libcpp_is_signed_impl<_Tp> {}; + + template struct __libcpp_is_signed<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_signed : public __libcpp_is_signed<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_signed_v + = is_signed<_Tp>::value; +#endif + + // is_unsigned + + template ::value> + struct __libcpp_is_unsigned_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(0) < _Tp(-1)) {}; + + template + struct __libcpp_is_unsigned_impl<_Tp, false> : public false_type {}; // floating point + + template ::value> + struct __libcpp_is_unsigned : public __libcpp_is_unsigned_impl<_Tp> {}; + + template struct __libcpp_is_unsigned<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_unsigned : public __libcpp_is_unsigned<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_unsigned_v + = is_unsigned<_Tp>::value; +#endif + + // rank + + template struct __WI_LIBCPP_TEMPLATE_VIS rank + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS rank<_Tp[]> + : public integral_constant::value + 1> {}; + template struct __WI_LIBCPP_TEMPLATE_VIS rank<_Tp[_Np]> + : public integral_constant::value + 1> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t rank_v + = rank<_Tp>::value; +#endif + + // extent + + template struct __WI_LIBCPP_TEMPLATE_VIS extent + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[], 0> + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[], _Ip> + : public integral_constant::value> {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[_Np], 0> + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[_Np], _Ip> + : public integral_constant::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t extent_v + = extent<_Tp, _Ip>::value; +#endif + + // remove_extent + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent<_Tp[]> + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent<_Tp[_Np]> + {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_extent_t = typename remove_extent<_Tp>::type; +#endif + + // remove_all_extents + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents<_Tp[]> + {typedef typename remove_all_extents<_Tp>::type type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents<_Tp[_Np]> + {typedef typename remove_all_extents<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_all_extents_t = typename remove_all_extents<_Tp>::type; +#endif + + // decay + + template + struct __decay { + typedef typename remove_cv<_Up>::type type; + }; + + template + struct __decay<_Up, true> { + public: + typedef typename conditional + < + is_array<_Up>::value, + typename remove_extent<_Up>::type*, + typename conditional + < + is_function<_Up>::value, + typename add_pointer<_Up>::type, + typename remove_cv<_Up>::type + >::type + >::type type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS decay + { + private: + typedef typename remove_reference<_Tp>::type _Up; + public: + typedef typename __decay<_Up, __is_referenceable<_Up>::value>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using decay_t = typename decay<_Tp>::type; +#endif + + // is_abstract + + template struct __WI_LIBCPP_TEMPLATE_VIS is_abstract + : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_abstract_v + = is_abstract<_Tp>::value; +#endif + + // is_final + +#if defined(__WI_LIBCPP_HAS_IS_FINAL) + template struct __WI_LIBCPP_TEMPLATE_VIS + __libcpp_is_final : public integral_constant {}; +#else + template struct __WI_LIBCPP_TEMPLATE_VIS + __libcpp_is_final : public false_type {}; +#endif + +#if defined(__WI_LIBCPP_HAS_IS_FINAL) && __WI_LIBCPP_STD_VER > 11 + template struct __WI_LIBCPP_TEMPLATE_VIS + is_final : public integral_constant {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_final_v + = is_final<_Tp>::value; +#endif + + // is_aggregate +#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_IS_AGGREGATE) + + template struct __WI_LIBCPP_TEMPLATE_VIS + is_aggregate : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_aggregate_v + = is_aggregate<_Tp>::value; +#endif + +#endif // __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_IS_AGGREGATE) + + // is_base_of + +#ifdef __WI_LIBCPP_HAS_IS_BASE_OF + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_base_of + : public integral_constant {}; + +#else // __WI_LIBCPP_HAS_IS_BASE_OF + + namespace __is_base_of_imp + { + template + struct _Dst + { + _Dst(const volatile _Tp &); + }; + template + struct _Src + { + operator const volatile _Tp &(); + template operator const _Dst<_Up> &(); + }; + template struct __one { typedef char type; }; + template typename __one(declval<_Src<_Dp> >()))>::type __test(int); + template __two __test(...); + } + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_base_of + : public integral_constant::value && + sizeof(__is_base_of_imp::__test<_Bp, _Dp>(0)) == 2> {}; + +#endif // __WI_LIBCPP_HAS_IS_BASE_OF + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_base_of_v + = is_base_of<_Bp, _Dp>::value; +#endif + + // is_convertible + +#if __WI_HAS_FEATURE_IS_CONVERTIBLE_TO && !defined(__WI_LIBCPP_USE_IS_CONVERTIBLE_FALLBACK) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_convertible + : public integral_constant::value> {}; + +#else // __WI_HAS_FEATURE_IS_CONVERTIBLE_TO + + namespace __is_convertible_imp + { + template void __test_convert(_Tp); + + template + struct __is_convertible_test : public false_type {}; + + template + struct __is_convertible_test<_From, _To, + decltype(__is_convertible_imp::__test_convert<_To>(declval<_From>()))> : public true_type + {}; + + template ::value, + bool _IsFunction = is_function<_Tp>::value, + bool _IsVoid = is_void<_Tp>::value> + struct __is_array_function_or_void {enum {value = 0};}; + template struct __is_array_function_or_void<_Tp, true, false, false> {enum {value = 1};}; + template struct __is_array_function_or_void<_Tp, false, true, false> {enum {value = 2};}; + template struct __is_array_function_or_void<_Tp, false, false, true> {enum {value = 3};}; + } + + template ::type>::value> + struct __is_convertible_check + { + static const size_t __v = 0; + }; + + template + struct __is_convertible_check<_Tp, 0> + { + static const size_t __v = sizeof(_Tp); + }; + + template ::value, + unsigned _T2_is_array_function_or_void = __is_convertible_imp::__is_array_function_or_void<_T2>::value> + struct __is_convertible + : public integral_constant::value +#if defined(__WI_LIBCPP_HAS_NO_RVALUE_REFERENCES) + && !(!is_function<_T1>::value && !is_reference<_T1>::value && is_reference<_T2>::value + && (!is_const::type>::value + || is_volatile::type>::value) + && (is_same::type, + typename remove_cv::type>::type>::value + || is_base_of::type, _T1>::value)) +#endif + > + {}; + + template struct __is_convertible<_T1, _T2, 0, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 1> : public false_type {}; + + template struct __is_convertible<_T1, _T2, 0, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 2> : public false_type {}; + + template struct __is_convertible<_T1, _T2, 0, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 3> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_convertible + : public __is_convertible<_T1, _T2> + { + static const size_t __complete_check1 = __is_convertible_check<_T1>::__v; + static const size_t __complete_check2 = __is_convertible_check<_T2>::__v; + }; + +#endif // __WI_HAS_FEATURE_IS_CONVERTIBLE_TO + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_convertible_v + = is_convertible<_From, _To>::value; +#endif + + // is_empty + +#if __WI_HAS_FEATURE_IS_EMPTY || (__WI_GNUC_VER >= 407) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_empty + : public integral_constant {}; + +#else // __WI_HAS_FEATURE_IS_EMPTY + + template + struct __is_empty1 + : public _Tp + { + double __lx; + }; + + struct __is_empty2 + { + double __lx; + }; + + template ::value> + struct __libcpp_empty : public integral_constant) == sizeof(__is_empty2)> {}; + + template struct __libcpp_empty<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_empty : public __libcpp_empty<_Tp> {}; + +#endif // __WI_HAS_FEATURE_IS_EMPTY + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_empty_v + = is_empty<_Tp>::value; +#endif + + // is_polymorphic + +#if __WI_HAS_FEATURE_IS_POLYMORPHIC + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_polymorphic + : public integral_constant {}; + +#else + + template char &__is_polymorphic_impl( + typename enable_if(declval<_Tp*>())) != 0, + int>::type); + template __two &__is_polymorphic_impl(...); + + template struct __WI_LIBCPP_TEMPLATE_VIS is_polymorphic + : public integral_constant(0)) == 1> {}; + +#endif // __WI_HAS_FEATURE_IS_POLYMORPHIC + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_polymorphic_v + = is_polymorphic<_Tp>::value; +#endif + + // has_virtual_destructor + +#if __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS has_virtual_destructor + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS has_virtual_destructor + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool has_virtual_destructor_v + = has_virtual_destructor<_Tp>::value; +#endif + + // has_unique_object_representations + +#if __WI_LIBCPP_STD_VER > 14 && defined(__WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + + template struct __WI_LIBCPP_TEMPLATE_VIS has_unique_object_representations + : public integral_constant>)> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool has_unique_object_representations_v + = has_unique_object_representations<_Tp>::value; +#endif + +#endif + + // alignment_of + + template struct __WI_LIBCPP_TEMPLATE_VIS alignment_of + : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t alignment_of_v + = alignment_of<_Tp>::value; +#endif + + // aligned_storage + + template + struct __type_list + { + typedef _Hp _Head; + typedef _Tp _Tail; + }; + + struct __nat + { +#ifndef __WI_LIBCPP_CXX03_LANG + __nat() = delete; + __nat(const __nat&) = delete; + __nat& operator=(const __nat&) = delete; + ~__nat() = delete; +#endif + }; + + template + struct __align_type + { + static const size_t value = alignment_of<_Tp>::value; + typedef _Tp type; + }; + + struct __struct_double {long double __lx;}; + struct __struct_double4 {double __lx[4];}; + + typedef + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type<__struct_double>, + __type_list<__align_type<__struct_double4>, + __type_list<__align_type, + __nat + > > > > > > > > > > __all_types; + + template struct __find_pod; + + template + struct __find_pod<__type_list<_Hp, __nat>, _Align> + { + typedef typename conditional< + _Align == _Hp::value, + typename _Hp::type, + void + >::type type; + }; + + template + struct __find_pod<__type_list<_Hp, _Tp>, _Align> + { + typedef typename conditional< + _Align == _Hp::value, + typename _Hp::type, + typename __find_pod<_Tp, _Align>::type + >::type type; + }; + + template + struct __has_pod_with_align : public integral_constant::type, void>::value> {}; + + template struct __find_max_align; + + template + struct __find_max_align<__type_list<_Hp, __nat>, _Len> : public integral_constant {}; + + template + struct __select_align + { + private: + static const size_t __min = _A2 < _A1 ? _A2 : _A1; + static const size_t __max = _A1 < _A2 ? _A2 : _A1; + public: + static const size_t value = _Len < __max ? __min : __max; + }; + + template + struct __find_max_align<__type_list<_Hp, _Tp>, _Len> + : public integral_constant::value>::value> {}; + + template ::value> + struct __aligned_storage + { + typedef typename __find_pod<__all_types, _Align>::type _Aligner; + static_assert(!is_void<_Aligner>::value, ""); + union type + { + _Aligner __align; + unsigned char __data[(_Len + _Align - 1)/_Align * _Align]; + }; + }; + +#define __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(n) \ + template \ + struct __aligned_storage<_Len, n, false>\ + {\ + struct __WI_ALIGNAS(n) type\ + {\ + unsigned char __lx[(_Len + n - 1)/n * n];\ + };\ + } + + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x1); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x2); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x4); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x8); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x10); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x20); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x40); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x80); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x100); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x200); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x400); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x800); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x1000); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x2000); + // PE/COFF does not support alignment beyond 8192 (=0x2000) +#if !defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x4000); +#endif // !defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + +#undef __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION + + template ::value> + struct __WI_LIBCPP_TEMPLATE_VIS aligned_storage : public __aligned_storage<_Len, _Align> {}; + +#if __WI_LIBCPP_STD_VER > 11 + template ::value> + using aligned_storage_t = typename aligned_storage<_Len, _Align>::type; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + + // aligned_union + + template + struct __static_max; + + template + struct __static_max<_I0> + { + static const size_t value = _I0; + }; + + template + struct __static_max<_I0, _I1, _In...> + { + static const size_t value = _I0 >= _I1 ? __static_max<_I0, _In...>::value : + __static_max<_I1, _In...>::value; + }; + + template + struct aligned_union + { + static const size_t alignment_value = __static_max<__alignof__(_Type0), + __alignof__(_Types)...>::value; + static const size_t __len = __static_max<_Len, sizeof(_Type0), + sizeof(_Types)...>::value; + typedef typename aligned_storage<__len, alignment_value>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using aligned_union_t = typename aligned_union<_Len, _Types...>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __numeric_type + { + static void __test(...); + static float __test(float); + static double __test(char); + static double __test(int); + static double __test(unsigned); + static double __test(long); + static double __test(unsigned long); + static double __test(long long); + static double __test(unsigned long long); + static double __test(double); + static long double __test(long double); + + typedef decltype(__test(declval<_Tp>())) type; + static const bool value = !is_same::value; + }; + + template <> + struct __numeric_type + { + static const bool value = true; + }; + + // __promote + + template ::value && + __numeric_type<_A2>::value && + __numeric_type<_A3>::value> + class __promote_imp + { + public: + static const bool value = false; + }; + + template + class __promote_imp<_A1, _A2, _A3, true> + { + private: + typedef typename __promote_imp<_A1>::type __type1; + typedef typename __promote_imp<_A2>::type __type2; + typedef typename __promote_imp<_A3>::type __type3; + public: + typedef decltype(__type1() + __type2() + __type3()) type; + static const bool value = true; + }; + + template + class __promote_imp<_A1, _A2, void, true> + { + private: + typedef typename __promote_imp<_A1>::type __type1; + typedef typename __promote_imp<_A2>::type __type2; + public: + typedef decltype(__type1() + __type2()) type; + static const bool value = true; + }; + + template + class __promote_imp<_A1, void, void, true> + { + public: + typedef typename __numeric_type<_A1>::type type; + static const bool value = true; + }; + + template + class __promote : public __promote_imp<_A1, _A2, _A3> {}; + + // make_signed / make_unsigned + + typedef + __type_list +#endif + > > > > > __signed_types; + + typedef + __type_list +#endif + > > > > > __unsigned_types; + + template struct __find_first; + + template + struct __find_first<__type_list<_Hp, _Tp>, _Size, true> + { + typedef _Hp type; + }; + + template + struct __find_first<__type_list<_Hp, _Tp>, _Size, false> + { + typedef typename __find_first<_Tp, _Size>::type type; + }; + + template ::type>::value, + bool = is_volatile::type>::value> + struct __apply_cv + { + typedef _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, true, false> + { + typedef const _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, false, true> + { + typedef volatile _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, true, true> + { + typedef const volatile _Up type; + }; + + template + struct __apply_cv<_Tp&, _Up, false, false> + { + typedef _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, true, false> + { + typedef const _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, false, true> + { + typedef volatile _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, true, true> + { + typedef const volatile _Up& type; + }; + + template ::value || is_enum<_Tp>::value> + struct __make_signed {}; + + template + struct __make_signed<_Tp, true> + { + typedef typename __find_first<__signed_types, sizeof(_Tp)>::type type; + }; + + template <> struct __make_signed {}; + template <> struct __make_signed< signed short, true> {typedef short type;}; + template <> struct __make_signed {typedef short type;}; + template <> struct __make_signed< signed int, true> {typedef int type;}; + template <> struct __make_signed {typedef int type;}; + template <> struct __make_signed< signed long, true> {typedef long type;}; + template <> struct __make_signed {typedef long type;}; + template <> struct __make_signed< signed long long, true> {typedef long long type;}; + template <> struct __make_signed {typedef long long type;}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __make_signed<__int128_t, true> {typedef __int128_t type;}; + template <> struct __make_signed<__uint128_t, true> {typedef __int128_t type;}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS make_signed + { + typedef typename __apply_cv<_Tp, typename __make_signed::type>::type>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using make_signed_t = typename make_signed<_Tp>::type; +#endif + + template ::value || is_enum<_Tp>::value> + struct __make_unsigned {}; + + template + struct __make_unsigned<_Tp, true> + { + typedef typename __find_first<__unsigned_types, sizeof(_Tp)>::type type; + }; + + template <> struct __make_unsigned {}; + template <> struct __make_unsigned< signed short, true> {typedef unsigned short type;}; + template <> struct __make_unsigned {typedef unsigned short type;}; + template <> struct __make_unsigned< signed int, true> {typedef unsigned int type;}; + template <> struct __make_unsigned {typedef unsigned int type;}; + template <> struct __make_unsigned< signed long, true> {typedef unsigned long type;}; + template <> struct __make_unsigned {typedef unsigned long type;}; + template <> struct __make_unsigned< signed long long, true> {typedef unsigned long long type;}; + template <> struct __make_unsigned {typedef unsigned long long type;}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __make_unsigned<__int128_t, true> {typedef __uint128_t type;}; + template <> struct __make_unsigned<__uint128_t, true> {typedef __uint128_t type;}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS make_unsigned + { + typedef typename __apply_cv<_Tp, typename __make_unsigned::type>::type>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using make_unsigned_t = typename make_unsigned<_Tp>::type; +#endif + +#ifdef __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type + { + public: + typedef typename common_type::type, _Vp>::type type; + }; + + template <> + struct __WI_LIBCPP_TEMPLATE_VIS common_type + { + public: + typedef void type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, void, void> + { + public: + typedef typename common_type<_Tp, _Tp>::type type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, void> + { + typedef typename decay() : declval<_Up>() + )>::type type; + }; + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + // bullet 1 - sizeof...(Tp) == 0 + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type {}; + + // bullet 2 - sizeof...(Tp) == 1 + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp> + : public common_type<_Tp, _Tp> {}; + + // bullet 3 - sizeof...(Tp) == 2 + + template + struct __common_type2_imp {}; + + template + struct __common_type2_imp<_Tp, _Up, + typename __void_t() : declval<_Up>() + )>::type> + { + typedef typename decay() : declval<_Up>() + )>::type type; + }; + + template ::type, + class _DUp = typename decay<_Up>::type> + using __common_type2 = + typename conditional< + is_same<_Tp, _DTp>::value && is_same<_Up, _DUp>::value, + __common_type2_imp<_Tp, _Up>, + common_type<_DTp, _DUp> + >::type; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up> + : __common_type2<_Tp, _Up> {}; + + // bullet 4 - sizeof...(Tp) > 2 + + template struct __common_types; + + template + struct __common_type_impl {}; + + template + struct __common_type_impl< + __common_types<_Tp, _Up>, + typename __void_t::type>::type> + { + typedef typename common_type<_Tp, _Up>::type type; + }; + + template + struct __common_type_impl<__common_types<_Tp, _Up, _Vp...>, + typename __void_t::type>::type> + : __common_type_impl< + __common_types::type, _Vp...> > + { + + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp...> + : __common_type_impl<__common_types<_Tp, _Up, _Vp...> > {}; + +#if __WI_LIBCPP_STD_VER > 11 + template using common_type_t = typename common_type<_Tp...>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + // is_assignable + + template struct __select_2nd { typedef _Tp type; }; + + template + typename __select_2nd() = declval<_Arg>())), true_type>::type + __is_assignable_test(int); + + template + false_type __is_assignable_test(...); + + + template ::value || is_void<_Arg>::value> + struct __is_assignable_imp + : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; + + template + struct __is_assignable_imp<_Tp, _Arg, true> + : public false_type + { + }; + + template + struct is_assignable + : public __is_assignable_imp<_Tp, _Arg> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_assignable_v + = is_assignable<_Tp, _Arg>::value; +#endif + + // is_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_copy_assignable + : public is_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_copy_assignable_v + = is_copy_assignable<_Tp>::value; +#endif + + // is_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_move_assignable +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_assignable::type, + typename add_rvalue_reference<_Tp>::type> {}; +#else + : public is_copy_assignable<_Tp> {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_move_assignable_v + = is_move_assignable<_Tp>::value; +#endif + + // is_destructible + +#if __WI_HAS_FEATURE_IS_DESTRUCTIBLE + + template + struct is_destructible + : public integral_constant {}; + +#else + + // if it's a reference, return true + // if it's a function, return false + // if it's void, return false + // if it's an array of unknown bound, return false + // Otherwise, return "std::declval<_Up&>().~_Up()" is well-formed + // where _Up is remove_all_extents<_Tp>::type + + template + struct __is_destructible_apply { typedef int type; }; + + template + struct __is_destructor_wellformed { + template + static char __test ( + typename __is_destructible_apply().~_Tp1())>::type + ); + + template + static __two __test (...); + + static const bool value = sizeof(__test<_Tp>(12)) == sizeof(char); + }; + + template + struct __destructible_imp; + + template + struct __destructible_imp<_Tp, false> + : public integral_constant::type>::value> {}; + + template + struct __destructible_imp<_Tp, true> + : public true_type {}; + + template + struct __destructible_false; + + template + struct __destructible_false<_Tp, false> : public __destructible_imp<_Tp, is_reference<_Tp>::value> {}; + + template + struct __destructible_false<_Tp, true> : public false_type {}; + + template + struct is_destructible + : public __destructible_false<_Tp, is_function<_Tp>::value> {}; + + template + struct is_destructible<_Tp[]> + : public false_type {}; + + template <> + struct is_destructible + : public false_type {}; + +#endif // __WI_HAS_FEATURE_IS_DESTRUCTIBLE + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_destructible_v + = is_destructible<_Tp>::value; +#endif + + // move + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename remove_reference<_Tp>::type&& + move(_Tp&& __t) WI_NOEXCEPT + { + typedef typename remove_reference<_Tp>::type _Up; + return static_cast<_Up&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + _Tp&& + forward(typename remove_reference<_Tp>::type& __t) WI_NOEXCEPT + { + return static_cast<_Tp&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + _Tp&& + forward(typename remove_reference<_Tp>::type&& __t) WI_NOEXCEPT + { + static_assert(!is_lvalue_reference<_Tp>::value, + "can not forward an rvalue as an lvalue"); + return static_cast<_Tp&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 + _T1 exchange(_T1& __obj, _T2 && __new_value) + { + _T1 __old_value = wistd::move(__obj); + __obj = wistd::forward<_T2>(__new_value); + return __old_value; + } + +#else // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _Tp& + move(_Tp& __t) + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + const _Tp& + move(const _Tp& __t) + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _Tp& + forward(typename remove_reference<_Tp>::type& __t) WI_NOEXCEPT + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _T1 exchange(_T1& __obj, const _T2& __new_value) + { + _T1 __old_value = __obj; + __obj = __new_value; + return __old_value; + } + + template + class __rv + { + typedef typename remove_reference<_Tp>::type _Trr; + _Trr& t_; + public: + __WI_LIBCPP_INLINE_VISIBILITY + _Trr* operator->() {return &t_;} + __WI_LIBCPP_INLINE_VISIBILITY + explicit __rv(_Trr& __t) : t_(__t) {} + }; + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#if __WI_LIBCPP_STD_VER > 11 + template +#else + template +#endif + struct __WI_LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool> + { + __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 __WI_LIBCPP_INLINE_VISIBILITY + bool operator()(const _Tp& __x, const _Tp& __y) const + {return __x < __y;} + }; + +#if __WI_LIBCPP_STD_VER > 11 + template <> + struct __WI_LIBCPP_TEMPLATE_VIS less + { + template + __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 __WI_LIBCPP_INLINE_VISIBILITY + auto operator()(_T1&& __t, _T2&& __u) const + __WI_NOEXCEPT_(noexcept(wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u))) + -> decltype (wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u)) + { return wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u); } + typedef void is_transparent; + }; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename decay<_Tp>::type + __decay_copy(_Tp&& __t) + { + return wistd::forward<_Tp>(__t); + } + +#else + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename decay<_Tp>::type + __decay_copy(const _Tp& __t) + { + return wistd::forward<_Tp>(__t); + } + +#endif + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + +#if __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS || \ + (defined(__WI_GNUC_VER) && __WI_GNUC_VER >= 409) + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) &, true, false> + { + typedef _Class& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) &, true, false> + { + typedef _Class& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const&, true, false> + { + typedef _Class const& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const&, true, false> + { + typedef _Class const& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile&, true, false> + { + typedef _Class volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile&, true, false> + { + typedef _Class volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile&, true, false> + { + typedef _Class const volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile&, true, false> + { + typedef _Class const volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) &&, true, false> + { + typedef _Class&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) &&, true, false> + { + typedef _Class&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const&&, true, false> + { + typedef _Class const&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const&&, true, false> + { + typedef _Class const&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile&&, true, false> + { + typedef _Class volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile&&, true, false> + { + typedef _Class volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile&&, true, false> + { + typedef _Class const volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile&&, true, false> + { + typedef _Class const volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + +#endif // __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS || __WI_GNUC_VER >= 409 + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp _Class::*, false, true> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + }; + + template + struct __member_pointer_traits + : public __member_pointer_traits_imp::type, + is_member_function_pointer<_Mp>::value, + is_member_object_pointer<_Mp>::value> + { + // typedef ... _ClassType; + // typedef ... _ReturnType; + // typedef ... _FnType; + }; + + + template + struct __member_pointer_class_type {}; + + template + struct __member_pointer_class_type<_Ret _ClassType::*> { + typedef _ClassType type; + }; + + // result_of + + template class result_of; + +#ifdef __WI_LIBCPP_HAS_NO_VARIADICS + + template + class __result_of + { + }; + + template + class __result_of<_Fn(), true, false> + { + public: + typedef decltype(declval<_Fn>()()) type; + }; + + template + class __result_of<_Fn(_A0), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>())) type; + }; + + template + class __result_of<_Fn(_A0, _A1), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>(), declval<_A1>())) type; + }; + + template + class __result_of<_Fn(_A0, _A1, _A2), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>(), declval<_A1>(), declval<_A2>())) type; + }; + + template + struct __result_of_mp; + + // member function pointer + + template + struct __result_of_mp<_Mp, _Tp, true> + : public __identity::_ReturnType> + { + }; + + // member data pointer + + template + struct __result_of_mdp; + + template + struct __result_of_mdp<_Rp _Class::*, _Tp, false> + { + typedef typename __apply_cv()), _Rp>::type& type; + }; + + template + struct __result_of_mdp<_Rp _Class::*, _Tp, true> + { + typedef typename __apply_cv<_Tp, _Rp>::type& type; + }; + + template + struct __result_of_mp<_Rp _Class::*, _Tp, false> + : public __result_of_mdp<_Rp _Class::*, _Tp, + is_base_of<_Class, typename remove_reference<_Tp>::type>::value> + { + }; + + + + template + class __result_of<_Fn(_Tp), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0, _A1), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0, _A1, _A2), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + // result_of + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn()> + : public __result_of<_Fn(), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0)> + : public __result_of<_Fn(_A0), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0, _A1)> + : public __result_of<_Fn(_A0, _A1), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0, _A1, _A2)> + : public __result_of<_Fn(_A0, _A1, _A2), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + // template struct is_constructible; + + namespace __is_construct + { + struct __nat {}; + } + +#if !defined(__WI_LIBCPP_CXX03_LANG) && (!__WI_HAS_FEATURE_IS_CONSTRUCTIBLE || \ + defined(__WI_LIBCPP_TESTING_FALLBACK_IS_CONSTRUCTIBLE)) + + template + struct __libcpp_is_constructible; + + template + struct __is_invalid_base_to_derived_cast { + static_assert(is_reference<_To>::value, "Wrong specialization"); + using _RawFrom = __uncvref_t<_From>; + using _RawTo = __uncvref_t<_To>; + static const bool value = __lazy_and< + __lazy_not>, + is_base_of<_RawFrom, _RawTo>, + __lazy_not<__libcpp_is_constructible<_RawTo, _From>> + >::value; + }; + + template + struct __is_invalid_lvalue_to_rvalue_cast : false_type { + static_assert(is_reference<_To>::value, "Wrong specialization"); + }; + + template + struct __is_invalid_lvalue_to_rvalue_cast<_ToRef&&, _FromRef&> { + using _RawFrom = __uncvref_t<_FromRef>; + using _RawTo = __uncvref_t<_ToRef>; + static const bool value = __lazy_and< + __lazy_not>, + __lazy_or< + is_same<_RawFrom, _RawTo>, + is_base_of<_RawTo, _RawFrom>> + >::value; + }; + + struct __is_constructible_helper + { + template + static void __eat(_To); + + // This overload is needed to work around a Clang bug that disallows + // static_cast(e) for non-reference-compatible types. + // Example: static_cast(declval()); + // NOTE: The static_cast implementation below is required to support + // classes with explicit conversion operators. + template (declval<_From>()))> + static true_type __test_cast(int); + + template (declval<_From>()))> + static integral_constant::value && + !__is_invalid_lvalue_to_rvalue_cast<_To, _From>::value + > __test_cast(long); + + template + static false_type __test_cast(...); + + template ()...))> + static true_type __test_nary(int); + template + static false_type __test_nary(...); + + template ()))> + static is_destructible<_Tp> __test_unary(int); + template + static false_type __test_unary(...); + }; + + template ::value> + struct __is_default_constructible + : decltype(__is_constructible_helper::__test_nary<_Tp>(0)) + {}; + + template + struct __is_default_constructible<_Tp, true> : false_type {}; + + template + struct __is_default_constructible<_Tp[], false> : false_type {}; + + template + struct __is_default_constructible<_Tp[_Nx], false> + : __is_default_constructible::type> {}; + + template + struct __libcpp_is_constructible + { + static_assert(sizeof...(_Args) > 1, "Wrong specialization"); + typedef decltype(__is_constructible_helper::__test_nary<_Tp, _Args...>(0)) + type; + }; + + template + struct __libcpp_is_constructible<_Tp> : __is_default_constructible<_Tp> {}; + + template + struct __libcpp_is_constructible<_Tp, _A0> + : public decltype(__is_constructible_helper::__test_unary<_Tp, _A0>(0)) + {}; + + template + struct __libcpp_is_constructible<_Tp&, _A0> + : public decltype(__is_constructible_helper:: + __test_cast<_Tp&, _A0>(0)) + {}; + + template + struct __libcpp_is_constructible<_Tp&&, _A0> + : public decltype(__is_constructible_helper:: + __test_cast<_Tp&&, _A0>(0)) + {}; + +#endif + +#if __WI_HAS_FEATURE_IS_CONSTRUCTIBLE + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public integral_constant + {}; +#elif !defined(__WI_LIBCPP_CXX03_LANG) + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public __libcpp_is_constructible<_Tp, _Args...>::type {}; +#else + // template struct is_constructible0; + + // main is_constructible0 test + + template + decltype((_Tp(), true_type())) + __is_constructible0_test(_Tp&); + + false_type + __is_constructible0_test(__any); + + template + decltype((_Tp(declval<_A0>()), true_type())) + __is_constructible1_test(_Tp&, _A0&); + + template + false_type + __is_constructible1_test(__any, _A0&); + + template + decltype((_Tp(declval<_A0>(), declval<_A1>()), true_type())) + __is_constructible2_test(_Tp&, _A0&, _A1&); + + template + false_type + __is_constructible2_test(__any, _A0&, _A1&); + + template + decltype((_Tp(declval<_A0>(), declval<_A1>(), declval<_A2>()), true_type())) + __is_constructible3_test(_Tp&, _A0&, _A1&, _A2&); + + template + false_type + __is_constructible3_test(__any, _A0&, _A1&, _A2&); + + template + struct __is_constructible0_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible0_test(declval<_Tp&>())) + >::type + {}; + + template + struct __is_constructible1_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible1_test(declval<_Tp&>(), declval<_A0&>())) + >::type + {}; + + template + struct __is_constructible2_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible2_test(declval<_Tp&>(), declval<_A0>(), declval<_A1>())) + >::type + {}; + + template + struct __is_constructible3_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible3_test(declval<_Tp&>(), declval<_A0>(), declval<_A1>(), declval<_A2>())) + >::type + {}; + + // handle scalars and reference types + + // Scalars are default constructible, references are not + + template + struct __is_constructible0_imp + : public is_scalar<_Tp> + {}; + + template + struct __is_constructible1_imp + : public is_convertible<_A0, _Tp> + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + + // Treat scalars and reference types separately + + template + struct __is_constructible0_void_check + : public __is_constructible0_imp::value || is_reference<_Tp>::value, + _Tp> + {}; + + template + struct __is_constructible1_void_check + : public __is_constructible1_imp::value || is_reference<_Tp>::value, + _Tp, _A0> + {}; + + template + struct __is_constructible2_void_check + : public __is_constructible2_imp::value || is_reference<_Tp>::value, + _Tp, _A0, _A1> + {}; + + template + struct __is_constructible3_void_check + : public __is_constructible3_imp::value || is_reference<_Tp>::value, + _Tp, _A0, _A1, _A2> + {}; + + // If any of T or Args is void, is_constructible should be false + + template + struct __is_constructible0_void_check + : public false_type + {}; + + template + struct __is_constructible1_void_check + : public false_type + {}; + + template + struct __is_constructible2_void_check + : public false_type + {}; + + template + struct __is_constructible3_void_check + : public false_type + {}; + + // is_constructible entry point + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public __is_constructible3_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value + || is_void<_A1>::value + || is_void<_A2>::value, + _Tp, _A0, _A1, _A2> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, __is_construct::__nat, __is_construct::__nat> + : public __is_constructible0_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value, + _Tp> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, _A0, __is_construct::__nat> + : public __is_constructible1_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value, + _Tp, _A0> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, _A0, _A1, __is_construct::__nat> + : public __is_constructible2_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value + || is_void<_A1>::value, + _Tp, _A0, _A1> + {}; + + // Array types are default constructible if their element type + // is default constructible + + template + struct __is_constructible0_imp + : public is_constructible::type> + {}; + + template + struct __is_constructible1_imp + : public false_type + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + + // Incomplete array types are not constructible + + template + struct __is_constructible0_imp + : public false_type + {}; + + template + struct __is_constructible1_imp + : public false_type + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + +#endif // __WI_HAS_FEATURE_IS_CONSTRUCTIBLE + + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_constructible_v + = is_constructible<_Tp, _Args...>::value; +#endif + + // is_default_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_default_constructible + : public is_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_default_constructible_v + = is_default_constructible<_Tp>::value; +#endif + + // is_copy_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_copy_constructible + : public is_constructible<_Tp, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_copy_constructible_v + = is_copy_constructible<_Tp>::value; +#endif + + // is_move_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_move_constructible_v + = is_move_constructible<_Tp>::value; +#endif + + // is_trivially_constructible + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE || __WI_GNUC_VER >= 501 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp> +#if __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&&> +#else + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp> +#endif + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&> + : integral_constant::value> + { + }; + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : false_type + { + }; + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE || __WI_GNUC_VER >= 501 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&, + __is_construct::__nat> + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&, + __is_construct::__nat> + : integral_constant::value> + { + }; + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_constructible_v + = is_trivially_constructible<_Tp, _Args...>::value; +#endif + + // is_trivially_default_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_default_constructible + : public is_trivially_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_default_constructible_v + = is_trivially_default_constructible<_Tp>::value; +#endif + + // is_trivially_copy_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copy_constructible + : public is_trivially_constructible<_Tp, typename add_lvalue_reference::type> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copy_constructible_v + = is_trivially_copy_constructible<_Tp>::value; +#endif + + // is_trivially_move_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_trivially_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_trivially_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_move_constructible_v + = is_trivially_move_constructible<_Tp>::value; +#endif + + // is_trivially_assignable + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE || __WI_GNUC_VER >= 501 + + template + struct is_trivially_assignable + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE + + template + struct is_trivially_assignable + : public false_type {}; + + template + struct is_trivially_assignable<_Tp&, _Tp> + : integral_constant::value> {}; + + template + struct is_trivially_assignable<_Tp&, _Tp&> + : integral_constant::value> {}; + + template + struct is_trivially_assignable<_Tp&, const _Tp&> + : integral_constant::value> {}; + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct is_trivially_assignable<_Tp&, _Tp&&> + : integral_constant::value> {}; + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_assignable_v + = is_trivially_assignable<_Tp, _Arg>::value; +#endif + + // is_trivially_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copy_assignable + : public is_trivially_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copy_assignable_v + = is_trivially_copy_assignable<_Tp>::value; +#endif + + // is_trivially_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_move_assignable + : public is_trivially_assignable::type, +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + typename add_rvalue_reference<_Tp>::type> +#else + typename add_lvalue_reference<_Tp>::type> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_move_assignable_v + = is_trivially_move_assignable<_Tp>::value; +#endif + + // is_trivially_destructible + +#if __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible + : public integral_constant::value && __has_trivial_destructor(_Tp)> {}; + +#else + + template struct __libcpp_trivial_destructor + : public integral_constant::value || + is_reference<_Tp>::value> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible + : public __libcpp_trivial_destructor::type> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible<_Tp[]> + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_destructible_v + = is_trivially_destructible<_Tp>::value; +#endif + + // is_nothrow_constructible + +#if 0 + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : public integral_constant + { + }; + +#else + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_constructible; + + template + struct __libcpp_is_nothrow_constructible + : public integral_constant()...))> + { + }; + + template + void __implicit_conversion_to(_Tp) noexcept { } + + template + struct __libcpp_is_nothrow_constructible + : public integral_constant(declval<_Arg>()))> + { + }; + + template + struct __libcpp_is_nothrow_constructible + : public false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : __libcpp_is_nothrow_constructible::value, is_reference<_Tp>::value, _Tp, _Args...> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp[_Ns]> + : __libcpp_is_nothrow_constructible::value, is_reference<_Tp>::value, _Tp> + { + }; + +#else // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp> +#if __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&&> +#else + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp> +#endif +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, const _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + +#endif // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, const _Tp&, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS +#endif // __has_feature(is_nothrow_constructible) + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_constructible_v + = is_nothrow_constructible<_Tp, _Args...>::value; +#endif + + // is_nothrow_default_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_default_constructible + : public is_nothrow_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_default_constructible_v + = is_nothrow_default_constructible<_Tp>::value; +#endif + + // is_nothrow_copy_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_copy_constructible + : public is_nothrow_constructible<_Tp, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_copy_constructible_v + = is_nothrow_copy_constructible<_Tp>::value; +#endif + + // is_nothrow_move_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_nothrow_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_nothrow_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_move_constructible_v + = is_nothrow_move_constructible<_Tp>::value; +#endif + + // is_nothrow_assignable + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_assignable; + + template + struct __libcpp_is_nothrow_assignable + : public false_type + { + }; + + template + struct __libcpp_is_nothrow_assignable + : public integral_constant() = declval<_Arg>()) > + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable + : public __libcpp_is_nothrow_assignable::value, _Tp, _Arg> + { + }; + +#else // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable + : public false_type {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, _Tp> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, const _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct is_nothrow_assignable<_Tp&, _Tp&&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#endif // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_assignable_v + = is_nothrow_assignable<_Tp, _Arg>::value; +#endif + + // is_nothrow_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_copy_assignable + : public is_nothrow_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_copy_assignable_v + = is_nothrow_copy_assignable<_Tp>::value; +#endif + + // is_nothrow_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_move_assignable + : public is_nothrow_assignable::type, +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + typename add_rvalue_reference<_Tp>::type> +#else + typename add_lvalue_reference<_Tp>::type> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_move_assignable_v + = is_nothrow_move_assignable<_Tp>::value; +#endif + + // is_nothrow_destructible + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_destructible; + + template + struct __libcpp_is_nothrow_destructible + : public false_type + { + }; + + template + struct __libcpp_is_nothrow_destructible + : public integral_constant().~_Tp()) > + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible + : public __libcpp_is_nothrow_destructible::value, _Tp> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp[_Ns]> + : public is_nothrow_destructible<_Tp> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp&> + : public true_type + { + }; + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp&&> + : public true_type + { + }; + +#endif + +#else + + template struct __libcpp_nothrow_destructor + : public integral_constant::value || + is_reference<_Tp>::value> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible + : public __libcpp_nothrow_destructor::type> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp[]> + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_destructible_v + = is_nothrow_destructible<_Tp>::value; +#endif + + // is_pod + +#if __WI_HAS_FEATURE_IS_POD || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pod + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pod + : public integral_constant::value && + is_trivially_copy_constructible<_Tp>::value && + is_trivially_copy_assignable<_Tp>::value && + is_trivially_destructible<_Tp>::value> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_pod_v + = is_pod<_Tp>::value; +#endif + + // is_literal_type; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_literal_type +#ifdef __WI_LIBCPP_IS_LITERAL + : public integral_constant +#else + : integral_constant::type>::value || + is_reference::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_literal_type_v + = is_literal_type<_Tp>::value; +#endif + + // is_standard_layout; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_standard_layout +#if __WI_HAS_FEATURE_IS_STANDARD_LAYOUT || (__WI_GNUC_VER >= 407) + : public integral_constant +#else + : integral_constant::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_standard_layout_v + = is_standard_layout<_Tp>::value; +#endif + + // is_trivially_copyable; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copyable +#if __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE + : public integral_constant +#elif __WI_GNUC_VER >= 501 + : public integral_constant::value && __is_trivially_copyable(_Tp)> +#else + : integral_constant::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copyable_v + = is_trivially_copyable<_Tp>::value; +#endif + + // is_trivial; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivial +#if __WI_HAS_FEATURE_IS_TRIVIAL || __WI_GNUC_VER >= 407 + : public integral_constant +#else + : integral_constant::value && + is_trivially_default_constructible<_Tp>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivial_v + = is_trivial<_Tp>::value; +#endif + + template struct __is_reference_wrapper_impl : public false_type {}; + template struct __is_reference_wrapper_impl > : public true_type {}; + template struct __is_reference_wrapper + : public __is_reference_wrapper_impl::type> {}; + +#ifndef __WI_LIBCPP_CXX03_LANG + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet1 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && is_base_of<_ClassT, _DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type> + using __enable_if_bullet2 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && __is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet3 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && !is_base_of<_ClassT, _DecayA0>::value + && !__is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet4 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && is_base_of<_ClassT, _DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type> + using __enable_if_bullet5 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && __is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet6 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && !is_base_of<_ClassT, _DecayA0>::value + && !__is_reference_wrapper<_DecayA0>::value + >::type; + + // __invoke forward declarations + + // fall back - none of the bullets + +#define __WI_LIBCPP_INVOKE_RETURN(...) \ + __WI_NOEXCEPT_(__WI_NOEXCEPT_(__VA_ARGS__)) -> decltype(__VA_ARGS__) \ + { return __VA_ARGS__; } + + template + auto __invoke(__any, _Args&& ...__args) -> __nat; + + template + auto __invoke_constexpr(__any, _Args&& ...__args) -> __nat; + + // bullets 1, 2 and 3 + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((wistd::forward<_A0>(__a0).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((wistd::forward<_A0>(__a0).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((__a0.get().*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((__a0.get().*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(((*wistd::forward<_A0>(__a0)).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(((*wistd::forward<_A0>(__a0)).*__f)(wistd::forward<_Args>(__args)...)) + + // bullets 4, 5 and 6 + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_A0>(__a0).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_A0>(__a0).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(__a0.get().*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(__a0.get().*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN((*wistd::forward<_A0>(__a0)).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN((*wistd::forward<_A0>(__a0)).*__f) + + // bullet 7 + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_Fp>(__f)(wistd::forward<_Args>(__args)...)) + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_Fp>(__f)(wistd::forward<_Args>(__args)...)) + +#undef __WI_LIBCPP_INVOKE_RETURN + + // __invokable + + template + struct __invokable_r + { + // FIXME: Check that _Ret, _Fp, and _Args... are all complete types, cv void, + // or incomplete array types as required by the standard. + using _Result = decltype( + __invoke(declval<_Fp>(), declval<_Args>()...)); + + using type = + typename conditional< + !is_same<_Result, __nat>::value, + typename conditional< + is_void<_Ret>::value, + true_type, + is_convertible<_Result, _Ret> + >::type, + false_type + >::type; + static const bool value = type::value; + }; + + template + using __invokable = __invokable_r; + + template + struct __nothrow_invokable_r_imp { + static const bool value = false; + }; + + template + struct __nothrow_invokable_r_imp + { + typedef __nothrow_invokable_r_imp _ThisT; + + template + static void __test_noexcept(_Tp) noexcept; + + static const bool value = noexcept(_ThisT::__test_noexcept<_Ret>( + __invoke(declval<_Fp>(), declval<_Args>()...))); + }; + + template + struct __nothrow_invokable_r_imp + { + static const bool value = noexcept( + __invoke(declval<_Fp>(), declval<_Args>()...)); + }; + + template + using __nothrow_invokable_r = + __nothrow_invokable_r_imp< + __invokable_r<_Ret, _Fp, _Args...>::value, + is_void<_Ret>::value, + _Ret, _Fp, _Args... + >; + + template + using __nothrow_invokable = + __nothrow_invokable_r_imp< + __invokable<_Fp, _Args...>::value, + true, void, _Fp, _Args... + >; + + template + struct __invoke_of + : public enable_if< + __invokable<_Fp, _Args...>::value, + typename __invokable_r::_Result> + { + }; + + // result_of + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> + : public __invoke_of<_Fp, _Args...> + { + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using result_of_t = typename result_of<_Tp>::type; +#endif + +#if __WI_LIBCPP_STD_VER > 14 + + // invoke_result + + template + struct __WI_LIBCPP_TEMPLATE_VIS invoke_result + : __invoke_of<_Fn, _Args...> + { + }; + + template + using invoke_result_t = typename invoke_result<_Fn, _Args...>::type; + + // is_invocable + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_invocable + : integral_constant::value> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_invocable_r + : integral_constant::value> {}; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_invocable_v + = is_invocable<_Fn, _Args...>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_invocable_r_v + = is_invocable_r<_Ret, _Fn, _Args...>::value; + + // is_nothrow_invocable + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_invocable + : integral_constant::value> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r + : integral_constant::value> {}; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_invocable_v + = is_nothrow_invocable<_Fn, _Args...>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_invocable_r_v + = is_nothrow_invocable_r<_Ret, _Fn, _Args...>::value; + +#endif // __WI_LIBCPP_STD_VER > 14 + +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + template struct __is_swappable; + template struct __is_nothrow_swappable; + + template + inline __WI_LIBCPP_INLINE_VISIBILITY +#ifndef __WI_LIBCPP_CXX03_LANG + typename enable_if + < + is_move_constructible<_Tp>::value && + is_move_assignable<_Tp>::value + >::type +#else + void +#endif + swap_wil(_Tp& __x, _Tp& __y) __WI_NOEXCEPT_(is_nothrow_move_constructible<_Tp>::value && + is_nothrow_move_assignable<_Tp>::value) + { + _Tp __t(wistd::move(__x)); + __x = wistd::move(__y); + __y = wistd::move(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _ForwardIterator2 + swap_ranges_wil(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) + { + for(; __first1 != __last1; ++__first1, (void) ++__first2) + swap_wil(*__first1, *__first2); + return __first2; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Tp>::value + >::type + swap_wil(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) __WI_NOEXCEPT_(__is_nothrow_swappable<_Tp>::value) + { + wistd::swap_ranges_wil(__a, __a + _Np, __b); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + iter_swap_wil(_ForwardIterator1 __a, _ForwardIterator2 __b) + // __WI_NOEXCEPT_(__WI_NOEXCEPT_(swap_wil(*__a, *__b))) + __WI_NOEXCEPT_(__WI_NOEXCEPT_(swap_wil(*declval<_ForwardIterator1>(), + *declval<_ForwardIterator2>()))) + { + swap_wil(*__a, *__b); + } + + // __swappable + + namespace __detail + { + // ALL generic swap overloads MUST already have a declaration available at this point. + + template ::value && !is_void<_Up>::value> + struct __swappable_with + { + template + static decltype(swap_wil(declval<_LHS>(), declval<_RHS>())) + __test_swap(int); + template + static __nat __test_swap(long); + + // Extra parens are needed for the C++03 definition of decltype. + typedef decltype((__test_swap<_Tp, _Up>(0))) __swap1; + typedef decltype((__test_swap<_Up, _Tp>(0))) __swap2; + + static const bool value = !is_same<__swap1, __nat>::value + && !is_same<__swap2, __nat>::value; + }; + + template + struct __swappable_with<_Tp, _Up, false> : false_type {}; + + template ::value> + struct __nothrow_swappable_with { + static const bool value = +#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT + noexcept(swap_wil(declval<_Tp>(), declval<_Up>())) + && noexcept(swap_wil(declval<_Up>(), declval<_Tp>())); +#else + false; +#endif + }; + + template + struct __nothrow_swappable_with<_Tp, _Up, false> : false_type {}; + + } // __detail + + template + struct __is_swappable + : public integral_constant::value> + { + }; + + template + struct __is_nothrow_swappable + : public integral_constant::value> + { + }; + +#if __WI_LIBCPP_STD_VER > 14 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_swappable_with + : public integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_swappable + : public conditional< + __is_referenceable<_Tp>::value, + is_swappable_with< + typename add_lvalue_reference<_Tp>::type, + typename add_lvalue_reference<_Tp>::type>, + false_type + >::type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_swappable_with + : public integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_swappable + : public conditional< + __is_referenceable<_Tp>::value, + is_nothrow_swappable_with< + typename add_lvalue_reference<_Tp>::type, + typename add_lvalue_reference<_Tp>::type>, + false_type + >::type + { + }; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_swappable_with_v + = is_swappable_with<_Tp, _Up>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_swappable_v + = is_swappable<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_swappable_with_v + = is_nothrow_swappable_with<_Tp, _Up>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_swappable_v + = is_nothrow_swappable<_Tp>::value; + +#endif // __WI_LIBCPP_STD_VER > 14 + +#ifdef __WI_LIBCPP_UNDERLYING_TYPE + + template + struct underlying_type + { + typedef __WI_LIBCPP_UNDERLYING_TYPE(_Tp) type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using underlying_type_t = typename underlying_type<_Tp>::type; +#endif + +#else // __WI_LIBCPP_UNDERLYING_TYPE + + template + struct underlying_type + { + static_assert(_Support, "The underyling_type trait requires compiler " + "support. Either no such support exists or " + "libc++ does not know how to use it."); + }; + +#endif // __WI_LIBCPP_UNDERLYING_TYPE + + + template ::value> + struct __sfinae_underlying_type + { + typedef typename underlying_type<_Tp>::type type; + typedef decltype(((type)1) + 0) __promoted_type; + }; + + template + struct __sfinae_underlying_type<_Tp, false> {}; + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + int __convert_to_integral(int __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned __convert_to_integral(unsigned __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + long __convert_to_integral(long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned long __convert_to_integral(unsigned long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + long long __convert_to_integral(long long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned long long __convert_to_integral(unsigned long long __val) {return __val; } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename enable_if::value, long long>::type + __convert_to_integral(_Fp __val) { return __val; } + +#ifndef __WI_LIBCPP_HAS_NO_INT128 + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + __int128_t __convert_to_integral(__int128_t __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + __uint128_t __convert_to_integral(__uint128_t __val) { return __val; } +#endif + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename __sfinae_underlying_type<_Tp>::__promoted_type + __convert_to_integral(_Tp __val) { return __val; } + +#ifndef __WI_LIBCPP_CXX03_LANG + + template + struct __has_operator_addressof_member_imp + { + template + static auto __test(int) + -> typename __select_2nd().operator&()), true_type>::type; + template + static auto __test(long) -> false_type; + + static const bool value = decltype(__test<_Tp>(0))::value; + }; + + template + struct __has_operator_addressof_free_imp + { + template + static auto __test(int) + -> typename __select_2nd())), true_type>::type; + template + static auto __test(long) -> false_type; + + static const bool value = decltype(__test<_Tp>(0))::value; + }; + + template + struct __has_operator_addressof + : public integral_constant::value + || __has_operator_addressof_free_imp<_Tp>::value> + {}; + +#endif // __WI_LIBCPP_CXX03_LANG + +#ifndef __WI_LIBCPP_CXX03_LANG + + template using void_t = void; + +# ifndef __WI_LIBCPP_HAS_NO_VARIADICS + template + struct conjunction : __and_<_Args...> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool conjunction_v + = conjunction<_Args...>::value; + + template + struct disjunction : __or_<_Args...> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool disjunction_v + = disjunction<_Args...>::value; + + template + struct negation : __not_<_Tp> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool negation_v + = negation<_Tp>::value; +# endif // __WI_LIBCPP_HAS_NO_VARIADICS +#endif // __WI_LIBCPP_CXX03_LANG + + // These traits are used in __tree and __hash_table +#ifndef __WI_LIBCPP_CXX03_LANG + struct __extract_key_fail_tag {}; + struct __extract_key_self_tag {}; + struct __extract_key_first_tag {}; + + template ::type> + struct __can_extract_key + : conditional::value, __extract_key_self_tag, + __extract_key_fail_tag>::type {}; + + template + struct __can_extract_key<_Pair, _Key, pair<_First, _Second>> + : conditional::type, _Key>::value, + __extract_key_first_tag, __extract_key_fail_tag>::type {}; + + // __can_extract_map_key uses true_type/false_type instead of the tags. + // It returns true if _Key != _ContainerValueTy (the container is a map not a set) + // and _ValTy == _Key. + template ::type> + struct __can_extract_map_key + : integral_constant::value> {}; + + // This specialization returns __extract_key_fail_tag for non-map containers + // because _Key == _ContainerValueTy + template + struct __can_extract_map_key<_ValTy, _Key, _Key, _RawValTy> + : false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 17 + enum class endian + { + little = 0xDEAD, + big = 0xFACE, +#if defined(__WI_LIBCPP_LITTLE_ENDIAN) + native = little +#elif defined(__WI_LIBCPP_BIG_ENDIAN) + native = big +#else + native = 0xCAFE +#endif + }; +#endif +} +/// @endcond + +#endif // _WISTD_TYPE_TRAITS_H_ diff --git a/Externals/WIL/include/wil/wrl.h b/Externals/WIL/include/wil/wrl.h new file mode 100644 index 0000000000..e7f7c6c08c --- /dev/null +++ b/Externals/WIL/include/wil/wrl.h @@ -0,0 +1,84 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WRL_INCLUDED +#define __WIL_WRL_INCLUDED + +#include +#include "result.h" +#include "common.h" // wistd type_traits helpers + +namespace wil +{ + +#ifdef WIL_ENABLE_EXCEPTIONS +#pragma region Object construction helpers that throw exceptions + + /** Used to construct a RuntimeClass based object that uses 2 phase construction. + Construct a RuntimeClass based object that uses 2 phase construction (by implementing + RuntimeClassInitialize() and returning error codes for failures. + ~~~~ + // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() + auto someClass = MakeAndInitializeOrThrow(L"input", true); + ~~~~ */ + + template + Microsoft::WRL::ComPtr MakeAndInitializeOrThrow(TArgs&&... args) + { + Microsoft::WRL::ComPtr obj; + THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&obj, Microsoft::WRL::Details::Forward(args)...)); + return obj; + } + + /** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does + not require 2 phase construction). + ~~~~ + // SomeClass uses exceptions for error handling in its constructor. + auto someClass = MakeOrThrow(L"input", true); + ~~~~ */ + + template + Microsoft::WRL::ComPtr MakeOrThrow(TArgs&&... args) + { + // This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use. + // Unfortunately this produces false positives as all RuntimeClass derived classes have + // a RuntimeClassInitialize() method from their base class. + // static_assert(!std::is_member_function_pointer::value, + // "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead"); + auto obj = Microsoft::WRL::Make(Microsoft::WRL::Details::Forward(args)...); + THROW_IF_NULL_ALLOC(obj.Get()); + return obj; + } +#pragma endregion + +#endif // WIL_ENABLE_EXCEPTIONS + + /** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>. + Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC() + from wil\result.h to test the result. */ + template + ::Microsoft::WRL::ComPtr MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + return Callback, TDelegateInterface, FtmBase>>(wistd::forward(args)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template + ::Microsoft::WRL::ComPtr MakeAgileCallback(Args&&... args) + { + auto result = MakeAgileCallbackNoThrow(wistd::forward(args)...); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS +} // namespace wil + +#endif // __WIL_WRL_INCLUDED diff --git a/Externals/WIL/packaging/CMakeLists.txt b/Externals/WIL/packaging/CMakeLists.txt new file mode 100644 index 0000000000..1f56884761 --- /dev/null +++ b/Externals/WIL/packaging/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(nuget) diff --git a/Externals/WIL/packaging/nuget/CMakeLists.txt b/Externals/WIL/packaging/nuget/CMakeLists.txt new file mode 100644 index 0000000000..f3fd938b22 --- /dev/null +++ b/Externals/WIL/packaging/nuget/CMakeLists.txt @@ -0,0 +1,20 @@ + +file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/build_tools/nuget.exe" nuget_exe) +file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" nupkg_dir) +file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_BUILD_VERSION}.nupkg" wil_nupkg) + +# The build servers don't have an up-to-date version of nuget, so pull it down ourselves... +file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe}) + +file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h) + +add_custom_command(OUTPUT ${wil_nupkg} + COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec + ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets + ${wil_headers} + ${CMAKE_SOURCE_DIR}/LICENSE + ${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt) + +add_custom_target(make_wil_nupkg DEPENDS ${wil_nupkg}) diff --git a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec new file mode 100644 index 0000000000..be709b9ca0 --- /dev/null +++ b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec @@ -0,0 +1,21 @@ + + + + Microsoft.Windows.ImplementationLibrary + 0.0.0 + Windows Implementation Library + Microsoft + false + The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers. + windows utility wil native + © Microsoft Corporation. All rights reserved. + LICENSE + https://github.com/Microsoft/wil + + + + + + + + \ No newline at end of file diff --git a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets new file mode 100644 index 0000000000..29d756a9f9 --- /dev/null +++ b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets @@ -0,0 +1,8 @@ + + + + + $(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories) + + + diff --git a/Externals/WIL/scripts/azure-pipelines.yml b/Externals/WIL/scripts/azure-pipelines.yml new file mode 100644 index 0000000000..4b428d8748 --- /dev/null +++ b/Externals/WIL/scripts/azure-pipelines.yml @@ -0,0 +1,41 @@ +# Windows Implementation Library Pipeline + +trigger: +- master + +jobs: +- job: BuildAndTest + timeoutInMinutes: 360 + + pool: + vmImage: 'windows-2019' + + steps: + - script: | + choco install llvm + if %ERRORLEVEL% NEQ 0 goto :eof + echo ##vso[task.setvariable variable=PATH]%PATH%;C:\Program Files\LLVM\bin + displayName: 'Install Clang' + + - script: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat" + if %ERRORLEVEL% NEQ 0 goto :eof + + call scripts\init_all.cmd --fast + if %ERRORLEVEL% NEQ 0 goto :eof + + call scripts\build_all.cmd + displayName: 'Build x86' + + - script: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if %ERRORLEVEL% NEQ 0 goto :eof + + call scripts\init_all.cmd --fast + if %ERRORLEVEL% NEQ 0 goto :eof + + call scripts\build_all.cmd + displayName: 'Build x64' + + - script: call scripts\runtests.cmd + displayName: 'Run Tests' diff --git a/Externals/WIL/scripts/build_all.cmd b/Externals/WIL/scripts/build_all.cmd new file mode 100644 index 0000000000..32d88f4c99 --- /dev/null +++ b/Externals/WIL/scripts/build_all.cmd @@ -0,0 +1,51 @@ +@echo off +setlocal EnableDelayedExpansion + +set BUILD_ROOT=%~dp0\..\build + +if "%Platform%"=="x64" ( + set BUILD_ARCH=64 +) else if "%Platform%"=="x86" ( + set BUILD_ARCH=32 +) else if [%Platform%]==[] ( + echo ERROR: The build_all.cmd script must be run from a Visual Studio command window + exit /B 1 +) else ( + echo ERROR: Unrecognized/unsupported platform %Platform% + exit /B 1 +) + +call :build clang debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build clang release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build clang relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build clang minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +call :build msvc debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build msvc release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build msvc relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :build msvc minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +echo All build completed successfully! + +goto :eof + +:: build [compiler] [type] +:build +set BUILD_DIR=%BUILD_ROOT%\%1%BUILD_ARCH%%2 +if not exist %BUILD_DIR% ( + goto :eof +) + +pushd %BUILD_DIR% +echo Building from %CD% +ninja +popd +goto :eof diff --git a/Externals/WIL/scripts/init.cmd b/Externals/WIL/scripts/init.cmd new file mode 100644 index 0000000000..cc8efbfbe7 --- /dev/null +++ b/Externals/WIL/scripts/init.cmd @@ -0,0 +1,202 @@ +@echo off +setlocal +setlocal EnableDelayedExpansion + +:: Globals +set BUILD_ROOT=%~dp0\..\build + +goto :init + +:usage + echo USAGE: + echo init.cmd [--help] [-c^|--compiler ^] [-g^|--generator ^] + echo [-b^|--build-type ^] [-l^|--linker link^|lld-link] + echo [--fast] [-v^|--version X.Y.Z] + echo. + echo ARGUMENTS + echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc' + echo -g^|--generator Controls the CMake generator used, either 'ninja' (the default) or 'msbuild' + echo -b^|--build-type Controls the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default), 'release', + echo 'relwithdebinfo', or 'minsizerel' + echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI + echo build and is typically not necessary when building locally + echo --fast Used to (slightly) reduce compile times and build output size. This is primarily + echo used by the CI build machines where resources are more constrained. This switch is + echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed + goto :eof + +:init + :: Initialize values as empty so that we can identify if we are using defaults later for error purposes + set COMPILER= + set GENERATOR= + set BUILD_TYPE= + set LINKER= + set CMAKE_ARGS= + set BITNESS= + set VERSION= + set FAST_BUILD=0 + +:parse + if /I "%~1"=="" goto :execute + + if /I "%~1"=="--help" call :usage & goto :eof + + set COMPILER_SET=0 + if /I "%~1"=="-c" set COMPILER_SET=1 + if /I "%~1"=="--compiler" set COMPILER_SET=1 + if %COMPILER_SET%==1 ( + if "%COMPILER%" NEQ "" echo ERROR: Compiler already specified & call :usage & exit /B 1 + + if /I "%~2"=="clang" set COMPILER=clang + if /I "%~2"=="msvc" set COMPILER=msvc + if "!COMPILER!"=="" echo ERROR: Unrecognized/missing compiler %~2 & call :usage & exit /B 1 + + shift + shift + goto :parse + ) + + set GENERATOR_SET=0 + if /I "%~1"=="-g" set GENERATOR_SET=1 + if /I "%~1"=="--generator" set GENERATOR_SET=1 + if %GENERATOR_SET%==1 ( + if "%GENERATOR%" NEQ "" echo ERROR: Generator already specified & call :usage & exit /B 1 + + if /I "%~2"=="ninja" set GENERATOR=ninja + if /I "%~2"=="msbuild" set GENERATOR=msbuild + if "!GENERATOR!"=="" echo ERROR: Unrecognized/missing generator %~2 & call :usage & exit /B 1 + + shift + shift + goto :parse + ) + + set BUILD_TYPE_SET=0 + if /I "%~1"=="-b" set BUILD_TYPE_SET=1 + if /I "%~1"=="--build-type" set BUILD_TYPE_SET=1 + if %BUILD_TYPE_SET%==1 ( + if "%BUILD_TYPE%" NEQ "" echo ERROR: Build type already specified & call :usage & exit /B 1 + + if /I "%~2"=="debug" set BUILD_TYPE=debug + if /I "%~2"=="release" set BUILD_TYPE=release + if /I "%~2"=="relwithdebinfo" set BUILD_TYPE=relwithdebinfo + if /I "%~2"=="minsizerel" set BUILD_TYPE=minsizerel + if "!BUILD_TYPE!"=="" echo ERROR: Unrecognized/missing build type %~2 & call :usage & exit /B 1 + + shift + shift + goto :parse + ) + + set LINKER_SET=0 + if /I "%~1"=="-l" set LINKER_SET=1 + if /I "%~1"=="--linker" set LINKER_SET=1 + if %LINKER_SET%==1 ( + if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1 + + if /I "%~2"=="link" set LINKER=link + if /I "%~2"=="lld-link" set LINKER=lld-link + if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1 + + shift + shift + goto :parse + ) + + set VERSION_SET=0 + if /I "%~1"=="-v" set VERSION_SET=1 + if /I "%~1"=="--version" set VERSION_SET=1 + if %VERSION_SET%==1 ( + if "%VERSION%" NEQ "" echo ERROR: Version alread specified & call :usage & exit /B 1 + if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1 + + set VERSION=%~2 + + shift + shift + goto :parse + ) + + if /I "%~1"=="--fast" ( + if %FAST_BUILD% NEQ 0 echo ERROR: Fast build already specified + set FAST_BUILD=1 + shift + goto :parse + ) + + echo ERROR: Unrecognized argument %~1 + call :usage + exit /B 1 + +:execute + :: Check for conflicting arguments + if "%GENERATOR%"=="msbuild" ( + if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1 + + :: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator + if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1 + ) + + :: Select defaults + if "%GENERATOR%"=="" set GENERATOR=ninja + if %GENERATOR%==msbuild set COMPILER=msvc + + if "%COMPILER%"=="" set COMPILER=clang + + if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug + + if "%LINKER%"=="" set LINKER=link + + :: Formulate CMake arguments + if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja + + if %COMPILER%==clang set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl + if %COMPILER%==msvc set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl + + if %GENERATOR% NEQ msbuild ( + if %BUILD_TYPE%==debug set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug + if %BUILD_TYPE%==release set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Release + if %BUILD_TYPE%==relwithdebinfo set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=RelWithDebInfo + if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel + ) else ( + :: The Visual Studio generator, by default, will use the most recent Windows SDK version installed that is not + :: greater than the host OS version. This decision is to ensure that anything built will be able to run on the + :: machine that built it. This experience is generally okay, if not desired, but affects us since we build with + :: '/permissive-' etc. and older versions of the SDK are typically not as clean as the most recent versions. + :: This flag will force the generator to select the most recent SDK version independent of host OS version. + set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0 + ) + + if %LINKER%==lld-link ( + set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link + ) + + if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION% + + if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON + + :: Figure out the platform + if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1 + if "%Platform%"=="x86" ( + set BITNESS=32 + if %COMPILER%==clang set CFLAGS=-m32 & set CXXFLAGS=-m32 + ) + if "%Platform%"=="x64" set BITNESS=64 + if "%BITNESS%"=="" echo ERROR: Unrecognized/unsupported platform %Platform% & exit /B 1 + + :: Set up the build directory + set BUILD_DIR=%BUILD_ROOT%\%COMPILER%%BITNESS%%BUILD_TYPE% + mkdir %BUILD_DIR% > NUL 2>&1 + + :: Run CMake + pushd %BUILD_DIR% + echo Using compiler....... %COMPILER% + echo Using linker......... %LINKER% + echo Using architecture... %Platform% + echo Using build type..... %BUILD_TYPE% + echo Using build root..... %CD% + echo. + cmake %CMAKE_ARGS% ..\.. + popd + + goto :eof diff --git a/Externals/WIL/scripts/init_all.cmd b/Externals/WIL/scripts/init_all.cmd new file mode 100644 index 0000000000..9fc1417556 --- /dev/null +++ b/Externals/WIL/scripts/init_all.cmd @@ -0,0 +1,13 @@ +@echo off + +:: NOTE: Architecture is picked up from the command window, so we can't control that here :( + +call %~dp0\init.cmd -c clang -g ninja -b debug %* +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %* +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +call %~dp0\init.cmd -c msvc -g ninja -b debug %* +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %* +if %ERRORLEVEL% NEQ 0 ( goto :eof ) diff --git a/Externals/WIL/scripts/runtests.cmd b/Externals/WIL/scripts/runtests.cmd new file mode 100644 index 0000000000..e9282fbbce --- /dev/null +++ b/Externals/WIL/scripts/runtests.cmd @@ -0,0 +1,67 @@ +@echo off +setlocal EnableDelayedExpansion + +set BUILD_ROOT=%~dp0\..\build + +:: Unlike building, we don't need to limit ourselves to the Platform of the command window +call :execute_tests clang64debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang64release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang64relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang64minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +call :execute_tests clang32debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang32release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang32relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests clang32minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +call :execute_tests msvc64debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc64release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc64relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc64minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +call :execute_tests msvc32debug +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc32release +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc32relwithdebinfo +if %ERRORLEVEL% NEQ 0 ( goto :eof ) +call :execute_tests msvc32minsizerel +if %ERRORLEVEL% NEQ 0 ( goto :eof ) + +goto :eof + +:execute_tests +set BUILD_DIR=%BUILD_ROOT%\%1 +if not exist %BUILD_DIR% ( goto :eof ) + +pushd %BUILD_DIR% +echo Running tests from %CD% +call :execute_test app witest.app.exe +if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +call :execute_test cpplatest witest.cpplatest.exe +if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +call :execute_test noexcept witest.noexcept.exe +if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +call :execute_test normal witest.exe +if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +popd + +goto :eof + +:execute_test +if not exist tests\%1\%2 ( goto :eof ) +echo Running %1 tests... +tests\%1\%2 +goto :eof diff --git a/Externals/WIL/tests/CMakeLists.txt b/Externals/WIL/tests/CMakeLists.txt new file mode 100644 index 0000000000..a9c0fae159 --- /dev/null +++ b/Externals/WIL/tests/CMakeLists.txt @@ -0,0 +1,19 @@ + +include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake) + +# All projects need to reference the WIL headers +include_directories(${CMAKE_SOURCE_DIR}/include) + +# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that +include_directories(BEFORE SYSTEM ./workarounds/wrl) + +# The build pipelines have limitations that local development environments do not, so turn a few knobs +if (${FAST_BUILD}) + replace_cxx_flag("/GR" "/GR-") # Disables RTTI + add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD) +endif() + +add_subdirectory(app) +add_subdirectory(cpplatest) +add_subdirectory(noexcept) +add_subdirectory(normal) diff --git a/Externals/WIL/tests/ComTests.cpp b/Externals/WIL/tests/ComTests.cpp new file mode 100644 index 0000000000..009d9d7007 --- /dev/null +++ b/Externals/WIL/tests/ComTests.cpp @@ -0,0 +1,2717 @@ + +#include // Bring in IObjectWithSite + +#include +#include + +#include "common.h" + +using namespace Microsoft::WRL; + +// avoid including #include , it fails to compile in noprivateapis +EXTERN_C const CLSID CLSID_ShellLink; +class DECLSPEC_UUID("00021401-0000-0000-C000-000000000046") ShellLink; + +// Uncomment this line to do a more exhaustive test of the concepts covered by this file. By +// default we don't fully compile every combination of tests as this test can substantially impact +// build times with template expansion. + +// #define WIL_EXHAUSTIVE_TEST + +// Helper objects / functions +class __declspec(uuid("a817e7a2-43fa-11d0-9e44-00aa00b6770a")) +IUnknownFake : public IUnknown +{ +public: + STDMETHOD_(ULONG, AddRef)() + { + AddRefCounter++; + return 0; + } + STDMETHOD_(ULONG, Release)() + { + ReleaseCounter++; + return 0; + } + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) + { + if (riid == __uuidof(IUnknown)) + { + *ppvObject = this; + return S_OK; + } + *ppvObject = nullptr; + return E_NOINTERFACE; + } + bool ReturnTRUE() + { + return true; + } + static void Clear() + { + AddRefCounter = 0; + ReleaseCounter = 0; + } + static int GetAddRef() + { + int res = AddRefCounter; + AddRefCounter = 0; + return res; + } + static int GetRelease() + { + int res = ReleaseCounter; + ReleaseCounter = 0; + return res; + } +protected: + static int AddRefCounter; + static int ReleaseCounter; +}; + +int IUnknownFake::AddRefCounter = 0; +int IUnknownFake::ReleaseCounter = 0; + +class __declspec(uuid("a817e7a2-43fa-11d0-9e44-00aa00b6770b")) +IUnknownFake2 : public IUnknownFake {}; + +TEST_CASE("ComTests::Test_Constructors", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + SECTION("Null/default construction") + { + wil::com_ptr_nothrow ptr; //default constructor + REQUIRE(ptr.get() == nullptr); + + wil::com_ptr_nothrow ptr2(nullptr); //default explicit null constructor + REQUIRE(ptr2.get() == nullptr); + + IUnknown* nullPtr = nullptr; + wil::com_ptr_nothrow ptr3(nullPtr); + REQUIRE(ptr3.get() == nullptr); + } + + SECTION("Valid pointer construction") + { + wil::com_ptr_nothrow ptr(&helper); // explicit + REQUIRE(IUnknownFake::GetAddRef() == 1); + REQUIRE(ptr.get() == &helper); + } + + SECTION("Copy construction") + { + wil::com_ptr_nothrow ptr(&helper); + wil::com_ptr_nothrow ptrCopy(ptr); // assign the same pointer + REQUIRE(IUnknownFake::GetAddRef() == 2); + REQUIRE(ptrCopy.get() == ptr.get()); + + IUnknownFake2 helper2; + wil::com_ptr_nothrow ptr2(&helper2); + wil::com_ptr_nothrow ptrCopy2(ptr2); + REQUIRE(IUnknownFake::GetAddRef() == 2); + REQUIRE(ptrCopy2.get() == &helper2); + } + + SECTION("Move construction") + { + IUnknownFake helper3; + wil::com_ptr_nothrow ptr(&helper3); + wil::com_ptr_nothrow ptrMove(reinterpret_cast&&>(ptr)); + REQUIRE(IUnknownFake::GetAddRef() == 1); + REQUIRE(ptrMove.get() == &helper3); + REQUIRE(ptr.get() == nullptr); + + IUnknownFake2 helper4; + wil::com_ptr_nothrow ptr2(&helper4); + wil::com_ptr_nothrow ptrMove2(reinterpret_cast&&>(ptr2)); + REQUIRE(IUnknownFake::GetAddRef() == 1); + REQUIRE(ptrMove2.get() == &helper4); + REQUIRE(ptr2.get() == nullptr); + } +} + +TEST_CASE("ComTests::Test_Assign", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + SECTION("Null pointer assignment") + { + wil::com_ptr_nothrow ptr(&helper); + ptr = nullptr; + REQUIRE(ptr.get() == nullptr); + REQUIRE(IUnknownFake::GetRelease() == 1); + } + + IUnknownFake::Clear(); + IUnknownFake helper2; + + SECTION("Different pointer assignment") + { + wil::com_ptr_nothrow ptr(&helper); + wil::com_ptr_nothrow ptr2(&helper2); + + ptr = static_cast&>(ptr2); + + REQUIRE(ptr.get() == &helper2); + REQUIRE(ptr2.get() == &helper2); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 3); + } + + SECTION("Self assignment") + { + wil::com_ptr_nothrow ptr(&helper); + + IUnknownFake::Clear(); + ptr = ptr; + + REQUIRE(ptr.get() == &helper); + // wil::com_ptr can do self-assignment without blowing up -- and chooses NOT to preserve the this comparison for performance + // as this should be a rare/never operation... + // REQUIRE(IUnknownFake::GetRelease() == 0); + // REQUIRE(IUnknownFake::GetAddRef() == 0); + + ptr = std::move(ptr); + REQUIRE(ptr.get() == &helper); + } + + IUnknownFake2 helper3; + + SECTION("Assign pointer with different interface") + { + wil::com_ptr_nothrow ptr(&helper); + wil::com_ptr_nothrow ptr2(&helper3); + + IUnknownFake::Clear(); + ptr = static_cast&>(ptr2); + + REQUIRE(ptr.get() == &helper3); + REQUIRE(ptr2.get() == &helper3); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 1); + } + + SECTION("Move assignment") + { + wil::com_ptr_nothrow ptr(&helper); + wil::com_ptr_nothrow ptr2(&helper2); + + IUnknownFake::Clear(); + ptr = static_cast&&>(ptr2); + + REQUIRE(ptr.get() == &helper2); + REQUIRE(ptr2.get() == nullptr); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + } + + SECTION("Move assign with different interface") + { + wil::com_ptr_nothrow ptr(&helper); + wil::com_ptr_nothrow ptr2(&helper3); + + IUnknownFake::Clear(); + ptr = static_cast&&>(ptr2); + + REQUIRE(ptr.get() == &helper3); + REQUIRE(ptr2.get() == nullptr); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + } +} + +TEST_CASE("ComTests::Test_Operators", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + IUnknownFake helper2; + IUnknownFake2 helper3; + + wil::com_ptr_nothrow ptrNULL; //NULL one + wil::com_ptr_nothrow ptrLT(&helper); + wil::com_ptr_nothrow ptrGT(&helper2); + wil::com_ptr_nothrow ptrDiff(&helper3); + + SECTION("equal operator") + { + REQUIRE_FALSE(ptrNULL == ptrLT); + REQUIRE(ptrNULL == ptrNULL); + REQUIRE(ptrLT == ptrLT); + REQUIRE_FALSE(ptrDiff == ptrLT); + REQUIRE_FALSE(ptrLT == ptrGT); + } + + SECTION("not equals operator") + { + REQUIRE(ptrNULL != ptrLT); + REQUIRE_FALSE(ptrNULL != ptrNULL); + REQUIRE_FALSE(ptrLT != ptrLT); + REQUIRE(ptrDiff != ptrLT); + REQUIRE(ptrLT != ptrGT); + } + + SECTION("less-than operator") + { + REQUIRE_FALSE(ptrNULL < ptrNULL); + REQUIRE(ptrNULL < ptrLT); + REQUIRE(ptrNULL < ptrLT); + + if (ptrLT.get() < ptrGT.get()) + { + REQUIRE(ptrLT < ptrGT); + } + else + { + REQUIRE(ptrGT < ptrLT); + } + } +} + +TEST_CASE("ComTests::Test_Conversion", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + wil::com_ptr_nothrow nullPtr; + wil::com_ptr_nothrow ptr(&helper); + + REQUIRE_FALSE(nullPtr); + REQUIRE(ptr); +} + +TEST_CASE("ComTests::Test_Address", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + IUnknownFake** pFakePtr; + SECTION("addressof") + { + wil::com_ptr_nothrow ptr(&helper); + + IUnknownFake::Clear(); + pFakePtr = ptr.addressof(); + REQUIRE(IUnknownFake::GetRelease() == 0); + REQUIRE(IUnknownFake::GetAddRef() == 0); + REQUIRE((*pFakePtr) == &helper); + } + + SECTION("put") + { + wil::com_ptr_nothrow ptr(&helper); + IUnknownFake::Clear(); + + pFakePtr = ptr.put(); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + REQUIRE((*pFakePtr) == nullptr); + REQUIRE(ptr == nullptr); + } + + SECTION("put_void") + { + wil::com_ptr_nothrow ptr(&helper); + IUnknownFake::Clear(); + + void** pvFakePtr = ptr.put_void(); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + REQUIRE((*pvFakePtr) == nullptr); + REQUIRE(ptr == nullptr); + } + + SECTION("put_unknown") + { + wil::com_ptr_nothrow ptr(&helper); + IUnknownFake::Clear(); + + IUnknown** puFakePtr = ptr.put_unknown(); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + REQUIRE((*puFakePtr) == nullptr); + REQUIRE(ptr == nullptr); + } + + SECTION("Address operator") + { + wil::com_ptr_nothrow ptr(&helper); + IUnknownFake::Clear(); + + pFakePtr = &ptr; + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + REQUIRE((*pFakePtr) == nullptr); + REQUIRE(ptr == nullptr); + } +} + +TEST_CASE("ComTests::Test_Helpers", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + IUnknownFake helper2; + IUnknownFake *ptrHelper; + wil::com_ptr_nothrow ptr(&helper); + + SECTION("detach") + { + IUnknownFake::Clear(); //clear addref counter + ptrHelper = ptr.detach(); + REQUIRE(ptr.get() == nullptr); + REQUIRE(ptrHelper == &helper); + REQUIRE(IUnknownFake::GetAddRef() == 0); + } + + SECTION("attach") + { + ptrHelper = &helper; + wil::com_ptr_nothrow ptr2(&helper2); //have some non null pointer + + IUnknownFake::Clear(); //clear addref counter + ptr2.attach(ptrHelper); + REQUIRE(ptr2.get() == ptrHelper); + REQUIRE(IUnknownFake::GetRelease() == 1); + REQUIRE(IUnknownFake::GetAddRef() == 0); + } + + SECTION("get") + { + wil::com_ptr_nothrow ptr2; + REQUIRE(ptr2.get() == nullptr); + + IUnknownFake helper3; + wil::com_ptr_nothrow ptr4(&helper3); + REQUIRE(ptr4.get() == &helper3); + } + + SECTION("l-value swap") + { + wil::com_ptr_nothrow ptr2(&helper); + wil::com_ptr_nothrow ptr3(&helper2); + + ptr2.swap(ptr3); + REQUIRE(ptr2.get() == &helper2); + REQUIRE(ptr3.get() == &helper); + } + + SECTION("r-value swap") + { + wil::com_ptr_nothrow ptr2(&helper); + wil::com_ptr_nothrow ptr3(&helper2); + + ptr2.swap(wistd::move(ptr3)); + REQUIRE(ptr2.get() == &helper2); + REQUIRE(ptr3.get() == &helper); + } +} + +TEST_CASE("ComTests::Test_As", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + + IUnknownFake helper; + wil::com_ptr_nothrow ptr(&helper); + + SECTION("query by IID") + { + wil::com_ptr_nothrow ptr2; + // REQUIRE(S_OK == ptr.AsIID(__uuidof(IUnknown), &ptr2)); + REQUIRE(S_OK == ptr.query_to(__uuidof(IUnknown), reinterpret_cast(&ptr2))); + REQUIRE(ptr2 != nullptr); + } + + SECTION("query by invalid IID") + { + wil::com_ptr_nothrow ptr2; + // REQUIRE(S_OK != ptr.AsIID(__uuidof(IDispatch), &ptr2)); + REQUIRE(S_OK != ptr.query_to(__uuidof(IDispatch), reinterpret_cast(&ptr2))); + REQUIRE(ptr2 == nullptr); + } + + SECTION("same interface query") + { + // wil::com_ptr optimizes same-type assignment to just call AddRef + IUnknownFake2 helper2; + wil::com_ptr_nothrow ptr2(&helper2); + wil::com_ptr_nothrow ptr3; + REQUIRE(S_OK == ptr2.query_to(&ptr3)); + REQUIRE(ptr3 != nullptr); + } + + SECTION("base interface query") + { + IUnknownFake2 helper2; + wil::com_ptr_nothrow ptr2(&helper2); + wil::com_ptr_nothrow ptr3; + REQUIRE(S_OK == ptr2.query_to(&ptr3)); + REQUIRE(ptr3 != nullptr); + } +} + +TEST_CASE("ComTests::Test_CopyTo", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + + IUnknownFake helper; + IUnknownFake2 helper2; + wil::com_ptr_nothrow ptr(&helper); + + SECTION("copy by IID") + { + wil::com_ptr_nothrow ptr2; + REQUIRE(S_OK == ptr.copy_to(__uuidof(IUnknown), reinterpret_cast(&ptr2))); + REQUIRE(ptr2 != nullptr); + } + + SECTION("copy by invalid IID") + { + wil::com_ptr_nothrow ptr2; + REQUIRE(S_OK != ptr.copy_to(__uuidof(IDispatch), reinterpret_cast(&ptr2))); + REQUIRE(ptr2 == nullptr); + } + + SECTION("same interface copy") + { + wil::com_ptr_nothrow ptr2(&helper2); + wil::com_ptr_nothrow ptr3; + REQUIRE(S_OK == ptr2.copy_to(&ptr3)); + REQUIRE(ptr3 != nullptr); + } + + SECTION("base interface copy") + { + wil::com_ptr_nothrow ptr2(&helper2); + wil::com_ptr_nothrow ptr3; + REQUIRE(S_OK == ptr2.copy_to(ptr3.addressof())); + REQUIRE(ptr3 != nullptr); + } +} + +// Helper used to verify correctness of IID_PPV_ARGS support +void IID_PPV_ARGS_Test_Helper(REFIID iid, void** pv) +{ + __analysis_assume(pv != nullptr); + REQUIRE(pv != nullptr); + REQUIRE(*pv == nullptr); + *pv = reinterpret_cast(0x01); // Set check value + + REQUIRE(iid == __uuidof(IUnknown)); +} + +TEST_CASE("ComTests::Test_IID_PPV_ARGS", "[com][com_ptr]") +{ + wil::com_ptr_nothrow unk; + IID_PPV_ARGS_Test_Helper(IID_PPV_ARGS(&unk)); + //Test if we got the correct check value back + REQUIRE(unk.get() == reinterpret_cast(0x01)); + // Make sure that we will not try to release some garbage + auto avoidWarning = unk.detach(); + (void)avoidWarning; +} + +// Helps with testing wil::com_ptr configuration when the operator -> is used +class ExtensionHelper +{ +public: + HRESULT Extend() const + { + return S_OK; + } + STDMETHOD_(ULONG, AddRef)() const + { + return 0; + } + STDMETHOD_(ULONG, Release)() const + { + return 0; + } +}; + +TEST_CASE("ComTests::Test_ConstPointer", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + const wil::com_ptr_nothrow spUnk(&helper); + wil::com_ptr_nothrow spUnkHelper; + wil::com_ptr_nothrow spInspectable; + + REQUIRE(spUnk.get() != nullptr); + REQUIRE(spUnk); + spUnk.addressof(); + spUnk.copy_to(spUnkHelper.addressof()); + spUnk.copy_to(spInspectable.addressof()); + spUnk.copy_to(IID_PPV_ARGS(&spInspectable)); + + spUnk.query_to(&spUnkHelper); + spUnk.query_to(&spInspectable); + spUnk.query_to(__uuidof(IUnknown), reinterpret_cast(&spUnkHelper)); + + const ExtensionHelper extHelper; + wil::com_ptr_nothrow spExt(&extHelper); + REQUIRE(spExt->Extend() == S_OK); +} + +// Make sure that the pointer can be defined just with forward declaration of the class +TEST_CASE("ComTests::Test_ComPtrWithForwardDeclaration", "[com][com_ptr]") +{ + class MyClass; + + wil::com_ptr_nothrow spClass; + + class MyClass : public IUnknown + { + public: + STDMETHOD_(ULONG, AddRef)() + { + return 0; + } + STDMETHOD_(ULONG, Release)() + { + return 0; + } + }; +} + +//***************************************************************************** +// various com_ptr tests +//***************************************************************************** + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a00")) +ITest : public IUnknown +{ + STDMETHOD_(void, Test)() = 0; +}; + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a01")) +IDerivedTest : public ITest +{ + STDMETHOD_(void, TestDerived)() = 0; +}; + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a02")) +ITestInspectable : public IInspectable +{ + STDMETHOD_(void, TestInspctable)() = 0; +}; + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a03")) +IDerivedTestInspectable : public ITestInspectable +{ + STDMETHOD_(void, TestInspctableDerived)() = 0; +}; + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a04")) +INever : public IUnknown +{ + STDMETHOD_(void, Never)() = 0; +}; + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a05")) +IAlways : public IUnknown +{ + STDMETHOD_(void, Always)() = 0; +}; + +class __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00")) // non-implemented to allow QI for the class to be attempted (and fail) +ComObject : witest::AllocatedObject, + public Microsoft::WRL::RuntimeClass, + Microsoft::WRL::ChainInterfaces, + IAlways>{ +public: + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) Test() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) TestDerived() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) Always() {} +}; + +class __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b01")) // non-implemented to allow QI for the class to be attempted (and fail) +WinRtObject : witest::AllocatedObject, + public Microsoft::WRL::RuntimeClass, + ITest, IDerivedTest, ITestInspectable, IDerivedTestInspectable, IAlways, Microsoft::WRL::FtmBase> +{ +public: + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) Test() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) TestDerived() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) TestInspctable() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) TestInspctableDerived() {} + COM_DECLSPEC_NOTHROW IFACEMETHODIMP_(void) Always() {} +}; + +class NoCom : witest::AllocatedObject +{ +public: + ULONG __stdcall AddRef() + { + return m_ref++; + } + + ULONG __stdcall Release() + { + auto retVal = (--m_ref); + if (retVal == 0) + { + delete this; + } + return retVal; + } + +private: + ULONG m_ref = 1; +}; + +template >> +T* cast_object(U*) +{ + FAIL_FAST(); +} + +template +T* cast_object(T* ptr) +{ + return ptr; +} + +template +static IFace* make_object() +{ + auto obj = Microsoft::WRL::Make(); + + IFace* result = nullptr; + if (FAILED(obj.Get()->QueryInterface(__uuidof(IFace), reinterpret_cast(&result)))) + { + // The QI only fails when we're asking for a CFoo from a CFoo (equivalent types)... in this + // case just return the original pointer -- the reinterpret_cast is needed as the code is shared + // and the other (nonuniform) cases also compile it (but do not execute it). + result = cast_object(obj.Detach()); + } + + return result; +} + +template <> +NoCom* make_object() +{ + return new NoCom(); +} + +template +void TestSmartPointer(const Ptr& ptr1, const Ptr& ptr2) +{ + SECTION("swap (method and global)") + { + auto p1 = ptr1; + auto p2 = ptr2; + p1.swap(p2); // l-value + REQUIRE(((p1 == ptr2) && (p2 == ptr1))); + p1.swap(wistd::move(p2)); // r-value + REQUIRE(((p1 == ptr1) && (p2 == ptr2))); + wil::swap(p1, p2); + REQUIRE(((p1 == ptr2) && (p2 == ptr1))); + } + + SECTION("WRL swap (method and global)") + { + auto p1 = ptr1; + Microsoft::WRL::ComPtr p2 = ptr2.get(); + p1.swap(p2); // l-value + REQUIRE(((p1 == ptr2) && (p2 == ptr1))); + p1.swap(wistd::move(p2)); // r-value + REQUIRE(((p1 == ptr1) && (p2 == ptr2))); + wil::swap(p1, p2); + REQUIRE(((p1 == ptr2) && (p2 == ptr1))); + wil::swap(p2, p1); + REQUIRE(((p1 == ptr1) && (p2 == ptr2))); + } + + SECTION("reset") + { + auto p = ptr1; + p.reset(); + REQUIRE_FALSE(p); + p = ptr1; + p.reset(nullptr); + REQUIRE_FALSE(p); + } + + SECTION("attach / detach") + { + auto p1 = ptr1; + auto p2 = ptr2; + p1.attach(p2.detach()); + REQUIRE(((p1.get() == ptr2.get()) && !p2)); + } + + SECTION("addressof") + { + auto p1 = ptr1; + auto p2 = ptr2; + p1.addressof(); // Doesn't reset + REQUIRE(p1.get() == ptr1.get()); + p1.reset(); + *(p1.addressof()) = p2.detach(); + REQUIRE(p1.get() == ptr2.get()); + } + + SECTION("put") + { + auto p1 = ptr1; + auto p2 = ptr2; + p1.put(); + REQUIRE_FALSE(p1); + *p1.put() = p2.detach(); + REQUIRE(p1.get() == ptr2.get()); + } + + SECTION("operator&") + { + auto p1 = ptr1; + auto p2 = ptr2; + &p1; + REQUIRE_FALSE(p1); + *(&p1) = p2.detach(); + REQUIRE(p1.get() == ptr2.get()); + } + + SECTION("exercise const methods on the const param (ensure const)") + { + auto address = ptr1.addressof(); + REQUIRE(*address == ptr1.get()); + (void)static_cast(ptr1); + ptr1.get(); + auto deref = ptr1.operator->(); + (void)deref; + if (ptr1) + { + auto& ref = ptr1.operator*(); + (void)ref; + } + } +} + +template +static void TestPointerCombination(IFace* p1, IFace* p2) +{ +#ifdef WIL_ENABLE_EXCEPTIONS + TestSmartPointer(wil::com_ptr(p1), wil::com_ptr(p2)); +#endif + TestSmartPointer(wil::com_ptr_failfast(p1), wil::com_ptr_failfast(p2)); + TestSmartPointer(wil::com_ptr_nothrow(p1), wil::com_ptr_nothrow(p2)); +} + +template +static void TestPointer() +{ + auto p1 = make_object(); + auto p2 = make_object(); + IFace* nullPtr = nullptr; + TestPointerCombination(p1, p2); + TestPointerCombination(nullPtr, p2); + TestPointerCombination(p1, nullPtr); + TestPointerCombination(nullPtr, nullPtr); + TestPointerCombination(p1, p1); // same object + + p1->Release(); + p2->Release(); +} + +TEST_CASE("ComTests::Test_MemberFunctions", "[com][com_ptr]") +{ + // avoid overwhelming debug logging, perhaps the COM helpers are over reporting + auto restoreDebugString = wil::g_fResultOutputDebugString; + wil::g_fResultOutputDebugString = false; + + TestPointer(); + + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + TestPointer(); + + REQUIRE_FALSE(witest::g_objectCount.Leaked()); + wil::g_fResultOutputDebugString = restoreDebugString; +} + +template +static void TestSmartPointerConversion(const Ptr1& ptr1, const Ptr2& ptr2) +{ + const Microsoft::WRL::ComPtr wrl1 = ptr1.get(); + const Microsoft::WRL::ComPtr wrl2 = ptr2.get(); + + SECTION("global comparison operators") + { + auto p1 = ptr1.get(); + auto p2 = ptr2.get(); + + // com_ptr to com_ptr + REQUIRE((ptr1 == ptr2) == (p1 == p2)); + REQUIRE((ptr1 != ptr2) == (p1 != p2)); + REQUIRE((ptr1 < ptr2) == (p1 < p2)); + REQUIRE((ptr1 <= ptr2) == (p1 <= p2)); + REQUIRE((ptr1 > ptr2) == (p1 > p2)); + REQUIRE((ptr1 >= ptr2) == (p1 >= p2)); + + // com_ptr to ComPtr + REQUIRE((wrl1 == ptr2) == (p1 == p2)); + REQUIRE((wrl1 != ptr2) == (p1 != p2)); + REQUIRE((wrl1 < ptr2) == (p1 < p2)); + REQUIRE((wrl1 <= ptr2) == (p1 <= p2)); + REQUIRE((wrl1 > ptr2) == (p1 > p2)); + REQUIRE((wrl1 >= ptr2) == (p1 >= p2)); + + REQUIRE((ptr1 == wrl2) == (p1 == p2)); + REQUIRE((ptr1 != wrl2) == (p1 != p2)); + REQUIRE((ptr1 < wrl2) == (p1 < p2)); + REQUIRE((ptr1 <= wrl2) == (p1 <= p2)); + REQUIRE((ptr1 > wrl2) == (p1 > p2)); + REQUIRE((ptr1 >= wrl2) == (p1 >= p2)); + + // com_ptr to raw pointer + REQUIRE((ptr1 == p2) == (p1 == p2)); + REQUIRE((ptr1 != p2) == (p1 != p2)); + REQUIRE((ptr1 < p2) == (p1 < p2)); + REQUIRE((ptr1 <= p2) == (p1 <= p2)); + REQUIRE((ptr1 > p2) == (p1 > p2)); + REQUIRE((ptr1 >= p2) == (p1 >= p2)); + + REQUIRE((p1 == ptr2) == (p1 == p2)); + REQUIRE((p1 != ptr2) == (p1 != p2)); + REQUIRE((p1 < ptr2) == (p1 < p2)); + REQUIRE((p1 <= ptr2) == (p1 <= p2)); + REQUIRE((p1 > ptr2) == (p1 > p2)); + REQUIRE((p1 >= ptr2) == (p1 >= p2)); + } + + SECTION("construct from raw pointer") + { + Ptr1 p1(ptr2.get()); + Ptr1 p2 = ptr2.get(); + REQUIRE(((p1 == ptr2) && (p2 == ptr2))); + } + + SECTION("construct from com_ptr ref<>") + { + Ptr1 p1(ptr2); + Ptr1 p2 = (ptr2); + REQUIRE(((p1 == ptr2) && (p2 == ptr2))); + } + + SECTION("r-value construct from com_ptr ref<>") + { + auto move1 = ptr2; + auto move2 = ptr2; + Ptr1 p1(wistd::move(move1)); + Ptr1 p2 = wistd::move(move2); + REQUIRE(((p1 == ptr2) && (p2 == ptr2))); + } + + SECTION("assign from raw pointer") + { + Ptr1 p = ptr1; + p = (ptr2.get()); + REQUIRE(p == ptr2); + } + + SECTION("assign from com_ptr ref<>") + { + Ptr1 p = ptr1; + p = ptr2; + REQUIRE(p == ptr2); + } + + SECTION("r-value assign from com_ptr ref<>") + { + Ptr1 p = ptr1; + p = Ptr2(ptr2); + REQUIRE(p == ptr2); + } + + SECTION("construct from ComPtr ref<>") + { + Ptr1 p1(wrl2); + Ptr1 p2 = (wrl2); + REQUIRE(((p1 == wrl2) && (p2 == wrl2))); + } + + SECTION("r-value construct from ComPtr ref<>") + { + auto move1 = wrl2; + auto move2 = wrl2; + Ptr1 p1(wistd::move(move1)); + Ptr1 p2 = wistd::move(move2); + REQUIRE(((p1 == wrl2) && (p2 == wrl2))); + } + + SECTION("assign from ComPtr ref<>") + { + Ptr1 p = ptr1; + p = wrl2; + REQUIRE(p == wrl2); + } + + SECTION("r-value assign from ComPtr ref<>") + { + Ptr1 p = ptr1; + p = decltype(wrl2)(wrl2); + REQUIRE(p == wrl2); + } +} + +template +static void TestPointerConversionCombination(IFace1* p1, IFace2* p2) +{ +#ifdef WIL_ENABLE_EXCEPTIONS + TestSmartPointerConversion(wil::com_ptr(p1), wil::com_ptr_nothrow(p2)); +#endif + TestSmartPointerConversion(wil::com_ptr_failfast(p1), wil::com_ptr_nothrow(p2)); + TestSmartPointerConversion(wil::com_ptr_nothrow(p1), wil::com_ptr_nothrow(p2)); + +#ifdef WIL_EXHAUSTIVE_TEST +#ifdef WIL_ENABLE_EXCEPTIONS + TestSmartPointerConversion(wil::com_ptr(p1), wil::com_ptr(p2)); + TestSmartPointerConversion(wil::com_ptr_failfast(p1), wil::com_ptr(p2)); + TestSmartPointerConversion(wil::com_ptr_nothrow(p1), wil::com_ptr(p2)); + + TestSmartPointerConversion(wil::com_ptr(p1), wil::com_ptr_failfast(p2)); +#endif + TestSmartPointerConversion(wil::com_ptr_failfast(p1), wil::com_ptr_failfast(p2)); + TestSmartPointerConversion(wil::com_ptr_nothrow(p1), wil::com_ptr_failfast(p2)); +#endif +} + +template +static void TestPointerConversion() +{ + auto p1 = make_object(); + auto p2 = make_object(); + IFace1* nullPtr1 = nullptr; + IFace2* nullPtr2 = nullptr; + TestPointerConversionCombination(p1, p2); + TestPointerConversionCombination(nullPtr1, p2); + TestPointerConversionCombination(p1, nullPtr2); + TestPointerConversionCombination(nullPtr1, nullPtr2); + TestPointerConversionCombination(static_cast(p2), p2); // same object + + p1->Release(); + p2->Release(); +} + +TEST_CASE("ComTests::Test_PointerConversion", "[com][com_ptr]") +{ + // avoid overwhelming debug logging, perhaps the COM helpers are over reporting + auto restoreDebugString = wil::g_fResultOutputDebugString; + wil::g_fResultOutputDebugString = false; + + TestPointerConversion(); + + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + +#ifdef WIL_EXHAUSTIVE_TEST + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); + TestPointerConversion(); +#endif + + REQUIRE_FALSE(witest::g_objectCount.Leaked()); + wil::g_fResultOutputDebugString = restoreDebugString; +} + +template +void TestGlobalQueryIidPpv(wistd::true_type, const Ptr& source) // interface +{ + using DestPtr = wil::com_ptr_nothrow; + wil::com_ptr_nothrow never; + + SECTION("com_query_to(iid, ppv)") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_query_to(source, IID_PPV_ARGS(&dest1)); + REQUIRE_ERROR(wil::com_query_to(source, IID_PPV_ARGS(&never))); + REQUIRE((dest1 && !never)); +#endif + + DestPtr dest2, dest3; + wil::com_query_to_failfast(source, IID_PPV_ARGS(&dest2)); + REQUIRE_ERROR(wil::com_query_to_failfast(source, IID_PPV_ARGS(&never))); + wil::com_query_to_nothrow(source, IID_PPV_ARGS(&dest3)); + REQUIRE_ERROR(wil::com_query_to_nothrow(source, IID_PPV_ARGS(&never))); + REQUIRE((dest2 && dest3 && !never)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_CRASH(wil::com_query_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE_CRASH(wil::com_query_to(source, IID_PPV_ARGS(&never))); +#endif + + DestPtr dest2, dest3; + REQUIRE_CRASH(wil::com_query_to_failfast(source, IID_PPV_ARGS(&dest2))); + REQUIRE_CRASH(wil::com_query_to_failfast(source, IID_PPV_ARGS(&never))); + REQUIRE_CRASH(wil::com_query_to_nothrow(source, IID_PPV_ARGS(&dest3))); + REQUIRE_CRASH(wil::com_query_to_nothrow(source, IID_PPV_ARGS(&never))); + } + } + + SECTION("try_com_query_to(iid, ppv)") + { + if (source) + { + DestPtr dest1; + REQUIRE(wil::try_com_query_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE_FALSE(wil::try_com_query_to(source, IID_PPV_ARGS(&never))); + REQUIRE((dest1 && !never)); + } + else + { + DestPtr dest1; + REQUIRE_CRASH(wil::try_com_query_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE_CRASH(wil::try_com_query_to(source, IID_PPV_ARGS(&never))); + } + } + + SECTION("com_copy_to(iid, ppv)") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_copy_to(source, IID_PPV_ARGS(&dest1)); + REQUIRE_ERROR(wil::com_copy_to(source, IID_PPV_ARGS(&never))); + REQUIRE((dest1 && !never)); +#endif + + DestPtr dest2, dest3; + wil::com_copy_to_failfast(source, IID_PPV_ARGS(&dest2)); + REQUIRE_ERROR(wil::com_copy_to_failfast(source, IID_PPV_ARGS(&never))); + wil::com_copy_to_nothrow(source, IID_PPV_ARGS(&dest3)); + REQUIRE_ERROR(wil::com_copy_to_nothrow(source, IID_PPV_ARGS(&never))); + REQUIRE((dest2 && dest3 && !never)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_copy_to(source, IID_PPV_ARGS(&dest1)); + wil::com_copy_to(source, IID_PPV_ARGS(&never)); +#endif + + DestPtr dest2, dest3; + wil::com_copy_to_failfast(source, IID_PPV_ARGS(&dest2)); + wil::com_copy_to_failfast(source, IID_PPV_ARGS(&never)); + wil::com_copy_to_nothrow(source, IID_PPV_ARGS(&dest3)); + wil::com_copy_to_nothrow(source, IID_PPV_ARGS(&never)); + } + } + + SECTION("try_com_copy_to(iid, ppv)") + { + if (source) + { + DestPtr dest1; + REQUIRE(wil::try_com_copy_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE_FALSE(wil::try_com_copy_to(source, IID_PPV_ARGS(&never))); + REQUIRE((dest1 && !never)); + } + else + { + DestPtr dest1; + REQUIRE_FALSE(wil::try_com_copy_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE_FALSE(wil::try_com_copy_to(source, IID_PPV_ARGS(&never))); + } + } +} + +template +void TestGlobalQueryIidPpv(wistd::false_type, const Ptr&) // class +{ + // we can't compile against iid, ppv with a class +} + +template +static void TestGlobalQuery(const Ptr& source) +{ + using DestPtr = wil::com_ptr_nothrow; + wil::com_ptr_nothrow never; + + SECTION("com_query") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(wil::com_query(source)); + REQUIRE_ERROR(wil::com_query(source)); +#endif + + REQUIRE(wil::com_query_failfast(source)); + REQUIRE_ERROR(wil::com_query_failfast(source)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_CRASH(wil::com_query(source)); + REQUIRE_CRASH(wil::com_query(source)); +#endif + + REQUIRE_CRASH(wil::com_query_failfast(source)); + REQUIRE_CRASH(wil::com_query_failfast(source)); + } + } + + SECTION("com_query_to(U**)") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_query_to(source, &dest1); + REQUIRE_ERROR(wil::com_query_to(source, &never)); + REQUIRE((dest1 && !never)); +#endif + + DestPtr dest2, dest3; + wil::com_query_to_failfast(source, &dest2); + REQUIRE_ERROR(wil::com_query_to_failfast(source, &never)); + wil::com_query_to_nothrow(source, &dest3); + REQUIRE_ERROR(wil::com_query_to_nothrow(source, &never)); + REQUIRE((dest2 && dest3 && !never)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_CRASH(wil::com_query_to(source, &dest1)); + REQUIRE_CRASH(wil::com_query_to(source, &never)); +#endif + + DestPtr dest2, dest3; + REQUIRE_CRASH(wil::com_query_to_failfast(source, &dest2)); + REQUIRE_CRASH(wil::com_query_to_failfast(source, &never)); + REQUIRE_CRASH(wil::com_query_to_nothrow(source, &dest3)); + REQUIRE_CRASH(wil::com_query_to_nothrow(source, &never)); + } + } + + SECTION("try_com_query") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(wil::try_com_query(source)); + REQUIRE_FALSE(wil::try_com_query(source)); +#endif + + REQUIRE(wil::try_com_query_failfast(source)); + REQUIRE_FALSE(wil::try_com_query_failfast(source)); + REQUIRE(wil::try_com_query_nothrow(source)); + REQUIRE_FALSE(wil::try_com_query_nothrow(source)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_CRASH(wil::try_com_query(source)); + REQUIRE_CRASH(wil::try_com_query(source)); +#endif + + REQUIRE_CRASH(wil::try_com_query_failfast(source)); + REQUIRE_CRASH(wil::try_com_query_failfast(source)); + REQUIRE_CRASH(wil::try_com_query_nothrow(source)); + REQUIRE_CRASH(wil::try_com_query_nothrow(source)); + } + } + + SECTION("try_com_query_to(U**)") + { + if (source) + { + DestPtr dest1; + REQUIRE(wil::try_com_query_to(source, &dest1)); + REQUIRE_FALSE(wil::try_com_query_to(source, &never)); + REQUIRE((dest1 && !never)); + } + else + { + DestPtr dest1; + REQUIRE_CRASH(wil::try_com_query_to(source, &dest1)); + REQUIRE_CRASH(wil::try_com_query_to(source, &never)); + } + } + + SECTION("com_copy") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(wil::com_copy(source)); + REQUIRE_ERROR(wil::com_copy(source)); +#endif + + REQUIRE(wil::com_copy_failfast(source)); + REQUIRE_ERROR(wil::com_copy_failfast(source)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_FALSE(wil::com_copy(source)); + REQUIRE_FALSE(wil::com_copy(source)); +#endif + + REQUIRE_FALSE(wil::com_copy_failfast(source)); + REQUIRE_FALSE(wil::com_copy_failfast(source)); + } + } + + SECTION("com_copy_to(U**)") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_copy_to(source, &dest1); + REQUIRE_ERROR(wil::com_copy_to(source, &never)); + REQUIRE((dest1 && !never)); +#endif + + DestPtr dest2, dest3; + wil::com_copy_to_failfast(source, &dest2); + REQUIRE_ERROR(wil::com_copy_to_failfast(source, &never)); + wil::com_copy_to_nothrow(source, &dest3); + REQUIRE_ERROR(wil::com_copy_to_nothrow(source, &never)); + REQUIRE((dest2 && dest3 && !never)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + wil::com_copy_to(source, &dest1); + wil::com_copy_to(source, &never); +#endif + + DestPtr dest2, dest3; + wil::com_copy_to_failfast(source, &dest2); + wil::com_copy_to_failfast(source, &never); + wil::com_copy_to_nothrow(source, &dest3); + wil::com_copy_to_nothrow(source, &never); + } + } + + SECTION("try_com_copy") + { + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(wil::try_com_copy(source)); + REQUIRE_FALSE(wil::try_com_copy(source)); +#endif + REQUIRE(wil::try_com_copy_failfast(source)); + REQUIRE_FALSE(wil::try_com_copy_failfast(source)); + REQUIRE(wil::try_com_copy_nothrow(source)); + REQUIRE_FALSE(wil::try_com_copy_nothrow(source)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_FALSE(wil::try_com_copy(source)); + REQUIRE_FALSE(wil::try_com_copy(source)); +#endif + REQUIRE_FALSE(wil::try_com_copy_failfast(source)); + REQUIRE_FALSE(wil::try_com_copy_failfast(source)); + REQUIRE_FALSE(wil::try_com_copy_nothrow(source)); + REQUIRE_FALSE(wil::try_com_copy_nothrow(source)); + } + } + + SECTION("try_com_copy_to(U**)") + { + if (source) + { + DestPtr dest1; + REQUIRE(wil::try_com_copy_to(source, &dest1)); + REQUIRE_FALSE(wil::try_com_copy_to(source, &never)); + REQUIRE((dest1 && !never)); + } + else + { + DestPtr dest1; + REQUIRE_FALSE(wil::try_com_copy_to(source, &dest1)); + REQUIRE_FALSE(wil::try_com_copy_to(source, &never)); + } + } + + TestGlobalQueryIidPpv(typename wistd::is_abstract::type(), source); +} + +// Test fluent query functions for types that support them (exception and fail fast) +template +void TestSmartPointerQueryFluent(wistd::true_type, const Ptr& source) // void return (non-error based) +{ + SECTION("query") + { + if (source) + { + REQUIRE(source.template query()); + REQUIRE_ERROR(source.template query()); + } + else + { + REQUIRE_CRASH(source.template query()); + REQUIRE_CRASH(source.template query()); + } + } + + SECTION("copy") + { + if (source) + { + REQUIRE(source.template copy()); + REQUIRE_ERROR(source.template copy()); + } + else + { + REQUIRE_FALSE(source.template copy()); + REQUIRE_FALSE(source.template copy()); + } + } +} + +// "Test" fluent query functions for error-based types (by doing nothing) +template +void TestSmartPointerQueryFluent(wistd::false_type, const Ptr& /*source*/) // error-code based return +{ + // error code based code cannot call the fluent error methods +} + +// Test iid, ppv queries for types that support them (interfaces yes, classes no) +template +void TestSmartPointerQueryIidPpv(wistd::true_type, const Ptr& source) // interface +{ + wil::com_ptr_nothrow never; + using DestPtr = wil::com_ptr_nothrow; + + SECTION("query_to(iid, ppv)") + { + if (source) + { + DestPtr dest; + source.query_to(IID_PPV_ARGS(&dest)); + REQUIRE_ERROR(source.query_to(IID_PPV_ARGS(&never))); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE_CRASH(source.query_to(IID_PPV_ARGS(&dest))); + REQUIRE_CRASH(source.query_to(IID_PPV_ARGS(&never))); + REQUIRE((!dest && !never)); + } + } + + SECTION("try_query_to(iid, ppv)") + { + if (source) + { + DestPtr dest; + REQUIRE(source.try_query_to(IID_PPV_ARGS(&dest))); + REQUIRE(!source.try_query_to(IID_PPV_ARGS(&never))); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE_CRASH(source.try_query_to(IID_PPV_ARGS(&dest))); + REQUIRE_CRASH(source.try_query_to(IID_PPV_ARGS(&never))); + REQUIRE((!dest && !never)); + } + } + + SECTION("copy_to(iid, ppv)") + { + if (source) + { + DestPtr dest; + source.copy_to(IID_PPV_ARGS(&dest)); + REQUIRE_ERROR(source.copy_to(IID_PPV_ARGS(&never))); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + source.copy_to(IID_PPV_ARGS(&dest)); + source.copy_to(IID_PPV_ARGS(&never)); + REQUIRE((!dest && !never)); + } + } + + SECTION("try_copy_to(iid, ppv)") + { + if (source) + { + DestPtr dest; + REQUIRE(source.try_copy_to(IID_PPV_ARGS(&dest))); + REQUIRE(!source.try_copy_to(IID_PPV_ARGS(&never))); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE(!source.try_copy_to(IID_PPV_ARGS(&dest))); + REQUIRE(!source.try_copy_to(IID_PPV_ARGS(&never))); + REQUIRE((!dest && !never)); + } + } +} + +// "Test" iid, ppv queries for types that support them for a class (unsupported same (interfaces yes, classes no) +template +void TestSmartPointerQueryIidPpv(wistd::false_type, const Ptr& /*source*/) // class +{ + // we can't compile against iid, ppv with a class +} + +// Test the various query and copy methods against the given source pointer (trying produce the given dest pointer) +template +void TestSmartPointerQuery(const Ptr& source) +{ + wil::com_ptr_nothrow never; + using DestPtr = wil::com_ptr_nothrow; + + SECTION("query_to(U**)") + { + if (source) + { + DestPtr dest; + source.query_to(&dest); + REQUIRE_ERROR(source.query_to(&never)); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE_CRASH(source.query_to(&dest)); + REQUIRE_CRASH(source.query_to(&never)); + REQUIRE((!dest && !never)); + } + } + + SECTION("try_query") + { + if (source) + { + REQUIRE(source.template try_query()); + REQUIRE_FALSE(source.template try_query()); + } + else + { + REQUIRE_CRASH(source.template try_query()); + REQUIRE_CRASH(source.template try_query()); + } + } + + SECTION("try_query_to(U**)") + { + if (source) + { + DestPtr dest; + REQUIRE(source.try_query_to(&dest)); + REQUIRE_FALSE(source.try_query_to(&never)); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE_CRASH(source.try_query_to(&dest)); + REQUIRE_CRASH(source.try_query_to(&never)); + REQUIRE((!dest && !never)); + } + } + + SECTION("copy_to(U**)") + { + if (source) + { + DestPtr dest; + source.copy_to(&dest); + REQUIRE_ERROR(source.copy_to(&never)); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + source.copy_to(&dest); + source.copy_to(&never); + REQUIRE((!dest && !never)); + } + } + + SECTION("try_copy") + { + if (source) + { + REQUIRE(source.template try_copy()); + REQUIRE_FALSE(source.template try_copy()); + } + else + { + REQUIRE_FALSE(source.template try_copy()); + REQUIRE_FALSE(source.template try_copy()); + } + } + + SECTION("try_copy_to(U**)") + { + if (source) + { + DestPtr dest; + REQUIRE(source.try_copy_to(&dest)); + REQUIRE_FALSE(source.try_copy_to(&never)); + REQUIRE((dest && !never)); + } + else + { + DestPtr dest; + REQUIRE_FALSE(source.try_copy_to(&dest)); + REQUIRE_FALSE(source.try_copy_to(&never)); + REQUIRE((!dest && !never)); + } + } + + TestSmartPointerQueryFluent(typename wistd::is_same::type(), source); + + TestSmartPointerQueryIidPpv(typename wistd::is_abstract::type(), source); +} + +template +static void TestQueryCombination(IFace* ptr) +{ + TestGlobalQuery(ptr); +#ifdef WIL_EXHAUSTIVE_TEST +#ifdef WIL_ENABLE_EXCEPTIONS + TestGlobalQuery(wil::com_ptr(ptr)); +#endif + TestGlobalQuery(wil::com_ptr_failfast(ptr)); +#endif + TestGlobalQuery(wil::com_ptr_nothrow(ptr)); + TestGlobalQuery(Microsoft::WRL::ComPtr(ptr)); + +#ifdef WIL_ENABLE_EXCEPTIONS + TestSmartPointerQuery(wil::com_ptr(ptr)); +#endif + TestSmartPointerQuery(wil::com_ptr_failfast(ptr)); + TestSmartPointerQuery(wil::com_ptr_nothrow(ptr)); +} + +template +static void TestQuery(IFace* ptr) +{ + IFace* nullPtr = nullptr; + TestQueryCombination(ptr); + TestQueryCombination(nullPtr); +} + +template +static void TestQuery() +{ + auto ptr = make_object(); + TestQuery(ptr); + ptr->Release(); +} + +TEST_CASE("ComTests::Test_Query", "[com][com_ptr]") +{ + // avoid overwhelming debug logging, perhaps the COM helpers are over reporting + auto restoreDebugString = wil::g_fResultOutputDebugString; + wil::g_fResultOutputDebugString = false; + + TestQuery(); // Same type (no QI) + TestQuery(); // Ambiguous base (must QI) + TestQuery(); // Non-ambiguous base (no QI) + + // This adds a significant amount of time to the compilation duration, so most tests are disabled by default... +#ifdef WIL_EXHAUSTIVE_TEST + TestQuery(); // ComObject + TestQuery(); + TestQuery(); // IUnknown + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // ITest + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IDerivedTest + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IAlways + TestQuery(); + TestQuery(); + TestQuery(); + + TestQuery(); // WinRtObject + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IUnknown + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IInspectable + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // ITest + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IDerivedTest + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // ITestInspectable + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IDerivedTestInspectable + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); // IAlways + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); + TestQuery(); +#endif + + REQUIRE_FALSE(witest::g_objectCount.Leaked()); + wil::g_fResultOutputDebugString = restoreDebugString; +} + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +template +void TestAgile(const Ptr& source) +{ + bool source_valid = (source != nullptr); + + if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + auto agile1 = wil::com_agile_query(source); + REQUIRE(agile1); +#endif + + auto agile2 = wil::com_agile_query_failfast(source); + wil::com_agile_ref_nothrow agile3; + REQUIRE_SUCCEEDED(wil::com_agile_query_nothrow(source, &agile3)); + REQUIRE((agile2 && agile3)); + } + else + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_CRASH(wil::com_agile_query(source)); +#endif + + REQUIRE_CRASH(wil::com_agile_query_failfast(source)); + wil::com_agile_ref_nothrow agile3; + REQUIRE_CRASH(wil::com_agile_query_nothrow(source, &agile3)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + auto agile1 = wil::com_agile_copy(source); + REQUIRE(static_cast(agile1) == source_valid); +#endif + + auto agile2 = wil::com_agile_copy_failfast(source); + wil::com_agile_ref_nothrow agile3; + REQUIRE_SUCCEEDED(wil::com_agile_copy_nothrow(source, &agile3)); + REQUIRE(static_cast(agile2) == source_valid); + REQUIRE(static_cast(agile3) == source_valid); +} + +template +void TestAgileCombinations() +{ + auto ptr = make_object(); + + REQUIRE_SUCCEEDED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)); + auto exit = wil::scope_exit([] { ::CoUninitialize(); }); + + TestAgile(ptr); + TestAgile(wil::com_ptr_nothrow(ptr)); + TestAgile(Microsoft::WRL::ComPtr(ptr)); + + auto agilePtr = wil::com_agile_query_failfast(ptr); + TestQuery(agilePtr.get()); +#ifdef WIL_EXHAUSTIVE_TEST + TestQuery(agilePtr.get()); + TestQuery(agilePtr.get()); + TestQuery(agilePtr.get()); + TestQuery(agilePtr.get()); + TestQuery(agilePtr.get()); + TestQuery(agilePtr.get()); +#endif + + ptr->Release(); +} + +TEST_CASE("ComTests::Test_Agile", "[com][com_agile_ref]") +{ + // TestAgileCombinations(); + TestAgileCombinations(); + TestAgileCombinations(); + TestAgileCombinations(); +#ifdef WIL_EXHAUSTIVE_TEST + TestAgileCombinations(); + TestAgileCombinations(); + TestAgileCombinations(); + TestAgileCombinations(); +#endif + + REQUIRE_FALSE(witest::g_objectCount.Leaked()); +} +#endif + +template +void TestWeak(const Ptr& source) +{ + bool supports_weak = (source && (wil::try_com_query_nothrow(source))); + + if (supports_weak && source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + auto weak1 = wil::com_weak_query(source); + REQUIRE(weak1); +#endif + + auto weak2 = wil::com_weak_query_failfast(source); + wil::com_weak_ref_nothrow weak3; + REQUIRE_SUCCEEDED(wil::com_weak_query_nothrow(source, &weak3)); + REQUIRE((weak2 && weak3)); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto weak1copy = wil::com_weak_copy(source); + REQUIRE(weak1copy); +#endif + auto weak2copy = wil::com_weak_copy_failfast(source); + wil::com_weak_ref_nothrow weak3copy; + REQUIRE_SUCCEEDED(wil::com_weak_copy_nothrow(source, &weak3copy)); + REQUIRE((weak2copy && weak3copy)); + } + else if (source) + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_ERROR(wil::com_weak_query(source)); +#endif + + REQUIRE_ERROR(wil::com_weak_query_failfast(source)); + wil::com_weak_ref_nothrow weak3err; + REQUIRE_ERROR(wil::com_weak_query_nothrow(source, &weak3err)); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_ERROR(wil::com_weak_copy(source)); +#endif + + REQUIRE_ERROR(wil::com_weak_copy_failfast(source)); + wil::com_weak_ref_nothrow weak3; + REQUIRE_ERROR(wil::com_weak_copy_nothrow(source, &weak3)); + } + else // !source + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_CRASH(wil::com_weak_query(source)); +#endif + + REQUIRE_CRASH(wil::com_weak_query_failfast(source)); + wil::com_weak_ref_nothrow weak3crash; + REQUIRE_CRASH(wil::com_weak_query_nothrow(source, &weak3crash)); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto weak1 = wil::com_weak_copy(source); + REQUIRE(!weak1); +#endif + + auto weak2 = wil::com_weak_copy_failfast(source); + wil::com_weak_ref_nothrow weak3; + REQUIRE_SUCCEEDED(wil::com_weak_copy_nothrow(source, &weak3)); + REQUIRE((!weak2 && !weak3)); + } +} + +template +void TestGlobalQueryWithFailedResolve(const Ptr& source) +{ + // No need to test the null source and wrong interface query + // since that's covered in the TestGlobalQuery. + using DestPtr = wil::com_ptr_nothrow; + + SECTION("com_query") + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_ERROR(wil::com_query(source)); +#endif + REQUIRE_ERROR(wil::com_query_failfast(source)); + } + + SECTION("com_query_to(U**)") + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_ERROR(wil::com_query_to(source, &dest1)); + REQUIRE(!dest1); +#endif + + DestPtr dest2, dest3; + REQUIRE_ERROR(wil::com_query_to_failfast(source, &dest2)); + REQUIRE_ERROR(wil::com_query_to_nothrow(source, &dest3)); + REQUIRE((!dest2 && !dest3)); + } + + SECTION("try_com_query") + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(!wil::try_com_query(source)); +#endif + REQUIRE(!wil::try_com_query_failfast(source)); + REQUIRE(!wil::try_com_query_nothrow(source)); + } + + SECTION("try_com_query_to(U**)") + { + DestPtr dest1; + REQUIRE(!wil::try_com_query_to(source, &dest1)); + REQUIRE(!dest1); + } + + SECTION("com_copy") + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_ERROR(wil::com_copy(source)); +#endif + REQUIRE_ERROR(wil::com_copy_failfast(source)); + } + + SECTION("com_copy_to(U**)") + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_ERROR(wil::com_copy_to(source, &dest1)); + REQUIRE(!dest1); +#endif + + DestPtr dest2, dest3; + REQUIRE_ERROR(wil::com_copy_to_failfast(source, &dest2)); + REQUIRE_ERROR(wil::com_copy_to_nothrow(source, &dest3)); + REQUIRE((!dest2 && !dest3)); + } + + + SECTION("try_com_copy") + { +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(!wil::try_com_copy(source)); +#endif + REQUIRE(!wil::try_com_copy_failfast(source)); + REQUIRE(!wil::try_com_copy_nothrow(source)); + } + + SECTION("try_com_copy_to(U**)") + { + DestPtr dest1; + REQUIRE(!wil::try_com_copy_to(source, &dest1)); + REQUIRE(!dest1); + } + + if (wistd::is_abstract::value) + { + SECTION("com_query_to(iid, ppv)") + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_ERROR(wil::com_query_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE(!dest1); +#endif + + DestPtr dest2, dest3; + REQUIRE_ERROR(wil::com_query_to_failfast(source, IID_PPV_ARGS(&dest2))); + REQUIRE_ERROR(wil::com_query_to_nothrow(source, IID_PPV_ARGS(&dest3))); + REQUIRE((!dest2 && !dest3)); + } + + SECTION("try_com_query_to(iid, ppv)") + { + DestPtr dest1; + REQUIRE(!wil::try_com_query_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE(!dest1); + } + + SECTION("com_copy_to(iid, ppv)") + { +#ifdef WIL_ENABLE_EXCEPTIONS + DestPtr dest1; + REQUIRE_ERROR(wil::com_copy_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE(!dest1); +#endif + + DestPtr dest2, dest3; + REQUIRE_ERROR(wil::com_copy_to_failfast(source, IID_PPV_ARGS(&dest2))); + REQUIRE_ERROR(wil::com_copy_to_nothrow(source, IID_PPV_ARGS(&dest3))); + REQUIRE((!dest2 && !dest3)); + } + + SECTION("try_com_copy_to(iid, ppv)") + { + DestPtr dest1; + REQUIRE(!wil::try_com_copy_to(source, IID_PPV_ARGS(&dest1))); + REQUIRE(!dest1); + } + } +} + +template +void TestSmartPointerQueryFluentWithFailedResolve(wistd::false_type, const Ptr& /*source*/) +{ +} + + template +void TestSmartPointerQueryFluentWithFailedResolve(wistd::true_type, const Ptr& source) +{ + REQUIRE_ERROR(source.template query()); + REQUIRE_ERROR(source.template copy()); +} + +template +void TestSmartPointerQueryWithFailedResolve(const Ptr source) +{ + using DestPtr = wil::com_ptr_nothrow; + + SECTION("query_to(U**)") + { + DestPtr dest; + REQUIRE_ERROR(source.query_to(&dest)); + REQUIRE(!dest); + } + + SECTION("try_query") + { + REQUIRE(!source.template try_query()); + } + + SECTION("try_query_to(U**)") + { + DestPtr dest; + REQUIRE(!source.try_query_to(&dest)); + REQUIRE(!dest); + } + + SECTION("copy_to(U**)") + { + DestPtr dest; + REQUIRE_ERROR(source.copy_to(&dest)); + REQUIRE(!dest); + } + + SECTION("try_copy") + { + REQUIRE(!source.template try_copy()); + } + + SECTION("try_copy_to(U**)") + { + DestPtr dest; + REQUIRE(!source.try_copy_to(&dest)); + REQUIRE(!dest); + } + + TestSmartPointerQueryFluentWithFailedResolve(typename wistd::is_same::type(), source); + + if (wistd::is_abstract::value) + { + SECTION("query_to(iid, ppv)") + { + DestPtr dest; + REQUIRE_ERROR(source.query_to(IID_PPV_ARGS(&dest))); + REQUIRE(!dest); + } + + SECTION("try_query_to(iid, ppv)") + { + DestPtr dest; + REQUIRE(!source.try_query_to(IID_PPV_ARGS(&dest))); + REQUIRE(!dest); + } + + SECTION("copy_to(iid, ppv)") + { + DestPtr dest; + REQUIRE_ERROR(source.copy_to(IID_PPV_ARGS(&dest))); + REQUIRE(!dest); + } + + SECTION("try_copy_to(iid, ppv)") + { + DestPtr dest; + REQUIRE(!source.try_copy_to(IID_PPV_ARGS(&dest))); + REQUIRE(!dest); + } + } +} + +template +void TestQueryWithFailedResolve(IFace* ptr) +{ + TestGlobalQueryWithFailedResolve(ptr); +#ifdef WIL_EXHAUSTIVE_TEST +#ifdef WIL_ENABLE_EXCEPTIONS + TestGlobalQueryWithFailedResolve(wil::com_ptr(ptr)); +#endif + TestGlobalQueryWithFailedResolve(wil::com_ptr_failfast(ptr)); +#endif + TestGlobalQueryWithFailedResolve(wil::com_ptr_nothrow(ptr)); + TestGlobalQueryWithFailedResolve(Microsoft::WRL::ComPtr(ptr)); + +#ifdef WIL_ENABLE_EXCEPTIONS + TestSmartPointerQueryWithFailedResolve(wil::com_ptr(ptr)); +#endif + TestSmartPointerQueryWithFailedResolve(wil::com_ptr_nothrow(ptr)); + TestSmartPointerQueryWithFailedResolve(wil::com_ptr_failfast(ptr)); +} + +template +void TestWeakCombinations() +{ + auto ptr = make_object(); + + TestWeak(ptr); + TestWeak(wil::com_ptr_nothrow(ptr)); + TestWeak(Microsoft::WRL::ComPtr(ptr)); + + auto weakPtr = wil::com_weak_query_failfast(ptr); + TestQuery(weakPtr.get()); // Not IInspectable derived + TestQuery(weakPtr.get()); // IInspectable derived + +#ifdef WIL_EXHAUSTIVE_TEST + TestQuery(weakPtr.get()); + TestQuery(weakPtr.get()); + TestQuery(weakPtr.get()); + TestQuery(weakPtr.get()); + TestQuery(weakPtr.get()); +#endif + + // On the final release of the pointer, the weak reference will no longer resolve + ptr->Release(); + TestQueryWithFailedResolve(weakPtr.get()); + TestQueryWithFailedResolve(weakPtr.get()); +#ifdef WIL_EXHAUSTIVE_TEST + TestQueryWithFailedResolve(weakPtr.get()); +#endif +} + +TEST_CASE("ComTests::Test_Weak", "[com][com_weak_ref]") +{ + // TestWeakCombinations(); + TestWeakCombinations(); +#ifdef WIL_EXHAUSTIVE_TEST + TestWeakCombinations(); + TestWeakCombinations(); + TestWeakCombinations(); + TestWeakCombinations(); + TestWeakCombinations(); + TestWeakCombinations(); +#endif + + REQUIRE_FALSE(witest::g_objectCount.Leaked()); +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +TEST_CASE("ComTests::VerifyCoCreate", "[com][CoCreateInstance]") +{ + auto init = wil::CoInitializeEx_failfast(); + + // success cases +#ifdef WIL_ENABLE_EXCEPTIONS + auto link1 = wil::CoCreateInstance(); + auto link2 = wil::CoCreateInstance(CLSID_ShellLink); +#endif + auto link3 = wil::CoCreateInstanceFailFast(); + auto link4 = wil::CoCreateInstanceFailFast(CLSID_ShellLink); + auto link5 = wil::CoCreateInstanceNoThrow(); + auto link6 = wil::CoCreateInstanceNoThrow(CLSID_ShellLink); + + // failure +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_THROWS((wil::CoCreateInstance())); +#endif + // skip this test, assume testing the exception based version is sufficient. + // auto link2 = wil::CoCreateInstanceFailFast(); + REQUIRE_FALSE(static_cast(wil::CoCreateInstanceNoThrow().get())); +} + +TEST_CASE("ComTests::VerifyCoGetClassObject", "[com][CoGetClassObject]") +{ + auto init = wil::CoInitializeEx_failfast(); + + // success cases +#ifdef WIL_ENABLE_EXCEPTIONS + auto linkFactory1 = wil::CoGetClassObject(); + auto linkFactory2 = wil::CoGetClassObject(CLSID_ShellLink); +#endif + auto linkFactory3 = wil::CoGetClassObjectFailFast(); + auto linkFactory4 = wil::CoGetClassObjectFailFast(CLSID_ShellLink); + auto linkFactory5 = wil::CoGetClassObjectNoThrow(); + auto linkFactory6 = wil::CoGetClassObjectNoThrow(CLSID_ShellLink); + + // failure +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_THROWS((wil::CoGetClassObject())); +#endif + // skip this test, assume testing the exception based version is sufficient. + // auto linkFactory2 = wil::CoGetClassObjectFailFast(); + REQUIRE_FALSE(static_cast(wil::CoGetClassObjectNoThrow())); +} +#endif + +#ifdef __IObjectWithSite_INTERFACE_DEFINED__ +TEST_CASE("ComTests::VerifyComSetSiteNullIsMoveOnly", "[com][com_set_site]") +{ + wil::unique_set_site_null_call call1; + + // intentional compilation errors for copy construction/assignment + // wil::unique_set_site_null_call call2 = call1; + // call2 = call1; + + auto siteSetter = wil::com_set_site(nullptr, nullptr); + auto siteSetter2 = std::move(siteSetter); // Move construction + siteSetter2 = std::move(siteSetter); // Move assignment +} + +TEST_CASE("ComTests::VerifyComSetSite", "[com][com_set_site]") +{ + class ObjectWithSite WrlFinal : public RuntimeClass, IObjectWithSite> + { + public: + STDMETHODIMP SetSite(IUnknown* val) noexcept override + { + m_site = val; + return S_OK; + } + + STDMETHODIMP GetSite(REFIID riid, void** ppv) noexcept override + { + m_site.try_copy_to(riid, ppv); + return S_OK; + } + + private: + wil::com_ptr_nothrow m_site; + }; + + class ServiceObject WrlFinal : public RuntimeClass, IServiceProvider> + { + public: + ServiceObject(IServiceProvider* site = nullptr) + { + m_site = site; + } + + STDMETHODIMP QueryService(REFIID /*sid*/, REFIID /*riid*/, void** ppv) noexcept override + { + *ppv = nullptr; + return E_NOTIMPL; + } + private: + wil::com_ptr_nothrow m_site; + }; + + auto objWithSite = Make(); + auto serviceObj = Make(); + auto serviceObj2 = Make(serviceObj.Get()); + + { + auto cleanupSite = wil::com_set_site(objWithSite.Get(), serviceObj2.Get()); + + wil::com_ptr_nothrow site; + REQUIRE_SUCCEEDED(objWithSite->GetSite(IID_PPV_ARGS(&site))); + REQUIRE(static_cast(site)); + + auto siteCount = 0; + wil::for_each_site(objWithSite.Get(), [&](IUnknown* /*site*/) + { + siteCount++; + }); + REQUIRE(siteCount == 2); + } + + wil::com_ptr_nothrow site; + REQUIRE_SUCCEEDED(objWithSite->GetSite(IID_PPV_ARGS(&site))); + REQUIRE_FALSE(static_cast(site)); +} +#endif + +class FakeStream : public IStream +{ +public: + + STDMETHOD(QueryInterface)(REFIID riid, PVOID* ppv) override + { + if ((riid == __uuidof(IStream)) || + (riid == __uuidof(ISequentialStream)) || + (riid == __uuidof(IUnknown))) + { + *ppv = static_cast(this); + return S_OK; + } + + return E_NOTIMPL; + } + + STDMETHOD_(ULONG, AddRef)() override + { + return 2; + } + + STDMETHOD_(ULONG, Release)() override + { + return 1; + } + + unsigned long long Position = 0; + unsigned long long PositionMax = 0; + unsigned long MaxReadSize = 0; + unsigned long MaxWriteSize = 0; + unsigned long long TotalSize = 0; + + // ISequentialStream + STDMETHOD(Read)(_Out_writes_bytes_to_(cb, *pcbRead) void *pv, _In_ ULONG cb, _Out_opt_ ULONG *pcbRead) override + { + if (pcbRead) + { + *pcbRead = min(MaxReadSize, cb); + } + + ZeroMemory(pv, cb); + return (MaxReadSize <= cb) ? S_OK : S_FALSE; + } + + STDMETHOD(Write)(_In_reads_bytes_(cb) const void *, _In_ ULONG cb, _Out_opt_ ULONG *pcbWritten) override + { + if (pcbWritten) + { + *pcbWritten = min(MaxWriteSize, cb); + } + + return (MaxWriteSize <= cb) ? S_OK : S_FALSE; + } + + // IStream + STDMETHOD(Seek)(LARGE_INTEGER dlibMove, DWORD dwOrigin, _Out_opt_ ULARGE_INTEGER *plibNewPosition) + { + if (dwOrigin == STREAM_SEEK_CUR) + { + if ((dlibMove.QuadPart < 0) && (static_cast(-dlibMove.QuadPart) > Position)) + { + Position = 0; + } + else + { + Position += dlibMove.QuadPart; + } + } + else if (dwOrigin == STREAM_SEEK_SET) + { + Position = static_cast(dlibMove.QuadPart); + } + else if (dwOrigin == STREAM_SEEK_END) + { + if ((dlibMove.QuadPart < 0) && (static_cast(-dlibMove.QuadPart) > Position)) + { + Position = 0; + } + else + { + Position = PositionMax + dlibMove.QuadPart; + } + } + + Position = min(Position, PositionMax); + + if (plibNewPosition) + { + plibNewPosition->QuadPart = Position; + } + + return S_OK; + } + + STDMETHOD(Stat)(__RPC__out STATSTG *pstatstg, DWORD) override + { + *pstatstg = {}; + pstatstg->cbSize.QuadPart = TotalSize; + return S_OK; + } + + STDMETHOD(Revert)(void) override + { + return E_NOTIMPL; + } + + STDMETHOD(SetSize)(ULARGE_INTEGER) override + { + return E_NOTIMPL; + } + + STDMETHOD(Clone)(__RPC__deref_out_opt IStream **ppstm) override + { + *ppstm = this; + return S_OK; + } + + STDMETHOD(Commit)(DWORD) override + { + return E_NOTIMPL; + } + + STDMETHOD(CopyTo)(_In_ IStream *pstm, ULARGE_INTEGER cb, _Out_opt_ ULARGE_INTEGER *pcbRead, _Out_opt_ ULARGE_INTEGER *pcbWritten) override + { + unsigned long didWrite; + unsigned long didRead; + + FAIL_FAST_IF(cb.HighPart != 0); + RETURN_IF_FAILED(this->Read(nullptr, cb.LowPart, &didRead)); + RETURN_IF_FAILED(pstm->Write(nullptr, didRead, &didWrite)); + + pcbRead->QuadPart = didRead; + pcbWritten->QuadPart = didWrite; + + return S_OK; + } + + STDMETHOD(LockRegion)(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) override + { + return E_NOTIMPL; + } + + STDMETHOD(UnlockRegion)(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) override + { + return E_NOTIMPL; + } + + void SetPosition(unsigned long long position, unsigned long long positionMax) + { + Position = position; + PositionMax = positionMax; + } + + void SetPosition(unsigned long long position) + { + return SetPosition(position, position); + } +}; + +TEST_CASE("StreamTests::ReadPartial", "[com][IStream]") +{ + FakeStream stream; + stream.MaxReadSize = 16; + BYTE buffer[32]; + ULONG readSize; + + // Reading more than what's available is OK + REQUIRE_SUCCEEDED(wil::stream_read_partial_nothrow(&stream, buffer, 32, &readSize)); + REQUIRE(stream.MaxReadSize == readSize); + + // Reading less than what's available is OK + REQUIRE_SUCCEEDED(wil::stream_read_partial_nothrow(&stream, buffer, 5, &readSize)); + REQUIRE(5 == readSize); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(stream.MaxReadSize == wil::stream_read_partial(&stream, buffer, 32)); + REQUIRE(5ULL == wil::stream_read_partial(&stream, buffer, 5)); +#endif +} + +TEST_CASE("StreamTests::Read", "[com][IStream]") +{ + FakeStream stream; + stream.MaxReadSize = 10; + BYTE buffer[32]; + + // Reading less than available is OK + REQUIRE_SUCCEEDED(wil::stream_read_nothrow(&stream, buffer, 5)); + + // Reading more is not. + REQUIRE(stream.MaxReadSize < sizeof(buffer)); + REQUIRE_FAILED(wil::stream_read_nothrow(&stream, buffer, sizeof(buffer))); + + struct Header + { + ULONG Flags; + ULONG Other; + } header; + + // Reading a POD when there's not enough fails + stream.MaxReadSize = sizeof(header) - 1; + REQUIRE_FAILED(wil::stream_read_nothrow(&stream, &header)); + + // Reading a POD when there is is OK (and prove that the read happened) + header.Flags = 1; + header.Other = 2; + stream.MaxReadSize = sizeof(header); + REQUIRE_SUCCEEDED(wil::stream_read_nothrow(&stream, &header)); + REQUIRE(0UL == header.Flags); + REQUIRE(0UL == header.Other); + +#ifdef WIL_ENABLE_EXCEPTIONS + // Reading less than available is OK + REQUIRE_NOTHROW(wil::stream_read(&stream, buffer, 5)); + REQUIRE_THROWS(wil::stream_read(&stream, buffer, sizeof(buffer))); + + // Reading a POD when there's not enough fails + stream.MaxReadSize = sizeof(Header) - 1; + REQUIRE_THROWS(wil::stream_read
(&stream)); + + // Reading a POD when there is is OK (and prove that the read happened) + stream.MaxReadSize = sizeof(Header); + header = wil::stream_read
(&stream); + REQUIRE(0UL == header.Flags); + REQUIRE(0UL == header.Other); +#endif +} + +TEST_CASE("StreamTests::Write", "[com][IStream]") +{ + FakeStream stream; + BYTE buffer[16] = { 8, 6, 7, 5, 3, 0, 9 }; + + stream.MaxWriteSize = sizeof(buffer) + 1; + REQUIRE_SUCCEEDED(wil::stream_write_nothrow(&stream, buffer, sizeof(buffer))); + + stream.MaxWriteSize = sizeof(buffer) - 1; + REQUIRE_FAILED(wil::stream_write_nothrow(&stream, buffer, sizeof(buffer))); + + struct Header + { + ULONG Flags; + ULONG Other; + } header = { 1, 2 }; + + stream.MaxWriteSize = sizeof(header) + 1; + REQUIRE_SUCCEEDED(wil::stream_write_nothrow(&stream, header)); + + stream.MaxWriteSize = sizeof(header) - 1; + REQUIRE_FAILED(wil::stream_write_nothrow(&stream, header)); + +#ifdef WIL_ENABLE_EXCEPTIONS + stream.MaxWriteSize = sizeof(buffer) + 1; + REQUIRE_NOTHROW(wil::stream_write(&stream, buffer, sizeof(buffer))); + + stream.MaxWriteSize = sizeof(buffer) - 1; + REQUIRE_THROWS(wil::stream_write(&stream, buffer, sizeof(buffer))); + + header = { 1, 2 }; + stream.MaxWriteSize = sizeof(header) + 1; + REQUIRE_NOTHROW(wil::stream_write(&stream, header)); + + stream.MaxWriteSize = sizeof(header) - 1; + REQUIRE_THROWS(wil::stream_write(&stream, header)); +#endif +} + +TEST_CASE("StreamTests::Size", "[com][IStream]") +{ + FakeStream stream; + unsigned long long size; + + stream.TotalSize = 150; + REQUIRE_SUCCEEDED(wil::stream_size_nothrow(&stream, &size)); + REQUIRE(stream.TotalSize == size); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(stream.TotalSize == wil::stream_size(&stream)); +#endif +} + +TEST_CASE("StreamTests::SeekStart", "[com][IStream]") +{ + FakeStream stream; + unsigned long long landed; + + // Seek within the stream + stream.SetPosition(100, 1000); + REQUIRE_SUCCEEDED(wil::stream_set_position_nothrow(&stream, 10)); + REQUIRE(10ULL == stream.Position); + + // Seek and get the landing position + REQUIRE_SUCCEEDED(wil::stream_set_position_nothrow(&stream, 11, &landed)); + REQUIRE(11ULL == stream.Position); + REQUIRE(11ULL == landed); + + // Seek past the end + REQUIRE_SUCCEEDED(wil::stream_set_position_nothrow(&stream, 5000, &landed)); + REQUIRE(stream.PositionMax == landed); + + // Seek to the start + REQUIRE_SUCCEEDED(wil::stream_reset_nothrow(&stream)); + REQUIRE(0ULL == stream.Position); + +#ifdef WIL_ENABLE_EXCEPTIONS + // Seek within the stream + stream.SetPosition(100, 1000); + REQUIRE(10ULL == wil::stream_set_position(&stream, 10)); + + // Seek past the end + REQUIRE(stream.PositionMax == wil::stream_set_position(&stream, 5000)); + + // Seek to the start + REQUIRE_NOTHROW(wil::stream_reset(&stream)); + REQUIRE(0ULL == stream.Position); +#endif +} + +TEST_CASE("StreamTests::SeekCur", "[com][IStream]") +{ + FakeStream stream; + unsigned long long landed; + + stream.SetPosition(100, 5000); + REQUIRE_SUCCEEDED(wil::stream_seek_from_current_position_nothrow(&stream, 10, &landed)); + REQUIRE(110ULL == landed); + + REQUIRE_SUCCEEDED(wil::stream_seek_from_current_position_nothrow(&stream, -10, &landed)); + REQUIRE(100ULL == landed); + + REQUIRE_SUCCEEDED(wil::stream_seek_from_current_position_nothrow(&stream, -1000, &landed)); + REQUIRE(0ULL == landed); + + REQUIRE_SUCCEEDED(wil::stream_seek_from_current_position_nothrow(&stream, 6000, &landed)); + REQUIRE(5000ULL == landed); + +#ifdef WIL_ENABLE_EXCEPTIONS + stream.SetPosition(100, 5000); + + REQUIRE(110ULL == wil::stream_seek_from_current_position(&stream, 10)); + + REQUIRE(100ULL == wil::stream_seek_from_current_position(&stream, -10)); + + REQUIRE(0ULL == wil::stream_seek_from_current_position(&stream, -1000)); + + REQUIRE(5000ULL == wil::stream_seek_from_current_position(&stream, 6000)); +#endif +} + +TEST_CASE("StreamTests::GetPosition", "[com][IStream]") +{ + FakeStream stream; + unsigned long long landed; + + stream.SetPosition(50); + REQUIRE_SUCCEEDED(wil::stream_get_position_nothrow(&stream, &landed)); + REQUIRE(stream.Position == landed); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(stream.Position == wil::stream_get_position(&stream)); +#endif +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("StreamTests::Saver", "[com][IStream]") +{ + FakeStream first; + FakeStream second; + + first.SetPosition(200); + { + auto saved = wil::stream_position_saver(&first); + first.SetPosition(250); + } + REQUIRE(200ULL == first.Position); + + first.SetPosition(200); + { + auto saved = wil::stream_position_saver(&first); + first.SetPosition(250); + saved.reset(); + REQUIRE(200ULL == first.Position); + } + + first.SetPosition(200); + { + auto saved = wil::stream_position_saver(&first); + first.SetPosition(250); + saved.dismiss(); + } + REQUIRE(250ULL == first.Position); + + first.SetPosition(200); + second.SetPosition(250); + { + auto saved = wil::stream_position_saver(&first); + first.SetPosition(210); + saved.reset(&second); + REQUIRE(200ULL == first.Position); + + second.SetPosition(300); + saved.reset(); + REQUIRE(250ULL == second.Position); + } +} +#endif diff --git a/Externals/WIL/tests/CommonTests.cpp b/Externals/WIL/tests/CommonTests.cpp new file mode 100644 index 0000000000..d8eb4b4a6f --- /dev/null +++ b/Externals/WIL/tests/CommonTests.cpp @@ -0,0 +1,243 @@ + +#include +#include +#include + +#include "common.h" + +TEST_CASE("CommonTests::OutParamHelpers", "[common]") +{ + int i = 2; + int *pOutTest = &i; + int *pNullTest = nullptr; + + SECTION("Value type") + { + wil::assign_to_opt_param(pNullTest, 3); + wil::assign_to_opt_param(pOutTest, 3); + REQUIRE(*pOutTest == 3); + } + + SECTION("Pointer to value type") + { + int **ppOutTest = &pOutTest; + int **ppNullTest = nullptr; + wil::assign_null_to_opt_param(ppNullTest); + wil::assign_null_to_opt_param(ppOutTest); + REQUIRE(*ppOutTest == nullptr); + } + + SECTION("COM out pointer") + { + Microsoft::WRL::ComPtr spUnk; + IUnknown **ppunkNull = nullptr; + IUnknown *pUnk = reinterpret_cast(1); + IUnknown **ppUnkValid = &pUnk; + + wil::detach_to_opt_param(ppunkNull, spUnk); + wil::detach_to_opt_param(ppUnkValid, spUnk); + REQUIRE(*ppUnkValid == nullptr); + } +} + +TEST_CASE("CommonTests::TypeValidation", "[common]") +{ + std::unique_ptr boolCastClass; + std::vector noBoolCastClass; + HRESULT hr = S_OK; + BOOL bigBool = true; + bool smallBool = true; + DWORD dword = 1; + Microsoft::WRL::ComPtr comPtr; + (void)dword; + + // NOTE: The commented out verify* calls should give compilation errors + SECTION("verify_bool") + { + REQUIRE(wil::verify_bool(smallBool)); + REQUIRE(wil::verify_bool(bigBool)); + REQUIRE_FALSE(wil::verify_bool(boolCastClass)); + REQUIRE_FALSE(wil::verify_bool(comPtr)); + //wil::verify_bool(noBoolCastClass); + //wil::verify_bool(dword); + //wil::verify_bool(hr); + } + + SECTION("verify_hresult") + { + //wil::verify_hresult(smallBool); + //wil::verify_hresult(bigBool); + //wil::verify_hresult(boolCastClass); + //wil::verify_hresult(noBoolCastClass); + //wil::verify_hresult(dword); + //wil::verify_hresult(comPtr); + REQUIRE(wil::verify_hresult(hr) == S_OK); + } + + SECTION("verify_BOOL") + { + //wil::verify_BOOL(smallBool); + REQUIRE(wil::verify_BOOL(bigBool)); + //wil::verify_BOOL(boolCastClass); + //wil::verify_BOOL(noBoolCastClass); + //wil::verify_BOOL(dword); + //wil::verify_BOOL(comPtr); + //wil::verify_BOOL(hr); + } +} + +template +static void FlagsMacrosNonStatic(T none, T one, T two, T three, T four) +{ + T eval = one | four; + + REQUIRE(WI_AreAllFlagsSet(MDEC(eval), MDEC(one | four))); + REQUIRE_FALSE(WI_AreAllFlagsSet(eval, one | three)); + REQUIRE_FALSE(WI_AreAllFlagsSet(eval, three | two)); + REQUIRE(WI_AreAllFlagsSet(eval, none)); + + REQUIRE(WI_IsAnyFlagSet(MDEC(eval), MDEC(one))); + REQUIRE(WI_IsAnyFlagSet(eval, one | four | three)); + REQUIRE_FALSE(WI_IsAnyFlagSet(eval, two)); + + REQUIRE(WI_AreAllFlagsClear(MDEC(eval), MDEC(three))); + REQUIRE(WI_AreAllFlagsClear(eval, three | two)); + REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | four)); + REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | three)); + + REQUIRE(WI_IsAnyFlagClear(MDEC(eval), MDEC(three))); + REQUIRE(WI_IsAnyFlagClear(eval, three | two)); + REQUIRE(WI_IsAnyFlagClear(eval, four | three)); + REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one)); + REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one | four)); + + REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eval))); + REQUIRE(WI_IsSingleFlagSet(eval & one)); + + REQUIRE(WI_IsSingleFlagSetInMask(MDEC(eval), MDEC(one))); + REQUIRE(WI_IsSingleFlagSetInMask(eval, one | three)); + REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, three)); + REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, one | four)); + + REQUIRE_FALSE(WI_IsClearOrSingleFlagSet(MDEC(eval))); + REQUIRE(WI_IsClearOrSingleFlagSet(eval & one)); + REQUIRE(WI_IsClearOrSingleFlagSet(none)); + + REQUIRE(WI_IsClearOrSingleFlagSetInMask(MDEC(eval), MDEC(one))); + REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, one | three)); + REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, three)); + REQUIRE_FALSE(WI_IsClearOrSingleFlagSetInMask(eval, one | four)); + + eval = none; + WI_SetAllFlags(MDEC(eval), MDEC(one)); + REQUIRE(eval == one); + WI_SetAllFlags(eval, one | two); + REQUIRE(eval == (one | two)); + + eval = one | two; + WI_ClearAllFlags(MDEC(eval), one); + REQUIRE(eval == two); + WI_ClearAllFlags(eval, two); + REQUIRE(eval == none); + + eval = one | two; + WI_UpdateFlagsInMask(MDEC(eval), MDEC(two | three), MDEC(three | four)); + REQUIRE(eval == (one | three)); + + eval = one; + WI_ToggleAllFlags(MDEC(eval), MDEC(one | two)); + REQUIRE(eval == two); +} + +enum class EClassTest +{ + None = 0x0, + One = 0x1, + Two = 0x2, + Three = 0x4, + Four = 0x8, +}; +DEFINE_ENUM_FLAG_OPERATORS(EClassTest); + +enum ERawTest +{ + ER_None = 0x0, + ER_One = 0x1, + ER_Two = 0x2, + ER_Three = 0x4, + ER_Four = 0x8, +}; +DEFINE_ENUM_FLAG_OPERATORS(ERawTest); + +TEST_CASE("CommonTests::FlagsMacros", "[common]") +{ + SECTION("Integral types") + { + FlagsMacrosNonStatic(static_cast(0), static_cast(0x1), static_cast(0x2), static_cast(0x4), static_cast(0x40)); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80u); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x4000); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000u); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80000000ul); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80000000ul); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000000000000000ull); + FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000000000000000ull); + } + + SECTION("Raw enum") + { + FlagsMacrosNonStatic(ER_None, ER_One, ER_Two, ER_Three, ER_Four); + } + + SECTION("Enum class") + { + FlagsMacrosNonStatic(EClassTest::None, EClassTest::One, EClassTest::Two, EClassTest::Three, EClassTest::Four); + + EClassTest eclass = EClassTest::One | EClassTest::Two; + REQUIRE(WI_IsFlagSet(MDEC(eclass), EClassTest::One)); + REQUIRE(WI_IsFlagSet(eclass, EClassTest::Two)); + REQUIRE_FALSE(WI_IsFlagSet(eclass, EClassTest::Three)); + + REQUIRE(WI_IsFlagClear(MDEC(eclass), EClassTest::Three)); + REQUIRE_FALSE(WI_IsFlagClear(eclass, EClassTest::One)); + + REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eclass))); + REQUIRE(WI_IsSingleFlagSet(eclass & EClassTest::One)); + + eclass = EClassTest::None; + WI_SetFlag(MDEC(eclass), EClassTest::One); + REQUIRE(eclass == EClassTest::One); + + eclass = EClassTest::None; + WI_SetFlagIf(eclass, EClassTest::One, false); + REQUIRE(eclass == EClassTest::None); + WI_SetFlagIf(eclass, EClassTest::One, true); + REQUIRE(eclass == EClassTest::One); + + eclass = EClassTest::None; + WI_SetFlagIf(eclass, EClassTest::One, false); + REQUIRE(eclass == EClassTest::None); + WI_SetFlagIf(eclass, EClassTest::One, true); + REQUIRE(eclass == EClassTest::One); + + eclass = EClassTest::One | EClassTest::Two; + WI_ClearFlag(eclass, EClassTest::Two); + REQUIRE(eclass == EClassTest::One); + + eclass = EClassTest::One | EClassTest::Two; + WI_ClearFlagIf(eclass, EClassTest::One, false); + REQUIRE(eclass == (EClassTest::One | EClassTest::Two)); + WI_ClearFlagIf(eclass, EClassTest::One, true); + REQUIRE(eclass == EClassTest::Two); + + eclass = EClassTest::None; + WI_UpdateFlag(eclass, EClassTest::One, true); + REQUIRE(eclass == EClassTest::One); + WI_UpdateFlag(eclass, EClassTest::One, false); + REQUIRE(eclass == EClassTest::None); + + eclass = EClassTest::One; + WI_ToggleFlag(eclass, EClassTest::One); + WI_ToggleFlag(eclass, EClassTest::Two); + REQUIRE(eclass == EClassTest::Two); + } +} diff --git a/Externals/WIL/tests/CppWinRT20Tests.cpp b/Externals/WIL/tests/CppWinRT20Tests.cpp new file mode 100644 index 0000000000..2b911ad80e --- /dev/null +++ b/Externals/WIL/tests/CppWinRT20Tests.cpp @@ -0,0 +1,28 @@ + +// Prior to C++/WinRT 2.0 this would cause issues since we're not including wil/cppwinrt.h in this translation unit. +// However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler' +// global function pointer should be set, so these should all run successfully + +#include +#include + +#include "common.h" + +TEST_CASE("CppWinRTTests::CppWinRT20Test", "[cppwinrt]") +{ + auto test = [](HRESULT hr) + { + try + { + THROW_HR(hr); + } + catch (...) + { + REQUIRE(hr == winrt::to_hresult()); + } + }; + + test(E_OUTOFMEMORY); + test(E_INVALIDARG); + test(E_UNEXPECTED); +} diff --git a/Externals/WIL/tests/CppWinRTTests.cpp b/Externals/WIL/tests/CppWinRTTests.cpp new file mode 100644 index 0000000000..199f7fd2ba --- /dev/null +++ b/Externals/WIL/tests/CppWinRTTests.cpp @@ -0,0 +1,144 @@ + +#include + +#include "catch.hpp" + +// HRESULT values that C++/WinRT throws as something other than winrt::hresult_error - e.g. a type derived from +// winrt::hresult_error, std::*, etc. +static const HRESULT cppwinrt_mapped_hresults[] = +{ + E_ACCESSDENIED, + RPC_E_WRONG_THREAD, + E_NOTIMPL, + E_INVALIDARG, + E_BOUNDS, + E_NOINTERFACE, + CLASS_E_CLASSNOTAVAILABLE, + E_CHANGED_STATE, + E_ILLEGAL_METHOD_CALL, + E_ILLEGAL_STATE_CHANGE, + E_ILLEGAL_DELEGATE_ASSIGNMENT, + HRESULT_FROM_WIN32(ERROR_CANCELLED), + E_OUTOFMEMORY, +}; + +TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]") +{ + auto test = [](HRESULT hr) + { + try + { + THROW_HR(hr); + } + catch (...) + { + REQUIRE(hr == winrt::to_hresult()); + } + }; + + for (auto hr : cppwinrt_mapped_hresults) + { + test(hr); + } + + // A non-mapped HRESULT + test(E_UNEXPECTED); +} + +TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]") +{ + auto test = [](HRESULT hr) + { + try + { + winrt::check_hresult(hr); + } + catch (...) + { + REQUIRE(hr == wil::ResultFromCaughtException()); + } + }; + + for (auto hr : cppwinrt_mapped_hresults) + { + test(hr); + } + + // A non-mapped HRESULT + test(E_UNEXPECTED); +} + +TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]") +{ + auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions) + { + auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]() + { + winrt::check_hresult(hr); + }); + REQUIRE(hr == result); + }; + + for (auto hr : cppwinrt_mapped_hresults) + { + test(hr, wil::SupportedExceptions::Known); + test(hr, wil::SupportedExceptions::All); + } + + // A non-mapped HRESULT + test(E_UNEXPECTED, wil::SupportedExceptions::Known); + test(E_UNEXPECTED, wil::SupportedExceptions::All); + + // Uncomment any of the following to validate SEH failfast + //test(E_UNEXPECTED, wil::SupportedExceptions::None); + //test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown); + //test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc); +} + +TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]") +{ + // Since setting 'winrt_to_hresult_handler' opts us into _all_ C++/WinRT exception translation handling, we need to + // make sure that we preserve behavior, at least with 'check_hresult', especially when C++/WinRT maps a particular + // HRESULT value to a different exception type + auto test = [](HRESULT hr) + { + try + { + winrt::check_hresult(hr); + } + catch (...) + { + REQUIRE(hr == winrt::to_hresult()); + } + }; + + for (auto hr : cppwinrt_mapped_hresults) + { + test(hr); + } + + // A non-mapped HRESULT + test(E_UNEXPECTED); + + // C++/WinRT also maps a few std::* exceptions to various HRESULTs. We should preserve this behavior + try + { + throw std::out_of_range("oopsie"); + } + catch (...) + { + REQUIRE(winrt::to_hresult() == E_BOUNDS); + } + + try + { + throw std::invalid_argument("daisy"); + } + catch (...) + { + REQUIRE(winrt::to_hresult() == E_INVALIDARG); + } + + // NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior + // that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION) +} diff --git a/Externals/WIL/tests/FakeWinRTTypes.h b/Externals/WIL/tests/FakeWinRTTypes.h new file mode 100644 index 0000000000..70e607a233 --- /dev/null +++ b/Externals/WIL/tests/FakeWinRTTypes.h @@ -0,0 +1,400 @@ +#pragma once + +#include +#include +#include +#include + +template +struct WinRTStorage +{ + T value = {}; + + HRESULT CopyTo(T* result) + { + *result = value; + return S_OK; + } + + HRESULT Set(T val) + { + value = val; + return S_OK; + } + + static void Destroy(T&) + { + } + + void Reset() + { + } + + bool Equals(T val) + { + // NOTE: Padding can through this off, but this isn't intended to be a robust solution... + return memcmp(&value, &val, sizeof(T)) == 0; + } +}; + +template <> +struct WinRTStorage +{ + Microsoft::WRL::Wrappers::HString value; + + HRESULT CopyTo(HSTRING* result) + { + return value.CopyTo(result); + } + + HRESULT Set(HSTRING val) + { + return value.Set(val); + } + + static void Destroy(HSTRING& val) + { + ::WindowsDeleteString(val); + val = nullptr; + } + + void Reset() + { + value = {}; + } + + bool Equals(HSTRING val) + { + return value == val; + } +}; + +template +struct WinRTStorage +{ + Microsoft::WRL::ComPtr value; + + HRESULT CopyTo(T** result) + { + *result = Microsoft::WRL::ComPtr(value).Detach(); + return S_OK; + } + + HRESULT Set(T* val) + { + value = val; + return S_OK; + } + + static void Destroy(T*& val) + { + val->Release(); + val = nullptr; + } + + void Reset() + { + value.Reset(); + } + + bool Equals(T* val) + { + return value.Get() == val; + } +}; + +// Very minimal IAsyncOperation implementation that gives calling tests control over when it completes +template +struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass< + ABI::Windows::Foundation::IAsyncInfo, + ABI::Windows::Foundation::IAsyncOperation> +{ + using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler; + + // IAsyncInfo + IFACEMETHODIMP get_Id(unsigned int*) override + { + return E_NOTIMPL; + } + + IFACEMETHODIMP get_Status(AsyncStatus* status) override + { + auto lock = m_lock.lock_shared(); + *status = m_status; + return S_OK; + } + + IFACEMETHODIMP get_ErrorCode(HRESULT* errorCode) override + { + auto lock = m_lock.lock_shared(); + *errorCode = m_result; + return S_OK; + } + + IFACEMETHODIMP Cancel() override + { + return E_NOTIMPL; + } + + IFACEMETHODIMP Close() override + { + return E_NOTIMPL; + } + + // IAsyncOperation + IFACEMETHODIMP put_Completed(Handler* handler) override + { + bool invoke = false; + { + auto lock = m_lock.lock_exclusive(); + if (m_handler) + { + return E_FAIL; + } + + m_handler = handler; + invoke = m_status != ABI::Windows::Foundation::AsyncStatus::Started; + } + + if (invoke) + { + handler->Invoke(this, m_status); + } + + return S_OK; + } + + IFACEMETHODIMP get_Completed(Handler** handler) override + { + auto lock = m_lock.lock_shared(); + *handler = Microsoft::WRL::ComPtr(m_handler).Detach(); + return S_OK; + } + + IFACEMETHODIMP GetResults(Abi* results) override + { + return m_storage.CopyTo(results); + } + + // Test functions + void Complete(HRESULT hr, Abi result) + { + using namespace ABI::Windows::Foundation; + Handler* handler = nullptr; + { + auto lock = m_lock.lock_exclusive(); + if (m_status == AsyncStatus::Started) + { + m_result = hr; + m_storage.Set(result); + m_status = SUCCEEDED(hr) ? AsyncStatus::Completed : AsyncStatus::Error; + handler = m_handler.Get(); + } + } + + if (handler) + { + handler->Invoke(this, m_status); + } + } + +private: + + wil::srwlock m_lock; + Microsoft::WRL::ComPtr m_handler; + ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started; + HRESULT m_result = S_OK; + WinRTStorage m_storage; +}; + +template +struct FakeVector : Microsoft::WRL::RuntimeClass< + ABI::Windows::Foundation::Collections::IVector, + ABI::Windows::Foundation::Collections::IVectorView> +{ + // IVector + IFACEMETHODIMP GetAt(unsigned index, Abi* item) override + { + if (index >= m_size) + { + return E_BOUNDS; + } + + return m_data[index].CopyTo(item); + } + + IFACEMETHODIMP get_Size(unsigned* size) override + { + *size = static_cast(m_size); + return S_OK; + } + + IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView** view) override + { + this->AddRef(); + *view = this; + return S_OK; + } + + IFACEMETHODIMP IndexOf(Abi value, unsigned* index, boolean* found) override + { + for (size_t i = 0; i < m_size; ++i) + { + if (m_data[i].Equals(value)) + { + *index = static_cast(i); + *found = true; + return S_OK; + } + } + + *index = 0; + *found = false; + return S_OK; + } + + IFACEMETHODIMP SetAt(unsigned index, Abi item) override + { + if (index >= m_size) + { + return E_BOUNDS; + } + + return m_data[index].Set(item); + } + + IFACEMETHODIMP InsertAt(unsigned index, Abi item) override + { + // Insert at the end and swap it into place + if (index > m_size) + { + return E_BOUNDS; + } + + auto hr = Append(item); + if (SUCCEEDED(hr)) + { + for (size_t i = m_size - 1; i > index; --i) + { + wistd::swap_wil(m_data[i], m_data[i - 1]); + } + } + + return hr; + } + + IFACEMETHODIMP RemoveAt(unsigned index) override + { + if (index >= m_size) + { + return E_BOUNDS; + } + + for (size_t i = index + 1; i < m_size; ++i) + { + wistd::swap_wil(m_data[i - 1], m_data[i]); + } + + m_data[--m_size].Reset(); + return S_OK; + } + + IFACEMETHODIMP Append(Abi item) override + { + if (m_size > MaxSize) + { + return E_OUTOFMEMORY; + } + + auto hr = m_data[m_size].Set(item); + if (SUCCEEDED(hr)) + { + ++m_size; + } + + return hr; + } + + IFACEMETHODIMP RemoveAtEnd() override + { + if (m_size == 0) + { + return E_BOUNDS; + } + + m_data[--m_size].Reset(); + return S_OK; + } + + IFACEMETHODIMP Clear() override + { + for (size_t i = 0; i < m_size; ++i) + { + m_data[i].Reset(); + } + + m_size = 0; + return S_OK; + } + + IFACEMETHODIMP GetMany(unsigned startIndex, unsigned capacity, Abi* value, unsigned* actual) override + { + *actual = 0; + if (startIndex >= m_size) + { + return S_OK; + } + + auto count = m_size - startIndex; + count = (count > capacity) ? capacity : count; + + HRESULT hr = S_OK; + unsigned i = 0; + for (; (i < count) && SUCCEEDED(hr); ++i) + { + hr = m_data[startIndex + i].CopyTo(value + i); + } + + if (SUCCEEDED(hr)) + { + *actual = static_cast(count); + } + else + { + while (i--) + { + WinRTStorage::Destroy(value[i]); + } + } + + return hr; + } + + IFACEMETHODIMP ReplaceAll(unsigned count, Abi* value) override + { + if (count > MaxSize) + { + return E_OUTOFMEMORY; + } + + Clear(); + + HRESULT hr = S_OK; + for (size_t i = 0; (i < count) && SUCCEEDED(hr); ++i) + { + hr = m_data[i].Set(value[i]); + } + + if (FAILED(hr)) + { + Clear(); + } + + return hr; + } + +private: + + size_t m_size = 0; + WinRTStorage m_data[MaxSize]; +}; diff --git a/Externals/WIL/tests/FileSystemTests.cpp b/Externals/WIL/tests/FileSystemTests.cpp new file mode 100644 index 0000000000..96aae140c4 --- /dev/null +++ b/Externals/WIL/tests/FileSystemTests.cpp @@ -0,0 +1,460 @@ + +#include +#include // For wil::unique_hstring + +#include +#ifdef WIL_ENABLE_EXCEPTIONS +#include +#endif + +// TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8) +namespace wil +{ + PCWSTR str_raw_ptr(HSTRING); +#ifdef WIL_ENABLE_EXCEPTIONS + PCWSTR str_raw_ptr(const std::wstring&); +#endif +} + +#include + +#ifdef WIL_ENABLE_EXCEPTIONS +#include // For std::wstring string_maker +#endif + +#include "common.h" + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +bool DirectoryExists(_In_ PCWSTR path) +{ + DWORD dwAttrib = GetFileAttributesW(path); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]") +{ + wchar_t basePath[MAX_PATH]; + REQUIRE(GetTempPathW(ARRAYSIZE(basePath), basePath)); + REQUIRE_SUCCEEDED(PathCchAppend(basePath, ARRAYSIZE(basePath), L"FileSystemTests")); + + REQUIRE_FALSE(DirectoryExists(basePath)); + REQUIRE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath))); + REQUIRE(DirectoryExists(basePath)); + + auto scopeGuard = wil::scope_exit([&] + { + REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(basePath)); + }); + + PCWSTR relativeTestPath = L"folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8"; + wchar_t absoluteTestPath[MAX_PATH]; + REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath, ARRAYSIZE(absoluteTestPath), basePath)); + REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath, ARRAYSIZE(absoluteTestPath), relativeTestPath)); + REQUIRE_FALSE(DirectoryExists(absoluteTestPath)); + REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteTestPath)); + + PCWSTR invalidCharsPath = L"Bad?Char|"; + wchar_t absoluteInvalidPath[MAX_PATH]; + REQUIRE_SUCCEEDED(StringCchCopyW(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), basePath)); + REQUIRE_SUCCEEDED(PathCchAppend(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), invalidCharsPath)); + REQUIRE_FALSE(DirectoryExists(absoluteInvalidPath)); + REQUIRE_FALSE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteInvalidPath))); + + PCWSTR testPath3 = L"folder1\\folder2\\folder3"; + wchar_t absoluteTestPath3[MAX_PATH]; + REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), basePath)); + REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), testPath3)); + REQUIRE(DirectoryExists(absoluteTestPath3)); + + PCWSTR testPath4 = L"folder1\\folder2\\folder3\\folder4"; + wchar_t absoluteTestPath4[MAX_PATH]; + REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), basePath)); + REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), testPath4)); + REQUIRE(DirectoryExists(absoluteTestPath4)); + + REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(absoluteTestPath3, wil::RemoveDirectoryOptions::KeepRootDirectory)); + REQUIRE(DirectoryExists(absoluteTestPath3)); + REQUIRE_FALSE(DirectoryExists(absoluteTestPath4)); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/ +// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files +// that are in the non-normalized form. +TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteFoldersWithNonNormalizedNames", "[filesystem]") +{ + // Extended length paths can access files with non-normalized names. + // This function creates a path with that ability. + auto CreatePathThatCanAccessNonNormalizedNames = [](PCWSTR root, PCWSTR name) + { + wil::unique_hlocal_string path; + THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, &path)); + REQUIRE(wil::is_extended_length_path(path.get())); + return path; + }; + + // Regular paths are normalized in the Win32 APIs thus can't address files in the non-normalized form. + // This function creates a regular path form but preserves the non-normalized parts of the input (for testing) + auto CreateRegularPath = [](PCWSTR root, PCWSTR name) + { + wil::unique_hlocal_string path; + THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &path)); + REQUIRE_FALSE(wil::is_extended_length_path(path.get())); + return path; + }; + + struct TestCases + { + PCWSTR CreateWithName; + PCWSTR DeleteWithName; + wil::unique_hlocal_string (*CreatePathFunction)(PCWSTR root, PCWSTR name); + HRESULT ExpectedResult; + }; + + PCWSTR NormalizedName = L"Foo"; + PCWSTR NonNormalizedName = L"Foo."; // The dot at the end is what makes this non-normalized. + const auto PathNotFoundError = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + + TestCases tests[] = + { + { NormalizedName, NormalizedName, CreateRegularPath, S_OK }, + { NonNormalizedName, NormalizedName, CreateRegularPath, PathNotFoundError }, + { NormalizedName, NonNormalizedName, CreateRegularPath, S_OK }, + { NonNormalizedName, NonNormalizedName, CreateRegularPath, PathNotFoundError }, + { NormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK }, + { NonNormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError }, + { NormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError }, + { NonNormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK }, + }; + + auto folderRoot = wil::ExpandEnvironmentStringsW(LR"(%TEMP%)"); + REQUIRE_FALSE(wil::is_extended_length_path(folderRoot.get())); + + auto EnsureFolderWithNonCanonicalNameAndContentsExists = [&](const TestCases& test) + { + const auto enableNonNormalized = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS; + + wil::unique_hlocal_string targetFolder; + // Create a folder for testing using the extended length form to enable + // access to non-normalized forms of the path + THROW_IF_FAILED(PathAllocCombine(folderRoot.get(), test.CreateWithName, enableNonNormalized, &targetFolder)); + + // This ensures the folder is there and won't fail if it already exists (common when testing). + wil::CreateDirectoryDeep(targetFolder.get()); + + // Create a file in that folder with a non-normalized name (with the dot at the end). + wil::unique_hlocal_string extendedFilePath; + THROW_IF_FAILED(PathAllocCombine(targetFolder.get(), L"NonNormalized.", enableNonNormalized, &extendedFilePath)); + wil::unique_hfile fileHandle(CreateFileW(extendedFilePath.get(), FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); + THROW_LAST_ERROR_IF(!fileHandle); + }; + + for (auto const& test : tests) + { + // remove remnants from previous test that will cause failures + wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NormalizedName).get()); + wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NonNormalizedName).get()); + + EnsureFolderWithNonCanonicalNameAndContentsExists(test); + auto deleteWithPath = test.CreatePathFunction(folderRoot.get(), test.DeleteWithName); + + const auto hr = wil::RemoveDirectoryRecursiveNoThrow(deleteWithPath.get()); + REQUIRE(test.ExpectedResult == hr); + } +} +#endif + +// real paths to test +const wchar_t c_variablePath[] = L"%systemdrive%\\Windows\\System32\\Windows.Storage.dll"; +const wchar_t c_expandedPath[] = L"c:\\Windows\\System32\\Windows.Storage.dll"; + +// // paths that should not exist on the system +const wchar_t c_missingVariable[] = L"%doesnotexist%\\doesnotexist.dll"; +const wchar_t c_missingPath[] = L"c:\\Windows\\System32\\doesnotexist.dll"; + +const int c_stackBufferLimitTest = 5; + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("FileSystemTests::VerifyGetCurrentDirectory", "[filesystem]") +{ + auto pwd = wil::GetCurrentDirectoryW(); + REQUIRE(*pwd.get() != L'\0'); +} + +TEST_CASE("FileSystemTests::VerifyGetFullPathName", "[filesystem]") +{ + PCWSTR fileName = L"ReadMe.txt"; + auto result = wil::GetFullPathNameW(fileName, nullptr); + + PCWSTR fileNameResult; + result = wil::GetFullPathNameW(fileName, &fileNameResult); + REQUIRE(wcscmp(fileName, fileNameResult) == 0); + auto result2 = wil::GetFullPathNameW(fileName, &fileNameResult); + REQUIRE(wcscmp(fileName, fileNameResult) == 0); + REQUIRE(wcscmp(result.get(), result2.get()) == 0); + + // The only negative test case I've found is a path > 32k. + std::wstring big(1024 * 32, L'a'); + wil::unique_hstring output; + auto hr = wil::GetFullPathNameW(big.c_str(), output, nullptr); + REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE)); +} + +TEST_CASE("FileSystemTests::VerifyGetFinalPathNameByHandle", "[filesystem]") +{ + wil::unique_hfile fileHandle(CreateFileW(c_expandedPath, FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + THROW_LAST_ERROR_IF(!fileHandle); + + auto name = wil::GetFinalPathNameByHandleW(fileHandle.get()); + auto name2 = wil::GetFinalPathNameByHandleW(fileHandle.get()); + REQUIRE(wcscmp(name.get(), name2.get()) == 0); + + std::wstring path; + auto hr = wil::GetFinalPathNameByHandleW(nullptr, path); + REQUIRE(hr == E_HANDLE); // should be a usage error so be a fail fast. + // A more legitimate case is a non file handler like a drive volume. + + wil::unique_hfile volumeHandle(CreateFileW(LR"(\\?\C:)", FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + THROW_LAST_ERROR_IF(!volumeHandle); + const auto hr2 = wil::GetFinalPathNameByHandleW(volumeHandle.get(), path); + REQUIRE(hr2 == HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION)); +} + +TEST_CASE("FileSystemTests::VerifyTrySearchPathW", "[filesystem]") +{ + auto pathToTest = wil::TrySearchPathW(nullptr, c_expandedPath, nullptr); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + pathToTest = wil::TrySearchPathW(nullptr, c_missingPath, nullptr); + REQUIRE(wil::string_get_not_null(pathToTest)[0] == L'\0'); +} +#endif + +// Simple test to expand an environmental string +TEST_CASE("FileSystemTests::VerifyExpandEnvironmentStringsW", "[filesystem]") +{ + wil::unique_cotaskmem_string pathToTest; + REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_variablePath, pathToTest)); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + // This should effectively be a no-op + REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_expandedPath, pathToTest)); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + // Environment variable does not exist, but the call should still succeed + REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_missingVariable, pathToTest)); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_missingVariable, -1, TRUE) == CSTR_EQUAL); +} + +TEST_CASE("FileSystemTests::VerifySearchPathW", "[filesystem]") +{ + wil::unique_cotaskmem_string pathToTest; + REQUIRE_SUCCEEDED(wil::SearchPathW(nullptr, c_expandedPath, nullptr, pathToTest)); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::SearchPathW(nullptr, c_missingPath, nullptr, pathToTest)); +} + +TEST_CASE("FileSystemTests::VerifyExpandEnvAndSearchPath", "[filesystem]") +{ + wil::unique_cotaskmem_string pathToTest; + REQUIRE_SUCCEEDED(wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest)); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + // This test will exercise the case where AdaptFixedSizeToAllocatedResult will need to + // reallocate the initial buffer to fit the final string. + // This test is sufficient to test both wil::ExpandEnvironmentStringsW and wil::SeachPathW + REQUIRE_SUCCEEDED((wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest))); + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL); + + pathToTest.reset(); + REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::ExpandEnvAndSearchPath(c_missingVariable, pathToTest)); + REQUIRE(pathToTest.get() == nullptr); +} + +TEST_CASE("FileSystemTests::VerifyGetSystemDirectoryW", "[filesystem]") +{ + wil::unique_cotaskmem_string pathToTest; + REQUIRE_SUCCEEDED(wil::GetSystemDirectoryW(pathToTest)); + + // allocate based on the string that wil::GetSystemDirectoryW returned + size_t length = wcslen(pathToTest.get()) + 1; + auto trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length); + REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast(length)) > 0); + + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL); + + // Force AdaptFixed* to realloc. Test stack boundary with small initial buffer limit, c_stackBufferLimitTest + REQUIRE_SUCCEEDED((wil::GetSystemDirectoryW(pathToTest))); + + // allocate based on the string that wil::GetSystemDirectoryW returned + length = wcslen(pathToTest.get()) + 1; + trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length); + REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast(length)) > 0); + + REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL); +} + +struct has_operator_pcwstr +{ + PCWSTR value; + operator PCWSTR() const + { + return value; + } +}; + +struct has_operator_pwstr +{ + PWSTR value; + operator PWSTR() const + { + return value; + } +}; + +#ifdef WIL_ENABLE_EXCEPTIONS +struct has_operator_wstr_ref +{ + std::wstring value; + operator const std::wstring&() const + { + return value; + } +}; + +// E.g. mimics something like std::filesystem::path +struct has_operator_wstr +{ + std::wstring value; + operator std::wstring() const + { + return value; + } +}; +#endif + +TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]") +{ + SECTION("Concat with multiple strings") + { + PCWSTR test1 = L"Test1"; +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring test2 = L"Test2"; +#else + PCWSTR test2 = L"Test2"; +#endif + WCHAR test3[6] = L"Test3"; + wil::unique_cotaskmem_string test4 = wil::make_unique_string_nothrow(L"test4"); + wil::unique_hstring test5 = wil::make_unique_string_nothrow(L"test5"); + + has_operator_pcwstr test6{ L"Test6" }; + WCHAR test7Buffer[] = L"Test7"; + has_operator_pwstr test7{ test7Buffer }; + +#ifdef WIL_ENABLE_EXCEPTIONS + has_operator_wstr_ref test8{ L"Test8" }; + has_operator_wstr test9{ L"Test9" }; +#else + PCWSTR test8 = L"Test8"; + PCWSTR test9 = L"Test9"; +#endif + PCWSTR expectedStr = L"Test1Test2Test3Test4Test5Test6Test7Test8Test9"; + +#ifdef WIL_ENABLE_EXCEPTIONS + auto combinedString = wil::str_concat(test1, test2, test3, test4, test5, test6, test7, test8, test9); + REQUIRE(CompareStringOrdinal(combinedString.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL); +#endif + + wil::unique_cotaskmem_string combinedStringNT; + REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1, test2, test3, test4, test5, test6, test7, test8, test9)); + REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL); + + auto combinedStringFF = wil::str_concat_failfast(test1, test2, test3, test4, test5, test6, test7, test8, test9); + REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL); + } + + SECTION("Concat with single string") + { + PCWSTR test1 = L"Test1"; + +#ifdef WIL_ENABLE_EXCEPTIONS + auto combinedString = wil::str_concat(test1); + REQUIRE(CompareStringOrdinal(combinedString.get(), -1, test1, -1, TRUE) == CSTR_EQUAL); +#endif + + wil::unique_cotaskmem_string combinedStringNT; + REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1)); + REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, test1, -1, TRUE) == CSTR_EQUAL); + + auto combinedStringFF = wil::str_concat_failfast(test1); + REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, test1, -1, TRUE) == CSTR_EQUAL); + } + + SECTION("Concat with existing string") + { + std::wstring test2 = L"Test2"; + WCHAR test3[6] = L"Test3"; + PCWSTR expectedStr = L"Test1Test2Test3"; + + wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow(L"Test1"); + REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3)); + REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL); + } +} + +TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]") +{ +#ifdef WIL_ENABLE_EXCEPTIONS + auto formattedString = wil::str_printf(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28); + REQUIRE(CompareStringOrdinal(formattedString.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL); +#endif + + wil::unique_cotaskmem_string formattedStringNT; + REQUIRE_SUCCEEDED(wil::str_printf_nothrow(formattedStringNT, L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28)); + REQUIRE(CompareStringOrdinal(formattedStringNT.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL); + + auto formattedStringFF = wil::str_printf_failfast(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28); + REQUIRE(CompareStringOrdinal(formattedStringFF.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL); +} + +TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]") +{ + wil::unique_cotaskmem_string path; + REQUIRE_SUCCEEDED(wil::GetModuleFileNameW(nullptr, path)); + auto len = wcslen(path.get()); + REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0))); + + // Call again, but force multiple retries through a small initial buffer + wil::unique_cotaskmem_string path2; + REQUIRE_SUCCEEDED((wil::GetModuleFileNameW(nullptr, path2))); + REQUIRE(wcscmp(path.get(), path2.get()) == 0); + + REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path)); +} + +TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]") +{ + wil::unique_cotaskmem_string path; + REQUIRE_SUCCEEDED(wil::GetModuleFileNameExW(nullptr, nullptr, path)); + auto len = wcslen(path.get()); + REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0))); + + // Call again, but force multiple retries through a small initial buffer + wil::unique_cotaskmem_string path2; + REQUIRE_SUCCEEDED((wil::GetModuleFileNameExW(nullptr, nullptr, path2))); + REQUIRE(wcscmp(path.get(), path2.get()) == 0); + + REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path)); +} + +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) diff --git a/Externals/WIL/tests/MallocSpy.h b/Externals/WIL/tests/MallocSpy.h new file mode 100644 index 0000000000..b2d50aa448 --- /dev/null +++ b/Externals/WIL/tests/MallocSpy.h @@ -0,0 +1,145 @@ +#pragma once + +#include "catch.hpp" +#include +#include +#include + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +// IMallocSpy requires you to implement all methods, but we often only want one or two... +struct MallocSpy : Microsoft::WRL::RuntimeClass, IMallocSpy> +{ + wistd::function PreAllocCallback; + virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override + { + if (PreAllocCallback) + { + return PreAllocCallback(requestSize); + } + + return requestSize; + } + + wistd::function PostAllocCallback; + virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override + { + if (PostAllocCallback) + { + return PostAllocCallback(ptr); + } + + return ptr; + } + + wistd::function PreFreeCallback; + virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override + { + if (wasSpyed && PreFreeCallback) + { + return PreFreeCallback(ptr); + } + + return ptr; + } + + virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override + { + } + + wistd::function PreReallocCallback; + virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override + { + *newPtr = ptr; + if (wasSpyed && PreReallocCallback) + { + return PreReallocCallback(ptr, requestSize, newPtr); + } + + return requestSize; + } + + wistd::function PostReallocCallback; + virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override + { + if (wasSpyed && PostReallocCallback) + { + return PostReallocCallback(ptr); + } + + return ptr; + } + + wistd::function PreGetSizeCallback; + virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override + { + if (wasSpyed && PreGetSizeCallback) + { + return PreGetSizeCallback(ptr); + } + + return ptr; + } + + wistd::function PostGetSizeCallback; + virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override + { + if (wasSpyed && PostGetSizeCallback) + { + return PostGetSizeCallback(size); + } + + return size; + } + + wistd::function PreDidAllocCallback; + virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override + { + if (wasSpyed && PreDidAllocCallback) + { + return PreDidAllocCallback(ptr); + } + + return ptr; + } + + virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override + { + return result; + } + + virtual void STDMETHODCALLTYPE PreHeapMinimize() override + { + } + + virtual void STDMETHODCALLTYPE PostHeapMinimize() override + { + } +}; + +Microsoft::WRL::ComPtr MakeSecureDeleterMallocSpy() +{ + using namespace Microsoft::WRL; + auto result = Make(); + REQUIRE(result); + + result->PreFreeCallback = [](void* ptr) + { + ComPtr malloc; + if (SUCCEEDED(::CoGetMalloc(1, &malloc))) + { + auto size = malloc->GetSize(ptr); + auto buffer = static_cast(ptr); + for (size_t i = 0; i < size; ++i) + { + REQUIRE(buffer[i] == 0); + } + } + + return ptr; + }; + + return result; +} + +#endif diff --git a/Externals/WIL/tests/ResourceTests.cpp b/Externals/WIL/tests/ResourceTests.cpp new file mode 100644 index 0000000000..c16daf1b69 --- /dev/null +++ b/Externals/WIL/tests/ResourceTests.cpp @@ -0,0 +1,735 @@ + +// Included first and then again later to ensure that we're able to "light up" new functionality based off new includes +#include + +#include +#include + +// Headers to "light up" functionality in resource.h +#include +#include +#include + +#include +#include + +#include "common.h" + +TEST_CASE("ResourceTests::TestLastErrorContext", "[resource][last_error_context]") +{ + // Destructing the last_error_context restores the error. + { + SetLastError(42); + auto error42 = wil::last_error_context(); + SetLastError(0); + } + REQUIRE(GetLastError() == 42); + + // The context can be moved. + { + SetLastError(42); + auto error42 = wil::last_error_context(); + SetLastError(0); + { + auto another_error42 = wil::last_error_context(std::move(error42)); + SetLastError(1); + } + REQUIRE(GetLastError() == 42); + SetLastError(0); + // error42 has been moved-from and should not do anything at destruction. + } + REQUIRE(GetLastError() == 0); + + // The context can be self-assigned, which has no effect. + { + SetLastError(42); + auto error42 = wil::last_error_context(); + SetLastError(0); + error42 = std::move(error42); + SetLastError(1); + } + REQUIRE(GetLastError() == 42); + + // The context can be dismissed, which cause it to do nothing at destruction. + { + SetLastError(42); + auto error42 = wil::last_error_context(); + SetLastError(0); + error42.release(); + SetLastError(1); + } + REQUIRE(GetLastError() == 1); +} + +TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]") +{ + int count = 0; + auto validate = [&](int expected) { REQUIRE(count == expected); count = 0; }; + + { + auto foo = wil::scope_exit([&] { count++; }); + } + validate(1); + + { + auto foo = wil::scope_exit([&] { count++; }); + foo.release(); + foo.reset(); + } + validate(0); + + { + auto foo = wil::scope_exit([&] { count++; }); + foo.reset(); + foo.reset(); + validate(1); + } + validate(0); + +#ifdef WIL_ENABLE_EXCEPTIONS + { + auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); }); + } + validate(1); + + { + auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); }); + foo.release(); + foo.reset(); + } + validate(0); + + { + auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); }); + foo.reset(); + foo.reset(); + validate(1); + } + validate(0); +#endif // WIL_ENABLE_EXCEPTIONS +} + +interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00")) +ITest : public IUnknown +{ + STDMETHOD_(void, Test)() = 0; +}; + +class PointerTestObject : witest::AllocatedObject, + public Microsoft::WRL::RuntimeClass, ITest> +{ +public: + STDMETHOD_(void, Test)() {}; +}; + +TEST_CASE("ResourceTests::TestOperationsOnGenericSmartPointerClasses", "[resource]") +{ +#ifdef WIL_ENABLE_EXCEPTIONS + { + // wil::unique_any_t example + wil::unique_event ptr2(wil::EventOptions::ManualReset); + // wil::com_ptr + wil::com_ptr ptr3 = Microsoft::WRL::Make(); + // wil::shared_any_t example + wil::shared_event ptr4(wil::EventOptions::ManualReset); + // wistd::unique_ptr example + auto ptr5 = wil::make_unique_failfast(); + + static_assert(wistd::is_same::pointer, HANDLE>::value, "type-mismatch"); + static_assert(wistd::is_same::pointer, PointerTestObject*>::value, "type-mismatch"); + + auto p2 = wil::detach_from_smart_pointer(ptr2); + auto p3 = wil::detach_from_smart_pointer(ptr3); + // auto p4 = wil::detach_from_smart_pointer(ptr4); // wil::shared_any_t and std::shared_ptr do not support release(). + HANDLE p4{}; + auto p5 = wil::detach_from_smart_pointer(ptr5); + + REQUIRE((!ptr2 && !ptr3)); + REQUIRE((p2 && p3)); + + wil::attach_to_smart_pointer(ptr2, p2); + wil::attach_to_smart_pointer(ptr3, p3); + wil::attach_to_smart_pointer(ptr4, p4); + wil::attach_to_smart_pointer(ptr5, p5); + + p2 = nullptr; + p3 = nullptr; + p4 = nullptr; + p5 = nullptr; + + wil::detach_to_opt_param(&p2, ptr2); + wil::detach_to_opt_param(&p3, ptr3); + + REQUIRE((!ptr2 && !ptr3)); + REQUIRE((p2 && p3)); + + wil::attach_to_smart_pointer(ptr2, p2); + wil::attach_to_smart_pointer(ptr3, p3); + p2 = nullptr; + p3 = nullptr; + + wil::detach_to_opt_param(&p2, ptr2); + wil::detach_to_opt_param(&p3, ptr3); + REQUIRE((!ptr2 && !ptr3)); + REQUIRE((p2 && p3)); + + [&](decltype(p2)* ptr) { *ptr = p2; } (wil::out_param(ptr2)); + [&](decltype(p3)* ptr) { *ptr = p3; } (wil::out_param(ptr3)); + [&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4)); + [&](decltype(p5)* ptr) { *ptr = p5; } (wil::out_param(ptr5)); + + REQUIRE((ptr2 && ptr3)); + + // Validate R-Value compilation + wil::detach_to_opt_param(&p2, decltype(ptr2){}); + wil::detach_to_opt_param(&p3, decltype(ptr3){}); + } +#endif + + std::unique_ptr ptr1(new int(1)); + Microsoft::WRL::ComPtr ptr4 = Microsoft::WRL::Make(); + + static_assert(wistd::is_same::pointer, int*>::value, "type-mismatch"); + static_assert(wistd::is_same::pointer, PointerTestObject*>::value, "type-mismatch"); + + auto p1 = wil::detach_from_smart_pointer(ptr1); + auto p4 = wil::detach_from_smart_pointer(ptr4); + + REQUIRE((!ptr1 && !ptr4)); + REQUIRE((p1 && p4)); + + wil::attach_to_smart_pointer(ptr1, p1); + wil::attach_to_smart_pointer(ptr4, p4); + + REQUIRE((ptr1 && ptr4)); + + p1 = nullptr; + p4 = nullptr; + + int** pNull = nullptr; + wil::detach_to_opt_param(pNull, ptr1); + REQUIRE(ptr1); + + wil::detach_to_opt_param(&p1, ptr1); + wil::detach_to_opt_param(&p4, ptr4); + + REQUIRE((!ptr1 && !ptr4)); + REQUIRE((p1 && p4)); + + [&](decltype(p1)* ptr) { *ptr = p1; } (wil::out_param(ptr1)); + [&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4)); + + REQUIRE((ptr1 && ptr4)); + + p1 = wil::detach_from_smart_pointer(ptr1); + [&](int** ptr) { *ptr = p1; } (wil::out_param_ptr(ptr1)); + REQUIRE(ptr1); +} + +// Compilation only test... +void StlAdlTest() +{ + // This test has exposed some Argument Dependent Lookup issues in wistd / stl. Primarily we're + // just looking for clean compilation. + + std::vector> v; + v.emplace_back(new int{ 1 }); + v.emplace_back(new int{ 2 }); + v.emplace_back(new int{ 3 }); + std::rotate(begin(v), begin(v) + 1, end(v)); + + REQUIRE(*v[0] == 1); + REQUIRE(*v[1] == 3); + REQUIRE(*v[2] == 2); + + decltype(v) v2; + v2 = std::move(v); + REQUIRE(*v2[0] == 1); + REQUIRE(*v2[1] == 3); + REQUIRE(*v2[2] == 2); + + decltype(v) v3; + std::swap(v2, v3); + REQUIRE(*v3[0] == 1); + REQUIRE(*v3[1] == 3); + REQUIRE(*v3[2] == 2); +} + +// Compilation only test... +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +void UniqueProcessInfo() +{ + wil::unique_process_information process; + CreateProcessW(nullptr, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &process); + ResumeThread(process.hThread); + WaitForSingleObject(process.hProcess, INFINITE); + wil::unique_process_information other(wistd::move(process)); +} +#endif + +struct FakeComInterface +{ + void AddRef() + { + refs++; + } + void Release() + { + refs--; + } + + HRESULT __stdcall Close() + { + closes++; + return S_OK; + } + + size_t refs = 0; + size_t closes = 0; + + bool called() + { + auto old = closes; + closes = 0; + return (old > 0); + } + + bool has_ref() + { + return (refs > 0); + } +}; + +static void __stdcall CloseFakeComInterface(FakeComInterface* fake) +{ + fake->Close(); +} + +using unique_fakeclose_call = wil::unique_com_call; + +TEST_CASE("ResourceTests::VerifyUniqueComCall", "[resource][unique_com_call]") +{ + unique_fakeclose_call call1; + unique_fakeclose_call call2; + + // intentional compilation errors + // unique_fakeclose_call call3 = call1; + // call2 = call1; + + FakeComInterface fake1; + unique_fakeclose_call call4(&fake1); + REQUIRE(fake1.has_ref()); + + unique_fakeclose_call call5(wistd::move(call4)); + REQUIRE(!call4); + REQUIRE(call5); + REQUIRE(fake1.has_ref()); + + call4 = wistd::move(call5); + REQUIRE(call4); + REQUIRE(!call5); + REQUIRE(fake1.has_ref()); + REQUIRE(!fake1.called()); + + FakeComInterface fake2; + { + unique_fakeclose_call scoped(&fake2); + } + REQUIRE(!fake2.has_ref()); + REQUIRE(fake2.called()); + + call4.reset(&fake2); + REQUIRE(fake1.called()); + REQUIRE(!fake1.has_ref()); + call4.reset(); + REQUIRE(!fake2.has_ref()); + REQUIRE(fake2.called()); + + call1.reset(&fake1); + call2.swap(call1); + REQUIRE((call2 && !call1)); + + call2.release(); + REQUIRE(!fake1.called()); + REQUIRE(!fake1.has_ref()); + REQUIRE(!call2); + + REQUIRE(*call1.addressof() == nullptr); + + call1.reset(&fake1); + fake2.closes = 0; + fake2.refs = 1; + *(&call1) = &fake2; + REQUIRE(!fake1.has_ref()); + REQUIRE(fake1.called()); + REQUIRE(fake2.has_ref()); + + call1.reset(&fake1); + fake2.closes = 0; + fake2.refs = 1; + *call1.put() = &fake2; + REQUIRE(!fake1.has_ref()); + REQUIRE(fake1.called()); + REQUIRE(fake2.has_ref()); + + call1.reset(); + REQUIRE(!fake2.has_ref()); + REQUIRE(fake2.called()); +} + +static bool g_called = false; +static bool called() +{ + auto call = g_called; + g_called = false; + return (call); +} + +static void __stdcall FakeCall() +{ + g_called = true; +} + +using unique_fake_call = wil::unique_call; + +TEST_CASE("ResourceTests::VerifyUniqueCall", "[resource][unique_call]") +{ + unique_fake_call call1; + unique_fake_call call2; + + // intentional compilation errors + // unique_fake_call call3 = call1; + // call2 = call1; + + unique_fake_call call4; + REQUIRE(!called()); + + unique_fake_call call5(wistd::move(call4)); + REQUIRE(!call4); + REQUIRE(call5); + + call4 = wistd::move(call5); + REQUIRE(call4); + REQUIRE(!call5); + REQUIRE(!called()); + + { + unique_fake_call scoped; + } + REQUIRE(called()); + + call4.reset(); + REQUIRE(called()); + call4.reset(); + REQUIRE(!called()); + + call1.release(); + REQUIRE((!call1 && call2)); + call2.swap(call1); + REQUIRE((call1 && !call2)); + + call2.release(); + REQUIRE(!called()); + REQUIRE(!call2); + +#ifdef __WIL__ROAPI_H_APPEXCEPTIONAL + { + auto call = wil::RoInitialize(); + } +#endif +#ifdef __WIL__ROAPI_H_APP + { + wil::unique_rouninitialize_call uninit; + uninit.release(); + + auto call = wil::RoInitialize_failfast(); + } +#endif +#ifdef __WIL__COMBASEAPI_H_APPEXCEPTIONAL + { + auto call = wil::CoInitializeEx(); + } +#endif +#ifdef __WIL__COMBASEAPI_H_APP + { + wil::unique_couninitialize_call uninit; + uninit.release(); + + auto call = wil::CoInitializeEx_failfast(); + } +#endif +} + +void UniqueCallCompilationTest() +{ +#ifdef __WIL__COMBASEAPI_H_EXCEPTIONAL + { + auto call = wil::CoImpersonateClient(); + } +#endif +#ifdef __WIL__COMBASEAPI_H_ + { + wil::unique_coreverttoself_call uninit; + uninit.release(); + + auto call = wil::CoImpersonateClient_failfast(); + } +#endif +} + +template +static void TestStringMaker(VerifyContents&& verifyContents) +{ + PCWSTR values[] = + { + L"", + L"value", + // 300 chars + L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + }; + + for (const auto& value : values) + { + auto const valueLength = wcslen(value); + + // Direct construction case. + wil::details::string_maker maker; + THROW_IF_FAILED(maker.make(value, valueLength)); + auto result = maker.release(); + verifyContents(value, valueLength, result); + + // Two phase construction case. + THROW_IF_FAILED(maker.make(nullptr, valueLength)); + REQUIRE(maker.buffer() != nullptr); + // In the case of the wil::unique_hstring and the empty string the buffer is in a read only + // section and can't be written to, so StringCchCopy(maker.buffer(), valueLength + 1, value) will fault adding the nul terminator. + // Use memcpy_s specifying exact size that will be zero in this case instead. + memcpy_s(maker.buffer(), valueLength * sizeof(*value), value, valueLength * sizeof(*value)); + result = maker.release(); + verifyContents(value, valueLength, result); + + { + // no promote, ensure no leaks (not tested here, inspect in the debugger) + wil::details::string_maker maker2; + THROW_IF_FAILED(maker2.make(value, valueLength)); + } + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +static void VerifyMakeUniqueString(bool nullValueSupported = true) +{ + if (nullValueSupported) + { + auto value0 = wil::make_unique_string(nullptr, 5); + } + + struct + { + PCWSTR expectedValue; + PCWSTR testValue; + // this is an optional parameter + size_t testLength = static_cast(-1); + } + const testCaseEntries[] = + { + { L"value", L"value", 5 }, + { L"value", L"value" }, + { L"va", L"va\0ue", 5 }, + { L"v", L"value", 1 }, + { L"\0", L"", 5 }, + { L"\0", nullptr, 5 }, + }; + + using maker = wil::details::string_maker; + for (auto const &entry : testCaseEntries) + { + bool shouldSkipNullString = ((wcscmp(entry.expectedValue, L"\0") == 0) && !nullValueSupported); + if (!shouldSkipNullString) + { + auto desiredValue = wil::make_unique_string(entry.expectedValue); + auto stringValue = wil::make_unique_string(entry.testValue, entry.testLength); + auto stringValueNoThrow = wil::make_unique_string_nothrow(entry.testValue, entry.testLength); + auto stringValueFailFast = wil::make_unique_string_failfast(entry.testValue, entry.testLength); + REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValue)) == 0); + REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueNoThrow)) == 0); + REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueFailFast)) == 0); + } + } +} + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerCoTaskMem", "[resource][string_maker]") +{ + VerifyMakeUniqueString(); + TestStringMaker( + [](PCWSTR value, size_t /*valueLength*/, const wil::unique_cotaskmem_string& result) + { + REQUIRE(wcscmp(value, result.get()) == 0); + }); +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerLocalAlloc", "[resource][string_maker]") +{ + VerifyMakeUniqueString(); + TestStringMaker( + [](PCWSTR value, size_t /*valueLength*/, const wil::unique_hlocal_string& result) + { + REQUIRE(wcscmp(value, result.get()) == 0); + }); +} + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerGlobalAlloc", "[resource][string_maker]") +{ + VerifyMakeUniqueString(); + TestStringMaker( + [](PCWSTR value, size_t /*valueLength*/, const wil::unique_hglobal_string& result) + { + REQUIRE(wcscmp(value, result.get()) == 0); + }); +} + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerProcessHeap", "[resource][string_maker]") +{ + VerifyMakeUniqueString(); + TestStringMaker( + [](PCWSTR value, size_t /*valueLength*/, const wil::unique_process_heap_string& result) + { + REQUIRE(wcscmp(value, result.get()) == 0); + }); +} +#endif + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerMidl", "[resource][string_maker]") +{ + VerifyMakeUniqueString(); + TestStringMaker( + [](PCWSTR value, size_t /*valueLength*/, const wil::unique_midl_string& result) + { + REQUIRE(wcscmp(value, result.get()) == 0); + }); +} + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerHString", "[resource][string_maker]") +{ + wil::unique_hstring value; + value.reset(static_cast(nullptr)); + + VerifyMakeUniqueString(false); + + TestStringMaker( + [](PCWSTR value, size_t valueLength, const wil::unique_hstring& result) + { + UINT32 length; + REQUIRE(wcscmp(value, WindowsGetStringRawBuffer(result.get(), &length)) == 0); + REQUIRE(valueLength == length); + }); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerStdWString", "[resource][string_maker]") +{ + std::string s; + wil::details::string_maker maker; + + TestStringMaker( + [](PCWSTR value, size_t valueLength, const std::wstring& result) + { + REQUIRE(wcscmp(value, result.c_str()) == 0); + REQUIRE(result == value); + REQUIRE(result.size() == valueLength); + }); +} +#endif + +TEST_CASE("UniqueStringAndStringMakerTests::VerifyLegacySTringMakers", "[resource][string_maker]") +{ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + auto l = wil::make_hlocal_string(L"value"); + l = wil::make_hlocal_string_nothrow(L"value"); + l = wil::make_hlocal_string_failfast(L"value"); + + auto p = wil::make_process_heap_string(L"value"); + p = wil::make_process_heap_string_nothrow(L"value"); + p = wil::make_process_heap_string_failfast(L"value"); +#endif + auto c = wil::make_cotaskmem_string(L"value"); + c = wil::make_cotaskmem_string_nothrow(L"value"); + c = wil::make_cotaskmem_string_failfast(L"value"); +} +#endif + +_Use_decl_annotations_ void* __RPC_USER MIDL_user_allocate(size_t size) +{ + return ::HeapAlloc(GetProcessHeap(), 0, size); +} + +_Use_decl_annotations_ void __RPC_USER MIDL_user_free(void* p) +{ + ::HeapFree(GetProcessHeap(), 0, p); +} + +TEST_CASE("UniqueMidlStringTests", "[resource][rpc]") +{ + wil::unique_midl_ptr intArray{ reinterpret_cast(::MIDL_user_allocate(sizeof(int) * 10)) }; + intArray[2] = 1; + + wil::unique_midl_ptr intSingle{ reinterpret_cast(::MIDL_user_allocate(sizeof(int) * 1)) }; +} + +TEST_CASE("UniqueEnvironmentStrings", "[resource][win32]") +{ + wil::unique_environstrings_ptr env{ ::GetEnvironmentStringsW() }; + const wchar_t* nextVar = env.get(); + while (nextVar &&* nextVar) + { + // consume 'nextVar' + nextVar += wcslen(nextVar) + 1; + } + + wil::unique_environansistrings_ptr envAnsi{ ::GetEnvironmentStringsA() }; + const char* nextVarAnsi = envAnsi.get(); + while (nextVarAnsi && *nextVarAnsi) + { + // consume 'nextVar' + nextVarAnsi += strlen(nextVarAnsi) + 1; + } +} + +TEST_CASE("UniqueVariant", "[resource][com]") +{ + wil::unique_variant var; + var.vt = VT_BSTR; + var.bstrVal = ::SysAllocString(L"25"); + REQUIRE(var.bstrVal != nullptr); + + auto call = [](const VARIANT&) {}; + call(var); + + VARIANT weakVar = var; + (void)weakVar; + + wil::unique_variant var2; + REQUIRE_SUCCEEDED(VariantChangeType(&var2, &var, 0, VT_UI4)); + REQUIRE(var2.vt == VT_UI4); + REQUIRE(var2.uiVal == 25); +} + +TEST_CASE("DefaultTemplateParamCompiles", "[resource]") +{ + wil::unique_process_heap_ptr<> a; + wil::unique_virtualalloc_ptr<> b; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + wil::unique_hlocal_ptr<> c; + wil::unique_hlocal_secure_ptr<> d; + wil::unique_hglobal_ptr<> e; + wil::unique_cotaskmem_secure_ptr<> f; +#endif + + wil::unique_midl_ptr<> g; + wil::unique_cotaskmem_ptr<> h; +} diff --git a/Externals/WIL/tests/ResultTests.cpp b/Externals/WIL/tests/ResultTests.cpp new file mode 100644 index 0000000000..be93719119 --- /dev/null +++ b/Externals/WIL/tests/ResultTests.cpp @@ -0,0 +1,575 @@ + +#include +#include +#include + +#include + +#include "common.h" + +static volatile long objectCount = 0; +struct SharedObject +{ + SharedObject() + { + ::InterlockedIncrement(&objectCount); + } + + ~SharedObject() + { + ::InterlockedDecrement(&objectCount); + } + + void ProcessShutdown() + { + } + + int value{}; +}; + +TEST_CASE("ResultTests::SemaphoreValue", "[result]") +{ + auto TestValue = [&](auto start, auto end) + { + wil::details_abi::SemaphoreValue semaphore; + for (auto index = start; index <= end; index++) + { + semaphore.Destroy(); + REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index))); + + auto num1 = index; + auto num2 = index; + REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1))); + REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2))); + REQUIRE(num1 == index); + REQUIRE(num2 == index); + } + }; + + // Test 32-bit values (edge cases) + TestValue(0u, 10u); + TestValue(250u, 260u); + TestValue(0x7FFFFFF0u, 0x7FFFFFFFu); + + // Test 64-bit values (edge cases) + TestValue(0ull, 10ull); + TestValue(250ull, 260ull); + TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full); + TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full); + TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full); + TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull); + + // Test pointer values + wil::details_abi::SemaphoreValue semaphore; + void* address = &semaphore; + REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address))); + void* ptr; + REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr))); + REQUIRE(ptr == address); +} + +TEST_CASE("ResultTests::ProcessLocalStorage", "[result]") +{ + // Test process local storage memory and ref-counting + { + wil::details_abi::ProcessLocalStorage obj1("ver1"); + wil::details_abi::ProcessLocalStorage obj2("ver1"); + + auto& o1 = *obj1.GetShared(); + auto& o2 = *obj2.GetShared(); + + REQUIRE(o1.value == 0); + REQUIRE(o2.value == 0); + o1.value = 42; + REQUIRE(o2.value == 42); + REQUIRE(objectCount == 1); + + wil::details_abi::ProcessLocalStorage obj3("ver3"); + auto& o3 = *obj3.GetShared(); + + REQUIRE(o3.value == 0); + REQUIRE(objectCount == 2); + } + + REQUIRE(objectCount == 0); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +#pragma warning(push) +#pragma warning(disable: 4702) // Unreachable code +TEST_CASE("ResultTests::ExceptionHandling", "[result]") +{ + witest::TestFailureCache failures; + + SECTION("Test 'what()' implementation on ResultException") + { + auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false); + try + { + THROW_HR(E_INVALIDARG); + FAIL("Expected an exception"); + } + catch (const std::exception& exception) + { + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == E_INVALIDARG); + auto what = exception.what(); + REQUIRE((what && *what)); + REQUIRE(strstr(what, "Exception") != nullptr); + } + } + failures.clear(); + + SECTION("Test messaging from an unhandled std exception") + { + // #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue. + auto hr = []() + { + try + { + throw std::runtime_error("runtime"); + } + catch (...) + { + RETURN_CAUGHT_EXCEPTION(); + } + }(); + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string... + REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + failures.clear(); + + SECTION("Test messaging from bad_alloc") + { + auto hr = []() -> HRESULT + { + try + { + throw std::bad_alloc(); + } + catch (...) + { + RETURN_CAUGHT_EXCEPTION(); + } + }(); + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == E_OUTOFMEMORY); + REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string... + REQUIRE(hr == E_OUTOFMEMORY); + } + failures.clear(); + + SECTION("Test messaging from a WIL exception") + { + auto hr = []() -> HRESULT + { + try + { + THROW_HR(E_INVALIDARG); + } + catch (...) + { + RETURN_CAUGHT_EXCEPTION(); + } + return S_OK; + }(); + REQUIRE(failures.size() == 2); + REQUIRE(failures[0].hr == E_INVALIDARG); + REQUIRE(failures[0].pszMessage == nullptr); + REQUIRE(failures[1].hr == E_INVALIDARG); + REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string... + REQUIRE(hr == E_INVALIDARG); + } + failures.clear(); + + SECTION("Fail fast an unknown exception") + { + REQUIRE(witest::DoesCodeCrash([]() + { + try + { + throw E_INVALIDARG; // bad throw... (long) + } + catch (...) + { + RETURN_CAUGHT_EXCEPTION(); + } + })); + } + failures.clear(); + + SECTION("Log test (returns hr)") + { + HRESULT hr = S_OK; + try + { + throw std::bad_alloc(); + } + catch (...) + { + hr = LOG_CAUGHT_EXCEPTION(); + auto hrDirect = wil::ResultFromCaughtException(); + REQUIRE(hr == hrDirect); + } + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == E_OUTOFMEMORY); + REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string... + REQUIRE(hr == E_OUTOFMEMORY); + } + failures.clear(); + + SECTION("Fail-fast test") + { + REQUIRE_CRASH([]() + { + try + { + throw std::bad_alloc(); + } + catch (...) + { + FAIL_FAST_CAUGHT_EXCEPTION(); + } + }()); + } + failures.clear(); + + SECTION("Exception test (different exception type thrown...)") + { + auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false); + size_t line = 0; + try + { + try + { + throw std::bad_alloc(); + } + catch (...) + { + line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION(); + } + } + catch (const wil::ResultException& exception) + { + REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number + REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY); + } + catch (...) + { + FAIL(); + } + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == E_OUTOFMEMORY); + REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string... + } + failures.clear(); + + SECTION("Exception test (rethrow same exception type...)") + { + auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false); + size_t line = 0; + try + { + try + { + line = __LINE__; THROW_HR(E_OUTOFMEMORY); + } + catch (...) + { + THROW_NORMALIZED_CAUGHT_EXCEPTION(); + } + } + catch (const wil::ResultException& exception) + { + REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number) + } + catch (...) + { + FAIL(); + } + } + failures.clear(); + + SECTION("Test catch message") + { + try + { + throw std::bad_alloc(); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); + } + REQUIRE(failures.size() == 1); + REQUIRE(failures[0].hr == E_OUTOFMEMORY); + REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string... + REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message... + REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr); + } + failures.clear(); + + SECTION("Test messaging from a WIL exception") + { + auto hr = []() -> HRESULT + { + try + { + throw std::bad_alloc(); + } + catch (...) + { + RETURN_CAUGHT_EXCEPTION_EXPECTED(); + } + }(); + REQUIRE(failures.size() == 0); + REQUIRE(hr == E_OUTOFMEMORY); + } + failures.clear(); + + SECTION("Test ResultFromException...") + { + auto hrOk = wil::ResultFromException([&] + { + }); + REQUIRE(hrOk == S_OK); + + auto hr = wil::ResultFromException([&] + { + throw std::bad_alloc(); + }); + REQUIRE(failures.size() == 0); + REQUIRE(hr == E_OUTOFMEMORY); + } + failures.clear(); + + SECTION("Explicit failfast for unrecognized") + { + REQUIRE_CRASH(wil::ResultFromException([&] + { + throw E_FAIL; + })); + } + failures.clear(); + + SECTION("Manual debug-only validation of the SEH failfast") + { + auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]() + { + // Uncomment to test SEH fail-fast + // throw E_FAIL; + }); + REQUIRE(hr1 == S_OK); + + auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&] + { + // Uncomment to test SEH fail-fast + // throw std::range_error("range"); + }); + REQUIRE(hr2 == S_OK); + + wil::FailFastException(WI_DIAGNOSTICS_INFO, [&] + { + // Uncomment to test SEH fail-fast + // THROW_HR(E_FAIL); + }); + } + failures.clear(); + + SECTION("Standard") + { + auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&] + { + THROW_HR(E_INVALIDARG); + }); + REQUIRE(failures.size() == 2); + REQUIRE(static_cast(failures[1].uLineNumber) == line); + REQUIRE(hr == E_INVALIDARG); + } + failures.clear(); + + SECTION("bad_alloc") + { + auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&] + { + throw std::bad_alloc(); + }); + REQUIRE(failures.size() == 1); + REQUIRE(hr == E_OUTOFMEMORY); + } + failures.clear(); + + SECTION("std::exception") + { + auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&] + { + throw std::range_error("range"); + }); + REQUIRE(failures.size() == 1); + REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr); + REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } +} + +void ExceptionHandlingCompilationTest() +{ + []{ try { throw std::bad_alloc(); } CATCH_RETURN(); }(); + []{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }(); + []{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }(); + []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }(); + []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }(); + []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }(); + + try { throw std::bad_alloc(); } CATCH_LOG(); + try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42); + try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); } + try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); } + + try { throw std::bad_alloc(); } CATCH_FAIL_FAST(); + try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42); + try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); } + try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); } + + try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {} + try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } catch (...) {} + try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {} + try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {} + + HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&] + { + THROW_HR(E_FAIL); + }); + + hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&] + { + }); + + hr = wil::ResultFromException([&] + { + }); + + wil::FailFastException(WI_DIAGNOSTICS_INFO, [&] + { + }); +} +#pragma warning(pop) +#endif + +TEST_CASE("ResultTests::ErrorMacros", "[result]") +{ + REQUIRE_ERROR(FAIL_FAST()); + REQUIRE_ERROR(FAIL_FAST_IF(true)); + REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr)); + + REQUIRE_NOERROR(FAIL_FAST_IF(false)); + REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress())); + + REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42)); + REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42)); + REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42)); + + REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42)); + REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42)); + + //wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback; + SetLastError(ERROR_PRINTER_ALREADY_EXISTS); + REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE)); + REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE)); +} + +// The originate helper isn't compatible with CX so don't test it in that mode. +#ifndef __cplusplus_winrt +TEST_CASE("ResultTests::NoOriginationByDefault", "[result]") +{ + ::wil::SetOriginateErrorCallback(nullptr); + wil::com_ptr_nothrow restrictedErrorInformation; + + // We can't guarantee test order, so clear the error payload prior to starting + SetRestrictedErrorInfo(nullptr); + + []() -> HRESULT + { + RETURN_HR(S_OK); + }(); + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); + +#ifdef WIL_ENABLE_EXCEPTIONS + try + { + THROW_HR(E_FAIL); + } + catch (...) {} + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); +#endif // WIL_ENABLE_EXCEPTIONS + + []() -> HRESULT + { + RETURN_HR(E_FAIL); + }(); + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); + + []() -> HRESULT + { + RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED); + return S_OK; + }(); + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); +} + +TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]") +{ + ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); + wil::com_ptr_nothrow restrictedErrorInformation; + + // Make sure we don't start with an error payload + SetRestrictedErrorInfo(nullptr); + + // Success codes shouldn't originate. + []() + { + RETURN_HR(S_OK); + }(); + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); + + auto validateOriginatedError = [&](HRESULT hrExpected) + { + wil::unique_bstr descriptionUnused; + HRESULT existingHr = S_OK; + wil::unique_bstr restrictedDescriptionUnused; + wil::unique_bstr capabilitySidUnused; + REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)); + REQUIRE(hrExpected == existingHr); + }; + +#ifdef WIL_ENABLE_EXCEPTIONS + // Throwing an error should originate. + constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND; + try + { + THROW_HR(thrownErrorCode); + } + catch (...) {} + REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation)); + validateOriginatedError(thrownErrorCode); +#endif // WIL_ENABLE_EXCEPTIONS + + // Returning an error code should originate. + static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG; + []() + { + RETURN_HR(returnedErrorCode); + }(); + REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation)); + validateOriginatedError(returnedErrorCode); + + // _EXPECTED errors should NOT originate. + static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED; + []() + { + RETURN_IF_FAILED_EXPECTED(expectedErrorCode); + return S_OK; + }(); + REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); +} +#endif // __cplusplus_winrt diff --git a/Externals/WIL/tests/Rpc.cpp b/Externals/WIL/tests/Rpc.cpp new file mode 100644 index 0000000000..e66a73eaef --- /dev/null +++ b/Externals/WIL/tests/Rpc.cpp @@ -0,0 +1,137 @@ +#include "common.h" + +#include + +void RpcMethodReturnsVoid(ULONG toRaise) +{ + if (toRaise) + { + RaiseException(toRaise, 0, 0, nullptr); + } +} + +struct FOO_CONTEXT_T {}; +typedef FOO_CONTEXT_T* FOO_CONTEXT; +typedef FOO_CONTEXT* PFOO_CONTEXT; + +void CloseContextHandle(_Inout_ PFOO_CONTEXT) +{ +} + +void CloseContextHandleRaise(_Inout_ PFOO_CONTEXT) +{ + return RpcMethodReturnsVoid(RPC_X_BAD_STUB_DATA); +} + +HRESULT AcquireContextHandle(_In_ handle_t binding, _Out_ PFOO_CONTEXT context) +{ + *context = reinterpret_cast(binding); + return S_OK; +} + +HRESULT RpcMethodReturnsHResult(HRESULT toReturn, ULONG toRaise) +{ + RpcMethodReturnsVoid(toRaise); + return toReturn; +} + +GUID RpcMethodReturnsGuid(ULONG toRaise) +{ + RpcMethodReturnsVoid(toRaise); + return __uuidof(IUnknown); +} + +TEST_CASE("Rpc::NonThrowing", "[rpc]") +{ + SECTION("Success paths") + { + REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, 0UL)); + REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, S_OK, 0UL)); + + GUID tmp{}; + REQUIRE_SUCCEEDED(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, 0UL)); + REQUIRE(tmp == __uuidof(IUnknown)); + } + + SECTION("Failures in the method") + { + REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, 0) == E_CHANGED_STATE); + } + + SECTION("Failures in the fabric") + { + REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); + REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); + + GUID tmp{}; + REQUIRE(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); + } + + SECTION("Context Handles") + { + using foo_context_t = wil::unique_rpc_context_handle; + foo_context_t ctx; + auto tempBinding = reinterpret_cast(-5); + REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(AcquireContextHandle, tempBinding, ctx.put())); + REQUIRE(ctx.get() == reinterpret_cast(tempBinding)); + ctx.reset(); + } + + SECTION("Context Handles Close Raised") + { + using foo_context_t = wil::unique_rpc_context_handle; + foo_context_t ctx{ reinterpret_cast(42) }; + ctx.reset(); + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS + +#include + +class WilExceptionMatcher : public Catch::MatcherBase +{ + HRESULT m_expected; +public: + WilExceptionMatcher(HRESULT ex) : m_expected(ex) { } + + bool match(wil::ResultException const& ex) const override { + return ex.GetErrorCode() == m_expected; + } + + std::string describe() const override { + std::ostringstream ss; + ss << "wil::ResultException expects code 0x%08lx" << std::hex << m_expected; + return ss.str(); + } +}; + +#define REQUIRE_THROWS_WIL_HR(hr, expr) REQUIRE_THROWS_MATCHES(expr, wil::ResultException, WilExceptionMatcher(hr)) + +TEST_CASE("Rpc::Throwing", "[rpc]") +{ + SECTION("Success paths") + { + REQUIRE_NOTHROW(wil::invoke_rpc(RpcMethodReturnsVoid, 0UL)); + + GUID tmp{}; + REQUIRE_NOTHROW(tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, 0UL)); + REQUIRE(tmp == __uuidof(IUnknown)); + } + + SECTION("Failures in the method") + { + REQUIRE_THROWS_WIL_HR(E_CHANGED_STATE, wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, 0UL)); + } + + SECTION("Failures in the fabric") + { + REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsVoid, RPC_S_CALL_FAILED)); + REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED)); + + GUID tmp{}; + REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, RPC_S_CALL_FAILED)); + REQUIRE(tmp == GUID{}); + } +} +#endif diff --git a/Externals/WIL/tests/SafeCastTests.cpp b/Externals/WIL/tests/SafeCastTests.cpp new file mode 100644 index 0000000000..e8fb282dbb --- /dev/null +++ b/Externals/WIL/tests/SafeCastTests.cpp @@ -0,0 +1,571 @@ + +#include + +#include "common.h" + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("SafeCastTests::SafeCastThrowsTemplateCheck", "[safecast]") +{ + // In all cases, a value of '1' should be cast-able to any signed or unsigned integral type without error + SECTION("Unqualified char") + { + char orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + // wil::safe_cast (orig); // No available conversion in intsafe + wil::safe_cast (orig); + wil::safe_cast (orig); + // wil::safe_cast (orig); // No available conversion in intsafe + wil::safe_cast (orig); + wil::safe_cast (orig); + // wil::safe_cast (orig); // No available conversion in intsafe + wil::safe_cast (orig); + // wil::safe_cast (orig); // No available conversion in intsafe + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + // wil::safe_cast (orig); // No available conversion in intsafe + wil::safe_cast<__int3264> (orig); + // wil::safe_cast(orig); // No available conversion in intsafe + // wil::safe_cast (orig); // No available conversion in intsafe + } + + SECTION("Signed char") + { + signed char orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned char") + { + unsigned char orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unqualified short") + { + short orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Signed short") + { + signed short orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned short") + { + unsigned short orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unqualified int") + { + int orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Signed int") + { + signed int orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned int") + { + unsigned int orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unqualified long") + { + long orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned log") + { + unsigned long orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unqualified int64") + { + __int64 orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Signed int64") + { + signed __int64 orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned int64") + { + unsigned __int64 orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unqualified int3264") + { + __int3264 orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("Unsigned int3264") + { + unsigned __int3264 orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } + + SECTION("wchar_t") + { + wchar_t orig = 1; + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int64> (orig); + wil::safe_cast (orig); + wil::safe_cast (orig); + wil::safe_cast<__int3264> (orig); + wil::safe_cast(orig); + wil::safe_cast (orig); + } +} +#endif + +TEST_CASE("SafeCastTests::SafeCastFailFastSyntaxCheck", "[safecast]") +{ + SECTION("safe_cast_failfast safe") + { + INT i = INT_MAX; + LONG l = wil::safe_cast_failfast(i); + REQUIRE(l == INT_MAX); + } + + SECTION("safe_cast_failfast unsafe") + { + INT i = 0; + SHORT s = wil::safe_cast_failfast(i); + REQUIRE(s == 0); + } + + SECTION("safe_cast_failfast unsafe to wchar_t") + { + INT i = 0; + wchar_t wc = wil::safe_cast_failfast(i); + REQUIRE(wc == 0); + } + + SECTION("safe_cast_failfast unsafe from wchar_t") + { + wchar_t wc = 0; + unsigned char uc = wil::safe_cast_failfast(wc); + REQUIRE(uc == 0); + } +} + +TEST_CASE("SafeCastTests::SafeCastNoThrowSyntaxCheck", "[safecast]") +{ + SECTION("safe_cast_nothrow safe") + { + INT i = INT_MAX; + LONG l = wil::safe_cast_nothrow(i); + REQUIRE(l == INT_MAX); + } + + // safe_cast_nothrow one parameter unsafe, throws compiler error as expected + // { + // __int64 i64 = 0; + // int i = wil::safe_cast_nothrow(i64); + // } + + SECTION("safe_cast_nothrow two parameter potentially unsafe due to usage of variable sized types") + { + SIZE_T st = 0; + UINT ui; + auto result = wil::safe_cast_nothrow(st, &ui); + REQUIRE_SUCCEEDED(result); + REQUIRE(ui == 0); + } + + // safe_cast_nothrow two parameter known safe, throws compiler error as expected + // { + // unsigned char uc = 0; + // unsigned short us; + // auto result = wil::safe_cast_nothrow(uc, &us); + // } + + SECTION("safe_cast_nothrow unsafe") + { + INT i = 0; + SHORT s; + auto result = wil::safe_cast_nothrow(i, &s); + REQUIRE_SUCCEEDED(result); + REQUIRE(s == 0); + } + + SECTION("safe_cast_nothrow unsafe to wchar_t") + { + INT i = 0; + wchar_t wc; + auto result = wil::safe_cast_nothrow(i, &wc); + REQUIRE_SUCCEEDED(result); + REQUIRE(wc == 0); + } + + SECTION("safe_cast_nothrow unsafe from wchar_t") + { + wchar_t wc = 0; + unsigned char uc; + auto result = wil::safe_cast_nothrow(wc, &uc); + REQUIRE_SUCCEEDED(result); + REQUIRE(uc == 0); + } +} + +TEST_CASE("SafeCastTests::SafeCastNoFailures", "[safecast]") +{ + SECTION("INT -> LONG") + { + INT i = INT_MAX; + LONG l = wil::safe_cast_nothrow(i); + REQUIRE(l == INT_MAX); + } + + SECTION("LONG -> INT") + { + LONG l = LONG_MAX; + INT i = wil::safe_cast_nothrow(l); + REQUIRE(i == LONG_MAX); + } + + SECTION("INT -> UINT") + { + INT i = INT_MAX; + UINT ui = wil::safe_cast_failfast(i); + REQUIRE(ui == INT_MAX); + } + + SECTION("SIZE_T -> SIZE_T") + { + SIZE_T st = SIZE_T_MAX; + SIZE_T st2 = wil::safe_cast_failfast(st); + REQUIRE(st2 == SIZE_T_MAX); + } + + SECTION("wchar_t -> uint") + { + wchar_t wc = 0; + UINT ui = wil::safe_cast_failfast(wc); + REQUIRE(ui == 0); + } + + SECTION("wchar_t -> unsigned char") + { + wchar_t wc = 0; + unsigned char uc = wil::safe_cast_failfast(wc); + REQUIRE(uc == 0); + auto result = wil::safe_cast_nothrow(wc, &uc); + REQUIRE_SUCCEEDED(result); + } + + SECTION("uint -> wchar_t") + { + UINT ui = 0; + wchar_t wc = wil::safe_cast_failfast(ui); + REQUIRE(wc == 0); + auto result = wil::safe_cast_nothrow(ui, &wc); + REQUIRE_SUCCEEDED(result); + } + +#ifndef _WIN64 + SECTION("SIZE_T -> UINT") + { + SIZE_T st = SIZE_T_MAX; + UINT ui = wil::safe_cast_nothrow(st); + REQUIRE(ui == SIZE_T_MAX); + } +#endif +} + +TEST_CASE("SafeCastTests::SafeCastNoThrowFail", "[safecast]") +{ + SECTION("size_t -> short") + { + size_t st = SIZE_T_MAX; + short s; + REQUIRE_FAILED(wil::safe_cast_nothrow(st, &s)); + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("SafeCastTests::SafeCastExpectFailFast", "[safecast]") +{ + // Template for safe_cast fail fast tests, fill out more instances needed + witest::TestFailureCache failures; + + failures.clear(); + { + size_t st = SIZE_T_MAX; + REQUIRE_CRASH(wil::safe_cast_failfast(st)); + REQUIRE(failures.size() == 1); + } + + failures.clear(); + { + size_t st = SIZE_T_MAX; + REQUIRE_THROWS(wil::safe_cast(st)); + REQUIRE(failures.size() == 1); + } +} +#endif diff --git a/Externals/WIL/tests/StlTests.cpp b/Externals/WIL/tests/StlTests.cpp new file mode 100644 index 0000000000..e006202ea5 --- /dev/null +++ b/Externals/WIL/tests/StlTests.cpp @@ -0,0 +1,47 @@ + +#include + +#include "common.h" + +#ifndef WIL_ENABLE_EXCEPTIONS +#error STL tests require exceptions +#endif + +struct dummy +{ + char value; +}; + +// Specialize std::allocator<> so that we don't actually allocate/deallocate memory +dummy g_memoryBuffer[256]; +namespace std +{ + template <> + struct allocator + { + using value_type = dummy; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + dummy* allocate(std::size_t count) + { + REQUIRE(count <= std::size(g_memoryBuffer)); + return g_memoryBuffer; + } + + void deallocate(dummy* ptr, std::size_t count) + { + for (std::size_t i = 0; i < count; ++i) + { + REQUIRE(ptr[i].value == 0); + } + } + }; +} + +TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]") +{ + { + wil::secure_vector sensitiveBytes(32, dummy{ 'a' }); + } +} diff --git a/Externals/WIL/tests/TokenHelpersTests.cpp b/Externals/WIL/tests/TokenHelpersTests.cpp new file mode 100644 index 0000000000..39295405bf --- /dev/null +++ b/Externals/WIL/tests/TokenHelpersTests.cpp @@ -0,0 +1,320 @@ + +#include + +#include "common.h" + +TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]") +{ + // Open current thread/process access token + wil::unique_handle token; + REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token)); + REQUIRE(token != nullptr); + + REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ)); + REQUIRE(token != nullptr); + + REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current)); + REQUIRE(token != nullptr); + + REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self)); + REQUIRE(token != nullptr); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]") +{ + // Open current thread/process access token + wil::unique_handle token(wil::open_current_access_token()); + REQUIRE(token != nullptr); + + token = wil::open_current_access_token(TOKEN_READ); + REQUIRE(token != nullptr); + + token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current); + REQUIRE(token != nullptr); + + token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self); + REQUIRE(token != nullptr); +} +#endif + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]") +{ + SECTION("Passing a null token") + { + wistd::unique_ptr tokenInfo; + REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr)); + REQUIRE(tokenInfo != nullptr); + } + + SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct") + { + HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE); + wistd::unique_ptr tokenInfo; + REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken)); + } +} + +// Pseudo tokens can be passed to token APIs and avoid the handle allocations +// making use more efficient. +TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]") +{ + wistd::unique_ptr tokenInfo; + REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken())); + REQUIRE(tokenInfo != nullptr); + + REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken())); + REQUIRE(tokenInfo != nullptr); + + // No thread token by default, this should fail + REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken())); + REQUIRE(tokenInfo == nullptr); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]") +{ + // Passing a null token + wistd::unique_ptr tokenInfo(wil::get_token_information(nullptr)); + REQUIRE(tokenInfo != nullptr); +} +#endif + +// This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable +#ifndef WIL_FAST_BUILD +TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]") +{ + wil::unique_token_linked_token theToken; + REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr)); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_NOTHROW(wil::get_linked_token_information()); +#endif +} +#endif + +bool IsImpersonating() +{ + wil::unique_handle token; + if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token)) + { + WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN); + return false; + } + + return true; +} + +wil::unique_handle GetTokenToImpersonate() +{ + wil::unique_handle processToken; + FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken)); + + wil::unique_handle impersonateToken; + FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken)); + + return impersonateToken; +} + +TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]") +{ + auto impersonateToken = GetTokenToImpersonate(); + + // Set the thread into a known state - no token. + wil::unique_token_reverter clearThreadToken; + REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken)); + REQUIRE_FALSE(IsImpersonating()); + + // Set a token on the thread - the process token, guaranteed to be friendly + wil::unique_token_reverter setThreadToken1; + REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1)); + REQUIRE(IsImpersonating()); + + SECTION("Clear the token again, should be not impersonating, explicit reset") + { + wil::unique_token_reverter clearThreadAgain; + REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain)); + REQUIRE_FALSE(IsImpersonating()); + clearThreadAgain.reset(); + REQUIRE(IsImpersonating()); + } + + SECTION("Clear the token again, should be not impersonating, dtor reset") + { + wil::unique_token_reverter clearThreadAgain; + REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain)); + REQUIRE_FALSE(IsImpersonating()); + } + REQUIRE(IsImpersonating()); + + // Clear what we were impersonating + setThreadToken1.reset(); + REQUIRE_FALSE(IsImpersonating()); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]") +{ + auto impersonateToken = GetTokenToImpersonate(); + + // Set the thread into a known state - no token. + auto clearThreadToken = wil::run_as_self(); + REQUIRE_FALSE(IsImpersonating()); + + // Set a token on the thread - the process token, guaranteed to be friendly + auto setThreadToken1 = wil::impersonate_token(impersonateToken.get()); + REQUIRE(IsImpersonating()); + + SECTION("Clear the token again, should be not impersonating, explicit reset") + { + auto clearThreadAgain = wil::run_as_self(); + REQUIRE_FALSE(IsImpersonating()); + clearThreadAgain.reset(); + REQUIRE(IsImpersonating()); + } + + SECTION("Clear the token again, should be not impersonating, dtor reset") + { + auto clearThreadAgain = wil::run_as_self(); + REQUIRE_FALSE(IsImpersonating()); + } + REQUIRE(IsImpersonating()); + + // Clear what we were impersonating + setThreadToken1.reset(); + REQUIRE_FALSE(IsImpersonating()); +} +#endif // WIL_ENABLE_EXCEPTIONS + +template ::FixedSize>* = nullptr> +void TestGetTokenInfoForCurrentThread() +{ + wistd::unique_ptr tokenInfo; + const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr); + REQUIRE(S_OK == hr); +} + +template ::FixedSize>* = nullptr> +void TestGetTokenInfoForCurrentThread() +{ + T tokenInfo{}; + const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr); + REQUIRE(S_OK == hr); +} + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]") +{ + // Variable sized cases + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + + // Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation) + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); + TestGetTokenInfoForCurrentThread(); +} + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]") +{ + // Fixed size and reports size using ERROR_BAD_LENGTH (bug) + TestGetTokenInfoForCurrentThread(); +} + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]") +{ + SECURITY_IMPERSONATION_LEVEL tokenInfo{}; + + // SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit. + // Demonstrate the error return in that case. + REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken())); + + // Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no + // impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use + // the exception forms if this case is not expected. + REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken())); + + // Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires. + FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification)); + TestGetTokenInfoForCurrentThread(); + + REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken())); + + RevertToSelf(); +} + +bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right) +{ + return memcmp(&left, &right, sizeof(left)) == 0; +} + +TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]") +{ + SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; + auto staticSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS); + auto largerSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, DOMAIN_ALIAS_RID_BACKUP_OPS); + + largerSid = staticSid; + largerSid = largerSid; + // staticSid = largerSid; // Uncommenting this correctly fails to compile. + + REQUIRE(IsValidSid(staticSid.get())); + REQUIRE(*GetSidSubAuthorityCount(staticSid.get()) == 2); + REQUIRE(*GetSidIdentifierAuthority(staticSid.get()) == ntAuthority); + REQUIRE(*GetSidSubAuthority(staticSid.get(), 0) == SECURITY_BUILTIN_DOMAIN_RID); + REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS); +} + +TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]") +{ + bool member; + REQUIRE_SUCCEEDED(wil::test_token_membership_nothrow( + &member, + GetCurrentThreadEffectiveToken(), + SECURITY_NT_AUTHORITY, + SECURITY_AUTHENTICATED_USER_RID)); +} + +#ifdef WIL_ENABLE_EXCEPTIONS + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]") +{ + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + REQUIRE_NOTHROW(wil::get_token_information()); + + // check a non-pointer size value to make sure the whole struct is returned. + DWORD resultSize{}; + TOKEN_SOURCE ts{}; + auto tokenSource = wil::get_token_information(); + GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize); + REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0); +} + +TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]") +{ + // fixed size + REQUIRE_NOTHROW(wil::get_token_information_failfast()); + // variable size + REQUIRE_NOTHROW(wil::get_token_information_failfast()); +} + +TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]") +{ + auto impersonationToken = wil::impersonate_token(); + REQUIRE_NOTHROW(wil::get_token_information()); +} +#endif // WIL_ENABLE_EXCEPTIONS diff --git a/Externals/WIL/tests/UniqueWinRTEventTokenTests.cpp b/Externals/WIL/tests/UniqueWinRTEventTokenTests.cpp new file mode 100644 index 0000000000..936e747c0a --- /dev/null +++ b/Externals/WIL/tests/UniqueWinRTEventTokenTests.cpp @@ -0,0 +1,266 @@ + +#include +#include + +#include "common.h" + +using namespace ABI::Windows::Foundation; +using namespace Microsoft::WRL; + +namespace wiltest +{ + class AbiTestEventSender WrlFinal : public RuntimeClass< + RuntimeClassFlags, + IClosable, + IMemoryBufferReference, + FtmBase> + { + public: + + // IMemoryBufferReference + IFACEMETHODIMP get_Capacity(_Out_ UINT32* value) + { + *value = 0; + return S_OK; + } + + IFACEMETHODIMP add_Closed( + _In_ ITypedEventHandler* handler, + _Out_ ::EventRegistrationToken* token) + { + return m_closedEvent.Add(handler, token); + } + + IFACEMETHODIMP remove_Closed(::EventRegistrationToken token) + { + return m_closedEvent.Remove(token); + } + + // IClosable + IFACEMETHODIMP Close() + { + RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr)); + return S_OK; + } + + private: + Microsoft::WRL::EventSource> m_closedEvent; + }; +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + { + wil::unique_winrt_event_token token; + REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); + REQUIRE(static_cast(token)); + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); + } + + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + wil::unique_winrt_event_token token; + REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); + REQUIRE(static_cast(token)); + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); + + token.reset(); + + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + wil::unique_winrt_event_token outerToken; + REQUIRE_FALSE(static_cast(outerToken)); + { + wil::unique_winrt_event_token token; + REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); + REQUIRE(static_cast(token)); + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); + + outerToken = std::move(token); + REQUIRE_FALSE(static_cast(token)); + REQUIRE(static_cast(outerToken)); + } + + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 2); +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + wil::unique_winrt_event_token firstToken; + REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); + REQUIRE(static_cast(firstToken)); + closable->Close(); + REQUIRE(timesInvoked == 1); + + wil::unique_winrt_event_token secondToken(std::move(firstToken)); + REQUIRE_FALSE(static_cast(firstToken)); + REQUIRE(static_cast(secondToken)); + + closable->Close(); + REQUIRE(timesInvoked == 2); + + firstToken.reset(); + closable->Close(); + REQUIRE(timesInvoked == 3); + + secondToken.reset(); + closable->Close(); + REQUIRE(timesInvoked == 3); +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + wil::unique_winrt_event_token firstToken; + REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); + REQUIRE(static_cast(firstToken)); + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 1); + + ::EventRegistrationToken rawToken = firstToken.release(); + REQUIRE_FALSE(static_cast(firstToken)); + REQUIRE(rawToken.value != 0); + + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 2); + + wil::unique_winrt_event_token secondToken( + rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed); + + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 3); + + secondToken.reset(); + REQUIRE_SUCCEEDED(closable->Close()); + REQUIRE(timesInvoked == 3); +} + +TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]") +{ + ComPtr testEventSender; + REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); + ComPtr closable; + testEventSender.As(&closable); + + int timesInvoked = 0; + auto handler = Callback, + ITypedEventHandler, FtmBase>> + ([×Invoked](IInspectable*, IInspectable*) + { + timesInvoked++; + return S_OK; + }); + REQUIRE(timesInvoked == 0); + + { +#ifdef WIL_ENABLE_EXCEPTIONS + auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get()); + REQUIRE(static_cast(exceptionPolicyToken)); +#endif + + auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get()); + REQUIRE(static_cast(failFastPolicyToken)); + + REQUIRE_SUCCEEDED(closable->Close()); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(timesInvoked == 2); +#else + REQUIRE(timesInvoked == 1); +#endif + } + + REQUIRE_SUCCEEDED(closable->Close()); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(timesInvoked == 2); +#else + REQUIRE(timesInvoked == 1); +#endif +} diff --git a/Externals/WIL/tests/WatcherTests.cpp b/Externals/WIL/tests/WatcherTests.cpp new file mode 100644 index 0000000000..aac41c6dae --- /dev/null +++ b/Externals/WIL/tests/WatcherTests.cpp @@ -0,0 +1,517 @@ + +#include +#include +#include + +#include // For shared_event_watcher +#include + +#include "common.h" + +TEST_CASE("EventWatcherTests::Construction", "[resource][event_watcher]") +{ + SECTION("Create unique_event_watcher_nothrow without event") + { + auto watcher = wil::make_event_watcher_nothrow([]{}); + REQUIRE(watcher != nullptr); + } + + SECTION("Create unique_event_watcher_nothrow with unique_event_nothrow") + { + wil::unique_event_nothrow eventToPass; + FAIL_FAST_IF_FAILED(eventToPass.create(wil::EventOptions::None)); + auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{}); + REQUIRE(watcher != nullptr); + REQUIRE(eventToPass.get() == nullptr); // move construction must take it + } + + SECTION("Create unique_event_watcher_nothrow with handle") + { + wil::unique_event_nothrow eventToDupe; + FAIL_FAST_IF_FAILED(eventToDupe.create(wil::EventOptions::None)); + auto watcher = wil::make_event_watcher_nothrow(eventToDupe.get(), []{}); + REQUIRE(watcher != nullptr); + REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create unique_event_watcher_nothrow with unique_event") + { + wil::unique_event eventToPass(wil::EventOptions::None); + auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{}); + REQUIRE(watcher != nullptr); + REQUIRE(eventToPass.get() == nullptr); // move construction must take it + } + + SECTION("Create unique_event_watcher without event") + { + auto watcher = wil::make_event_watcher([]{}); + } + + SECTION("Create unique_event_watcher with unique_event_nothrow") + { + wil::unique_event_nothrow eventToPass; + THROW_IF_FAILED(eventToPass.create(wil::EventOptions::None)); + auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{}); + REQUIRE(eventToPass.get() == nullptr); // move construction must take it + } + + SECTION("Create unique_event_watcher with unique_event") + { + wil::unique_event eventToPass(wil::EventOptions::None); + auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{}); + REQUIRE(eventToPass.get() == nullptr); // move construction must take it + } + + SECTION("Create unique_event_watcher with handle") + { + wil::unique_event eventToDupe(wil::EventOptions::None); + auto watcher = wil::make_event_watcher(eventToDupe.get(), []{}); + REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case + } + + SECTION("Create unique_event_watcher shared watcher") + { + wil::shared_event_watcher sharedWatcher = wil::make_event_watcher([]{}); + } +#endif +} + +static auto make_event(wil::EventOptions options = wil::EventOptions::None) +{ + wil::unique_event_nothrow result; + FAIL_FAST_IF_FAILED(result.create(options)); + return result; +} + +TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]") +{ + auto notificationReceived = make_event(); + + int volatile countObserved = 0; + auto watcher = wil::make_event_watcher_nothrow([&] + { + countObserved++; + notificationReceived.SetEvent(); + }); + REQUIRE(watcher != nullptr); + + watcher.SetEvent(); + REQUIRE(notificationReceived.wait(5000)); // 5 second max wait + + watcher.SetEvent(); + REQUIRE(notificationReceived.wait(5000)); // 5 second max wait + REQUIRE(countObserved == 2); +} + +TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watcher]") +{ + wil::EventOptions const eventOptions[] = + { + wil::EventOptions::None, + wil::EventOptions::ManualReset, + wil::EventOptions::Signaled, + wil::EventOptions::ManualReset | wil::EventOptions::Signaled, + }; + + for (auto const &eventOption : eventOptions) + { + auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset to avoid hang in case where 2 callbacks are generated (a test failure). + auto processedChange = make_event(); + + DWORD volatile stateToObserve = 0; + DWORD volatile lastObservedState = 0; + int volatile countObserved = 0; + auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&] + { + allChangesMade.wait(); + countObserved++; + lastObservedState = stateToObserve; + processedChange.SetEvent(); + }); + REQUIRE(watcher != nullptr); + + stateToObserve = 1; + watcher.SetEvent(); + stateToObserve = 2; + watcher.SetEvent(); + + allChangesMade.SetEvent(); + REQUIRE(processedChange.wait(5000)); + + REQUIRE((countObserved == 1 || countObserved == 2)); // ensure the race worked how we wanted it to + REQUIRE(lastObservedState == stateToObserve); + } +} + +#define ROOT_KEY_PAIR HKEY_CURRENT_USER, L"Software\\Microsoft\\RegistryWatcherTest" + +TEST_CASE("RegistryWatcherTests::Construction", "[registry][registry_watcher]") +{ + SECTION("Create unique_registry_watcher_nothrow with string") + { + auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){}); + REQUIRE(watcher); + } + + SECTION("Create unique_registry_watcher_nothrow with unique_hkey") + { + wil::unique_hkey keyToMove; + REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr))); + + auto watcher = wil::make_registry_watcher_nothrow(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){}); + REQUIRE(watcher); + REQUIRE(keyToMove.get() == nullptr); // ownership is transferred + } + + SECTION("Create unique_registry_watcher_nothrow with handle") + { + // construct with just an open registry key + wil::unique_hkey rootKey; + REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &rootKey, nullptr))); + + auto watcher = wil::make_registry_watcher_nothrow(rootKey.get(), L"", true, [&](wil::RegistryChangeKind){}); + REQUIRE(watcher); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create unique_registry_watcher with string") + { + REQUIRE_NOTHROW(wil::make_registry_watcher(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){})); + } + + SECTION("Create unique_registry_watcher with unique_hkey") + { + wil::unique_hkey keyToMove; + THROW_IF_FAILED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr))); + + REQUIRE_NOTHROW(wil::make_registry_watcher(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){})); + REQUIRE(keyToMove.get() == nullptr); // ownership is transferred + } +#endif +} + +void SetRegistryValue( + _In_ HKEY hKey, + _In_opt_ LPCWSTR lpSubKey, + _In_opt_ LPCWSTR lpValueName, + _In_ DWORD dwType, + _In_reads_bytes_opt_(cbData) LPCVOID lpData, + _In_ DWORD cbData) +{ + wil::unique_hkey key; + REQUIRE(RegOpenKeyExW(hKey, lpSubKey, 0, KEY_WRITE, &key) == ERROR_SUCCESS); + REQUIRE(RegSetValueExW(key.get(), lpValueName, 0, dwType, static_cast(lpData), cbData) == ERROR_SUCCESS); +} + +TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]") +{ + RegDeleteTreeW(ROOT_KEY_PAIR); // So that we get the 'Modify' event + auto notificationReceived = make_event(); + + int volatile countObserved = 0; + auto volatile observedChangeType = wil::RegistryChangeKind::Delete; + auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) + { + countObserved++; + observedChangeType = changeType; + notificationReceived.SetEvent(); + }); + REQUIRE(watcher); + + DWORD value = 1; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + REQUIRE(notificationReceived.wait(5000)); + REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify); + + value++; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + REQUIRE(notificationReceived.wait(5000)); + REQUIRE(countObserved == 2); + REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify); +} + +TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry_watcher]") +{ + RegDeleteTreeW(ROOT_KEY_PAIR); + auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset for the case where both registry operations result in a callback. + auto processedChange = make_event(); + + DWORD volatile stateToObserve = 0; + DWORD volatile lastObservedState = 0; + DWORD volatile lastObservedValue = 0; + int volatile countObserved = 0; + auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&, called = false](wil::RegistryChangeKind) mutable + { + // This callback may be called more than once (since we modify the key twice), but we're holding references to + // local variables. Therefore, bail out if this is not the first time we're called + if (called) + { + return; + } + called = true; + + allChangesMade.wait(); + countObserved++; + lastObservedState = stateToObserve; + DWORD value, cbValue = sizeof(value); + RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue); + lastObservedValue = value; + processedChange.SetEvent(); + }); + REQUIRE(watcher); + + DWORD value; + // make 2 changes and verify that only the last gets observed + stateToObserve = 1; + value = 0; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + + stateToObserve = 2; + value = 1; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + + allChangesMade.SetEvent(); + REQUIRE(processedChange.wait(5000)); + + REQUIRE(countObserved >= 1); // Sometimes 2 events are observed, see if this can be eliminated. + REQUIRE(lastObservedState == stateToObserve); + REQUIRE(lastObservedValue == 1); +} + +TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_watcher]") +{ + auto notificationReceived = make_event(); + + int volatile countObserved = 0; + auto volatile observedChangeType = wil::RegistryChangeKind::Modify; + auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) + { + countObserved++; + observedChangeType = changeType; + notificationReceived.SetEvent(); + }); + REQUIRE(watcher); + + RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case + REQUIRE(notificationReceived.wait(5000)); + REQUIRE(countObserved == 1); + REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete); +} + +TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_watcher]") +{ + auto notificationReceived = make_event(); + + wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind) + { + watcher.reset(); + DWORD value = 2; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + notificationReceived.SetEvent(); + }); + REQUIRE(watcher); + + DWORD value = 1; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + REQUIRE(notificationReceived.wait(5000)); +} + +// Stress test, disabled by default +TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]") +{ + for (DWORD value = 0; value < 10000; ++value) + { + wil::srwlock lock; + auto notificationReceived = make_event(); + + wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind) + { + { + auto al = lock.lock_exclusive(); + watcher.reset(); // get m_refCount to 1 to ensure the Release happens on the background thread + } + ++value; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + notificationReceived.SetEvent(); + }); + REQUIRE(watcher); + + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + notificationReceived.wait(); + + { + auto al = lock.lock_exclusive(); + watcher.reset(); + } + } +} + +TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_watcher]") +{ + auto notificationReceived = make_event(); + + int volatile countObserved = 0; + auto volatile observedChangeType = wil::RegistryChangeKind::Modify; + wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) + { + countObserved++; + observedChangeType = changeType; + notificationReceived.SetEvent(); + watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) + { + countObserved++; + observedChangeType = changeType; + notificationReceived.SetEvent(); + }); + REQUIRE(watcher); + }); + REQUIRE(watcher); + + RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case + notificationReceived.wait(); + REQUIRE(countObserved == 1); + REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete); + + // wait for the reset to finish. The constructor creates the registry key + notificationReceived.wait(300); + DWORD value = 1; + SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value)); + + notificationReceived.wait(); + REQUIRE(countObserved == 2); + REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify); +} + +TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry][registry_watcher]") +{ + auto notificationReceived = make_event(); + auto deleteNotification = make_event(); + + int volatile deleteObserved = 0; + auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind) + { + notificationReceived.SetEvent(); + // ensure that the callback is still being executed while the watcher is reset(). + deleteNotification.wait(200); + deleteObserved++; + notificationReceived.SetEvent(); + }); + + RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case + REQUIRE(notificationReceived.wait(5000)); + + watcher.reset(); + deleteNotification.SetEvent(); + REQUIRE(notificationReceived.wait(5000)); + REQUIRE(deleteObserved == 1); +} + +TEST_CASE("FileSystemWatcherTests::Construction", "[resource][folder_watcher]") +{ + SECTION("Create unique_folder_watcher_nothrow with valid path") + { + auto watcher = wil::make_folder_watcher_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{}); + REQUIRE(watcher); + } + + SECTION("Create unique_folder_watcher_nothrow with invalid path") + { + auto watcher = wil::make_folder_watcher_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{}); + REQUIRE(!watcher); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create unique_folder_watcher with valid path") + { + REQUIRE_NOTHROW(wil::make_folder_watcher(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{})); + } + + SECTION("Create unique_folder_watcher with invalid path") + { + REQUIRE_THROWS(wil::make_folder_watcher(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{})); + } +#endif +} + +TEST_CASE("FileSystemWatcherTests::VerifyDelivery", "[resource][folder_watcher]") +{ + witest::TestFolder folder; + REQUIRE(folder); + + auto notificationEvent = make_event(); + int observedCount = 0; + auto watcher = wil::make_folder_watcher_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, [&] + { + ++observedCount; + notificationEvent.SetEvent(); + }); + REQUIRE(watcher); + + witest::TestFile file(folder.Path(), L"file.txt"); + REQUIRE(file); + REQUIRE(notificationEvent.wait(5000)); + REQUIRE(observedCount == 1); + + witest::TestFile file2(folder.Path(), L"file2.txt"); + REQUIRE(file2); + REQUIRE(notificationEvent.wait(5000)); + REQUIRE(observedCount == 2); +} + +TEST_CASE("FolderChangeReaderTests::Construction", "[resource][folder_change_reader]") +{ + SECTION("Create folder_change_reader_nothrow with valid path") + { + auto reader = wil::make_folder_change_reader_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {}); + REQUIRE(reader); + } + + SECTION("Create folder_change_reader_nothrow with invalid path") + { + auto reader = wil::make_folder_change_reader_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {}); + REQUIRE(!reader); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Create folder_change_reader with valid path") + { + REQUIRE_NOTHROW(wil::make_folder_change_reader(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {})); + } + + SECTION("Create folder_change_reader with invalid path") + { + REQUIRE_THROWS(wil::make_folder_change_reader(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {})); + } +#endif +} + +TEST_CASE("FolderChangeReaderTests::VerifyDelivery", "[resource][folder_change_reader]") +{ + witest::TestFolder folder; + REQUIRE(folder); + + auto notificationEvent = make_event(); + wil::FolderChangeEvent observedEvent; + wchar_t observedFileName[MAX_PATH] = L""; + auto reader = wil::make_folder_change_reader_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, + [&](wil::FolderChangeEvent event, PCWSTR fileName) + { + observedEvent = event; + StringCchCopyW(observedFileName, ARRAYSIZE(observedFileName), fileName); + notificationEvent.SetEvent(); + }); + REQUIRE(reader); + + witest::TestFile testFile(folder.Path(), L"file.txt"); + REQUIRE(testFile); + REQUIRE(notificationEvent.wait(5000)); + REQUIRE(observedEvent == wil::FolderChangeEvent::Added); + REQUIRE(wcscmp(observedFileName, L"file.txt") == 0); + + witest::TestFile testFile2(folder.Path(), L"file2.txt"); + REQUIRE(testFile2); + REQUIRE(notificationEvent.wait(5000)); + REQUIRE(observedEvent == wil::FolderChangeEvent::Added); + REQUIRE(wcscmp(observedFileName, L"file2.txt") == 0); +} diff --git a/Externals/WIL/tests/WinRTTests.cpp b/Externals/WIL/tests/WinRTTests.cpp new file mode 100644 index 0000000000..da53ae430e --- /dev/null +++ b/Externals/WIL/tests/WinRTTests.cpp @@ -0,0 +1,894 @@ + +#include // TODO: https://github.com/microsoft/wil/issues/44 +#include + +#ifdef WIL_ENABLE_EXCEPTIONS +#include +#include +#endif + +// Required for pinterface template specializations that we depend on in this test +#include +#pragma push_macro("GetCurrentTime") +#undef GetCurrentTime +#include +#pragma pop_macro("GetCurrentTime") + +#include "common.h" +#include "FakeWinRTTypes.h" +#include "test_objects.h" + +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Storage; +using namespace ABI::Windows::System; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +TEST_CASE("WinRTTests::VerifyTraitsTypes", "[winrt]") +{ + static_assert(wistd::is_same_v::type>, ""); + static_assert(wistd::is_same_v::type>, ""); + + static_assert(wistd::is_same_v, ""); + static_assert(wistd::is_same_v*, decltype(wil::details::GetReturnParamPointerType(&ILauncherStatics::LaunchUriAsync))>, ""); + + static_assert(wistd::is_same_v(nullptr)))>, ""); + static_assert(wistd::is_same_v*>(nullptr)))>, ""); + static_assert(wistd::is_same_v*>(nullptr)))>, ""); +} + +template +void DoHStringComparisonTest(LhsT&& lhs, RhsT&& rhs, int relation) +{ + using compare = wil::details::hstring_compare; + + // == and != + REQUIRE(compare::equals(lhs, rhs) == (relation == 0)); + REQUIRE(compare::not_equals(lhs, rhs) == (relation != 0)); + + REQUIRE(compare::equals(rhs, lhs) == (relation == 0)); + REQUIRE(compare::not_equals(rhs, lhs) == (relation != 0)); + + // < and >= + REQUIRE(compare::less(lhs, rhs) == (relation < 0)); + REQUIRE(compare::greater_equals(lhs, rhs) == (relation >= 0)); + + REQUIRE(compare::less(rhs, lhs) == (relation > 0)); + REQUIRE(compare::greater_equals(rhs, lhs) == (relation <= 0)); + + // > and <= + REQUIRE(compare::greater(lhs, rhs) == (relation > 0)); + REQUIRE(compare::less_equals(lhs, rhs) == (relation <= 0)); + + REQUIRE(compare::greater(rhs, lhs) == (relation < 0)); + REQUIRE(compare::less_equals(rhs, lhs) == (relation >= 0)); + + // We wish to test with both const and non-const values. We can do this for free here so long as the type is + // not an array since changing the const-ness of an array may change the expected results +#pragma warning(suppress: 4127) + if (!wistd::is_array>::value && + !wistd::is_const>::value) + { + const wistd::remove_reference_t& constLhs = lhs; + DoHStringComparisonTest(constLhs, rhs, relation); + } + +#pragma warning(suppress: 4127) + if (!wistd::is_array>::value && + !wistd::is_const>::value) + { + const wistd::remove_reference_t& constRhs = rhs; + DoHStringComparisonTest(lhs, constRhs, relation); + } +} + +// The two string arguments are expected to compare equal to one another using the specified IgnoreCase argument and +// contain at least one embedded null character +template +void DoHStringSameValueComparisonTest(const wchar_t (&lhs)[Size], const wchar_t (&rhs)[Size]) +{ + wchar_t lhsNonConstArray[Size + 5]; + wchar_t rhsNonConstArray[Size + 5]; + wcsncpy_s(lhsNonConstArray, lhs, Size); + wcsncpy_s(rhsNonConstArray, rhs, Size); + + // For non-const arrays, we should never deduce length, so even though we append different values to each string, we + // do so after the last null character, so they should never be read + wcsncpy_s(lhsNonConstArray + Size + 1, 4, L"foo", 3); + wcsncpy_s(rhsNonConstArray + Size + 1, 4, L"bar", 3); + + const wchar_t* lhsCstr = lhs; + const wchar_t* rhsCstr = rhs; + + HStringReference lhsRef(lhs); + HStringReference rhsRef(rhs); + HString lhsStr; + HString rhsStr; + REQUIRE_SUCCEEDED(lhsStr.Set(lhs)); + REQUIRE_SUCCEEDED(rhsStr.Set(rhs)); + auto lhsHstr = lhsStr.Get(); + auto rhsHstr = rhsStr.Get(); + + wil::unique_hstring lhsUniqueStr; + wil::unique_hstring rhsUniqueStr; + REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr)); + REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr)); + + // Const array - embedded nulls are included only if InhibitArrayReferences is false + DoHStringComparisonTest(lhs, rhs, 0); + DoHStringComparisonTest(lhs, rhsNonConstArray, InhibitArrayReferences ? 0 : 1); + DoHStringComparisonTest(lhs, rhsCstr, InhibitArrayReferences ? 0 : 1); + DoHStringComparisonTest(lhs, rhsRef, InhibitArrayReferences ? -1 : 0); + DoHStringComparisonTest(lhs, rhsStr, InhibitArrayReferences ? -1 : 0); + DoHStringComparisonTest(lhs, rhsHstr, InhibitArrayReferences ? -1 : 0); + DoHStringComparisonTest(lhs, rhsUniqueStr, InhibitArrayReferences ? -1 : 0); + + // Non-const array - *never* deduce length + DoHStringComparisonTest(lhsNonConstArray, rhsNonConstArray, 0); + DoHStringComparisonTest(lhsNonConstArray, rhsCstr, 0); + DoHStringComparisonTest(lhsNonConstArray, rhsRef, -1); + DoHStringComparisonTest(lhsNonConstArray, rhsStr, -1); + DoHStringComparisonTest(lhsNonConstArray, rhsHstr, -1); + DoHStringComparisonTest(lhsNonConstArray, rhsUniqueStr, -1); + + // C string - impossible to deduce length + DoHStringComparisonTest(lhsCstr, rhsCstr, 0); + DoHStringComparisonTest(lhsCstr, rhsRef, -1); + DoHStringComparisonTest(lhsCstr, rhsStr, -1); + DoHStringComparisonTest(lhsCstr, rhsHstr, -1); + DoHStringComparisonTest(lhsCstr, rhsUniqueStr, -1); + + // HStringReference + DoHStringComparisonTest(lhsRef, rhsRef, 0); + DoHStringComparisonTest(lhsRef, rhsStr, 0); + DoHStringComparisonTest(lhsRef, rhsHstr, 0); + DoHStringComparisonTest(lhsRef, rhsUniqueStr, 0); + + // HString + DoHStringComparisonTest(lhsStr, rhsStr, 0); + DoHStringComparisonTest(lhsStr, rhsHstr, 0); + DoHStringComparisonTest(lhsStr, rhsUniqueStr, 0); + + // Raw HSTRING + DoHStringComparisonTest(lhsHstr, rhsHstr, 0); + DoHStringComparisonTest(lhsHstr, rhsUniqueStr, 0); + + // wil::unique_hstring + DoHStringComparisonTest(lhsUniqueStr, rhsUniqueStr, 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring lhsWstr(lhs, 7); + std::wstring rhsWstr(rhs, 7); + DoHStringComparisonTest(lhsWstr, rhsWstr, 0); + DoHStringComparisonTest(lhsWstr, rhs, InhibitArrayReferences ? 1 : 0); + DoHStringComparisonTest(lhsWstr, rhsNonConstArray, 1); + DoHStringComparisonTest(lhsWstr, rhsCstr, 1); + DoHStringComparisonTest(lhsWstr, rhsRef, 0); + DoHStringComparisonTest(lhsWstr, rhsStr, 0); + DoHStringComparisonTest(lhsWstr, rhsHstr, 0); + DoHStringComparisonTest(lhsWstr, rhsUniqueStr, 0); +#endif +} + +// It's expected that the first argument (lhs) compares greater than the second argument (rhs) +template +void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const wchar_t (&rhs)[RhsSize]) +{ + wchar_t lhsNonConstArray[LhsSize]; + wchar_t rhsNonConstArray[RhsSize]; + wcsncpy_s(lhsNonConstArray, lhs, LhsSize); + wcsncpy_s(rhsNonConstArray, rhs, RhsSize); + + const wchar_t* lhsCstr = lhs; + const wchar_t* rhsCstr = rhs; + + HStringReference lhsRef(lhs); + HStringReference rhsRef(rhs); + HString lhsStr; + HString rhsStr; + REQUIRE_SUCCEEDED(lhsStr.Set(lhs)); + REQUIRE_SUCCEEDED(rhsStr.Set(rhs)); + auto lhsHstr = lhsStr.Get(); + auto rhsHstr = rhsStr.Get(); + + wil::unique_hstring lhsUniqueStr; + wil::unique_hstring rhsUniqueStr; + REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr)); + REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr)); + + // Const array + DoHStringComparisonTest(lhs, rhs, 1); + DoHStringComparisonTest(lhs, rhsNonConstArray, 1); + DoHStringComparisonTest(lhs, rhsCstr, 1); + DoHStringComparisonTest(lhs, rhsRef, 1); + DoHStringComparisonTest(lhs, rhsStr, 1); + DoHStringComparisonTest(lhs, rhsHstr, 1); + DoHStringComparisonTest(lhs, rhsUniqueStr, 1); + + // Non-const array + DoHStringComparisonTest(lhsNonConstArray, rhsNonConstArray, 1); + DoHStringComparisonTest(lhsNonConstArray, rhsCstr, 1); + DoHStringComparisonTest(lhsNonConstArray, rhsRef, 1); + DoHStringComparisonTest(lhsNonConstArray, rhsStr, 1); + DoHStringComparisonTest(lhsNonConstArray, rhsHstr, 1); + DoHStringComparisonTest(lhsNonConstArray, rhsUniqueStr, 1); + + // C string + DoHStringComparisonTest(lhsCstr, rhsCstr, 1); + DoHStringComparisonTest(lhsCstr, rhsRef, 1); + DoHStringComparisonTest(lhsCstr, rhsStr, 1); + DoHStringComparisonTest(lhsCstr, rhsHstr, 1); + DoHStringComparisonTest(lhsCstr, rhsUniqueStr, 1); + + // HStringReference + DoHStringComparisonTest(lhsRef, rhsRef, 1); + DoHStringComparisonTest(lhsRef, rhsStr, 1); + DoHStringComparisonTest(lhsRef, rhsHstr, 1); + DoHStringComparisonTest(lhsRef, rhsUniqueStr, 1); + + // HString + DoHStringComparisonTest(lhsStr, rhsStr, 1); + DoHStringComparisonTest(lhsStr, rhsHstr, 1); + DoHStringComparisonTest(lhsStr, rhsUniqueStr, 1); + + // Raw HSTRING + DoHStringComparisonTest(lhsHstr, rhsHstr, 1); + DoHStringComparisonTest(lhsHstr, rhsUniqueStr, 1); + + // wil::unique_hstring + DoHStringComparisonTest(lhsUniqueStr, rhsUniqueStr, 1); + +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring lhsWstr(lhs, 7); + std::wstring rhsWstr(rhs, 7); + DoHStringComparisonTest(lhsWstr, rhsWstr, 1); + DoHStringComparisonTest(lhsWstr, rhs, 1); + DoHStringComparisonTest(lhsWstr, rhsNonConstArray, 1); + DoHStringComparisonTest(lhsWstr, rhsCstr, 1); + DoHStringComparisonTest(lhsWstr, rhsRef, 1); + DoHStringComparisonTest(lhsWstr, rhsStr, 1); + DoHStringComparisonTest(lhsWstr, rhsHstr, 1); + DoHStringComparisonTest(lhsWstr, rhsUniqueStr, 1); +#endif +} + +TEST_CASE("WinRTTests::HStringComparison", "[winrt][hstring_compare]") +{ + SECTION("Don't inhibit arrays") + { + DoHStringSameValueComparisonTest(L"foo\0bar", L"foo\0bar"); + DoHStringDifferentValueComparisonTest(L"foo", L"bar"); + } + + SECTION("Inhibit arrays") + { + DoHStringSameValueComparisonTest(L"foo\0bar", L"foo\0bar"); + DoHStringDifferentValueComparisonTest(L"foo", L"bar"); + } + + SECTION("Ignore case") + { + DoHStringSameValueComparisonTest(L"foo\0bar", L"FoO\0bAR"); + DoHStringDifferentValueComparisonTest(L"Foo", L"baR"); + } + + SECTION("Empty string") + { + const wchar_t constArray[] = L""; + wchar_t nonConstArray[] = L""; + const wchar_t* cstr = constArray; + const wchar_t* nullCstr = nullptr; + + // str may end up referencing a null HSTRING. That's fine; we'll just test null HSTRING twice + HString str; + REQUIRE_SUCCEEDED(str.Set(constArray)); + HSTRING nullHstr = nullptr; + + // Const array - impossible to use null value + DoHStringComparisonTest(constArray, constArray, 0); + DoHStringComparisonTest(constArray, nonConstArray, 0); + DoHStringComparisonTest(constArray, cstr, 0); + DoHStringComparisonTest(constArray, nullCstr, 0); + DoHStringComparisonTest(constArray, str.Get(), 0); + DoHStringComparisonTest(constArray, nullHstr, 0); + + // Non-const array - impossible to use null value + DoHStringComparisonTest(nonConstArray, nonConstArray, 0); + DoHStringComparisonTest(nonConstArray, cstr, 0); + DoHStringComparisonTest(nonConstArray, nullCstr, 0); + DoHStringComparisonTest(nonConstArray, str.Get(), 0); + DoHStringComparisonTest(nonConstArray, nullHstr, 0); + + // Non-null c-string + DoHStringComparisonTest(cstr, cstr, 0); + DoHStringComparisonTest(cstr, nullCstr, 0); + DoHStringComparisonTest(cstr, str.Get(), 0); + DoHStringComparisonTest(cstr, nullHstr, 0); + + // Null c-string + DoHStringComparisonTest(nullCstr, nullCstr, 0); + DoHStringComparisonTest(nullCstr, str.Get(), 0); + DoHStringComparisonTest(nullCstr, nullHstr, 0); + + // (Possibly) non-null HSTRING + DoHStringComparisonTest(str.Get(), str.Get(), 0); + DoHStringComparisonTest(str.Get(), nullHstr, 0); + + // Null HSTRING + DoHStringComparisonTest(nullHstr, nullHstr, 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring wstr; + DoHStringComparisonTest(wstr, wstr, 0); + DoHStringComparisonTest(wstr, constArray, 0); + DoHStringComparisonTest(wstr, nonConstArray, 0); + DoHStringComparisonTest(wstr, cstr, 0); + DoHStringComparisonTest(wstr, nullCstr, 0); + DoHStringComparisonTest(wstr, str.Get(), 0); + DoHStringComparisonTest(wstr, nullHstr, 0); +#endif + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("WinRTTests::HStringMapTest", "[winrt][hstring_compare]") +{ + int nextValue = 0; + std::map wstringMap; + wstringMap.emplace(L"foo", nextValue++); + wstringMap.emplace(L"bar", nextValue++); + wstringMap.emplace(std::wstring(L"foo\0bar", 7), nextValue++); + wstringMap.emplace(L"adding", nextValue++); + wstringMap.emplace(L"quite", nextValue++); + wstringMap.emplace(L"a", nextValue++); + wstringMap.emplace(L"few", nextValue++); + wstringMap.emplace(L"more", nextValue++); + wstringMap.emplace(L"values", nextValue++); + wstringMap.emplace(L"for", nextValue++); + wstringMap.emplace(L"testing", nextValue++); + wstringMap.emplace(L"", nextValue++); + + std::map hstringMap; + for (auto& pair : wstringMap) + { + HString str; + THROW_IF_FAILED(str.Set(pair.first.c_str(), static_cast(pair.first.length()))); + hstringMap.emplace(std::move(str), pair.second); + } + + // Order should be the same as the map of wstring + auto itr = hstringMap.begin(); + for (auto& pair : wstringMap) + { + REQUIRE(itr != hstringMap.end()); + REQUIRE(itr->first == HStringReference(pair.first.c_str(), static_cast(pair.first.length()))); + + // Should also be able to find the value + REQUIRE(hstringMap.find(pair.first) != hstringMap.end()); + + ++itr; + } + REQUIRE(itr == hstringMap.end()); + + const wchar_t constArray[] = L"foo\0bar"; + wchar_t nonConstArray[] = L"foo\0bar"; + const wchar_t* cstr = constArray; + + HString key; + wil::unique_hstring uniqueHstr; + THROW_IF_FAILED(key.Set(constArray)); + THROW_IF_FAILED(key.CopyTo(&uniqueHstr)); + + HStringReference ref(constArray); + std::wstring wstr(constArray, 7); + + auto verifyFunc = [&](int expectedValue, auto&& keyValue) + { + auto itr = hstringMap.find(std::forward(keyValue)); + REQUIRE(itr != hstringMap.end()); + REQUIRE(expectedValue == itr->second); + }; + + // The following should find "foo\0bar" + auto expectedValue = wstringMap[wstr]; + verifyFunc(expectedValue, uniqueHstr); + verifyFunc(expectedValue, key); + verifyFunc(expectedValue, key.Get()); + verifyFunc(expectedValue, ref); + verifyFunc(expectedValue, wstr); + + // Arrays/strings should not deduce length and should therefore find "foo" + expectedValue = wstringMap[L"foo"]; + verifyFunc(expectedValue, constArray); + verifyFunc(expectedValue, nonConstArray); + verifyFunc(expectedValue, cstr); + + // Should not ignore case + REQUIRE(hstringMap.find(L"FOO") == hstringMap.end()); + + // Should also be able to find empty values + const wchar_t constEmptyArray[] = L""; + wchar_t nonConstEmptyArray[] = L""; + const wchar_t* emptyCstr = constEmptyArray; + const wchar_t* nullCstr = nullptr; + + HString emptyStr; + HSTRING nullHstr = nullptr; + + std::wstring emptyWstr; + + expectedValue = wstringMap[L""]; + verifyFunc(expectedValue, constEmptyArray); + verifyFunc(expectedValue, nonConstEmptyArray); + verifyFunc(expectedValue, emptyCstr); + verifyFunc(expectedValue, nullCstr); + verifyFunc(expectedValue, emptyStr); + verifyFunc(expectedValue, nullHstr); + verifyFunc(expectedValue, emptyWstr); +} + +TEST_CASE("WinRTTests::HStringCaseInsensitiveMapTest", "[winrt][hstring_compare]") +{ + std::map hstringMap; + + auto emplaceFunc = [&](auto&& key, int value) + { + HString str; + THROW_IF_FAILED(str.Set(std::forward(key))); + hstringMap.emplace(std::move(str), value); + }; + + int nextValue = 0; + int fooValue = nextValue++; + emplaceFunc(L"foo", fooValue); + emplaceFunc(L"bar", nextValue++); + int foobarValue = nextValue++; + emplaceFunc(L"foo\0bar", foobarValue); + emplaceFunc(L"foobar", nextValue++); + emplaceFunc(L"adding", nextValue++); + emplaceFunc(L"some", nextValue++); + emplaceFunc(L"more", nextValue++); + emplaceFunc(L"values", nextValue++); + emplaceFunc(L"for", nextValue++); + emplaceFunc(L"testing", nextValue++); + WI_ASSERT(static_cast(nextValue) == hstringMap.size()); + + const wchar_t constArray[] = L"FoO\0BAr"; + wchar_t nonConstArray[] = L"fOo\0baR"; + const wchar_t* cstr = constArray; + + HString key; + wil::unique_hstring uniqueHstr; + THROW_IF_FAILED(key.Set(constArray)); + THROW_IF_FAILED(key.CopyTo(&uniqueHstr)); + + HStringReference ref(constArray); + std::wstring wstr(constArray, 7); + + auto verifyFunc = [&](int expectedValue, auto&& key) + { + auto itr = hstringMap.find(std::forward(key)); + REQUIRE(itr != std::end(hstringMap)); + REQUIRE(expectedValue == itr->second); + }; + + // The following should find "foo\0bar" + verifyFunc(foobarValue, uniqueHstr); + verifyFunc(foobarValue, key); + verifyFunc(foobarValue, key.Get()); + verifyFunc(foobarValue, ref); + verifyFunc(foobarValue, wstr); + + // Arrays/strings should not deduce length and should therefore find "foo" + verifyFunc(fooValue, constArray); + verifyFunc(fooValue, nonConstArray); + verifyFunc(fooValue, cstr); +} +#endif + +// This is not a test method, nor should it be called. This is a compilation-only test. +#ifdef WIL_ENABLE_EXCEPTIONS +void RunWhenCompleteCompilationTest() +{ + { + ComPtr> stringOp; + wil::run_when_complete(stringOp.Get(), [](HRESULT /* result */, HSTRING /* value */) {}); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + auto result = wil::wait_for_completion(stringOp.Get()); +#endif + } + + { + ComPtr> stringOpWithProgress; + wil::run_when_complete(stringOpWithProgress.Get(), [](HRESULT /* result */, HSTRING /* value */) {}); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + auto result = wil::wait_for_completion(stringOpWithProgress.Get()); +#endif + } +} +#endif + +TEST_CASE("WinRTTests::RunWhenCompleteMoveOnlyTest", "[winrt][run_when_complete]") +{ + auto op = Make>(); + REQUIRE(op); + + bool gotEvent = false; + auto hr = wil::run_when_complete_nothrow(op.Get(), [&gotEvent, enforce = cannot_copy{}](HRESULT hr, int result) + { + (void)enforce; + REQUIRE_SUCCEEDED(hr); + REQUIRE(result == 42); + gotEvent = true; + return S_OK; + }); + REQUIRE_SUCCEEDED(hr); + + op->Complete(S_OK, 42); + REQUIRE(gotEvent); +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +TEST_CASE("WinRTTests::WaitForCompletionTimeout", "[winrt][wait_for_completion]") +{ + auto op = Make>(); + REQUIRE(op); + + // The wait_for_completion* functions don't properly deduce the "decayed" async type, so force it here + auto asyncOp = static_cast*>(op.Get()); + + bool timedOut = false; + REQUIRE_SUCCEEDED(wil::wait_for_completion_or_timeout_nothrow(asyncOp, 1, &timedOut)); + REQUIRE(timedOut); +} + +// This is not a test method, nor should it be called. This is a compilation-only test. +#pragma warning(push) +#pragma warning(disable: 4702) // Unreachable code +void WaitForCompletionCompilationTest() +{ + // Ensure the wait_for_completion variants compile + FAIL_FAST_HR_MSG(E_UNEXPECTED, "This is a compilation test, and should not be called"); + + // template + // inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + IAsyncAction* action = nullptr; + wil::wait_for_completion_nothrow(action); + wil::wait_for_completion_nothrow(action, COWAIT_DEFAULT); + + // template + // HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + // _Out_ typename wil::details::MapAsyncOpResultType::type* result, + // COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + IAsyncOperation* operation = nullptr; + wil::wait_for_completion_nothrow(operation); + wil::wait_for_completion_nothrow(operation, COWAIT_DEFAULT); + + // template + // HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + // _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + // COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + + ComPtr> operationWithResult; + boolean result = false; + wil::wait_for_completion_nothrow(operationWithResult.Get(), &result); + wil::wait_for_completion_nothrow(operationWithResult.Get(), &result, COWAIT_DEFAULT); + + DWORD timeoutValue = 1000; // arbitrary + bool timedOut = false; + + // template + // inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation, + // DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS); + wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut); + wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut, COWAIT_DEFAULT); + + // template + // HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + // _Out_ typename wil::details::MapAsyncOpResultType::type* result, + // DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS); + wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut); + wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut, COWAIT_DEFAULT); + + // template + // HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + // _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + // DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS); + wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut); + wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut, COWAIT_DEFAULT); + +#ifdef WIL_ENABLE_EXCEPTIONS + // template + // inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + wil::wait_for_completion(action); + wil::wait_for_completion(action, COWAIT_DEFAULT); + + // template ::type>::type> + // TReturn + // wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + wil::wait_for_completion(operation); + wil::wait_for_completion(operation, COWAIT_DEFAULT); + + // template ::type>::type> + // TReturn + // wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE); + result = wil::wait_for_completion(operationWithResult.Get()); + result = wil::wait_for_completion(operationWithResult.Get(), COWAIT_DEFAULT); +#endif +} +#pragma warning(pop) +#endif + +TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]") +{ + // Verifying that converting DateTime variable set as the date that means 0 as time_t works + DateTime time1 = { wil::SecondsToStartOf1970 * wil::HundredNanoSecondsInSecond }; + __time64_t time_t1 = wil::DateTime_to_time_t(time1); + REQUIRE(time_t1 == 0); + + // Verifying that converting back to DateTime would return the same value + DateTime time2 = wil::time_t_to_DateTime(time_t1); + REQUIRE(time1.UniversalTime == time2.UniversalTime); + + // Verifying that converting to time_t for non-zero value also works + time2.UniversalTime += wil::HundredNanoSecondsInSecond * 123; + __time64_t time_t2 = wil::DateTime_to_time_t(time2); + REQUIRE(time_t2 - time_t1 == 123); + + // Verifying that converting back to DateTime for non-zero value also works + time1 = wil::time_t_to_DateTime(time_t2); + REQUIRE(time1.UniversalTime == time2.UniversalTime); +} + +ComPtr> MakeSampleInspectableVector() +{ + auto result = Make>(); + REQUIRE(result); + + ComPtr propStatics; + REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics)); + + for (UINT32 i = 0; i < 5; ++i) + { + ComPtr myProp; + REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp)); + REQUIRE_SUCCEEDED(result->Append(myProp.Get())); + } + + return result; +} + +ComPtr> MakeSampleStringVector() +{ + auto result = Make>(); + REQUIRE(result); + + const HStringReference items[] = { HStringReference(L"one"), HStringReference(L"two"), HStringReference(L"three") }; + for (const auto& i : items) + { + REQUIRE_SUCCEEDED(result->Append(i.Get())); + } + + return result; +} + +ComPtr> MakeSamplePointVector() +{ + auto result = Make>(); + REQUIRE(result); + + for (int i = 0; i < 5; ++i) + { + auto value = static_cast(i); + REQUIRE_SUCCEEDED(result->Append(Point{ value, value })); + } + + return result; +} + +TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]") +{ + auto uninit = wil::RoInitialize_failfast(); + + auto inspectables = MakeSampleInspectableVector(); + unsigned count = 0; + REQUIRE_SUCCEEDED(inspectables->get_Size(&count)); + + unsigned idx = 0; + HRESULT success = S_OK; + for (const auto& i : wil::get_range_nothrow(inspectables.Get(), &success)) + { + // Duplications are not a typo - they verify the thing is callable twice + + UINT32 value; + ComPtr> intRef; + REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef))); + REQUIRE_SUCCEEDED(intRef->get_Value(&value)); + REQUIRE(idx == value); + REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef))); + REQUIRE_SUCCEEDED(intRef->get_Value(&value)); + REQUIRE(idx == value); + + ++idx; + + HString rtc; + REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf())); + REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf())); + } + REQUIRE_SUCCEEDED(success); + REQUIRE(count == idx); + + auto strings = MakeSampleStringVector(); + for (const auto& i : wil::get_range_nothrow(strings.Get(), &success)) + { + REQUIRE(i.Get()); + REQUIRE(i.Get()); + } + REQUIRE_SUCCEEDED(success); + + int index = 0; + auto points = MakeSamplePointVector(); + for (auto value : wil::get_range_nothrow(points.Get(), &success)) + { + REQUIRE(index++ == value.Get().X); + } + REQUIRE_SUCCEEDED(success); + + // operator-> should not clear out the pointer + auto inspRange = wil::get_range_nothrow(inspectables.Get()); + for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr) + { + REQUIRE(itr->Get()); + } + + auto strRange = wil::get_range_nothrow(strings.Get()); + for (auto itr = strRange.begin(); itr != strRange.end(); ++itr) + { + REQUIRE(itr->Get()); + } + + index = 0; + auto pointRange = wil::get_range_nothrow(points.Get()); + for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr) + { + REQUIRE(index++ == itr->Get().X); + } + +#if (defined WIL_ENABLE_EXCEPTIONS) + idx = 0; + for (const auto& i : wil::get_range(inspectables.Get())) + { + // Duplications are not a typo - they verify the thing is callable twice + + UINT32 value; + ComPtr> intRef; + REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef))); + REQUIRE_SUCCEEDED(intRef->get_Value(&value)); + REQUIRE(idx == value); + REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef))); + REQUIRE_SUCCEEDED(intRef->get_Value(&value)); + REQUIRE(idx == value); + + ++idx; + + HString rtc; + REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf())); + REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf())); + } + REQUIRE(count == idx); + + for (const auto& i : wil::get_range(strings.Get())) + { + REQUIRE(i.Get()); + REQUIRE(i.Get()); + } + + index = 0; + for (auto value : wil::get_range(points.Get())) + { + REQUIRE(index++ == value.Get().X); + } + + // operator-> should not clear out the pointer + for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr) + { + REQUIRE(itr->Get()); + } + + for (auto itr = strRange.begin(); itr != strRange.end(); ++itr) + { + REQUIRE(itr->Get()); + } + + index = 0; + for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr) + { + REQUIRE(index++ == itr->Get().X); + } + + // Iterator self-assignment is a nop. + { + auto inspRange2 = wil::get_range(inspectables.Get()); + auto itr = inspRange2.begin(); + REQUIRE(itr != inspRange2.end()); // should have something in it + auto& ref = *itr; + auto val = ref; + itr = itr; + REQUIRE(val == ref); + itr = std::move(itr); + REQUIRE(val == ref); + } + + { + auto strRange2 = wil::get_range(strings.Get()); + auto itr = strRange2.begin(); + REQUIRE(itr != strRange2.end()); // should have something in it + auto& ref = *itr; + auto val = ref.Get(); + itr = itr; + REQUIRE(val == ref); + itr = std::move(itr); + REQUIRE(val == ref.Get()); + } +#endif +} + +unsigned long GetComObjectRefCount(IUnknown* unk) { unk->AddRef(); return unk->Release(); } + +TEST_CASE("WinRTTests::VectorRangeLeakTest", "[winrt][vector_range]") +{ + auto uninit = wil::RoInitialize_failfast(); + + auto inspectables = MakeSampleInspectableVector(); + ComPtr verifyNotLeaked; + HRESULT hr = S_OK; + for (const auto& ptr : wil::get_range_nothrow(inspectables.Get(), &hr)) + { + if (!verifyNotLeaked) + { + verifyNotLeaked = ptr; + } + } + inspectables = nullptr; // clear all refs to verifyNotLeaked + REQUIRE_SUCCEEDED(hr); + REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1); + + inspectables = MakeSampleInspectableVector(); + for (const auto& ptr : wil::get_range_failfast(inspectables.Get())) + { + if (!verifyNotLeaked) + { + verifyNotLeaked = ptr; + } + } + inspectables = nullptr; // clear all refs to verifyNotLeaked + REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1); + +#if (defined WIL_ENABLE_EXCEPTIONS) + inspectables = MakeSampleInspectableVector(); + for (const auto& ptr : wil::get_range(inspectables.Get())) + { + if (!verifyNotLeaked) + { + verifyNotLeaked = ptr; + } + } + inspectables = nullptr; // clear all refs to verifyNotLeaked + REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1); +#endif +} + +TEST_CASE("WinRTTests::TwoPhaseConstructor", "[winrt][hstring]") +{ + const wchar_t left[] = L"left"; + const wchar_t right[] = L"right"; + ULONG needed = ARRAYSIZE(left) + ARRAYSIZE(right) - 1; + auto maker = wil::TwoPhaseHStringConstructor::Preallocate(needed); + REQUIRE_SUCCEEDED(StringCbCopyW(maker.Get(), maker.ByteSize(), left)); + REQUIRE_SUCCEEDED(StringCbCatW(maker.Get(), maker.ByteSize(), right)); + REQUIRE_SUCCEEDED(maker.Validate(needed * sizeof(wchar_t))); + + wil::unique_hstring promoted{ maker.Promote() }; + REQUIRE(wcscmp(L"leftright", str_raw_ptr(promoted)) == 0); +} \ No newline at end of file diff --git a/Externals/WIL/tests/WistdTests.cpp b/Externals/WIL/tests/WistdTests.cpp new file mode 100644 index 0000000000..b4a6974060 --- /dev/null +++ b/Externals/WIL/tests/WistdTests.cpp @@ -0,0 +1,209 @@ + +#include + +#include "common.h" +#include "test_objects.h" + +// Test methods/objects +int GetValue() +{ + return 42; +} + +int GetOtherValue() +{ + return 8; +} + +int Negate(int value) +{ + return -value; +} + +int Add(int lhs, int rhs) +{ + return lhs + rhs; +} + +TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]") +{ + wistd::function getValue = GetValue; + REQUIRE(GetValue() == getValue()); + + wistd::function negate = Negate; + REQUIRE(Negate(42) == negate(42)); + + wistd::function add = Add; + REQUIRE(Add(42, 8) == add(42, 8)); +} + +TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]") +{ + wistd::function fn = GetValue; + REQUIRE(GetValue() == fn()); + + fn = GetOtherValue; + REQUIRE(GetOtherValue() == fn()); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]") +{ + // We should be able to capture a std::function in a wistd::function + wistd::function fn; + + { + value_holder holder{ 42 }; + std::function stdFn = [holder]() + { + return holder.value; + }; + + fn = stdFn; + } + + REQUIRE(42 == fn()); +} +#endif + +TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]") +{ + object_counter_state state; + { + wistd::function copyFrom = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + REQUIRE(0 == copyFrom()); + + auto copyTo = copyFrom; + REQUIRE(1 == copyTo()); + } + + REQUIRE(0 == state.instance_count()); +} + +TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]") +{ + object_counter_state state; + { + wistd::function copyTo; + { + wistd::function copyFrom = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + REQUIRE(0 == copyFrom()); + + copyTo = copyFrom; + } + + REQUIRE(1 == copyTo()); + } + + REQUIRE(0 == state.instance_count()); +} + +TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]") +{ + object_counter_state state; + { + wistd::function moveFrom = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + REQUIRE(0 == moveFrom()); + + auto moveTo = std::move(moveFrom); + REQUIRE(0 == moveTo()); + + // Because we move the underlying function object, we _must_ invalidate the moved from function + REQUIRE_FALSE(moveFrom != nullptr); + } + + REQUIRE(0 == state.instance_count()); +} + +TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]") +{ + object_counter_state state; + { + wistd::function moveTo; + { + wistd::function moveFrom = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + REQUIRE(0 == moveFrom()); + + moveTo = std::move(moveFrom); + } + + REQUIRE(0 == moveTo()); + } + + REQUIRE(0 == state.instance_count()); +} + +TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]") +{ + object_counter_state state; + { + wistd::function first; + wistd::function second; + + first.swap(second); + REQUIRE_FALSE(first != nullptr); + REQUIRE_FALSE(second != nullptr); + + first = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + + first.swap(second); + REQUIRE_FALSE(first != nullptr); + REQUIRE(second != nullptr); + REQUIRE(0 == second()); + + first.swap(second); + REQUIRE(first != nullptr); + REQUIRE_FALSE(second != nullptr); + REQUIRE(0 == first()); + + second = [counter = object_counter{ state }]() + { + return counter.state->copy_count; + }; + + first.swap(second); + REQUIRE(first != nullptr); + REQUIRE(second != nullptr); + REQUIRE(0 == first()); + } + + REQUIRE(0 == state.instance_count()); +} + +// MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a +// function that accepts the arguments by value. This test exercises the workaround that we have in place. Note +// that this of course requires building with optimizations enabled +void ForwardingTest(wistd::function getValue, wistd::function negate, wistd::function add) +{ + // Previously, this would cause a runtime crash + REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8))); +} + +template +void CallForwardingTest(Args&&... args) +{ + ForwardingTest(wistd::forward(args)...); +} + +TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]") +{ + CallForwardingTest( + wistd::function(GetValue), + wistd::function(Negate), + wistd::function(Add)); +} diff --git a/Externals/WIL/tests/app/CMakeLists.txt b/Externals/WIL/tests/app/CMakeLists.txt new file mode 100644 index 0000000000..1118f23535 --- /dev/null +++ b/Externals/WIL/tests/app/CMakeLists.txt @@ -0,0 +1,21 @@ + +project(witest.app) +add_executable(witest.app) + +add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP) + +target_sources(witest.app PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp + ) diff --git a/Externals/WIL/tests/catch.hpp b/Externals/WIL/tests/catch.hpp new file mode 100644 index 0000000000..1850fff125 --- /dev/null +++ b/Externals/WIL/tests/catch.hpp @@ -0,0 +1,14934 @@ +/* + * Catch v2.7.0 + * Generated: 2019-03-07 21:34:30.252164 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +#include + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +// MSVC is adding extra space and needs more calls to properly remove () +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include + +namespace Catch { +template< typename... > +struct TypeList {}; + +template< typename... > +struct append; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 +> +struct append< L1, L2 > { + using type = L1; +}; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 + , typename...Rest +> +struct append< L1, L2, Rest...> { + using type = typename append< L1, Rest... >::type; +}; + +template< template class + , typename... +> +struct rewrap; + +template< template class Container + , template class List + , typename...elems +> +struct rewrap> { + using type = TypeList< Container< elems... > >; +}; + +template< template class Container + , template class List + , class...Elems + , typename...Elements> + struct rewrap, Elements...> { + using type = typename append>, typename rewrap::type>::type; +}; + +template< template class...Containers > +struct combine { + template< typename...Types > + struct with_types { + template< template class Final > + struct into { + using type = typename append, typename rewrap::type...>::type; + }; + }; +}; + +template +struct always_false : std::false_type {}; + +} // namespace Catch + +// end catch_meta.hpp +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \ + template \ + static void TestName() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test(); \ + }; \ + } \ + template \ + void TestName::test() +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc();\ + namespace {\ + template \ + struct TestName{\ + template \ + TestName(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc() + +#if defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case"); +#else +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case"); +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ + return 0;\ + }(); + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template static void TestFuncName(); \ + namespace { \ + template \ + struct TestName { \ + TestName() { \ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ + int index = 0; \ + using expander = int[]; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = Catch::combine \ + ::with_types::into::type; \ + TestInit(); \ + return 0; \ + }(); \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ),Name,Tags,__VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + template \ + struct TestNameClass{\ + template \ + TestNameClass(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + namespace {\ + template\ + struct TestNameClass{\ + TestNameClass(){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ + int index = 0;\ + using expander = int[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = Catch::combine\ + ::with_types::into::type;\ + TestInit();\ + return 0;\ + }(); \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +namespace Catch { + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + typename std::enable_if::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::optional& optional) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { + template<> + struct StringMaker { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template + struct StringMaker> { + static std::string convert(const std::variant& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + + template + auto operator && ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator == ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator != ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator > ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator < ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator >= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator <= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, static_cast(lhs) }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + template + auto operator && ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ); + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved; + }; + + class Capturer { + std::vector m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ + Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +// end catch_timer.h +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +// end catch_section.h +// start catch_benchmark.h + +#include +#include + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +// end catch_benchmark.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template + struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst == *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include + +namespace Catch { + + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif +} // namespace Catch; + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( ( Catch::ReusableStringStream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) +#define CATCH_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) +#define CATCH_RUNTIME_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +#include +#include +#include + +#include +#include + +namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template + std::unique_ptr make_unique( Args&&... args ) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } + + template + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const& get() const = 0; + using type = T; + }; + + template + class SingleValueGenerator final : public IGenerator { + T m_value; + public: + SingleValueGenerator(T const& value) : m_value( value ) {} + SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + + T const& get() const override { + return m_value; + } + bool next() override { + return false; + } + }; + + template + class FixedValuesGenerator final : public IGenerator { + std::vector m_values; + size_t m_idx = 0; + public: + FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} + + T const& get() const override { + return m_values[m_idx]; + } + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; + + template + class GeneratorWrapper final { + std::unique_ptr> m_generator; + public: + GeneratorWrapper(std::unique_ptr> generator): + m_generator(std::move(generator)) + {} + T const& get() const { + return m_generator->get(); + } + bool next() { + return m_generator->next(); + } + }; + + template + GeneratorWrapper value(T&& value) { + return GeneratorWrapper(pf::make_unique>(std::forward(value))); + } + template + GeneratorWrapper values(std::initializer_list values) { + return GeneratorWrapper(pf::make_unique>(values)); + } + + template + class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper&& generator) { + m_generators.emplace_back(std::move(generator)); + } + void populate(T&& val) { + m_generators.emplace_back(value(std::move(val))); + } + template + void populate(U&& val) { + populate(T(std::move(val))); + } + template + void populate(U&& valueOrGenerator, Gs... moreGenerators) { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } + + public: + template + Generators(Gs... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } + + T const& get() const override { + return m_generators[m_current].get(); + } + + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; + + template + GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { + return values>( tuples ); + } + + // Tag type to signal that a generator sequence should convert arguments to a specific type + template + struct as {}; + + template + auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { + return Generators(std::move(generator), std::forward(moreGenerators)...); + } + template + auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { + return Generators(std::move(generator)); + } + template + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); + } + template + auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const& get() const override { + return m_generator.get(); + } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + public: + template + FilterGenerator(P&& pred, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_predicate(std::forward

(pred)) + { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = next(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const& get() const override { + return m_generator.get(); + } + + bool next() override { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); + return success; + } + }; + + template + GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { + return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); + } + + template + class RepeatGenerator : public IGenerator { + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + public: + RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target_repeats(repeats) + { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const& get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template + GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); + } + + template + class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + public: + template + MapGenerator(F2&& function, GeneratorWrapper&& generator) : + m_generator(std::move(generator)), + m_function(std::forward(function)), + m_cache(m_function(m_generator.get())) + {} + + T const& get() const override { + return m_cache; + } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + + template + class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + public: + ChunkGenerator(size_t size, GeneratorWrapper generator) : + m_chunk_size(size), m_generator(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + std::vector const& get() const override { + return m_chunk; + } + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; + + template + GeneratorWrapper> chunk(size_t size, GeneratorWrapper&& generator) { + return GeneratorWrapper>( + pf::make_unique>(size, std::move(generator)) + ); + } + +} // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +#include + +namespace Catch { +namespace Generators { + +template +class RandomFloatingGenerator final : public IGenerator { + // FIXME: What is the right seed? + std::minstd_rand m_rand; + std::uniform_real_distribution m_dist; + Float m_current_number; +public: + + RandomFloatingGenerator(Float a, Float b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Float const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +template +class RandomIntegerGenerator final : public IGenerator { + std::minstd_rand m_rand; + std::uniform_int_distribution m_dist; + Integer m_current_number; +public: + + RandomIntegerGenerator(Integer a, Integer b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Integer const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template +typename std::enable_if::value && !std::is_same::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +typename std::enable_if::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const& start, T const& end, T const& step): + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const& start, T const& end): + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + {} + + T const& get() const override { + return m_current; + } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template +GeneratorWrapper range(T const& start, T const& end, T const& step) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end, step)); +} + +template +GeneratorWrapper range(T const& start, T const& end) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end)); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared( token ); + if( m_exclusion ) + pattern = std::make_shared( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector const& getTestsOrTags() const; + std::vector const& getSectionsToRun() const override; + + virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +// start catch_option.hpp + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + + void Approx::setMargin(double margin) { + CATCH_ENFORCE(margin >= 0, + "Invalid Approx::margin: " << margin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = margin; + } + + void Approx::setEpsilon(double epsilon) { + CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, + "Invalid Approx::epsilon: " << epsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = epsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() +#else + #define CATCH_BREAK_INTO_DEBUGGER() []{}() +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +#else + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h +#include + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + void emplaceUnscopedMessage( MessageBuilder const& builder ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker = nullptr; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << (std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; + +class Columns { + std::vector m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); + + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +#include + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include +# include +# include +# include +# include +# include +# include + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif +} // namespace Catch; +// end catch_enforce.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#endif // signals/SEH handling + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include +#include + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { + return m_msg; +} + +namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option list( std::shared_ptr const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( std::shared_ptr const& config ) { + Option listedCount; + getCurrentMutableContext().setConfig( config ); + if( config->listTests() ) + listedCount = listedCount.valueOr(0) + listTests( *config ); + if( config->listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config ); + if( config->listTags() ) + listedCount = listedCount.valueOr(0) + listTags( *config ); + if( config->listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include + +namespace Catch { + template + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include +#include + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ), m_moved() + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::ScopedMessage( ScopedMessage&& old ) + : m_info( old.m_info ), m_moved() + { + old.m_moved = true; + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() && !m_moved ){ + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, names.size() - 1); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, std::string const& value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; + } + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const&) = delete; + RedirectedStreams& operator=(RedirectedStreams const&) = delete; + RedirectedStreams(RedirectedStreams&&) = delete; + RedirectedStreams& operator=(RedirectedStreams&&) = delete; + + RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); + ~RedirectedStreams(); + private: + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + } + + using RegistryHubSingleton = Singleton; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // Generator interface only finds out if it has another item on atual move + if (m_runState == CompletedSuccessfully && m_generator->next()) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + + void useConfigData( ConfigData const& configData ); + + template + int run(int argc, CharT const * const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include +#include + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto& multi = static_cast(*ret); + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; + } + + Catch::Totals runTests(std::shared_ptr const& config) { + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( m_config ) ) + return static_cast( *listed ); + + auto totals = runTests( m_config ); + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + } + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include +#include + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isGeneratorTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if ((m_filters.empty() || m_filters[0] == "") || + std::find(m_filters.begin(), m_filters.end(), + m_nameAndLocation.name) != m_filters.end()) + complete = TrackerBase::isComplete(); + return complete; + + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / ( i + 1u ); + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 7, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include + +using uchar = unsigned char; + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + std::sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + std::set TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + if( m_config->rngSeed() != 0 ) { + xml.startElement( "properties" ); + xml.scopedElement( "property" ) + .writeAttribute( "name", "random-seed" ) + .writeAttribute( "value", m_config->rngSeed() ); + xml.endElement(); + } + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/Externals/WIL/tests/common.h b/Externals/WIL/tests/common.h new file mode 100644 index 0000000000..66d561a30a --- /dev/null +++ b/Externals/WIL/tests/common.h @@ -0,0 +1,510 @@ +#pragma once + +#include +#include + +#include "catch.hpp" + +#include +#include + +#define REPORTS_ERROR(expr) witest::ReportsError(wistd::is_same{}, [&]() { return expr; }) +#define REQUIRE_ERROR(expr) REQUIRE(REPORTS_ERROR(expr)) +#define REQUIRE_NOERROR(expr) REQUIRE_FALSE(REPORTS_ERROR(expr)) + +#define CRASHES(expr) witest::DoesCodeCrash([&]() { return expr; }) +#define REQUIRE_CRASH(expr) REQUIRE(CRASHES(expr)) +#define REQUIRE_NOCRASH(expr) REQUIRE_FALSE(CRASHES(expr)) + +// NOTE: SUCCEEDED/FAILED macros not used here since Catch2 can give us better diagnostics if it knows the HRESULT value +#define REQUIRE_SUCCEEDED(expr) REQUIRE((HRESULT)(expr) >= 0) +#define REQUIRE_FAILED(expr) REQUIRE((HRESULT)(expr) < 0) + +// MACRO double evaluation check. +// The following macro illustrates a common problem with writing macros: +// #define MY_MAX(a, b) (((a) > (b)) ? (a) : (b)) +// The issue is that whatever code is being used for both a and b is being executed twice. +// This isn't harmful when thinking of constant numerics, but consider this example: +// MY_MAX(4, InterlockedIncrement(&cCount)) +// This evaluates the (B) parameter twice and results in incrementing the counter twice. +// We use MDEC in unit tests to verify that this kind of pattern is not present. A test +// of this kind: +// MY_MAX(MDEC(4), MDEC(InterlockedIncrement(&cCount)) +// will verify that the parameters are not evaluated more than once. +#define MDEC(PARAM) (witest::details::MacroDoubleEvaluationCheck(__LINE__, #PARAM), PARAM) + +// There's some functionality that we need for testing that's not available for the app partition. Since those tests are +// primarily compilation tests, declare what's needed here +extern "C" { + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) +WINBASEAPI _Ret_maybenull_ +PVOID WINAPI AddVectoredExceptionHandler(_In_ ULONG First, _In_ PVECTORED_EXCEPTION_HANDLER Handler); + +WINBASEAPI +ULONG WINAPI RemoveVectoredExceptionHandler(_In_ PVOID Handle); +#endif + +} + +#pragma warning(push) +#pragma warning(disable: 4702) // Unreachable code + +namespace witest +{ + namespace details + { + inline void MacroDoubleEvaluationCheck(size_t uLine, _In_ const char* pszCode) + { + struct SEval + { + size_t uLine; + const char* pszCode; + }; + + static SEval rgEval[15] = {}; + static size_t nOffset = 0; + + for (auto& eval : rgEval) + { + if ((eval.uLine == uLine) && (eval.pszCode != nullptr) && (0 == strcmp(pszCode, eval.pszCode))) + { + // This verification indicates that macro-double-evaluation check is firing for a particular usage of MDEC(). + FAIL("Expression '" << pszCode << "' double evaluated in macro on line " << uLine); + } + } + + rgEval[nOffset].uLine = uLine; + rgEval[nOffset].pszCode = pszCode; + nOffset = (nOffset + 1) % ARRAYSIZE(rgEval); + } + + template + class AssignTemporaryValueCleanup + { + public: + AssignTemporaryValueCleanup(_In_ AssignTemporaryValueCleanup const &) = delete; + AssignTemporaryValueCleanup & operator=(_In_ AssignTemporaryValueCleanup const &) = delete; + + explicit AssignTemporaryValueCleanup(_Inout_ T *pVal, T val) WI_NOEXCEPT : + m_pVal(pVal), + m_valOld(*pVal) + { + *pVal = val; + } + + AssignTemporaryValueCleanup(_Inout_ AssignTemporaryValueCleanup && other) WI_NOEXCEPT : + m_pVal(other.m_pVal), + m_valOld(other.m_valOld) + { + other.m_pVal = nullptr; + } + + ~AssignTemporaryValueCleanup() WI_NOEXCEPT + { + operator()(); + } + + void operator()() WI_NOEXCEPT + { + if (m_pVal != nullptr) + { + *m_pVal = m_valOld; + m_pVal = nullptr; + } + } + + void Dismiss() WI_NOEXCEPT + { + m_pVal = nullptr; + } + + private: + T *m_pVal; + T m_valOld; + }; + } + + // Use the following routine to allow for a variable to be swapped with another and automatically revert the + // assignment at the end of the scope. + // Example: + // int nFoo = 10 + // { + // auto revert = witest::AssignTemporaryValue(&nFoo, 12); + // // nFoo will now be 12 within this scope... + // } + // // and nFoo is back to 10 within the outer scope + template + inline witest::details::AssignTemporaryValueCleanup AssignTemporaryValue(_Inout_ T *pVal, T val) WI_NOEXCEPT + { + return witest::details::AssignTemporaryValueCleanup(pVal, val); + } + + //! Global class which tracks objects that derive from @ref AllocatedObject. + //! Use `witest::g_objectCount.Leaked()` to determine if an object deriving from `AllocatedObject` has been leaked. + class GlobalCount + { + public: + int m_count = 0; + + //! Returns `true` if there are any objects that derive from @ref AllocatedObject still in memory. + bool Leaked() const + { + return (m_count != 0); + } + + ~GlobalCount() + { + if (Leaked()) + { + // NOTE: This runs when no test is active, but will still cause an assert failure to notify + FAIL("GlobalCount is non-zero; there is a leak somewhere"); + } + } + }; + __declspec(selectany) GlobalCount g_objectCount; + + //! Derive an allocated test object from witest::AllocatedObject to ensure that those objects aren't leaked in the test. + //! Note that you can call g_objectCount.Leaked() at any point to determine if a leak has already occurred (assuming that + //! all objects should have been destroyed at that point. + class AllocatedObject + { + public: + AllocatedObject() { g_objectCount.m_count++; } + ~AllocatedObject() { g_objectCount.m_count--; } + }; + + template + bool DoesCodeThrow(Lambda&& callOp) + { +#ifdef WIL_ENABLE_EXCEPTIONS + try +#endif + { + callOp(); + } +#ifdef WIL_ENABLE_EXCEPTIONS + catch (...) + { + return true; + } +#endif + + return false; + } + + [[noreturn]] + inline void __stdcall TranslateFailFastException(PEXCEPTION_RECORD rec, PCONTEXT, DWORD) + { + // RaiseFailFastException cannot be continued or handled. By instead calling RaiseException, it allows us to + // handle exceptions + ::RaiseException(rec->ExceptionCode, rec->ExceptionFlags, rec->NumberParameters, rec->ExceptionInformation); +#ifdef __clang__ + __builtin_unreachable(); +#endif + } + + constexpr DWORD msvc_exception_code = 0xE06D7363; + + // This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below - + // that interprets a set of exception codes as fatal. We don't want this behavior since we may be expecting such + // crashes, so instead translate all exception codes to something not fatal + inline LONG WINAPI TranslateExceptionCodeHandler(PEXCEPTION_POINTERS info) + { + if (info->ExceptionRecord->ExceptionCode != witest::msvc_exception_code) + { + info->ExceptionRecord->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + } + + return EXCEPTION_CONTINUE_SEARCH; + } + + namespace details + { + inline bool DoesCodeCrash(wistd::function& callOp) + { + bool result = false; + __try + { + callOp(); + } + // Let C++ exceptions pass through + __except ((::GetExceptionCode() != msvc_exception_code) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + result = true; + } + return result; + } + } + + inline bool DoesCodeCrash(wistd::function callOp) + { + // See above; we don't want to actually fail fast, so make sure we raise a different exception instead + auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException); + + auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler); + auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); }); + + return details::DoesCodeCrash(callOp); + } + + template + bool ReportsError(wistd::false_type, Lambda&& callOp) + { + bool doesThrow = false; + bool doesCrash = DoesCodeCrash([&]() + { + doesThrow = DoesCodeThrow(callOp); + }); + + return doesThrow || doesCrash; + } + + template + bool ReportsError(wistd::true_type, Lambda&& callOp) + { + return FAILED(callOp()); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + class TestFailureCache final : + public wil::details::IFailureCallback + { + public: + TestFailureCache() : + m_callbackHolder(this) + { + } + + void clear() + { + m_failures.clear(); + } + + size_t size() const + { + return m_failures.size(); + } + + bool empty() const + { + return m_failures.empty(); + } + + const wil::FailureInfo& operator[](size_t pos) const + { + return m_failures.at(pos).GetFailureInfo(); + } + + // IFailureCallback + bool NotifyFailure(wil::FailureInfo const & failure) WI_NOEXCEPT override + { + m_failures.emplace_back(failure); + return false; + } + + private: + std::vector m_failures; + wil::details::ThreadFailureCallbackHolder m_callbackHolder; + }; +#endif + + inline HRESULT GetTempFileName(wchar_t (&result)[MAX_PATH]) + { + wchar_t dir[MAX_PATH]; + RETURN_LAST_ERROR_IF(::GetTempPathW(MAX_PATH, dir) == 0); + RETURN_LAST_ERROR_IF(::GetTempFileNameW(dir, L"wil", 0, result) == 0); + return S_OK; + } + + inline HRESULT CreateUniqueFolderPath(wchar_t (&buffer)[MAX_PATH], PCWSTR root = nullptr) + { + if (root) + { + RETURN_LAST_ERROR_IF(::GetTempFileNameW(root, L"wil", 0, buffer) == 0); + } + else + { + wchar_t tempPath[MAX_PATH]; + RETURN_LAST_ERROR_IF(::GetTempPathW(ARRAYSIZE(tempPath), tempPath) == 0); + RETURN_LAST_ERROR_IF(::GetLongPathNameW(tempPath, tempPath, ARRAYSIZE(tempPath)) == 0); + RETURN_LAST_ERROR_IF(::GetTempFileNameW(tempPath, L"wil", 0, buffer) == 0); + } + RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(buffer)); + PathCchRemoveExtension(buffer, ARRAYSIZE(buffer)); + return S_OK; + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + + struct TestFolder + { + TestFolder() + { + if (SUCCEEDED(CreateUniqueFolderPath(m_path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path))) + { + m_valid = true; + } + } + + TestFolder(PCWSTR path) + { + if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path))) + { + m_valid = true; + } + } + + TestFolder(const TestFolder&) = delete; + TestFolder& operator=(const TestFolder&) = delete; + + TestFolder(TestFolder&& other) + { + if (other.m_valid) + { + m_valid = true; + other.m_valid = false; + wcscpy_s(m_path, other.m_path); + } + } + + ~TestFolder() + { + if (m_valid) + { + wil::RemoveDirectoryRecursiveNoThrow(m_path); + } + } + + operator bool() const + { + return m_valid; + } + + operator PCWSTR() const + { + return m_path; + } + + PCWSTR Path() const + { + return m_path; + } + + private: + + bool m_valid = false; + wchar_t m_path[MAX_PATH] = L""; + }; + + struct TestFile + { + TestFile(PCWSTR path) + { + if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path))) + { + Create(); + } + } + + TestFile(PCWSTR dirPath, PCWSTR fileName) + { + if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), dirPath)) && SUCCEEDED(PathCchAppend(m_path, ARRAYSIZE(m_path), fileName))) + { + Create(); + } + } + + TestFile(const TestFile&) = delete; + TestFile& operator=(const TestFile&) = delete; + + TestFile(TestFile&& other) + { + if (other.m_valid) + { + m_valid = true; + m_deleteDir = other.m_deleteDir; + other.m_valid = other.m_deleteDir = false; + wcscpy_s(m_path, other.m_path); + } + } + + ~TestFile() + { + // Best effort on all of these + if (m_valid) + { + ::DeleteFileW(m_path); + } + if (m_deleteDir) + { + size_t parentLength; + if (wil::try_get_parent_path_range(m_path, &parentLength)) + { + m_path[parentLength] = L'\0'; + ::RemoveDirectoryW(m_path); + m_path[parentLength] = L'\\'; + } + } + } + + operator bool() const + { + return m_valid; + } + + operator PCWSTR() const + { + return m_path; + } + + PCWSTR Path() const + { + return m_path; + } + + private: + + HRESULT Create() + { + WI_ASSERT(!m_valid && !m_deleteDir); + wil::unique_hfile fileHandle(::CreateFileW(m_path, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); + if (!fileHandle) + { + auto err = ::GetLastError(); + size_t parentLength; + if ((err == ERROR_PATH_NOT_FOUND) && wil::try_get_parent_path_range(m_path, &parentLength)) + { + m_path[parentLength] = L'\0'; + RETURN_IF_FAILED(wil::CreateDirectoryDeepNoThrow(m_path)); + m_deleteDir = true; + + m_path[parentLength] = L'\\'; + fileHandle.reset(::CreateFileW(m_path, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); + RETURN_LAST_ERROR_IF(!fileHandle); + } + else + { + RETURN_WIN32(err); + } + } + + m_valid = true; + return S_OK; + } + + bool m_valid = false; + bool m_deleteDir = false; + wchar_t m_path[MAX_PATH] = L""; + }; + +#endif +} + +#pragma warning(pop) diff --git a/Externals/WIL/tests/cpplatest/CMakeLists.txt b/Externals/WIL/tests/cpplatest/CMakeLists.txt new file mode 100644 index 0000000000..22e45cc940 --- /dev/null +++ b/Externals/WIL/tests/cpplatest/CMakeLists.txt @@ -0,0 +1,31 @@ + +# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned +# on compiler) as new standards are ratified/support is available +set(CMAKE_CXX_STANDARD 17) + +project(witest.cpplatest) +add_executable(witest.cpplatest) + +# Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0" +if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0") + target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp) +endif() + +target_sources(witest.cpplatest PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp + ) diff --git a/Externals/WIL/tests/main.cpp b/Externals/WIL/tests/main.cpp new file mode 100644 index 0000000000..37b9d7cbe7 --- /dev/null +++ b/Externals/WIL/tests/main.cpp @@ -0,0 +1,8 @@ + +#pragma comment(lib, "Pathcch.lib") +#pragma comment(lib, "RuntimeObject.lib") +#pragma comment(lib, "Synchronization.lib") +#pragma comment(lib, "RpcRt4.lib") + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/Externals/WIL/tests/noexcept/CMakeLists.txt b/Externals/WIL/tests/noexcept/CMakeLists.txt new file mode 100644 index 0000000000..76feacdd92 --- /dev/null +++ b/Externals/WIL/tests/noexcept/CMakeLists.txt @@ -0,0 +1,27 @@ + +project(witest.noexcept) +add_executable(witest.noexcept) + +# Turn off exceptions for this test +replace_cxx_flag("/EHsc" "") +add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS) + +# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch +# statements... Thankfully MSVC just gives us a warning that we can disable +append_cxx_flag("/wd4530") + +target_sources(witest.noexcept PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp) diff --git a/Externals/WIL/tests/normal/CMakeLists.txt b/Externals/WIL/tests/normal/CMakeLists.txt new file mode 100644 index 0000000000..b146e8e4aa --- /dev/null +++ b/Externals/WIL/tests/normal/CMakeLists.txt @@ -0,0 +1,21 @@ + +project(witest) +add_executable(witest) + +target_sources(witest PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp + ) diff --git a/Externals/WIL/tests/test_objects.h b/Externals/WIL/tests/test_objects.h new file mode 100644 index 0000000000..3da2005b18 --- /dev/null +++ b/Externals/WIL/tests/test_objects.h @@ -0,0 +1,108 @@ +#pragma once + +#include "catch.hpp" + +// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that +// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that +// require CopyConstructible (e.g. for wistd::function) +struct fail_on_copy +{ + fail_on_copy() = default; + + fail_on_copy(const fail_on_copy&) + { + FAIL("Copy constructor invoked for fail_on_copy type"); + } + + fail_on_copy(fail_on_copy&&) = default; + + fail_on_copy& operator=(const fail_on_copy&) + { + FAIL("Copy assignment operator invoked for fail_on_copy type"); + return *this; + } + + fail_on_copy& operator=(fail_on_copy&&) = default; +}; + +// Useful for validating that objects get copied e.g. as opposed to capturing a reference +struct value_holder +{ + int value = 0xbadf00d; + + ~value_holder() + { + value = 0xbadf00d; + } +}; + +// Useful for validating that functions, etc. are callable with move-only types +// Example real type that is move only is Microsoft::WRL::Wrappers::HString +struct cannot_copy +{ + cannot_copy() = default; + cannot_copy(const cannot_copy&) = delete; + cannot_copy& operator=(const cannot_copy&) = delete; + + cannot_copy(cannot_copy&&) = default; + cannot_copy& operator=(cannot_copy&&) = default; +}; + +// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in +// contexts that require a default constructible type, but has the nice property that it allows for tests to run +// concurrently +struct object_counter_state +{ + volatile LONG constructed_count = 0; + volatile LONG destructed_count = 0; + volatile LONG copy_count = 0; + volatile LONG move_count = 0; + + LONG instance_count() + { + return constructed_count - destructed_count; + } +}; + +struct object_counter +{ + object_counter_state* state; + + object_counter(object_counter_state& s) : + state(&s) + { + ::InterlockedIncrement(&state->constructed_count); + } + + object_counter(const object_counter& other) : + state(other.state) + { + ::InterlockedIncrement(&state->constructed_count); + ::InterlockedIncrement(&state->copy_count); + } + + object_counter(object_counter&& other) WI_NOEXCEPT : + state(other.state) + { + ::InterlockedIncrement(&state->constructed_count); + ::InterlockedIncrement(&state->move_count); + } + + ~object_counter() + { + ::InterlockedIncrement(&state->destructed_count); + state = nullptr; + } + + object_counter& operator=(const object_counter&) + { + ::InterlockedIncrement(&state->copy_count); + return *this; + } + + object_counter& operator=(object_counter&&) WI_NOEXCEPT + { + ::InterlockedIncrement(&state->move_count); + return *this; + } +}; diff --git a/Externals/WIL/tests/wiTest.cpp b/Externals/WIL/tests/wiTest.cpp new file mode 100644 index 0000000000..c422fbb916 --- /dev/null +++ b/Externals/WIL/tests/wiTest.cpp @@ -0,0 +1,3472 @@ + +#include +#include +#include +#include +#include +#include + +#ifdef WIL_ENABLE_EXCEPTIONS +#include +#include +#include +#endif + +// Do not include most headers until after the WIL headers to ensure that we're not inadvertently adding any unnecessary +// dependencies to STL, WRL, or indirectly retrieved headers + +#ifndef __cplusplus_winrt +#include +#include +#endif + +// Include Resource.h a second time after including other headers +#include + +#include "common.h" +#include "MallocSpy.h" +#include "test_objects.h" + +#pragma warning(push) +#pragma warning(disable: 4702) // Unreachable code + +TEST_CASE("WindowsInternalTests::CommonHelpers", "[resource]") +{ + { + wil::unique_handle spHandle; + REQUIRE(spHandle == nullptr); + REQUIRE(nullptr == spHandle); + REQUIRE_FALSE(spHandle != nullptr); + REQUIRE_FALSE(nullptr != spHandle); + + //equivalence check will static_assert because spMutex does not allow pointer access + wil::mutex_release_scope_exit spMutex; + //REQUIRE(spMutex == nullptr); + //REQUIRE(nullptr == spMutex); + + //equivalence check will static_assert because spFile does not use nullptr_t as a invalid value + wil::unique_hfile spFile; + //REQUIRE(spFile == nullptr); + } +#ifdef __WIL_WINBASE_STL + { + wil::shared_handle spHandle; + REQUIRE(spHandle == nullptr); + REQUIRE(nullptr == spHandle); + REQUIRE_FALSE(spHandle != nullptr); + REQUIRE_FALSE(nullptr != spHandle); + } +#endif +} + +TEST_CASE("WindowsInternalTests::AssertMacros", "[result_macros]") +{ + //WI_ASSERT macros are all no-ops if in retail +#ifndef RESULT_DEBUG + WI_ASSERT(false); + WI_ASSERT_MSG(false, "WI_ASSERT_MSG"); + WI_ASSERT_NOASSUME(false); + WI_ASSERT_MSG_NOASSUME(false, "WI_ASSERT_MSG_NOASSUME"); + WI_VERIFY(false); + WI_VERIFY_MSG(false, "WI_VERIFY_MSG"); +#endif + + WI_ASSERT(true); + WI_ASSERT_MSG(true, "WI_ASSERT_MSG"); + WI_ASSERT_NOASSUME(true); + WI_ASSERT_MSG_NOASSUME(true, "WI_ASSERT_MSG_NOASSUME"); + WI_VERIFY(true); + WI_VERIFY_MSG(true, "WI_VERIFY_MSG"); +} + +void __stdcall EmptyResultMacrosLoggingCallback(wil::FailureInfo*, PWSTR, size_t) WI_NOEXCEPT +{ +} + +#ifdef WIL_ENABLE_EXCEPTIONS +// Test Result Macros +void TestErrorCallbacks() +{ + { + size_t callbackCount = 0; + auto monitor = wil::ThreadFailureCallback([&](wil::FailureInfo const &failure) -> bool + { + REQUIRE(failure.hr == E_ACCESSDENIED); + callbackCount++; + return false; + }); + + size_t const depthCount = 10; + for (size_t index = 0; index < depthCount; index++) + { + LOG_HR(E_ACCESSDENIED); + } + REQUIRE(callbackCount == depthCount); + } + { + wil::ThreadFailureCache cache; + + LOG_HR(E_ACCESSDENIED); + REQUIRE(cache.GetFailure() != nullptr); + REQUIRE(cache.GetFailure()->hr == E_ACCESSDENIED); + + wil::ThreadFailureCache cacheNested; + + LOG_HR(E_FAIL); unsigned long errorLine = __LINE__; + LOG_HR(E_FAIL); + LOG_HR(E_FAIL); + REQUIRE(cache.GetFailure()->hr == E_FAIL); + REQUIRE(cache.GetFailure()->uLineNumber == errorLine); + REQUIRE(cacheNested.GetFailure()->hr == E_FAIL); + REQUIRE(cacheNested.GetFailure()->uLineNumber == errorLine); + } +} + +DWORD WINAPI ErrorCallbackThreadTest(_In_ LPVOID lpParameter) +{ + try + { + HANDLE hEvent = reinterpret_cast(lpParameter); + + for (size_t stress = 0; stress < 200; stress++) + { + Sleep(1); // allow the threadpool to saturate the thread count... + TestErrorCallbacks(); + } + THROW_IF_WIN32_BOOL_FALSE(::SetEvent(hEvent)); + } + catch (...) + { + FAIL(); + } + return 1; +} + +void StressErrorCallbacks() +{ + auto restore = witest::AssignTemporaryValue(&wil::g_fResultOutputDebugString, false); + + size_t const threadCount = 20; + wil::unique_event eventArray[threadCount]; + + for (size_t index = 0; index < threadCount; index++) + { + eventArray[index].create(); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + THROW_IF_WIN32_BOOL_FALSE(::QueueUserWorkItem(ErrorCallbackThreadTest, eventArray[index].get(), 0)); +#else + ErrorCallbackThreadTest(eventArray[index].get()); +#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ + } + for (size_t index = 0; index < threadCount; index++) + { + eventArray[index].wait(); + } +} + +TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[!hide][result_macros][stress]") +{ + auto restore = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, EmptyResultMacrosLoggingCallback); + StressErrorCallbacks(); +} +#endif + +#define E_AD HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) +void SetAD() +{ + ::SetLastError(ERROR_ACCESS_DENIED); +} + +class AlternateAccessDeniedException +{ +}; + +#ifdef WIL_ENABLE_EXCEPTIONS +class DerivedAccessDeniedException : public wil::ResultException +{ +public: + DerivedAccessDeniedException() : ResultException(E_AD) {} +}; + +HRESULT __stdcall TestResultCaughtFromException() WI_NOEXCEPT +{ + try + { + throw; + } + catch (AlternateAccessDeniedException) + { + return E_AD; + } + catch (...) + { + } + return S_OK; +} +#endif + +HANDLE hValid = reinterpret_cast(1); +HANDLE& hValidRef() { return hValid; } +HANDLE hNull = NULL; +HANDLE hInvalid = INVALID_HANDLE_VALUE; +void* pValid = reinterpret_cast(1); +void*& pValidRef() { return pValid; } +void* pNull = nullptr; +void*& pNullRef() { return pNull; } +bool fTrue = true; +bool& fTrueRef() { return fTrue; } +bool fFalse = false; +bool& fFalseRef() { return fFalse; } +BOOL fTRUE = TRUE; +BOOL& fTRUERef() { return fTRUE; } +BOOL fFALSE = FALSE; +DWORD errSuccess = ERROR_SUCCESS; +DWORD& errSuccessRef() { return errSuccess; } +HRESULT hrOK = S_OK; +HRESULT& hrOKRef() { return hrOK; } +HRESULT hrFAIL = E_FAIL; +HRESULT& hrFAILRef() { return hrFAIL; } +const HRESULT E_hrOutOfPaper = HRESULT_FROM_WIN32(ERROR_OUT_OF_PAPER); +NTSTATUS ntOK = STATUS_SUCCESS; +NTSTATUS& ntOKRef() { return ntOK; } +NTSTATUS ntFAIL = STATUS_NO_MEMORY; +NTSTATUS& ntFAILRef() { return ntFAIL; } +const HRESULT S_hrNtOkay = wil::details::NtStatusToHr(STATUS_SUCCESS); +const HRESULT E_hrNtAssertionFailure = wil::details::NtStatusToHr(STATUS_ASSERTION_FAILURE); + +wil::StoredFailureInfo g_log; + +void __stdcall ResultMacrosLoggingCallback(wil::FailureInfo *pFailure, PWSTR, size_t) WI_NOEXCEPT +{ + g_log = *pFailure; +} + +enum class EType +{ + None = 0x00, + Expected = 0x02, + Msg = 0x04, + FailFast = 0x08, // overall fail fast (throw exception on successful result code, for example) + FailFastMacro = 0x10, // explicit use of fast fail fast (FAIL_FAST_IF...) + NoContext = 0x20 // file and line info can be wrong (throw does not happen in context to code) +}; +DEFINE_ENUM_FLAG_OPERATORS(EType); + +template +bool VerifyResult(unsigned int lineNumber, EType type, HRESULT hr, TLambda&& lambda) +{ + bool succeeded = true; +#ifdef WIL_ENABLE_EXCEPTIONS + try + { +#endif + HRESULT lambdaResult = E_FAIL; + bool didFailFast = true; + { + didFailFast = witest::DoesCodeCrash([&]() + { + lambdaResult = lambda(); + }); + } + if (WI_IsFlagSet(type, EType::FailFast)) + { + REQUIRE(didFailFast); + } + else + { + if (WI_IsFlagClear(type, EType::Expected)) + { + if (SUCCEEDED(hr)) + { + REQUIRE(hr == lambdaResult); + REQUIRE(lineNumber != g_log.GetFailureInfo().uLineNumber); + REQUIRE(!didFailFast); + } + else + { + REQUIRE((WI_IsFlagSet(type, EType::NoContext) || (g_log.GetFailureInfo().uLineNumber == lineNumber))); + REQUIRE(g_log.GetFailureInfo().hr == hr); + REQUIRE((WI_IsFlagClear(type, EType::Msg) || (nullptr != wcsstr(g_log.GetFailureInfo().pszMessage, L"msg")))); + REQUIRE((WI_IsFlagClear(type, EType::FailFastMacro) || (didFailFast))); + REQUIRE((WI_IsFlagSet(type, EType::FailFastMacro) || (!didFailFast))); + } + } + } +#ifdef WIL_ENABLE_EXCEPTIONS + } + catch (...) + { + succeeded = false; + } +#endif + + // Ensure we come out clean... + ::SetLastError(ERROR_SUCCESS); + return succeeded; +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +HRESULT TranslateException(TLambda&& lambda) +{ + try + { + lambda(); + } + catch (wil::ResultException &re) + { + return re.GetErrorCode(); + } +#ifdef __cplusplus_winrt + catch (Platform::Exception ^pe) + { + return wil::details::GetErrorCode(pe); + } +#endif + catch (...) + { + FAIL(); + } + return S_OK; +} +#endif + +#define REQUIRE_RETURNS(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::None, hr, lambda)) +#define REQUIRE_RETURNS_MSG(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, lambda)) +#define REQUIRE_RETURNS_EXPECTED(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::Expected, hr, lambda)) + +#ifdef WIL_ENABLE_EXCEPTIONS +#define REQUIRE_THROWS_RESULT(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::None, hr, [&] { return TranslateException(lambda); })) +#define REQUIRE_THROWS_MSG(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, [&] { return TranslateException(lambda); })) +#else +#define REQUIRE_THROWS_RESULT(hr, lambda) +#define REQUIRE_THROWS_MSG(hr, lambda) +#endif + +#define REQUIRE_LOG(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::None, hr, [&] { auto fn = (lambda); fn(); return hr; })) +#define REQUIRE_LOG_MSG(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, [&] { auto fn = (lambda); fn(); return hr; })) + +#define REQUIRE_FAILFAST(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro, hr, [&] { auto fn = (lambda); fn(); return hr; })) +#define REQUIRE_FAILFAST_MSG(hr, lambda) REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro | EType::Msg, hr, [&] { auto fn = (lambda); fn(); return hr; })) +#define REQUIRE_FAILFAST_UNSPECIFIED(lambda) REQUIRE(VerifyResult(__LINE__, EType::FailFast, S_OK, [&] { auto fn = (lambda); fn(); return S_OK; })) + +TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]") +{ + auto restoreLoggingCallback = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, ResultMacrosLoggingCallback); +#ifdef WIL_ENABLE_EXCEPTIONS + auto restoreExceptionCallback = witest::AssignTemporaryValue(&wil::g_pfnResultFromCaughtException, TestResultCaughtFromException); +#endif + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR(MDEC(hrOKRef())); }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR(MDEC(hrOKRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR(E_FAIL); }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_MSG(E_FAIL, "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR(E_FAIL); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_MSG(E_FAIL, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_FAIL, [] { LOG_HR(E_FAIL); }); + REQUIRE_LOG_MSG(E_FAIL, [] { LOG_HR_MSG(E_FAIL, "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR(E_FAIL); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_MSG(E_FAIL, "msg: %d", __LINE__); }); + + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR(); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_MSG("msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR(); }); + REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_MSG("msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR(); }); + REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_MSG("msg: %d", __LINE__); }); + REQUIRE_LOG(E_AD, [] { SetAD(); LOG_LAST_ERROR(); }); + REQUIRE_LOG_MSG(E_AD, [] { SetAD(); LOG_LAST_ERROR_MSG("msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR(); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_MSG("msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_WIN32(MDEC(errSuccessRef())); }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_WIN32_MSG(MDEC(errSuccessRef()), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_WIN32(MDEC(errSuccessRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_WIN32_MSG(MDEC(errSuccessRef()), "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_AD, [] { RETURN_WIN32(ERROR_ACCESS_DENIED); }); + REQUIRE_RETURNS_MSG(E_AD, [] { RETURN_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_AD, [] { THROW_WIN32(ERROR_ACCESS_DENIED); }); + REQUIRE_THROWS_MSG(E_AD, [] { THROW_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_AD, [] { LOG_WIN32(ERROR_ACCESS_DENIED); }); + REQUIRE_LOG_MSG(E_AD, [] { LOG_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_AD, [] { FAIL_FAST_WIN32(ERROR_ACCESS_DENIED); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { FAIL_FAST_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_IF_FAILED(MDEC(hrOKRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_FAILED_EXPECTED(MDEC(hrOKRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(S_OK == THROW_IF_FAILED(MDEC(hrOKRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(S_OK == THROW_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED(MDEC(hrOKRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_FAILED(MDEC(hrOKRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(E_FAIL, [] { RETURN_IF_FAILED(E_FAIL); return S_OK; }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_EXPECTED(E_FAIL); return S_OK; }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_IF_FAILED(E_FAIL); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED(E_FAIL)); }); + REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_IF_FAILED(E_FAIL); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(MDEC(fTRUERef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fTRUE == THROW_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fTRUE == THROW_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fTRUE == LOG_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fTRUE == LOG_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fTRUE == FAIL_FAST_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fTRUE == FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE(fFALSE); return S_OK; }); + REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fFALSE); return S_OK; }); + REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_IF_WIN32_BOOL_FALSE(fFALSE); }); + REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(fFALSE == LOG_IF_WIN32_BOOL_FALSE(fFALSE)); }); + REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(fFALSE == LOG_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_IF_WIN32_BOOL_FALSE(fFALSE); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_IF_WIN32_ERROR(MDEC(hrOKRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_WIN32_ERROR_EXPECTED(MDEC(hrOKRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(S_OK == THROW_IF_WIN32_ERROR(MDEC(hrOKRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(S_OK == THROW_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_WIN32_ERROR(MDEC(hrOKRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(S_OK == LOG_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_WIN32_ERROR(MDEC(hrOKRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); return S_OK; }); + REQUIRE_RETURNS_MSG(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR_EXPECTED(ERROR_OUT_OF_PAPER); return S_OK; }); + REQUIRE_THROWS_RESULT(E_hrOutOfPaper, [] { THROW_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); }); + REQUIRE_THROWS_MSG(E_hrOutOfPaper, [] { THROW_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_hrOutOfPaper, [] { REQUIRE(ERROR_OUT_OF_PAPER == LOG_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER)); }); + REQUIRE_LOG_MSG(E_hrOutOfPaper, [] { REQUIRE(ERROR_OUT_OF_PAPER == LOG_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_hrOutOfPaper, [] { FAIL_FAST_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); }); + REQUIRE_FAILFAST_MSG(E_hrOutOfPaper, [] { FAIL_FAST_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_hrNtOkay, [] { RETURN_NTSTATUS(MDEC(ntOKRef())); }); + REQUIRE_RETURNS_MSG(S_hrNtOkay, [] { RETURN_NTSTATUS_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_NTSTATUS(MDEC(ntOKRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_NTSTATUS_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_hrNtAssertionFailure, [] { RETURN_NTSTATUS(STATUS_ASSERTION_FAILURE); }); + REQUIRE_RETURNS_MSG(E_hrNtAssertionFailure, [] { RETURN_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_NTSTATUS(STATUS_ASSERTION_FAILURE); }); + REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_hrNtAssertionFailure, [] { LOG_NTSTATUS(STATUS_ASSERTION_FAILURE); }); + REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { LOG_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_NTSTATUS(STATUS_ASSERTION_FAILURE); }); + REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(MDEC(ntOKRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(STATUS_WAIT_0 == THROW_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == THROW_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == LOG_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == LOG_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(STATUS_WAIT_0 == FAIL_FAST_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == FAIL_FAST_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); return S_OK; }); + REQUIRE_RETURNS_MSG(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_ASSERTION_FAILURE); return S_OK; }); + REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); }); + REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE)); }); + REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); }); + REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); return S_OK; }); + REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_NO_MEMORY); return S_OK; }); + REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); }); + REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY)); }); + REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); }); + REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_IF_NULL_ALLOC(MDEC(pValidRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pValidRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_IF_NULL_ALLOC(MDEC(pValidRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_IF_NULL_ALLOC(MDEC(pValidRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IF_NULL_ALLOC(MDEC(pValidRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC(pNull); return S_OK; }); + REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC_EXPECTED(pNull); return S_OK; }); + REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NULL_ALLOC(pNull); }); + REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(pNull == LOG_IF_NULL_ALLOC(pNull)); }); + REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(pNull == LOG_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NULL_ALLOC(pNull); }); + REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); }); + REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF(E_FAIL, fTrue); return S_OK; }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_EXPECTED(E_FAIL, fTrue); return S_OK; }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF(E_FAIL, fTrue); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF(E_FAIL, fTrue)); }); + REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF(E_FAIL, fTrue); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); }); + REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF(E_FAIL, fTrue); return S_OK; }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_EXPECTED(E_FAIL, fTrue); return S_OK; }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF(E_FAIL, fTrue); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF(E_FAIL, fTrue)); }); + REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF(E_FAIL, fTrue); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(E_FAIL, MDEC(fFalseRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(E_FAIL, MDEC(fFalseRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(E_FAIL, MDEC(fFalseRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(E_FAIL, MDEC(fFalseRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(E_FAIL, MDEC(fFalseRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(S_OK, pNull); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(S_OK, pNull, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(S_OK, pNull); return S_OK; }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_NULL(S_OK, pNull); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_NULL_MSG(S_OK, pNull, "msg: %d", __LINE__); }); + REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF_NULL(E_FAIL, pNull); return S_OK; }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_NULL_EXPECTED(E_FAIL, pNull); return S_OK; }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF_NULL(E_FAIL, pNull); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(pNull == LOG_HR_IF_NULL(E_FAIL, pNull)); }); + REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(pNull == LOG_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF_NULL(E_FAIL, pNull); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(MDEC(S_OK), MDEC(pValidRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(E_FAIL, MDEC(pValidRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(E_FAIL, MDEC(pValidRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); }); + + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF(fTrue); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); }); + REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF(fTrue); return S_OK; }); + REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_EXPECTED(fTrue); return S_OK; }); + REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF(fTrue); }); + REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(fTrue == LOG_LAST_ERROR_IF(fTrue)); }); + REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(fTrue == LOG_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF(fTrue); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_LAST_ERROR_IF(MDEC(fFalseRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_LAST_ERROR_IF_EXPECTED(MDEC(fFalseRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_LAST_ERROR_IF(MDEC(fFalseRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_LAST_ERROR_IF(MDEC(fFalseRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_LAST_ERROR_IF(MDEC(fFalseRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); }); + + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_NULL(pNull); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); }); + REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL(pNull); return S_OK; }); + REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(pNull); return S_OK; }); + REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_NULL(pNull); }); + REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); }); + REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(pNull == LOG_LAST_ERROR_IF_NULL(pNull)); }); + REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(pNull == LOG_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL(pNull); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); }); + + REQUIRE_RETURNS(S_OK, [] { RETURN_LAST_ERROR_IF_NULL(MDEC(pValidRef())); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pValidRef())); return S_OK; }); + REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pNull != THROW_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); }); + REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pNull != THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(pNull != LOG_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); }); + REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pNull != LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pNull != FAIL_FAST_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pNull != FAIL_FAST_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); }); + + REQUIRE_LOG(S_OK, [] { REQUIRE(true == SUCCEEDED_LOG(MDEC(S_OK))); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(false == SUCCEEDED_LOG(E_FAIL)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(false == FAILED_LOG(MDEC(S_OK))); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(true == FAILED_LOG(E_FAIL)); }); + + REQUIRE_LOG(ERROR_SUCCESS, [] { REQUIRE(true == SUCCEEDED_WIN32_LOG(MDEC(ERROR_SUCCESS))); }); + REQUIRE_LOG(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), [] { REQUIRE(false == SUCCEEDED_WIN32_LOG(ERROR_ACCESS_DENIED)); }); + REQUIRE_LOG(ERROR_SUCCESS, [] { REQUIRE(false == FAILED_WIN32_LOG(MDEC(ERROR_SUCCESS))); }); + REQUIRE_LOG(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), [] { REQUIRE(true == FAILED_WIN32_LOG(ERROR_ACCESS_DENIED)); }); + + REQUIRE_LOG(ntOK, [] { REQUIRE(true == SUCCEEDED_NTSTATUS_LOG(MDEC(ntOK))); }); + REQUIRE_LOG(wil::details::NtStatusToHr(ntFAIL), [] { REQUIRE(false == SUCCEEDED_NTSTATUS_LOG(ntFAIL)); }); + REQUIRE_LOG(ntOK, [] { REQUIRE(false == FAILED_NTSTATUS_LOG(MDEC(ntOK))); }); + REQUIRE_LOG(wil::details::NtStatusToHr(ntFAIL), [] { REQUIRE(true == FAILED_NTSTATUS_LOG(ntFAIL)); }); + + // FAIL_FAST_IMMEDIATE* directly invokes __fastfail, which we can't catch, so disabled for now + // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE(); }); + // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF_FAILED(E_FAIL); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IMMEDIATE_IF_FAILED(MDEC(S_OK))); }); + // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF(fTrue); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_IMMEDIATE_IF(MDEC(fFalseRef()))); }); + // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF_NULL(pNull); }); + REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IMMEDIATE_IF_NULL(MDEC(pValidRef()))); }); + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_RETURNS(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN(); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN_MSG("msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN_EXPECTED(); return S_OK; }); + REQUIRE_LOG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_LOG(); }); + REQUIRE_LOG_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_LOG_MSG("msg: %d", __LINE__); }); + REQUIRE_FAILFAST(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_FAIL_FAST(); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_THROW_NORMALIZED(); }); + REQUIRE_THROWS_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_THROW_NORMALIZED_MSG("msg: %d", __LINE__); }); + + REQUIRE_RETURNS(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN(); return S_OK; }); + REQUIRE_RETURNS_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN_MSG("msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN_EXPECTED(); return S_OK; }); + REQUIRE_LOG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG(); }); + REQUIRE_LOG_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG_MSG("msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_FAIL_FAST(); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_THROW_NORMALIZED(); }); + REQUIRE_THROWS_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_THROW_NORMALIZED_MSG("msg: %d", __LINE__); }); + + REQUIRE_FAILFAST_UNSPECIFIED([] { try { if (FAILED(hrFAIL)) { throw E_FAIL; } } CATCH_FAIL_FAST(); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { try { if (FAILED(hrFAIL)) { throw E_FAIL; } } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); }); + + REQUIRE_THROWS_RESULT(E_AD, [] { THROW_EXCEPTION(MDEC(DerivedAccessDeniedException())); }); + REQUIRE_THROWS_MSG(E_AD, [] { THROW_EXCEPTION_MSG(MDEC(DerivedAccessDeniedException()), "msg: %d", __LINE__); }); + + REQUIRE_LOG(E_AD, [] { try { throw AlternateAccessDeniedException(); } CATCH_LOG(); }); + REQUIRE_THROWS_RESULT(E_AD, [] { try { throw AlternateAccessDeniedException(); } CATCH_THROW_NORMALIZED(); }); + + REQUIRE_RETURNS(S_OK, [] { return wil::ResultFromException([] { THROW_IF_FAILED(hrOK); }); }); + REQUIRE_RETURNS(E_FAIL, [] { return wil::ResultFromException([] { THROW_IF_FAILED(hrFAIL); }); }); + REQUIRE(E_AD == wil::ResultFromException([] { throw AlternateAccessDeniedException(); })); + + try { THROW_HR(E_FAIL); } + catch (...) { REQUIRE(E_FAIL == wil::ResultFromCaughtException()); }; +#endif + +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE_LOG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG(); }); +#endif + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_IF_NULL_ALLOC(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_IF_NULL_ALLOC(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pInt)); return S_OK; }); + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_HR_IF_NULL(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_HR_IF_NULL_MSG(E_OUTOFMEMORY, pInt, "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { std::unique_ptr pInt; RETURN_HR_IF_NULL_EXPECTED(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_HR_IF_NULL(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_HR_IF_NULL_MSG(E_OUTOFMEMORY, MDEC(pInt), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr pInt(new int(5)); RETURN_HR_IF_NULL_EXPECTED(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; }); + + REQUIRE_RETURNS(E_AD, [] { std::unique_ptr pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(E_AD, [] { std::unique_ptr pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_AD, [] { std::unique_ptr pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS(S_OK, [] { std::unique_ptr pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL(MDEC(pInt)); return S_OK; }); + REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pInt)); return S_OK; }); + + REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { std::unique_ptr pInt; THROW_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; THROW_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; LOG_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; LOG_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { std::unique_ptr pInt; FAIL_FAST_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_LOG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_IF_NULL_ALLOC(MDEC(pInt)); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + + REQUIRE_LOG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); }); + REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; LOG_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_FAIL, [] { std::unique_ptr pInt; FAIL_FAST_HR_IF_NULL(MDEC(E_FAIL), MDEC(pInt)); }); + REQUIRE_FAILFAST_MSG(E_FAIL, [] { std::unique_ptr pInt; FAIL_FAST_HR_IF_NULL_MSG(MDEC(E_FAIL), MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { std::unique_ptr pInt; THROW_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); }); + REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr pInt; THROW_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_LOG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); }); + REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_HR_IF_NULL(MDEC(E_FAIL), MDEC(pInt)); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_HR_IF_NULL_MSG(MDEC(E_FAIL), MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); }); + REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); }); + + REQUIRE_LOG(E_AD, [] { std::unique_ptr pInt; SetAD(); LOG_LAST_ERROR_IF_NULL(MDEC(pInt)); }); + REQUIRE_LOG_MSG(E_AD, [] { std::unique_ptr pInt; SetAD(); LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(E_AD, [] { std::unique_ptr pInt; SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL(pInt); }); + REQUIRE_FAILFAST_MSG(E_AD, [] { std::unique_ptr pInt; SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pInt, "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(E_AD, [] { std::unique_ptr pInt; SetAD(); THROW_LAST_ERROR_IF_NULL(MDEC(pInt)); }); + REQUIRE_THROWS_MSG(E_AD, [] { std::unique_ptr pInt; SetAD(); THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_LOG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_LAST_ERROR_IF_NULL(MDEC(pInt)); }); + REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_LAST_ERROR_IF_NULL(pInt); }); + REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pInt, "msg: %d", __LINE__); }); + REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_LAST_ERROR_IF_NULL(MDEC(pInt)); }); + REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr pInt(new int(5)); THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); }); + + // REQUIRE_FAILFAST_UNSPECIFIED([] { std::unique_ptr pInt; FAIL_FAST_IMMEDIATE_IF_NULL(pNull); }); + REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_IMMEDIATE_IF_NULL(MDEC(pValidRef())); }); + REQUIRE_FAILFAST_UNSPECIFIED([] { std::unique_ptr pInt; FAIL_FAST_IF_NULL(pNull); }); + REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr pInt(new int(5)); FAIL_FAST_IF_NULL(MDEC(pInt)); }); + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { Microsoft::WRL::ComPtr ptr; RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { Microsoft::WRL::ComPtr ptr; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); }); + + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::shared_ptr ptr; RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { std::shared_ptr ptr; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); }); + REQUIRE_RETURNS(S_OK, [] { std::shared_ptr ptr(new int(5)); RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; }); + REQUIRE_LOG(S_OK, [] { std::shared_ptr ptr(new int(5)); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); }); + +#ifdef __cplusplus_winrt + REQUIRE_RETURNS(E_OUTOFMEMORY, [] { Platform::String^ str(nullptr); RETURN_IF_NULL_ALLOC(MDEC(str)); return S_OK; }); + REQUIRE_LOG(E_OUTOFMEMORY, [] { Platform::String^ str(nullptr); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(str)); }); + REQUIRE_RETURNS(S_OK, [] { Platform::String^ str(L"a"); RETURN_IF_NULL_ALLOC(MDEC(str)); return S_OK; }); + REQUIRE_LOG(S_OK, [] { Platform::String^ str(L"a"); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(str)); }); +#endif +} + +#define WRAP_LAMBDA(code) [&] {code;}; + +//these macros should all have compile errors due to use of an invalid type +void InvalidTypeChecks() +{ + std::unique_ptr boolCastClass; + std::vector noBoolCastClass; + + //WRAP_LAMBDA(RETURN_IF_FAILED(fTrue)); + //WRAP_LAMBDA(RETURN_IF_FAILED(fTRUE)); + //WRAP_LAMBDA(RETURN_IF_FAILED(boolCastClass)); + //WRAP_LAMBDA(RETURN_IF_FAILED(noBoolCastClass)); + //WRAP_LAMBDA(RETURN_IF_FAILED(errSuccess)); + + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(fTrue)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(noBoolCastClass)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(hrOK)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(errSuccess)); + + //WRAP_LAMBDA(RETURN_HR_IF(errSuccess, false)); + //WRAP_LAMBDA(RETURN_HR_IF(errSuccess, true)); + //WRAP_LAMBDA(RETURN_HR_IF(hrOK, noBoolCastClass)); + //WRAP_LAMBDA(RETURN_HR_IF(hrOK, hrOK)); + //WRAP_LAMBDA(RETURN_HR_IF(hrOK, errSuccess)); + + //WRAP_LAMBDA(RETURN_HR_IF_NULL(errSuccess, nullptr)); + //WRAP_LAMBDA(RETURN_HR_IF_NULL(errSuccess, pValid)); + + //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(noBoolCastClass)); + //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(errSuccess)); + //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(hrOK)); + + //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(fTrue)); + //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(fTRUE)); + //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(boolCastClass)); + //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(noBoolCastClass)); + //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(errSuccess)); + + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fTrue)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(noBoolCastClass)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(hrOK)); + //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(errSuccess)); + + //LOG_IF_FAILED(fTrue); + //LOG_IF_FAILED(fTRUE); + //LOG_IF_FAILED(boolCastClass); + //LOG_IF_FAILED(noBoolCastClass); + //LOG_IF_FAILED(errSuccess); + + //LOG_IF_WIN32_BOOL_FALSE(fTrue); + //LOG_IF_WIN32_BOOL_FALSE(noBoolCastClass); + //LOG_IF_WIN32_BOOL_FALSE(hrOK); + //LOG_IF_WIN32_BOOL_FALSE(errSuccess); + + //LOG_HR_IF(errSuccess, false); + //LOG_HR_IF(errSuccess, true); + //LOG_HR_IF(hrOK, noBoolCastClass); + //LOG_HR_IF(hrOK, hrOK); + //LOG_HR_IF(hrOK, errSuccess); + + //FAIL_FAST_IF_FAILED(fTrue); + //FAIL_FAST_IF_FAILED(fTRUE); + //FAIL_FAST_IF_FAILED(boolCastClass); + //FAIL_FAST_IF_FAILED(noBoolCastClass); + //FAIL_FAST_IF_FAILED(errSuccess); + + //FAIL_FAST_IF_WIN32_BOOL_FALSE(fTrue); + //FAIL_FAST_IF_WIN32_BOOL_FALSE(noBoolCastClass); + //FAIL_FAST_IF_WIN32_BOOL_FALSE(hrOK); + //FAIL_FAST_IF_WIN32_BOOL_FALSE(errSuccess); + + //FAIL_FAST_HR_IF(errSuccess, false); + //FAIL_FAST_HR_IF(errSuccess, true); + //FAIL_FAST_HR_IF(hrOK, noBoolCastClass); + //FAIL_FAST_HR_IF(hrOK, hrOK); + //FAIL_FAST_HR_IF(hrOK, errSuccess); + + //THROW_IF_FAILED(fTrue); + //THROW_IF_FAILED(fTRUE); + //THROW_IF_FAILED(boolCastClass); + //THROW_IF_FAILED(noBoolCastClass); + //THROW_IF_FAILED(errSuccess); + + //THROW_IF_WIN32_BOOL_FALSE(fTrue); + //THROW_IF_WIN32_BOOL_FALSE(noBoolCastClass); + //THROW_IF_WIN32_BOOL_FALSE(hrOK); + //THROW_IF_WIN32_BOOL_FALSE(errSuccess); + + //THROW_HR_IF(errSuccess, false); + //THROW_HR_IF(errSuccess, true); + //THROW_HR_IF(hrOK, noBoolCastClass); + //THROW_HR_IF(hrOK, hrOK); + //THROW_HR_IF(hrOK, errSuccess); + + //FAIL_FAST_IF(noBoolCastClass); + //FAIL_FAST_IF(hrOK); + //FAIL_FAST_IF(errSuccess); + + //FAIL_FAST_IMMEDIATE_IF_FAILED(fTrue); + //FAIL_FAST_IMMEDIATE_IF_FAILED(fTRUE); + //FAIL_FAST_IMMEDIATE_IF_FAILED(boolCastClass); + //FAIL_FAST_IMMEDIATE_IF_FAILED(noBoolCastClass); + //FAIL_FAST_IMMEDIATE_IF_FAILED(errSuccess); + + //FAIL_FAST_IMMEDIATE_IF(noBoolCastClass); + //FAIL_FAST_IMMEDIATE_IF(hrOK); + //FAIL_FAST_IMMEDIATE_IF(errSuccess); +} + +TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]") +{ + { + // default construction test + wil::unique_handle spHandle; + REQUIRE(spHandle.get() == nullptr); + + // null ptr assignment creation + wil::unique_handle spNullHandle = nullptr; + REQUIRE(spNullHandle.get() == nullptr); + + // explicit construction from the invalid value + wil::unique_handle spInvalidHandle(nullptr); + REQUIRE(spInvalidHandle.get() == nullptr); + + // valid handle creation + wil::unique_handle spValidHandle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + REQUIRE(spValidHandle.get() != nullptr); + auto const handleValue = spValidHandle.get(); + + // r-value construction + wil::unique_handle spMoveHandle = wistd::move(spValidHandle); + REQUIRE(spValidHandle.get() == nullptr); + REQUIRE(spMoveHandle.get() == handleValue); + + // nullptr-assignment + spNullHandle = nullptr; + REQUIRE(spNullHandle.get() == nullptr); + + // r-value assignment + spValidHandle = wistd::move(spMoveHandle); + REQUIRE(spValidHandle.get() == handleValue); + REQUIRE(spMoveHandle.get() == nullptr); + + // swap + spValidHandle.swap(spMoveHandle); + REQUIRE(spValidHandle.get() == nullptr); + REQUIRE(spMoveHandle.get() == handleValue); + + // operator bool + REQUIRE_FALSE(spValidHandle); + REQUIRE(spMoveHandle); + + // release + auto ptrValidHandle = spValidHandle.release(); + auto ptrMoveHandle = spMoveHandle.release(); + REQUIRE(ptrValidHandle == nullptr); + REQUIRE(ptrMoveHandle == handleValue); + REQUIRE(spValidHandle.get() == nullptr); + REQUIRE(spMoveHandle.get() == nullptr); + + // reset + spValidHandle.reset(); + spMoveHandle.reset(); + REQUIRE(spValidHandle.get() == nullptr); + REQUIRE(spMoveHandle.get() == nullptr); + spValidHandle.reset(ptrValidHandle); + spMoveHandle.reset(ptrMoveHandle); + REQUIRE(spValidHandle.get() == nullptr); + REQUIRE(spMoveHandle.get() == handleValue); + spNullHandle.reset(nullptr); + REQUIRE(spNullHandle.get() == nullptr); + + // address + REQUIRE(*spMoveHandle.addressof() == handleValue); + REQUIRE(*spMoveHandle.put() == nullptr); + *spMoveHandle.put() = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + REQUIRE(spMoveHandle); + REQUIRE(*(&spMoveHandle) == nullptr); + *(&spMoveHandle) = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + REQUIRE(spMoveHandle); + } + { + // default construction test + wil::unique_hfile spHandle; + REQUIRE(spHandle.get() == INVALID_HANDLE_VALUE); + + // implicit construction from the invalid value + wil::unique_hfile spNullHandle; // = nullptr; // method explicitly disabled as nullptr isn't the invalid value + REQUIRE(spNullHandle.get() == INVALID_HANDLE_VALUE); + + // assignment from the invalid value + // spNullHandle = nullptr; // method explicitly disabled as nullptr isn't the invalid value + REQUIRE(spNullHandle.get() == INVALID_HANDLE_VALUE); + + // explicit construction from the invalid value + wil::unique_hfile spInvalidHandle(INVALID_HANDLE_VALUE); + REQUIRE(spInvalidHandle.get() == INVALID_HANDLE_VALUE); + + // valid handle creation + wchar_t tempFileName[MAX_PATH]; + REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName)); + + CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(params) }; + params.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY; + wil::unique_hfile spValidHandle(::CreateFile2(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms)); + + ::DeleteFileW(tempFileName); + REQUIRE(spValidHandle.get() != INVALID_HANDLE_VALUE); + auto const handleValue = spValidHandle.get(); + + // r-value construction + wil::unique_hfile spMoveHandle = wistd::move(spValidHandle); + REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE); + REQUIRE(spMoveHandle.get() == handleValue); + + // nullptr-assignment -- uncomment to check intentional compilation error + // spNullHandle = nullptr; + + // r-value assignment + spValidHandle = wistd::move(spMoveHandle); + REQUIRE(spValidHandle.get() == handleValue); + REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE); + + // swap + spValidHandle.swap(spMoveHandle); + REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE); + REQUIRE(spMoveHandle.get() == handleValue); + + // operator bool + REQUIRE_FALSE(spValidHandle); + REQUIRE(spMoveHandle); + + // release + auto ptrValidHandle = spValidHandle.release(); + auto ptrMoveHandle = spMoveHandle.release(); + REQUIRE(ptrValidHandle == INVALID_HANDLE_VALUE); + REQUIRE(ptrMoveHandle == handleValue); + REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE); + REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE); + + // reset + spValidHandle.reset(); + spMoveHandle.reset(); + REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE); + REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE); + spValidHandle.reset(ptrValidHandle); + spMoveHandle.reset(ptrMoveHandle); + REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE); + REQUIRE(spMoveHandle.get() == handleValue); + // uncomment to test intentional compilation error due to conflict with INVALID_HANDLE_VALUE + // spNullHandle.reset(nullptr); + + // address + REQUIRE(*spMoveHandle.addressof() == handleValue); + REQUIRE(*(&spMoveHandle) == INVALID_HANDLE_VALUE); + + wchar_t tempFileName2[MAX_PATH]; + REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName2)); + + CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(params2) }; + params2.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY; + *(&spMoveHandle) = ::CreateFile2(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms2); + + ::DeleteFileW(tempFileName2); + REQUIRE(spMoveHandle); + + // ensure that mistaken nullptr usage is not valid... + spMoveHandle.reset(); + *(&spMoveHandle) = nullptr; + REQUIRE_FALSE(spMoveHandle); + } + + auto hFirst = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + auto hSecond= ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + + wil::unique_handle spLeft(hFirst); + wil::unique_handle spRight(hSecond); + + REQUIRE(spRight.get() == hSecond); + REQUIRE(spLeft.get() == hFirst); + swap(spLeft, spRight); + REQUIRE(spLeft.get() == hSecond); + REQUIRE(spRight.get() == hFirst); + swap(spLeft, spRight); + + REQUIRE((spLeft.get() == spRight.get()) == (spLeft == spRight)); + REQUIRE((spLeft.get() != spRight.get()) == (spLeft != spRight)); + REQUIRE((spLeft.get() < spRight.get()) == (spLeft < spRight)); + REQUIRE((spLeft.get() <= spRight.get()) == (spLeft <= spRight)); + REQUIRE((spLeft.get() >= spRight.get()) == (spLeft >= spRight)); + REQUIRE((spLeft.get() > spRight.get()) == (spLeft > spRight)); + + // test stl container use (hash & std::less) +#ifdef WIL_ENABLE_EXCEPTIONS + std::unordered_set hashSet; + hashSet.insert(std::move(spLeft)); + hashSet.insert(std::move(spRight)); + std::multiset set; + set.insert(std::move(spLeft)); + set.insert(std::move(spRight)); +#endif +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("WindowsInternalTests::SharedHandle", "[resource][shared_any]") +{ + // default construction + wil::shared_handle spHandle; + REQUIRE(spHandle.get() == nullptr); + + // pointer construction + wil::shared_handle spValid(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + auto ptr = spValid.get(); + REQUIRE(spValid.get() != nullptr); + + // null construction + wil::shared_handle spNull = nullptr; + REQUIRE(spNull.get() == nullptr); + + // Present to verify that it doesn't compile (disabled) + // wil::shared_hfile spFile = nullptr; + + // copy construction + wil::shared_handle spCopy = spValid; + REQUIRE(spCopy.get() == ptr); + + // r-value construction + wil::shared_handle spMove = wistd::move(spCopy); + REQUIRE(spMove.get() == ptr); + REQUIRE(spCopy.get() == nullptr); + + // unique handle construction + wil::shared_handle spFromUnique = wil::unique_handle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + REQUIRE(spFromUnique.get() != nullptr); + + // direct assignment + wil::shared_handle spAssign; + spAssign = spValid; + REQUIRE(spAssign.get() == ptr); + + // empty reset + spFromUnique.reset(); + REQUIRE(spFromUnique.get() == nullptr); + + // reset against unique ptr + spFromUnique.reset(wil::unique_handle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0))); + REQUIRE(spFromUnique.get() != nullptr); + + // reset against raw pointer + spAssign.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + REQUIRE(spAssign.get() != nullptr); + REQUIRE(spAssign.get() != ptr); + + // ref-count checks + REQUIRE(spAssign.use_count() == 1); + + // bool operator + REQUIRE(spAssign); + spAssign.reset(); + REQUIRE_FALSE(spAssign); + + // swap and compare + wil::shared_handle sp1(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + wil::shared_handle sp2(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + auto ptr1 = sp1.get(); + auto ptr2 = sp2.get(); + sp1.swap(sp2); + REQUIRE(sp1.get() == ptr2); + REQUIRE(sp2.get() == ptr1); + swap(sp1, sp2); + REQUIRE(sp1.get() == ptr1); + REQUIRE(sp2.get() == ptr2); + REQUIRE((ptr1 == ptr2) == (sp1 == sp2)); + REQUIRE((ptr1 != ptr2) == (sp1 != sp2)); + REQUIRE((ptr1 < ptr2) == (sp1 < sp2)); + REQUIRE((ptr1 <= ptr2) == (sp1 <= sp2)); + REQUIRE((ptr1 > ptr2) == (sp1 > sp2)); + REQUIRE((ptr1 >= ptr2) == (sp1 >= sp2)); + + // construction + wil::weak_handle wh; + REQUIRE_FALSE(wh.lock()); + wil::weak_handle wh1 = sp1; + REQUIRE(wh1.lock()); + REQUIRE(wh1.lock().get() == ptr1); + wil::weak_handle wh1copy = wh1; + REQUIRE(wh1copy.lock()); + + // assignment + wh = wh1; + REQUIRE(wh.lock().get() == ptr1); + wh = sp2; + REQUIRE(wh.lock().get() == ptr2); + + // reset + wh.reset(); + REQUIRE_FALSE(wh.lock()); + + // expiration + wh = sp1; + sp1.reset(); + REQUIRE(wh.expired()); + REQUIRE_FALSE(wh.lock()); + + // swap + wh1 = sp1; + wil::weak_handle wh2 = sp2; + ptr1 = sp1.get(); + ptr2 = sp2.get(); + REQUIRE(wh1.lock().get() == ptr1); + REQUIRE(wh2.lock().get() == ptr2); + wh1.swap(wh2); + REQUIRE(wh1.lock().get() == ptr2); + REQUIRE(wh2.lock().get() == ptr1); + swap(wh1, wh2); + REQUIRE(wh1.lock().get() == ptr1); + REQUIRE(wh2.lock().get() == ptr2); + + // put + sp1.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + REQUIRE(sp1); + sp1.put(); // frees the pointer... + REQUIRE_FALSE(sp1); + sp2 = sp1; + REQUIRE_FALSE(sp2); + *sp1.put() = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + REQUIRE(sp1); + REQUIRE_FALSE(sp2); + + // address + sp1.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)); + REQUIRE(sp1); + &sp1; // frees the pointer... + REQUIRE_FALSE(sp1); + sp2 = sp1; + REQUIRE_FALSE(sp2); + *(&sp1) = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0); + REQUIRE(sp1); + REQUIRE_FALSE(sp2); + + // test stl container use (hash & std::less) + std::unordered_set hashSet; + hashSet.insert(sp1); + hashSet.insert(sp2); + std::set set; + set.insert(sp1); + set.insert(sp2); +} +#endif + +template +void EventTestCommon() +{ + // Constructor tests... + event_t e1; + REQUIRE_FALSE(e1); + event_t e2(::CreateEventEx(nullptr, nullptr, 0, 0)); + REQUIRE(e2); + wil::unique_handle h1(::CreateEventEx(nullptr, nullptr, 0, 0)); + REQUIRE(h1); + event_t e3(h1.release()); + REQUIRE(e3); + REQUIRE_FALSE(h1); + event_t e4(std::move(e2)); + REQUIRE(e4); + REQUIRE_FALSE(e2); + + // inherited address tests... + REQUIRE(e4); + &e4; + REQUIRE_FALSE(e4); + auto hFill = ::CreateEventEx(nullptr, nullptr, 0, 0); + *(&e4) = hFill; + REQUIRE(e4); + REQUIRE(*e4.addressof() == hFill); + REQUIRE(e4); + + // assignment... + event_t e5; + e5 = std::move(e4); + REQUIRE(e5); + REQUIRE_FALSE(e4); + + // various event-based tests + event_t eManual; + eManual.create(wil::EventOptions::ManualReset); + REQUIRE_FALSE(eManual.is_signaled()); + eManual.SetEvent(); + REQUIRE(eManual.is_signaled()); + eManual.ResetEvent(); + REQUIRE_FALSE(eManual.is_signaled()); + { + auto exit = eManual.SetEvent_scope_exit(); + REQUIRE_FALSE(eManual.is_signaled()); + } + REQUIRE(eManual.is_signaled()); + { + auto exit = eManual.ResetEvent_scope_exit(); + REQUIRE(eManual.is_signaled()); + } + REQUIRE_FALSE(eManual.is_signaled()); + REQUIRE_FALSE(eManual.wait(50)); + REQUIRE_FALSE(wil::handle_wait(eManual.get(), 50)); + eManual.SetEvent(); + REQUIRE(eManual.wait(50)); + REQUIRE(wil::handle_wait(eManual.get(), 50)); + + REQUIRE(eManual.wait(50)); + + REQUIRE(eManual.try_create(wil::EventOptions::ManualReset, L"IExist")); + REQUIRE_FALSE(eManual.try_open(L"IDontExist")); +} + +template +void MutexTestCommon() +{ + // Constructor tests... + mutex_t m1; + REQUIRE_FALSE(m1); + mutex_t m2(::CreateMutexEx(nullptr, nullptr, 0, 0)); + REQUIRE(m2); + wil::unique_handle h1(::CreateMutexEx(nullptr, nullptr, 0, 0)); + REQUIRE(h1); + mutex_t m3(h1.release()); + REQUIRE(m3); + REQUIRE_FALSE(h1); + mutex_t m4(std::move(m2)); + REQUIRE(m4); + REQUIRE_FALSE(m2); + + // inherited address tests... + REQUIRE(m4); + &m4; + REQUIRE_FALSE(m4); + auto hFill = ::CreateMutexEx(nullptr, nullptr, 0, 0); + *(&m4) = hFill; + REQUIRE(m4); + REQUIRE(*m4.addressof() == hFill); + REQUIRE(m4); + + // assignment... + mutex_t m5; + m5 = std::move(m4); + REQUIRE(m5); + REQUIRE_FALSE(m4); + + // various mutex-based tests + mutex_t eManual; + eManual.create(nullptr, CREATE_MUTEX_INITIAL_OWNER); + eManual.ReleaseMutex(); + eManual.create(nullptr, CREATE_MUTEX_INITIAL_OWNER); + { + auto release = eManual.ReleaseMutex_scope_exit(); + } + { + DWORD dwStatus; + auto release = eManual.acquire(&dwStatus); + REQUIRE(release); + REQUIRE(dwStatus == WAIT_OBJECT_0); + } + + // pass-through methods -- test compilation; + REQUIRE(eManual.try_create(L"FOO-TEST")); + REQUIRE(eManual.try_open(L"FOO-TEST")); +} + +template +void SemaphoreTestCommon() +{ + // Constructor tests... + semaphore_t m1; + REQUIRE_FALSE(m1); + semaphore_t m2(::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0)); + REQUIRE(m2); + wil::unique_handle h1(::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0)); + REQUIRE(h1); + semaphore_t m3(h1.release()); + REQUIRE(m3); + REQUIRE_FALSE(h1); + semaphore_t m4(std::move(m2)); + REQUIRE(m4); + REQUIRE_FALSE(m2); + + // inherited address tests... + REQUIRE(m4); + &m4; + REQUIRE_FALSE(m4); + auto hFill = ::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0); + *(&m4) = hFill; + REQUIRE(m4); + REQUIRE(*m4.addressof() == hFill); + REQUIRE(m4); + + // assignment... + semaphore_t m5; + m5 = std::move(m4); + REQUIRE(m5); + REQUIRE_FALSE(m4); + + // various semaphore-based tests + semaphore_t eManual; + eManual.create(1, 1); + WaitForSingleObjectEx(eManual.get(), INFINITE, true); + eManual.ReleaseSemaphore(); + eManual.create(1, 1); + WaitForSingleObjectEx(eManual.get(), INFINITE, true); + { + auto release = eManual.ReleaseSemaphore_scope_exit(); + } + { + DWORD dwStatus; + auto release = eManual.acquire(&dwStatus); + REQUIRE(release); + REQUIRE(dwStatus == WAIT_OBJECT_0); + } + + // pass-through methods -- test compilation; + REQUIRE(eManual.try_create(1, 1, L"BAR-TEST")); + REQUIRE(eManual.try_open(L"BAR-TEST")); +} + +TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]") +{ + EventTestCommon(); + EventTestCommon(); + + // intentionally disabled in the non-exception version... + // wil::unique_event_nothrow testEvent2(wil::EventOptions::ManualReset); + wil::unique_event_failfast testEvent3(wil::EventOptions::ManualReset); +#ifdef WIL_ENABLE_EXCEPTIONS + EventTestCommon(); + + wil::unique_event testEvent(wil::EventOptions::ManualReset); + { + REQUIRE_FALSE(wil::event_is_signaled(testEvent.get())); + auto eventSet = wil::SetEvent_scope_exit(testEvent.get()); + REQUIRE_FALSE(wil::event_is_signaled(testEvent.get())); + } + { + REQUIRE(wil::event_is_signaled(testEvent.get())); + auto eventSet = wil::ResetEvent_scope_exit(testEvent.get()); + REQUIRE(wil::event_is_signaled(testEvent.get())); + } + REQUIRE_FALSE(wil::event_is_signaled(testEvent.get())); + REQUIRE_FALSE(wil::handle_wait(testEvent.get(), 0)); + + // Exception-based - no return + testEvent.create(wil::EventOptions::ManualReset); +#endif + + // Error-code based -- returns HR + wil::unique_event_nothrow testEventNoExcept; + REQUIRE(SUCCEEDED(testEventNoExcept.create(wil::EventOptions::ManualReset))); + + + MutexTestCommon(); + MutexTestCommon(); + + // intentionally disabled in the non-exception version... + // wil::unique_mutex_nothrow testMutex2(L"FOO-TEST-2"); + wil::unique_mutex_failfast testMutex3(L"FOO-TEST-3"); +#ifdef WIL_ENABLE_EXCEPTIONS + MutexTestCommon(); + + wil::unique_mutex testMutex(L"FOO-TEST"); + WaitForSingleObjectEx(testMutex.get(), INFINITE, TRUE); + { + auto release = wil::ReleaseMutex_scope_exit(testMutex.get()); + } + + // Exception-based - no return + testMutex.create(nullptr); +#endif + + // Error-code based -- returns HR + wil::unique_mutex_nothrow testMutexNoExcept; + REQUIRE(SUCCEEDED(testMutexNoExcept.create(nullptr))); + + + SemaphoreTestCommon(); + SemaphoreTestCommon(); + + // intentionally disabled in the non-exception version... + // wil::unique_semaphore_nothrow testSemaphore2(1, 1); + wil::unique_semaphore_failfast testSemaphore3(1, 1); +#ifdef WIL_ENABLE_EXCEPTIONS + SemaphoreTestCommon(); + + wil::unique_semaphore testSemaphore(1, 1); + WaitForSingleObjectEx(testSemaphore.get(), INFINITE, true); + { + auto release = wil::ReleaseSemaphore_scope_exit(testSemaphore.get()); + } + + // Exception-based - no return + testSemaphore.create(1, 1); +#endif + + // Error-code based -- returns HR + wil::unique_semaphore_nothrow testSemaphoreNoExcept; + REQUIRE(SUCCEEDED(testSemaphoreNoExcept.create(1, 1))); + + auto unique_cotaskmem_string_failfast1 = wil::make_cotaskmem_string_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_failfast1.get()) == 0); + + auto unique_cotaskmem_string_nothrow1 = wil::make_cotaskmem_string_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_nothrow1.get()) == 0); + + auto unique_cotaskmem_string_nothrow2 = wil::make_cotaskmem_string_nothrow(L""); + REQUIRE(wcscmp(L"", unique_cotaskmem_string_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_cotaskmem_string_te1 = wil::make_cotaskmem_string(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_te1.get()) == 0); + + auto unique_cotaskmem_string_te2 = wil::make_cotaskmem_string(L""); + REQUIRE(wcscmp(L"", unique_cotaskmem_string_te2.get()) == 0); + + auto unique_cotaskmem_string_range1 = wil::make_cotaskmem_string(L"Foo", 2); + REQUIRE(wcscmp(L"Fo", unique_cotaskmem_string_range1.get()) == 0); + + auto unique_cotaskmem_string_range2 = wil::make_cotaskmem_string(nullptr, 2); + unique_cotaskmem_string_range2.get()[0] = L'F'; + unique_cotaskmem_string_range2.get()[1] = L'o'; + REQUIRE(wcscmp(L"Fo", unique_cotaskmem_string_range2.get()) == 0); + + auto unique_cotaskmem_string_range3 = wil::make_cotaskmem_string(nullptr, 0); + REQUIRE(wcscmp(L"", unique_cotaskmem_string_range3.get()) == 0); +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + auto verify = MakeSecureDeleterMallocSpy(); + REQUIRE_SUCCEEDED(::CoRegisterMallocSpy(verify.Get())); + auto removeSpy = wil::scope_exit([&] { ::CoRevokeMallocSpy(); }); + + auto unique_cotaskmem_string_secure_failfast1 = wil::make_cotaskmem_string_secure_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_failfast1.get()) == 0); + + auto unique_cotaskmem_string_secure_nothrow1 = wil::make_cotaskmem_string_secure_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_nothrow1.get()) == 0); + + auto unique_cotaskmem_string_secure_nothrow2 = wil::make_cotaskmem_string_secure_nothrow(L""); + REQUIRE(wcscmp(L"", unique_cotaskmem_string_secure_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_cotaskmem_string_secure_te1 = wil::make_cotaskmem_string_secure(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_te1.get()) == 0); + + auto unique_cotaskmem_string_secure_te2 = wil::make_cotaskmem_string_secure(L""); + REQUIRE(wcscmp(L"", unique_cotaskmem_string_secure_te2.get()) == 0); +#endif + } + + auto unique_hlocal_string_failfast1 = wil::make_hlocal_string_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_failfast1.get()) == 0); + + auto unique_hlocal_string_nothrow1 = wil::make_hlocal_string_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_nothrow1.get()) == 0); + + auto unique_hlocal_string_nothrow2 = wil::make_hlocal_string_nothrow(L""); + REQUIRE(wcscmp(L"", unique_hlocal_string_nothrow2.get()) == 0); + + auto unique_hlocal_ansistring_failfast1 = wil::make_hlocal_ansistring_failfast("Foo"); + REQUIRE(strcmp("Foo", unique_hlocal_ansistring_failfast1.get()) == 0); + + auto unique_hlocal_ansistring_nothrow1 = wil::make_hlocal_ansistring_nothrow("Foo"); + REQUIRE(strcmp("Foo", unique_hlocal_ansistring_nothrow1.get()) == 0); + + auto unique_hlocal_ansistring_nothrow2 = wil::make_hlocal_ansistring_nothrow(""); + REQUIRE(strcmp("", unique_hlocal_ansistring_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_hlocal_string_te1 = wil::make_hlocal_string(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_te1.get()) == 0); + + auto unique_hlocal_string_te2 = wil::make_hlocal_string(L""); + REQUIRE(wcscmp(L"", unique_hlocal_string_te2.get()) == 0); + + auto unique_hlocal_string_range1 = wil::make_hlocal_string(L"Foo", 2); + REQUIRE(wcscmp(L"Fo", unique_hlocal_string_range1.get()) == 0); + + auto unique_hlocal_string_range2 = wil::make_hlocal_string(nullptr, 2); + unique_hlocal_string_range2.get()[0] = L'F'; + unique_hlocal_string_range2.get()[1] = L'o'; + REQUIRE(wcscmp(L"Fo", unique_hlocal_string_range2.get()) == 0); + + auto unique_hlocal_string_range3 = wil::make_hlocal_string(nullptr, 0); + REQUIRE(wcscmp(L"", unique_hlocal_string_range3.get()) == 0); + + auto unique_hlocal_ansistring_te1 = wil::make_hlocal_ansistring("Foo"); + REQUIRE(strcmp("Foo", unique_hlocal_ansistring_te1.get()) == 0); + + auto unique_hlocal_ansistring_te2 = wil::make_hlocal_ansistring(""); + REQUIRE(strcmp("", unique_hlocal_ansistring_te2.get()) == 0); + + auto unique_hlocal_ansistring_range1 = wil::make_hlocal_ansistring("Foo", 2); + REQUIRE(strcmp("Fo", unique_hlocal_ansistring_range1.get()) == 0); + + auto unique_hlocal_ansistring_range2 = wil::make_hlocal_ansistring(nullptr, 2); + unique_hlocal_ansistring_range2.get()[0] = L'F'; + unique_hlocal_ansistring_range2.get()[1] = L'o'; + REQUIRE(strcmp("Fo", unique_hlocal_ansistring_range2.get()) == 0); + + auto unique_hlocal_ansistring_range3 = wil::make_hlocal_ansistring(nullptr, 0); + REQUIRE(strcmp("", unique_hlocal_ansistring_range3.get()) == 0); +#endif + + { + auto verify = MakeSecureDeleterMallocSpy(); + REQUIRE_SUCCEEDED(::CoRegisterMallocSpy(verify.Get())); + auto removeSpy = wil::scope_exit([&] { ::CoRevokeMallocSpy(); }); + + auto unique_hlocal_string_secure_failfast1 = wil::make_hlocal_string_secure_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_failfast1.get()) == 0); + + auto unique_hlocal_string_secure_nothrow1 = wil::make_hlocal_string_secure_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_nothrow1.get()) == 0); + + auto unique_hlocal_string_secure_nothrow2 = wil::make_hlocal_string_secure_nothrow(L""); + REQUIRE(wcscmp(L"", unique_hlocal_string_secure_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_hlocal_string_secure_te1 = wil::make_hlocal_string_secure(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_te1.get()) == 0); + + auto unique_hlocal_string_secure_te2 = wil::make_hlocal_string_secure(L""); + REQUIRE(wcscmp(L"", unique_hlocal_string_secure_te2.get()) == 0); +#endif + } + + auto unique_process_heap_string_failfast1 = wil::make_process_heap_string_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_process_heap_string_failfast1.get()) == 0); + + auto unique_process_heap_string_nothrow1 = wil::make_process_heap_string_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_process_heap_string_nothrow1.get()) == 0); + + auto unique_process_heap_string_nothrow2 = wil::make_process_heap_string_nothrow(L""); + REQUIRE(wcscmp(L"", unique_process_heap_string_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_process_heap_string_te1 = wil::make_process_heap_string(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_process_heap_string_te1.get()) == 0); + + auto unique_process_heap_string_te2 = wil::make_process_heap_string(L""); + REQUIRE(wcscmp(L"", unique_process_heap_string_te2.get()) == 0); + + auto unique_process_heap_string_range1 = wil::make_process_heap_string(L"Foo", 2); + REQUIRE(wcscmp(L"Fo", unique_process_heap_string_range1.get()) == 0); + + auto unique_process_heap_string_range2 = wil::make_process_heap_string(nullptr, 2); + unique_process_heap_string_range2.get()[0] = L'F'; + unique_process_heap_string_range2.get()[1] = L'o'; + REQUIRE(wcscmp(L"Fo", unique_process_heap_string_range2.get()) == 0); + + auto unique_process_heap_string_range3 = wil::make_process_heap_string(nullptr, 0); + REQUIRE(wcscmp(L"", unique_process_heap_string_range3.get()) == 0); +#endif + +#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ + + auto unique_bstr_failfast1 = wil::make_bstr_failfast(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_bstr_failfast1.get()) == 0); + + auto unique_bstr_nothrow1 = wil::make_bstr_nothrow(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_bstr_nothrow1.get()) == 0); + + auto unique_bstr_nothrow2 = wil::make_bstr_nothrow(L""); + REQUIRE(wcscmp(L"", unique_bstr_nothrow2.get()) == 0); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto unique_bstr_te1 = wil::make_bstr(L"Foo"); + REQUIRE(wcscmp(L"Foo", unique_bstr_te1.get()) == 0); + + auto unique_bstr_te2 = wil::make_bstr(L""); + REQUIRE(wcscmp(L"", unique_bstr_te2.get()) == 0); + + + auto testString = wil::make_cotaskmem_string(L"Foo"); + { + auto cleanupMemory = wil::SecureZeroMemory_scope_exit(testString.get()); + } + REQUIRE(0 == testString.get()[0]); + + auto testString2 = wil::make_cotaskmem_string(L"Bar"); + { + auto cleanupMemory = wil::SecureZeroMemory_scope_exit(testString2.get(), wcslen(testString2.get()) * sizeof(testString2.get()[0])); + } + REQUIRE(0 == testString2.get()[0]); +#endif +} + +TEST_CASE("WindowsInternalTests::Locking", "[resource]") +{ + { + SRWLOCK rwlock = SRWLOCK_INIT; + { + auto lock = wil::AcquireSRWLockExclusive(&rwlock); + REQUIRE(lock); + + auto lockRecursive = wil::TryAcquireSRWLockExclusive(&rwlock); + REQUIRE_FALSE(lockRecursive); + + auto lockRecursiveShared = wil::TryAcquireSRWLockShared(&rwlock); + REQUIRE_FALSE(lockRecursiveShared); + } + { + auto lock = wil::AcquireSRWLockShared(&rwlock); + REQUIRE(lock); + + auto lockRecursive = wil::TryAcquireSRWLockShared(&rwlock); + REQUIRE(lockRecursive); + + auto lockRecursiveExclusive = wil::TryAcquireSRWLockExclusive(&rwlock); + REQUIRE_FALSE(lockRecursiveExclusive); + } + { + auto lock = wil::TryAcquireSRWLockExclusive(&rwlock); + REQUIRE(lock); + } + { + auto lock = wil::TryAcquireSRWLockShared(&rwlock); + REQUIRE(lock); + } + } + + { + wil::srwlock rwlock; + { + auto lock = rwlock.lock_exclusive(); + REQUIRE(lock); + + auto lockRecursive = rwlock.try_lock_exclusive(); + REQUIRE_FALSE(lockRecursive); + + auto lockRecursiveShared = rwlock.try_lock_shared(); + REQUIRE_FALSE(lockRecursiveShared); + } + { + auto lock = rwlock.lock_shared(); + REQUIRE(lock); + + auto lockRecursive = rwlock.try_lock_shared(); + REQUIRE(lockRecursive); + + auto lockRecursiveExclusive = rwlock.try_lock_exclusive(); + REQUIRE_FALSE(lockRecursiveExclusive); + } + { + auto lock = rwlock.try_lock_exclusive(); + REQUIRE(lock); + } + { + auto lock = rwlock.try_lock_shared(); + REQUIRE(lock); + } + } + + { + CRITICAL_SECTION cs; + ::InitializeCriticalSectionEx(&cs, 0, 0); + auto lock = wil::EnterCriticalSection(&cs); + REQUIRE(lock); + auto tryLock = wil::TryEnterCriticalSection(&cs); + REQUIRE(tryLock); + } + { + wil::critical_section cs; + auto lock = cs.lock(); + REQUIRE(lock); + auto tryLock = cs.try_lock(); + REQUIRE(tryLock); + } +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +TEST_CASE("WindowsInternalTests::GDIWrappers", "[resource]") +{ + { + auto dc = wil::GetDC(::GetDesktopWindow()); + } + { + auto dc = wil::GetWindowDC(::GetDesktopWindow()); + } + { + auto dc = wil::BeginPaint(::GetDesktopWindow()); + wil::unique_hbrush brush(::CreateSolidBrush(0xffffff)); + auto select = wil::SelectObject(dc.get(), brush.get()); + } +} +#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ + +void TestOutHandle(_Out_ HANDLE *pHandle) +{ + *pHandle = nullptr; +} + +void TestOutAlloc(_Out_ int **ppInt) +{ + *ppInt = new int(5); +} + +void TestCoTask(_Outptr_result_buffer_(*charCount) PWSTR *ppsz, size_t *charCount) +{ + *charCount = 0; + PWSTR psz = static_cast(::CoTaskMemAlloc(10)); + if (psz != nullptr) + { + *charCount = 5; + *psz = L'\0'; + } + *ppsz = psz; +} + +void TestVoid(_Out_ void **ppv) +{ + *ppv = nullptr; +} + +void TestByte(_Out_ BYTE **ppByte) +{ + *ppByte = nullptr; +} + +struct my_deleter +{ + template + void operator()(T* p) const + { + delete p; + } +}; + +TEST_CASE("WindowsInternalTests::WistdTests", "[resource][wistd]") +{ + wil::unique_handle spHandle; + TestOutHandle(wil::out_param(spHandle)); + + wistd::unique_ptr spInt; + TestOutAlloc(wil::out_param(spInt)); + + std::unique_ptr spIntStd; + TestOutAlloc(wil::out_param(spIntStd)); + + wil::unique_cotaskmem_string spsz0; + size_t count; + TestCoTask(wil::out_param(spsz0), &count); + + std::unique_ptr spsz1; + TestCoTask(wil::out_param(spsz1), &count); + + wistd::unique_ptr spsz2; + TestCoTask(wil::out_param(spsz2), &count); + + wil::unique_cotaskmem_ptr spsz3; + TestCoTask(wil::out_param(spsz3), &count); + + wil::unique_cotaskmem_ptr spv; + TestVoid(wil::out_param(spv)); + + std::unique_ptr spIntStd2; + TestByte(wil::out_param_ptr(spIntStd2)); + + struct Nothing + { + int n; + Nothing(int param) : n(param) {} + void Method() {} + }; + + auto spff = wil::make_unique_failfast(3); + auto sp = wil::make_unique_nothrow(3); + REQUIRE(sp); +#ifdef WIL_ENABLE_EXCEPTIONS + THROW_IF_NULL_ALLOC(sp.get()); + THROW_IF_NULL_ALLOC(sp); +#endif + sp->Method(); + decltype(sp) sp2; + sp2 = wistd::move(sp); + sp2.get(); + + wistd::unique_ptr spConstruct; + wistd::unique_ptr spConstruct2 = nullptr; + spConstruct = nullptr; + wistd::unique_ptr spConstruct3(new int(3)); + my_deleter d; + wistd::unique_ptr spConstruct4(new int(4), d); + wistd::unique_ptr spConstruct5(new int(5), my_deleter()); + wistd::unique_ptr spConstruct6(wistd::unique_ptr(new int(6))); + spConstruct = std::move(spConstruct2); + spConstruct.swap(spConstruct2); + REQUIRE(*spConstruct4 == 4); + spConstruct4.get(); + if (spConstruct4) + { + } + spConstruct.reset(); + spConstruct.release(); + + auto spTooBig = wil::make_unique_nothrow(static_cast(-1)); + REQUIRE_FALSE(spTooBig); + // REQUIRE_FAILFAST_UNSPECIFIED([]{ auto spTooBigFF = wil::make_unique_failfast(static_cast(-1)); }); + + object_counter_state state; + count = 0; + { + object_counter c{ state }; + REQUIRE(state.instance_count() == 1); + + wistd::function fn = [&count, c](int param) + { + count += param; + }; + REQUIRE(state.instance_count() == 2); + + fn(3); + REQUIRE(count == 3); + } + REQUIRE(state.instance_count() == 0); + + count = 0; + { + wistd::function fn; + { + object_counter c{ state }; + REQUIRE(state.instance_count() == 1); + fn = [&count, c](int param) + { + count += param; + }; + REQUIRE(state.instance_count() == 2); + } + REQUIRE(state.instance_count() == 1); + fn(3); + REQUIRE(count == 3); + } + + { + // Size Check -- the current implementation allows for 10 pointers to be passed through the lambda + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12; + (void)a11; (void)a12; + + wistd::function fn = [&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10]() + { + (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; (void)a6; (void)a7; (void)a8; (void)a9; (void)a10; + }; + auto fnCopy = fn; + + // Uncomment to double-check static assert. Reports: + // "The sizeof(wistd::function) has grown too large for the reserved buffer (10 pointers). Refactor to reduce size of the capture." + // wistd::function fn2 = [&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11]() + // { + // a1; a2; a3; a4; a5; a6; a7; a8; a9; a10; a11; + // }; + } +} + +template +void NullptrRaiiTests(lambda_t const &fnCreate) +{ + // nullptr_t construct + test_t var1 = nullptr; // implicit + REQUIRE_FALSE(var1); + test_t var2(nullptr); // explicit + REQUIRE_FALSE(var2); + + // nullptr_t assingment + var1.reset(fnCreate()); + REQUIRE(var1); + var1 = nullptr; + REQUIRE_FALSE(var1); + + // nullptr_t reset + var1.reset(fnCreate()); + REQUIRE(var1); + var1.reset(nullptr); + REQUIRE_FALSE(var1); +} + +template +void ReleaseRaiiTests(lambda_t const &fnCreate) +{ + test_t var1(fnCreate()); + REQUIRE(var1); + auto ptr = var1.release(); + REQUIRE_FALSE(var1); + REQUIRE(ptr != test_t::policy::invalid_value()); + REQUIRE(var1.get() == test_t::policy::invalid_value()); + + var1.reset(ptr); +} + +template +void GetRaiiTests(lambda_t const &fnCreate) +{ + test_t var1; + REQUIRE_FALSE(var1); + REQUIRE(var1.get() == test_t::policy::invalid_value()); + + var1.reset(fnCreate()); + REQUIRE(var1); + REQUIRE(var1.get() != test_t::policy::invalid_value()); +} + +template +void SharedRaiiTests(lambda_t const &fnCreate) +{ + // copy construction + test_t var1(fnCreate()); + REQUIRE(var1); + test_t var2 = var1; // implicit + REQUIRE(var1); + REQUIRE(var2); + test_t var3(var1); // explicit + + // copy assignment + test_t var4(fnCreate()); + test_t var5; + var5 = var4; + REQUIRE(var5); + REQUIRE(var4); + + // r-value construction from unique_ptr + typename test_t::unique_t unique1(fnCreate()); + test_t var7(std::move(unique1)); // explicit + REQUIRE(var7); + REQUIRE_FALSE(unique1); + typename test_t::unique_t unique2(fnCreate()); + test_t var8 = std::move(unique2); // implicit + REQUIRE(var8); + REQUIRE_FALSE(unique2); + + // r-value assignment from unique_ptr + var8.reset(); + REQUIRE_FALSE(var8); + unique2.reset(fnCreate()); + var8 = std::move(unique2); + REQUIRE(var8); + REQUIRE_FALSE(unique2); + + // use_count() + REQUIRE(var8.use_count() == 1); + auto var9 = var8; + REQUIRE(var8.use_count() == 2); +} + +template +void WeakRaiiTests(lambda_t const &fnCreate) +{ + typedef typename test_t::shared_t shared_type; + + // base constructor + test_t weak1; + + // construct from shared + shared_type shared1(fnCreate()); + test_t weak2 = shared1; // implicit + test_t weak3(shared1); // explicit + + // construct from weak + test_t weak4 = weak2; // implicit + test_t weak5(weak2); // explicit + + // assign from weak + weak2 = weak5; + + // assign from shared + weak2 = shared1; + + // reset + weak2.reset(); + REQUIRE_FALSE(weak2.lock()); + + // swap + test_t swap1 = shared1; + test_t swap2; + REQUIRE(swap1.lock()); + REQUIRE_FALSE(swap2.lock()); + swap1.swap(swap2); + REQUIRE_FALSE(swap1.lock()); + REQUIRE(swap2.lock()); + + // expired + REQUIRE_FALSE(swap2.expired()); + shared1.reset(); + REQUIRE(swap2.expired()); + + // lock + shared1.reset(fnCreate()); + weak1 = shared1; + auto shared2 = weak1.lock(); + REQUIRE(shared2); + shared2.reset(); + REQUIRE(weak1.lock()); + shared1.reset(); + shared2 = weak1.lock(); + REQUIRE_FALSE(shared2); +} + +template +void AddressRaiiTests(lambda_t const &fnCreate) +{ + test_t var1(fnCreate()); + REQUIRE(var1); + + &var1; + REQUIRE_FALSE(var1); // the address operator does an auto-release + + *(&var1) = fnCreate(); + REQUIRE(var1); + + var1.put(); + REQUIRE_FALSE(var1); // verify that 'put()' does an auto-release + + *var1.put() = fnCreate(); + REQUIRE(var1); + + REQUIRE(var1.addressof() != nullptr); + REQUIRE(var1); // verify that 'addressof()' does not auto-release +} + +template +void BasicRaiiTests(lambda_t const &fnCreate) +{ + auto invalidHandle = test_t::policy::invalid_value(); + + // no-constructor construction + test_t var1; + REQUIRE_FALSE(var1); + + // construct from a given resource + test_t var2(fnCreate()); // r-value + REQUIRE(var2); + test_t var3(invalidHandle); // l-value + REQUIRE_FALSE(var3); + + // r-value construct from the same type + test_t var4(std::move(var2)); // explicit + REQUIRE(var4); + REQUIRE_FALSE(var2); + test_t varMove(fnCreate()); + test_t var4implicit = std::move(varMove); // implicit + REQUIRE(var4implicit); + + // move assignment + var2 = std::move(var4); + REQUIRE(var2); + REQUIRE_FALSE(var4); + + // swap + var2.swap(var4); + REQUIRE(var4); + REQUIRE_FALSE(var2); + + // explicit bool cast + REQUIRE(static_cast(var4)); + REQUIRE_FALSE(static_cast(var2)); + + // reset + var4.reset(); + REQUIRE_FALSE(var4); + var4.reset(fnCreate()); // r-value + REQUIRE(var4); + var4.reset(invalidHandle); // l-value + REQUIRE_FALSE(var4); +} + +template +void EventRaiiTests() +{ + test_t var1; + var1.create(wil::EventOptions::ManualReset); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + + // SetEvent/ResetEvent + var1.SetEvent(); + REQUIRE(wil::event_is_signaled(var1.get())); + var1.ResetEvent(); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + + // SetEvent/ResetEvent scope_exit + { + auto exit = var1.SetEvent_scope_exit(); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + } + REQUIRE(wil::event_is_signaled(var1.get())); + { + auto exit = var1.ResetEvent_scope_exit(); + REQUIRE(wil::event_is_signaled(var1.get())); + } + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + + // is_signaled + REQUIRE_FALSE(var1.is_signaled()); + + // wait + REQUIRE_FALSE(var1.wait(50)); + + // try_create + bool exists = false; + REQUIRE(var1.try_create(wil::EventOptions::ManualReset, L"wiltestevent", nullptr, &exists)); + REQUIRE_FALSE(exists); + test_t var2; + REQUIRE(var2.try_create(wil::EventOptions::ManualReset, L"wiltestevent", nullptr, &exists)); + REQUIRE(exists); + test_t var3; + REQUIRE_FALSE(var3.try_create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces", nullptr, &exists)); + REQUIRE(::GetLastError() != ERROR_SUCCESS); + + // try_open + test_t var4; + REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces")); + REQUIRE(::GetLastError() != ERROR_SUCCESS); + REQUIRE(var4.try_open(L"wiltestevent")); +} + +void EventTests() +{ + static_assert(sizeof(wil::unique_event_nothrow) == sizeof(HANDLE), "event_t should be sizeof(HANDLE) to allow for raw array utilization"); + + auto fnCreate = []() { return CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, 0); }; + + BasicRaiiTests(fnCreate); + NullptrRaiiTests(fnCreate); + GetRaiiTests(fnCreate); + ReleaseRaiiTests(fnCreate); + AddressRaiiTests(fnCreate); + EventRaiiTests(); + + BasicRaiiTests(fnCreate); + NullptrRaiiTests(fnCreate); + GetRaiiTests(fnCreate); + ReleaseRaiiTests(fnCreate); + AddressRaiiTests(fnCreate); + EventRaiiTests(); + + wil::unique_event_nothrow event4; + REQUIRE(S_OK == event4.create(wil::EventOptions::ManualReset)); + REQUIRE(FAILED(event4.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces"))); + +#ifdef WIL_ENABLE_EXCEPTIONS + static_assert(sizeof(wil::unique_event) == sizeof(HANDLE), "event_t should be sizeof(HANDLE) to allow for raw array utilization"); + + BasicRaiiTests(fnCreate); + NullptrRaiiTests(fnCreate); + GetRaiiTests(fnCreate); + ReleaseRaiiTests(fnCreate); + AddressRaiiTests(fnCreate); + EventRaiiTests(); + + BasicRaiiTests(fnCreate); + NullptrRaiiTests(fnCreate); + GetRaiiTests(fnCreate); + AddressRaiiTests(fnCreate); + SharedRaiiTests(fnCreate); + EventRaiiTests(); + + WeakRaiiTests(fnCreate); + + // explicitly disabled + // wil::unique_event_nothrow event1(wil::EventOptions::ManualReset); + wil::unique_event event2(wil::EventOptions::ManualReset); + wil::shared_event event3(wil::EventOptions::ManualReset); + + event2.create(wil::EventOptions::ManualReset); + REQUIRE(event2); + event3.create(wil::EventOptions::ManualReset); + REQUIRE(event3); + REQUIRE_THROWS(event2.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces") ); + REQUIRE_THROWS(event3.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces") ); + + wil::unique_event var1(wil::EventOptions::ManualReset); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + { + auto autoset = wil::SetEvent_scope_exit(var1.get()); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + REQUIRE(autoset.get() == var1.get()); + // &autoset; // verified disabled + // autoset.addressof(); // verified disabled + } + REQUIRE(wil::event_is_signaled(var1.get())); + { + auto autoreset = wil::ResetEvent_scope_exit(var1.get()); + REQUIRE(wil::event_is_signaled(var1.get())); + autoreset.reset(); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + } + { + auto autoset = wil::SetEvent_scope_exit(var1.get()); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + autoset.release(); + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); + } + REQUIRE_FALSE(wil::event_is_signaled(var1.get())); +#endif +} + +typedef wil::unique_struct unique_prop_variant_no_init; + +void SetPropVariantValue(_In_ int intVal, _Out_ PROPVARIANT* ppropvar) +{ + ppropvar->intVal = intVal; + ppropvar->vt = VT_INT; +} + +template +void TestUniquePropVariant() +{ + { + wil::unique_prop_variant spPropVariant; + REQUIRE(spPropVariant.vt == VT_EMPTY); + } + + // constructor test + { + PROPVARIANT propVariant; + SetPropVariantValue(12, &propVariant); + T spPropVariant(propVariant); + REQUIRE(((spPropVariant.intVal == 12) && (spPropVariant.vt == VT_INT))); + + T spPropVariant2(wistd::move(propVariant)); + REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT))); + + //spPropVariant = propVariant; // deleted function + //spPropVariant = wistd::move(propVariant); // deleted function + //spPropVariant.swap(propVariant); //deleted function + } + + // move constructor + { + T spPropVariant; + SetPropVariantValue(12, &spPropVariant); + REQUIRE(((spPropVariant.intVal == 12) && (spPropVariant.vt == VT_INT))); + + T spPropVariant2(wistd::move(spPropVariant)); + REQUIRE(spPropVariant.vt == VT_EMPTY); + REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT))); + + //T spPropVariant3(spPropVariant); // deleted function + //spPropVariant2 = spPropVariant; // deleted function + } + + // move operator + { + T spPropVariant; + SetPropVariantValue(12, &spPropVariant); + T spPropVariant2 = wistd::move(spPropVariant); + REQUIRE(spPropVariant.vt == VT_EMPTY); + REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT))); + } + + // reset + { + PROPVARIANT propVariant; + SetPropVariantValue(22, &propVariant); + T spPropVariant; + SetPropVariantValue(12, &spPropVariant); + T spPropVariant2; + + //spPropVariant2.reset(spPropVariant); // deleted function + spPropVariant.reset(propVariant); + REQUIRE(spPropVariant.intVal == 22); + REQUIRE(propVariant.intVal == 22); + + spPropVariant.reset(); + REQUIRE(spPropVariant.vt == VT_EMPTY); + } + + // swap + { + T spPropVariant; + SetPropVariantValue(12, &spPropVariant); + T spPropVariant2; + SetPropVariantValue(22, &spPropVariant2); + + spPropVariant.swap(spPropVariant2); + REQUIRE(spPropVariant.intVal == 22); + REQUIRE(spPropVariant2.intVal == 12); + } + + // release, addressof, reset_and_addressof + { + T spPropVariant; + SetPropVariantValue(12, &spPropVariant); + + [](PROPVARIANT* propVariant) + { + REQUIRE(propVariant->vt == VT_EMPTY); + }(spPropVariant.reset_and_addressof()); + + SetPropVariantValue(12, &spPropVariant); + PROPVARIANT* pPropVariant = spPropVariant.addressof(); + REQUIRE(pPropVariant->intVal == 12); + REQUIRE(spPropVariant.intVal == 12); + + PROPVARIANT propVariant = spPropVariant.release(); + REQUIRE(propVariant.intVal == 12); + REQUIRE(spPropVariant.vt == VT_EMPTY); + } +} + +TEST_CASE("WindowsInternalTests::ResourceTemplateTests", "[resource]") +{ + EventTests(); + TestUniquePropVariant(); + TestUniquePropVariant(); +} + +inline unsigned long long ToInt64(const FILETIME &ft) +{ + return (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; +} + +inline FILETIME FromInt64(unsigned long long i64) +{ + FILETIME ft = { static_cast(i64), static_cast(i64 >> 32) }; + return ft; +} + +TEST_CASE("WindowsInternalTests::Win32HelperTests", "[win32_helpers]") +{ + auto systemTime = wil::filetime::get_system_time(); + REQUIRE(ToInt64(systemTime) == wil::filetime::to_int64(systemTime)); + auto systemTime64 = wil::filetime::to_int64(systemTime); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + auto ft1 = FromInt64(systemTime64); + auto ft2 = wil::filetime::from_int64(systemTime64); + REQUIRE(CompareFileTime(&ft1, &ft2) == 0); +#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ + + REQUIRE(systemTime64 == wil::filetime::to_int64(wil::filetime::from_int64(systemTime64))); + REQUIRE((systemTime64 + wil::filetime_duration::one_hour) == (systemTime64 + (wil::filetime_duration::one_minute * 60))); + auto systemTimePlusOneHour = wil::filetime::add(systemTime, wil::filetime_duration::one_hour); + auto systemTimePlusOneHour64 = wil::filetime::to_int64(systemTimePlusOneHour); + REQUIRE(systemTimePlusOneHour64 == (systemTime64 + wil::filetime_duration::one_hour)); +} + +TEST_CASE("WindowsInternalTests::InitOnceNonTests") +{ + bool called = false; + bool winner = false; + INIT_ONCE init{}; + REQUIRE_FALSE(wil::init_once_initialized(init)); + + // Call, but fail. Should transport the HRESULT back, but mark us as not the winner + called = false; + winner = false; + REQUIRE(E_FAIL == wil::init_once_nothrow(init, [&] { called = true; return E_FAIL; }, &winner)); + REQUIRE_FALSE(wil::init_once_initialized(init)); + REQUIRE(called); + REQUIRE_FALSE(winner); + + // Call, succeed. Should mark us as the winner. + called = false; + winner = false; + REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = true; return S_OK; }, &winner)); + REQUIRE(wil::init_once_initialized(init)); + REQUIRE(called); + REQUIRE(winner); + + // Call again. Should not actually be invoked and should not be the winner + called = false; + winner = false; + REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = false; return S_OK; }, &winner)); + REQUIRE(wil::init_once_initialized(init)); + REQUIRE_FALSE(called); + REQUIRE_FALSE(winner); + + // Call again. Still not invoked, but we don't care if we're the winner + called = false; + REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = false; return S_OK; })); + REQUIRE(wil::init_once_initialized(init)); + REQUIRE_FALSE(called); + +#ifdef WIL_ENABLE_EXCEPTIONS + called = false; + winner = false; + init = {}; + + // A thrown exception leaves the object un-initialized + REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; throw wil::ResultException(E_FAIL); }), wil::ResultException); + REQUIRE_FALSE(wil::init_once_initialized(init)); + REQUIRE(called); + REQUIRE_FALSE(winner); + + // Success! + called = false; + winner = false; + REQUIRE_NOTHROW(winner = wil::init_once(init, [&] { called = true; })); + REQUIRE(wil::init_once_initialized(init)); + REQUIRE(called); + REQUIRE(winner); + + // No-op success! + called = false; + winner = false; + REQUIRE_NOTHROW(winner = wil::init_once(init, [&] { called = true; })); + REQUIRE(wil::init_once_initialized(init)); + REQUIRE_FALSE(called); + REQUIRE_FALSE(winner); +#endif // WIL_ENABLE_EXCEPTIONS +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +TEST_CASE("WindowsInternalTests::TestUniquePointerCases", "[resource][unique_any]") +{ + // wil::unique_process_heap_ptr tests + { + wil::unique_process_heap_ptr empty; // null case + } + { + wil::unique_process_heap_ptr heapMemory(::HeapAlloc(::GetProcessHeap(), 0, 100)); + REQUIRE(static_cast(heapMemory)); + } + + // wil::unique_cotaskmem_ptr tests + { + wil::unique_cotaskmem_ptr empty; // null case + } + { + wil::unique_cotaskmem_ptr cotaskmemMemory(CoTaskMemAlloc(100)); + REQUIRE(static_cast(cotaskmemMemory)); + } + { + auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow(42); + REQUIRE(static_cast(cotaskmemMemory)); + REQUIRE(*cotaskmemMemory == static_cast(42)); + } + { + struct S { size_t s; S() : s(42) {} }; + auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow(); + REQUIRE(static_cast(cotaskmemMemory)); + REQUIRE(cotaskmemMemory->s == static_cast(42)); + } + { + auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow(12); + REQUIRE(static_cast(cotaskmemArrayMemory)); + } + { + struct S { size_t s; S() : s(42) {} }; + const size_t size = 12; + auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow(size); + REQUIRE(static_cast(cotaskmemArrayMemory)); + bool verified = true; + for (auto& elem : wil::make_range(cotaskmemArrayMemory.get(), size)) if (elem.s != 42) verified = false; + REQUIRE(verified); + } + + // wil::unique_cotaskmem_secure_ptr tests + { + wil::unique_cotaskmem_secure_ptr empty; // null case + } + { + wil::unique_cotaskmem_secure_ptr cotaskmemMemory(CoTaskMemAlloc(100)); + REQUIRE(static_cast(cotaskmemMemory)); + } + { + auto cotaskmemMemory = wil::make_unique_cotaskmem_secure_nothrow(42); + REQUIRE(static_cast(cotaskmemMemory)); + REQUIRE(*cotaskmemMemory == static_cast(42)); + } + { + struct S { size_t s; S() : s(42) {} }; + auto cotaskmemMemory = wil::make_unique_cotaskmem_secure_nothrow(); + REQUIRE(static_cast(cotaskmemMemory)); + REQUIRE(cotaskmemMemory->s == static_cast(42)); + } + { + auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_secure_nothrow(12); + REQUIRE(static_cast(cotaskmemArrayMemory)); + } + { + struct S { size_t s; S() : s(42) {} }; + const size_t size = 12; + auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_secure_nothrow(size); + REQUIRE(static_cast(cotaskmemArrayMemory)); + bool verified = true; + for (auto& elem : wil::make_range(cotaskmemArrayMemory.get(), size)) if (elem.s != 42) verified = false; + REQUIRE(verified); + } + + // wil::unique_hlocal_ptr tests + { + wil::unique_hlocal_ptr empty; // null case + } + { + wil::unique_hlocal_ptr localMemory(LocalAlloc(LPTR, 100)); + REQUIRE(static_cast(localMemory)); + } + { + auto localMemory = wil::make_unique_hlocal_nothrow(42); + REQUIRE(static_cast(localMemory)); + REQUIRE(*localMemory == static_cast(42)); + } + { + struct S { size_t s; S() : s(42) {} }; + auto localMemory = wil::make_unique_hlocal_nothrow(); + REQUIRE(static_cast(localMemory)); + REQUIRE(localMemory->s == static_cast(42)); + } + { + auto localArrayMemory = wil::make_unique_hlocal_nothrow(12); + REQUIRE(static_cast(localArrayMemory)); + } + { + struct S { size_t s; S() : s(42) {} }; + const size_t size = 12; + auto localArrayMemory = wil::make_unique_hlocal_nothrow(size); + REQUIRE(static_cast(localArrayMemory)); + bool verified = true; + for (auto& elem : wil::make_range(localArrayMemory.get(), size)) if (elem.s != 42) verified = false; + REQUIRE(verified); + } + + // wil::unique_hlocal_secure_ptr tests + { + wil::unique_hlocal_secure_ptr empty; // null case + } + { + wil::unique_hlocal_secure_ptr localMemory(LocalAlloc(LPTR, 100)); + REQUIRE(static_cast(localMemory)); + } + { + auto localMemory = wil::make_unique_hlocal_secure_nothrow(42); + REQUIRE(static_cast(localMemory)); + REQUIRE(*localMemory == static_cast(42)); + } + { + struct S { size_t s; S() : s(42) {} }; + auto localMemory = wil::make_unique_hlocal_secure_nothrow(); + REQUIRE(static_cast(localMemory)); + REQUIRE(localMemory->s == static_cast(42)); + } + { + auto localArrayMemory = wil::make_unique_hlocal_secure_nothrow(12); + REQUIRE(static_cast(localArrayMemory)); + } + { + struct S { size_t s; S() : s(42) {} }; + const size_t size = 12; + auto localArrayMemory = wil::make_unique_hlocal_secure_nothrow(size); + REQUIRE(static_cast(localArrayMemory)); + bool verified = true; + for (auto& elem : wil::make_range(localArrayMemory.get(), size)) if (elem.s != 42) verified = false; + REQUIRE(verified); + } + + // wil::unique_hglobal_ptr tests + { + wil::unique_hglobal_ptr empty; // null case + } + { + wil::unique_hglobal_ptr globalMemory(GlobalAlloc(GPTR, 100)); + REQUIRE(static_cast(globalMemory)); + } + + { + // The following uses are blocked due to a static assert failure + + //struct S { ~S() {} }; + + //auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow(); + //auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow(1); + //auto cotaskmemMemory2 = wil::make_unique_cotaskmem_secure_nothrow(); + //auto cotaskmemArrayMemory2 = wil::make_unique_cotaskmem_secure_nothrow(1); + + //auto localMemory = wil::make_unique_hlocal_nothrow(); + //auto localArrayMemory = wil::make_unique_hlocal_nothrow(1); + //auto localMemory2 = wil::make_unique_hlocal_secure_nothrow(); + //auto localArrayMemory2 = wil::make_unique_hlocal_secure_nothrow(1); + } +} +#endif + +void GetDWORDArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) DWORD** numbers) +{ + const size_t size = 5; + auto ptr = static_cast(::CoTaskMemAlloc(sizeof(DWORD) * size)); + REQUIRE(ptr); + ::ZeroMemory(ptr, sizeof(DWORD) * size); + *numbers = ptr; + *count = size; +} + +void GetHSTRINGArray(_Out_ ULONG* count, _Outptr_result_buffer_(*count) HSTRING** strings) +{ + const size_t size = 5; + auto ptr = static_cast(::CoTaskMemAlloc(sizeof(HSTRING) * size)); + REQUIRE(ptr); + for (UINT i = 0; i < size; ++i) + { + REQUIRE_SUCCEEDED(WindowsCreateString(L"test", static_cast(wcslen(L"test")), &ptr[i])); + } + *strings = ptr; + *count = static_cast(size); +} + +void GetPOINTArray(_Out_ UINT32* count, _Outptr_result_buffer_(*count) POINT** points) +{ + const size_t size = 5; + auto ptr = static_cast(::CoTaskMemAlloc(sizeof(POINT) * size)); + REQUIRE(ptr); + for (UINT i = 0; i < size; ++i) + { + ptr[i].x = ptr[i].y = i; + } + *points = ptr; + *count = static_cast(size); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +void GetHANDLEArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) HANDLE** events) +{ + const size_t size = 5; + HANDLE* ptr = reinterpret_cast(::CoTaskMemAlloc(sizeof(HANDLE) * size)); + for (auto& val : wil::make_range(ptr, size)) + { + val = wil::unique_event(wil::EventOptions::ManualReset).release(); + } + *events = ptr; + *count = size; +} +#endif + +interface __declspec(uuid("EDCA4ADC-DF46-442A-A69D-FDFD8BC37B31")) IFakeObject : public IUnknown +{ + STDMETHOD_(void, DoStuff)() = 0; +}; + +class ArrayTestObject : witest::AllocatedObject, + public Microsoft::WRL::RuntimeClass, IFakeObject> +{ +public: + HRESULT RuntimeClassInitialize(UINT n) { m_number = n; return S_OK; }; + STDMETHOD_(void, DoStuff)() {} +private: + UINT m_number{}; +}; + +void GetUnknownArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) IFakeObject*** objects) +{ + const size_t size = 5; + auto ptr = reinterpret_cast(::CoTaskMemAlloc(sizeof(IFakeObject*) * size)); + REQUIRE(ptr); + for (UINT i = 0; i < size; ++i) + { + Microsoft::WRL::ComPtr obj; + REQUIRE_SUCCEEDED(Microsoft::WRL::MakeAndInitialize(&obj, i)); + ptr[i] = obj.Detach(); + } + *objects = ptr; + *count = size; +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +TEST_CASE("WindowsInternalTests::TestUniqueArrayCases", "[resource]") +{ + // wil::unique_cotaskmem_array_ptr tests + { + wil::unique_cotaskmem_array_ptr values; + GetDWORDArray(values.size_address(), &values); + } + { + wil::unique_cotaskmem_array_ptr strings; + GetHSTRINGArray(strings.size_address(), &strings); + for (ULONG i = 0; i < strings.size(); ++i) + { + REQUIRE(WindowsGetStringLen(strings[i]) == wcslen(L"test")); + } + } + { + wil::unique_cotaskmem_array_ptr points; + GetPOINTArray(points.size_address(), &points); + for (ULONG i = 0; i < points.size(); ++i) + { + REQUIRE((ULONG)points[i].x == i); + } + } +#ifdef WIL_ENABLE_EXCEPTIONS + { + wil::unique_cotaskmem_array_ptr events; + GetHANDLEArray(events.size_address(), &events); + } + { + wil::unique_cotaskmem_array_ptr> objects; + GetUnknownArray(objects.size_address(), &objects); + for (ULONG i = 0; i < objects.size(); ++i) + { + objects[i]->DoStuff(); + } + } +#endif + { + wil::unique_cotaskmem_array_ptr values = nullptr; + REQUIRE(!values); + REQUIRE(values.size() == 0); + + // move onto self + values = wistd::move(values); + REQUIRE(!values); + + // fetch + GetDWORDArray(values.size_address(), &values); + REQUIRE(!!values); + REQUIRE(values.size() > 0); + REQUIRE(!values.empty()); + + // move onto self + values = wistd::move(values); + REQUIRE(!!values); + + decltype(values) values2(wistd::move(values)); + REQUIRE(!values); + REQUIRE(!!values2); + REQUIRE(values2.size() > 0); + + values = wistd::move(values2); + REQUIRE(!!values); + REQUIRE(!values2); + + values = nullptr; + REQUIRE(!values); + GetDWORDArray(values.size_address(), values.put()); + REQUIRE(!!values); + + values = nullptr; + REQUIRE(!values); + GetDWORDArray(values.size_address(), &values); + REQUIRE(!!values); + + auto size = values.size(); + auto ptr = values.release(); + + REQUIRE(!values); + REQUIRE(values.empty()); + + decltype(values) values3(ptr, size); + REQUIRE(!!values3); + REQUIRE(values3.size() == size); + + values3.swap(values); + REQUIRE(!!values); + REQUIRE(!values.empty()); + REQUIRE(!values3); + REQUIRE(values3.empty()); + + REQUIRE(!values.empty()); + size_t count = 0; + for (auto it = values.begin(); it != values.end(); ++it) + { + ++count; + } + REQUIRE(count == values.size()); + + count = 0; + for (auto it = values.cbegin(); it != values.cend(); ++it) + { + ++count; + } + REQUIRE(count == values.size()); + + for (size_t index = 0; index < values.size(); index++) + { + auto& val = values[index]; + REQUIRE(val == 0); + } + + auto& front = values.front(); + REQUIRE(front == 0); + auto& back = values.back(); + REQUIRE(back == 0); + + [](const wil::unique_cotaskmem_array_ptr& cvalues) + { + size_t count = 0; + for (auto it = cvalues.begin(); it != cvalues.end(); ++it) + { + ++count; + } + REQUIRE(count == cvalues.size()); + for (size_t index = 0; index < cvalues.size(); index++) + { + auto& val = cvalues[index]; + REQUIRE(val == 0); + } + + auto& front = cvalues.front(); + REQUIRE(front == 0); + auto& back = cvalues.back(); + REQUIRE(back == 0); + + REQUIRE(cvalues.data() != nullptr); + }(values); + + auto data1 = values.data(); + auto data2 = values.get(); + REQUIRE((data1 && (data1 == data2))); + + values.reset(); + REQUIRE(!values); + REQUIRE(values.empty()); + + GetDWORDArray(values2.size_address(), &values2); + size = values2.size(); + ptr = values2.release(); + + values.reset(ptr, size); + REQUIRE(!!values); + REQUIRE(!values.empty()); + + REQUIRE(values2.put() == values2.addressof()); + REQUIRE(&values2 == values2.addressof()); + } +} +#endif + +#ifndef __cplusplus_winrt +TEST_CASE("WindowsInternalTests::VerifyMakeAgileCallback", "[wrl]") +{ + using namespace ABI::Windows::Foundation; + + class CallbackClient + { + public: + HRESULT On(IMemoryBufferReference*, IInspectable*) + { + return S_OK; + } + }; + CallbackClient callbackClient; + +#ifdef WIL_ENABLE_EXCEPTIONS + auto cbAgile = wil::MakeAgileCallback>([](IMemoryBufferReference*, IInspectable*) -> HRESULT + { + return S_OK; + }); + REQUIRE(wil::is_agile(cbAgile)); + + auto cbAgileWithMember = wil::MakeAgileCallback>(&callbackClient, &CallbackClient::On); + REQUIRE(wil::is_agile(cbAgileWithMember)); +#endif + auto cbAgileNoThrow = wil::MakeAgileCallbackNoThrow>([](IMemoryBufferReference*, IInspectable*) -> HRESULT + { + return S_OK; + }); + REQUIRE(wil::is_agile(cbAgileNoThrow)); + + auto cbAgileWithMemberNoThrow = wil::MakeAgileCallbackNoThrow>(&callbackClient, &CallbackClient::On); + REQUIRE(wil::is_agile(cbAgileWithMemberNoThrow)); +} +#endif + +TEST_CASE("WindowsInternalTests::Ranges", "[common]") +{ + { + int things[10]{}; + unsigned int count = 0; + for (auto& m : wil::make_range(things, ARRAYSIZE(things))) + { + ++count; + m = 1; + } + REQUIRE(ARRAYSIZE(things) == count); + REQUIRE(1 == things[1]); + } + + { + int things[10]{}; + unsigned int count = 0; + for (auto m : wil::make_range(things, ARRAYSIZE(things))) + { + ++count; + m = 1; + } + REQUIRE(ARRAYSIZE(things) == count); + REQUIRE(0 == things[0]); + } + + { + int things[10]{}; + unsigned int count = 0; + auto range = wil::make_range(things, ARRAYSIZE(things)); + for (auto m : range) + { + (void)m; + ++count; + } + REQUIRE(ARRAYSIZE(things) == count); + } + + { + int things[10]{}; + unsigned int count = 0; + const auto range = wil::make_range(things, ARRAYSIZE(things)); + for (auto m : range) + { + (void)m; + ++count; + } + REQUIRE(ARRAYSIZE(things) == count); + } +} + +TEST_CASE("WindowsInternalTests::HStringTests", "[resource][unique_any]") +{ + const wchar_t kittens[] = L"kittens"; + + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + REQUIRE_SUCCEEDED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer)); + REQUIRE_SUCCEEDED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens)); + + // Promote sets the promoted-to value but resets theBuffer + wil::unique_hstring promoted; + REQUIRE_SUCCEEDED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), &promoted)); + REQUIRE(static_cast(promoted)); + REQUIRE_FALSE(static_cast(theBuffer)); + } + + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + REQUIRE_SUCCEEDED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer)); + REQUIRE_SUCCEEDED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens)); + + // Failure to promote retains the buffer state + REQUIRE_FAILED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), nullptr)); + REQUIRE(static_cast(theBuffer)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + THROW_IF_FAILED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer)); + THROW_IF_FAILED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens)); + + wil::unique_hstring promoted; + REQUIRE_NOTHROW(promoted = wil::make_hstring_from_buffer(wistd::move(theBuffer))); + REQUIRE(static_cast(promoted)); + REQUIRE_FALSE(static_cast(theBuffer)); + } +#endif +} + +struct ThreadPoolWaitTestContext +{ + volatile LONG Counter = 0; + wil::unique_event_nothrow Event; +}; + +static void __stdcall ThreadPoolWaitTestCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*instance*/, + _Inout_opt_ void* context, + _Inout_ PTP_WAIT wait, + _In_ TP_WAIT_RESULT /*waitResult*/) +{ + ThreadPoolWaitTestContext& myContext = *reinterpret_cast(context); + SetThreadpoolWait(wait, myContext.Event.get(), nullptr); + ::InterlockedIncrement(&myContext.Counter); +} + +template +void ThreadPoolWaitTestHelper(bool requireExactCallbackCount) +{ + ThreadPoolWaitTestContext myContext; + REQUIRE_SUCCEEDED(myContext.Event.create()); + + WaitResourceT wait; + wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, NULL)); + REQUIRE(wait); + + SetThreadpoolWait(wait.get(), myContext.Event.get(), nullptr); + + const int loopCount = 5; + for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount) + { + // Signal event. + myContext.Event.SetEvent(); + + // Wait until 'myContext.Counter' increments by 1. + for (int itr = 0; itr != 50 && currCallbackCount == myContext.Counter; ++itr) + { + Sleep(10); + } + + // Ensure we didn't timeout + REQUIRE(currCallbackCount + 1 == myContext.Counter); + } + + // Signal one last event. + myContext.Event.SetEvent(); + + // Close thread-pool wait. + wait.reset(); + myContext.Event.reset(); + + // Verify counter. + if (requireExactCallbackCount) + { + REQUIRE(loopCount + 1 == myContext.Counter); + } + else + { + REQUIRE((loopCount + 1 == myContext.Counter || loopCount == myContext.Counter)); + } +} + +TEST_CASE("WindowsInternalTests::ThreadPoolWaitTest", "[resource][unique_threadpool_wait]") +{ + ThreadPoolWaitTestHelper(false); + ThreadPoolWaitTestHelper(true); +} + +struct ThreadPoolWaitWorkContext +{ + volatile LONG Counter = 0; +}; + +static void __stdcall ThreadPoolWaitWorkCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*instance*/, + _Inout_opt_ void* context, + _Inout_ PTP_WORK /*work*/) +{ + ThreadPoolWaitWorkContext& myContext = *reinterpret_cast(context); + ::InterlockedIncrement(&myContext.Counter); +} + +template +void ThreadPoolWaitWorkHelper(bool requireExactCallbackCount) +{ + ThreadPoolWaitWorkContext myContext; + + WaitResourceT work; + work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, NULL)); + REQUIRE(work); + + const int loopCount = 5; + for (int itr = 0; itr != loopCount; ++itr) + { + SubmitThreadpoolWork(work.get()); + } + + work.reset(); + + if (requireExactCallbackCount) + { + REQUIRE(loopCount == myContext.Counter); + } + else + { + REQUIRE(loopCount >= myContext.Counter); + } +} + +TEST_CASE("WindowsInternalTests::ThreadPoolWorkTest", "[resource][unique_threadpool_work]") +{ + ThreadPoolWaitWorkHelper(false); + ThreadPoolWaitWorkHelper(true); +} + +struct ThreadPoolTimerWorkContext +{ + volatile LONG Counter = 0; + wil::unique_event_nothrow Event; +}; + +static void __stdcall ThreadPoolTimerWorkCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*instance*/, + _Inout_opt_ void* context, + _Inout_ PTP_TIMER /*timer*/) +{ + ThreadPoolTimerWorkContext& myContext = *reinterpret_cast(context); + myContext.Event.SetEvent(); + ::InterlockedIncrement(&myContext.Counter); +} + +template +void ThreadPoolTimerWorkHelper(SetThreadpoolTimerT const &setThreadpoolTimerFn, bool requireExactCallbackCount) +{ + ThreadPoolTimerWorkContext myContext; + REQUIRE_SUCCEEDED(myContext.Event.create()); + + TimerResourceT timer; + timer.reset(CreateThreadpoolTimer(ThreadPoolTimerWorkCallback, &myContext, nullptr)); + REQUIRE(timer); + + const int loopCount = 5; + for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount) + { + // Schedule timer + myContext.Event.ResetEvent(); + const auto allowedWindow = 0; + LONGLONG dueTime = -5 * 10000I64; // 5ms + setThreadpoolTimerFn(timer.get(), reinterpret_cast(&dueTime), 0, allowedWindow); + + // Wait until 'myContext.Counter' increments by 1. + REQUIRE(myContext.Event.wait(500)); + for (int itr = 0; itr != 50 && currCallbackCount == myContext.Counter; ++itr) + { + Sleep(10); + } + + // Ensure we didn't timeout + REQUIRE(currCallbackCount + 1 == myContext.Counter); + } + + // Schedule one last timer. + myContext.Event.ResetEvent(); + const auto allowedWindow = 0; + LONGLONG dueTime = -5 * 10000I64; // 5ms + setThreadpoolTimerFn(timer.get(), reinterpret_cast(&dueTime), 0, allowedWindow); + + if (requireExactCallbackCount) + { + // Wait for the event to be set + REQUIRE(myContext.Event.wait(500)); + } + + // Close timer. + timer.reset(); + myContext.Event.reset(); + + // Verify counter. + if (requireExactCallbackCount) + { + REQUIRE(loopCount + 1 == myContext.Counter); + } + else + { + REQUIRE((loopCount + 1 == myContext.Counter || loopCount == myContext.Counter)); + } +} + +TEST_CASE("WindowsInternalTests::ThreadPoolTimerTest", "[resource][unique_threadpool_timer]") +{ + static_assert(sizeof(FILETIME) == sizeof(LONGLONG), "FILETIME and LONGLONG must be same size"); + ThreadPoolTimerWorkHelper(SetThreadpoolTimer, false); + ThreadPoolTimerWorkHelper(SetThreadpoolTimer, true); +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +static void __stdcall SlimEventTrollCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*instance*/, + _Inout_opt_ void* context, + _Inout_ PTP_TIMER /*timer*/) +{ + auto event = reinterpret_cast(context); + + // Wake up the thread without setting the event. + // Note: This relies on the fact that the 'wil::slim_event' class only has a single member variable. + WakeByAddressAll(event); +} + +static void __stdcall SlimEventFriendlyCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*instance*/, + _Inout_opt_ void* context, + _Inout_ PTP_TIMER /*timer*/) +{ + auto event = reinterpret_cast(context); + event->SetEvent(); +} + +TEST_CASE("WindowsInternalTests::SlimEventTests", "[resource][slim_event]") +{ + { + wil::slim_event event; + + // Verify simple timeouts work on an auto-reset event. + REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 0)); + REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 10)); + + wil::unique_threadpool_timer trollTimer(CreateThreadpoolTimer(SlimEventTrollCallback, &event, nullptr)); + REQUIRE(trollTimer); + + FILETIME trollDueTime = wil::filetime::from_int64(0); + SetThreadpoolTimer(trollTimer.get(), &trollDueTime, /*period(ms)*/ 5, /*window(ms)*/ 0); + + // Ensure we timeout in spite of being constantly woken up unnecessarily. + REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 100)); + + wil::unique_threadpool_timer friendlyTimer(CreateThreadpoolTimer(SlimEventFriendlyCallback, &event, nullptr)); + REQUIRE(friendlyTimer); + + FILETIME friendlyDueTime = wil::filetime::from_int64(UINT64(-100 * wil::filetime_duration::one_millisecond)); // 100ms (relative to now) + SetThreadpoolTimer(friendlyTimer.get(), &friendlyDueTime, /*period(ms)*/ 0, /*window(ms)*/ 0); + + // Now that the 'friendlyTimer' is queued, we should succeed. + REQUIRE(event.wait(INFINITE)); + + // Ensure event is auto-reset. + REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 100)); + } + + { + wil::slim_event_manual_reset manualResetEvent; + + // Verify simple timeouts work on a manual-reset event. + REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 0)); + REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 10)); + + // Ensure multiple waits can occur on a manual-reset event. + manualResetEvent.SetEvent(); + REQUIRE(manualResetEvent.wait()); + REQUIRE(manualResetEvent.wait(/*timeout(ms)*/ 100)); + REQUIRE(manualResetEvent.wait(INFINITE)); + + // Verify 'ResetEvent' works. + manualResetEvent.ResetEvent(); + REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 10)); + } + +} +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +struct ConditionVariableCSCallbackContext +{ + wil::condition_variable event; + wil::critical_section lock; + auto acquire() { return lock.lock(); } +}; + +struct ConditionVariableSRWCallbackContext +{ + wil::condition_variable event; + wil::srwlock lock; + auto acquire() { return lock.lock_exclusive(); } +}; + +template +static void __stdcall ConditionVariableCallback( + _Inout_ PTP_CALLBACK_INSTANCE /*Instance*/, + _In_ void* Context) +{ + auto callbackContext = reinterpret_cast(Context); + + // Acquire the lock to ensure we don't notify the condition variable before the other thread has + // gone to sleep. + auto gate = callbackContext->acquire(); + + // Signal the condition variable. + callbackContext->event.notify_all(); +} + +// A quick sanity check of the 'wil::condition_variable' type. +TEST_CASE("WindowsInternalTests::ConditionVariableTests", "[resource][condition_variable]") +{ + SECTION("Test 'wil::condition_variable' with 'wil::critical_section'") + { + ConditionVariableCSCallbackContext callbackContext; + auto gate = callbackContext.lock.lock(); + + // Schedule the thread that will wake up this thread. + REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback, &callbackContext, nullptr)); + + // Wait on the condition variable. + REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500)); + } + + SECTION("Test 'wil::condition_variable' with 'wil::srwlock'") + { + ConditionVariableSRWCallbackContext callbackContext; + + // Test exclusive lock. + { + auto gate = callbackContext.lock.lock_exclusive(); + + // Schedule the thread that will wake up this thread. + REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback, &callbackContext, nullptr)); + + // Wait on the condition variable. + REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500)); + } + + // Test shared lock. + { + auto gate = callbackContext.lock.lock_shared(); + + // Schedule the thread that will wake up this thread. + REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback, &callbackContext, nullptr)); + + // Wait on the condition variable. + REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500)); + } + } +} + +TEST_CASE("WindowsInternalTests::ReturnWithExpectedTests", "[result_macros]") +{ + wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback; + + // Succeeded + REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_FAILED_WITH_EXPECTED(MDEC(hrOKRef()), E_UNEXPECTED); return S_OK; }); + + // Expected + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_FAIL); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_UNEXPECTED, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG); return S_OK; }); + + // Unexpected + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED); return S_OK; }); + REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG); return S_OK; }); +} + +TEST_CASE("WindowsInternalTests::LogWithExpectedTests", "[result_macros]") +{ + wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback; + + // Succeeded + REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED_WITH_EXPECTED(MDEC(hrOKRef()), E_FAIL, E_INVALIDARG)); }); + + // Expected + REQUIRE_LOG(S_OK, [] { REQUIRE(E_UNEXPECTED == LOG_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_UNEXPECTED, E_INVALIDARG)); }); + REQUIRE_LOG(S_OK, [] { REQUIRE(E_UNEXPECTED == LOG_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG)); }); + + // Unexpected + REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED)); }); + REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG)); }); +} + +// Verifies that the shutdown-aware objects respect the alignment +// of the wrapped object. +template class Wrapper> +void VerifyAlignment() +{ + // Some of the wrappers require a method called ProcessShutdown(), so we'll give it one. + struct alignment_sensitive_struct + { + // Use SLIST_HEADER as our poster child alignment-sensitive data type. + SLIST_HEADER value; + void ProcessShutdown() { } + }; + static_assert(alignof(alignment_sensitive_struct) != alignof(char), "Need to choose a better alignment-sensitive type"); + + // Create a custom structure that tries to force misalignment. + struct attempted_misalignment + { + char c; + Wrapper wrapper; + } possibly_misaligned{}; + + static_assert(alignof(attempted_misalignment) == alignof(alignment_sensitive_struct), "Wrapper type does not respect alignment"); + + // Verify that the wrapper type placed the inner object at proper alignment. + // Note: use std::addressof in case the alignment_sensitive_struct overrides the & operator. + REQUIRE(reinterpret_cast(std::addressof(possibly_misaligned.wrapper.get())) % alignof(alignment_sensitive_struct) == 0); +} + +TEST_CASE("WindowsInternalTests::ShutdownAwareObjectAlignmentTests", "[result_macros]") +{ + VerifyAlignment(); + VerifyAlignment(); + VerifyAlignment(); +} + +#pragma warning(pop) diff --git a/Externals/WIL/tests/workarounds/readme.md b/Externals/WIL/tests/workarounds/readme.md new file mode 100644 index 0000000000..d2f248f6af --- /dev/null +++ b/Externals/WIL/tests/workarounds/readme.md @@ -0,0 +1,2 @@ + +We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first. diff --git a/Externals/WIL/tests/workarounds/wrl/wrl/async.h b/Externals/WIL/tests/workarounds/wrl/wrl/async.h new file mode 100644 index 0000000000..ac8296a27e --- /dev/null +++ b/Externals/WIL/tests/workarounds/wrl/wrl/async.h @@ -0,0 +1,1356 @@ +// +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// Code in details namespace is for internal usage within the library code +// +#ifndef _WRL_ASYNC_H_ +#define _WRL_ASYNC_H_ + +#ifdef _MSC_VER +#pragma once +#endif // _MSC_VER + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpragma-pack" +#pragma clang diagnostic ignored "-Wignored-qualifiers" +#pragma clang diagnostic ignored "-Wextra-tokens" +#pragma clang diagnostic ignored "-Wreorder" +#endif + +#include +#include +#include + +#include +#include +#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CDiagnostics_CITracingStatusChangedEventArgs_FWD_DEFINED__) +namespace ABI { +namespace Windows { +namespace Foundation { +typedef ::Windows::Foundation::IAsyncActionCompletedHandler IAsyncActionCompletedHandler; +namespace Diagnostics { +typedef ::Windows::Foundation::Diagnostics::CausalitySource CausalitySource; +typedef ::Windows::Foundation::Diagnostics::IAsyncCausalityTracerStatics IAsyncCausalityTracerStatics; +typedef ::Windows::Foundation::Diagnostics::TracingStatusChangedEventArgs TracingStatusChangedEventArgs; +typedef ::Windows::Foundation::Diagnostics::ITracingStatusChangedEventArgs ITracingStatusChangedEventArgs; + +typedef ::Windows::Foundation::Diagnostics::CausalityTraceLevel CausalityTraceLevel; +const ::Windows::Foundation::Diagnostics::CausalityTraceLevel CausalityTraceLevel_Verbose = ::Windows::Foundation::Diagnostics::CausalityTraceLevel_Verbose; +const ::Windows::Foundation::Diagnostics::CausalityTraceLevel CausalityTraceLevel_Important = ::Windows::Foundation::Diagnostics::CausalityTraceLevel_Important; +const ::Windows::Foundation::Diagnostics::CausalityTraceLevel CausalityTraceLevel_Required = ::Windows::Foundation::Diagnostics::CausalityTraceLevel_Required; + +const ::Windows::Foundation::Diagnostics::CausalityRelation CausalityRelation_Join = ::Windows::Foundation::Diagnostics::CausalityRelation_Join; +const ::Windows::Foundation::Diagnostics::CausalityRelation CausalityRelation_Choice = ::Windows::Foundation::Diagnostics::CausalityRelation_Choice; +const ::Windows::Foundation::Diagnostics::CausalityRelation CausalityRelation_Error = ::Windows::Foundation::Diagnostics::CausalityRelation_Error; +const ::Windows::Foundation::Diagnostics::CausalityRelation CausalityRelation_Cancel = ::Windows::Foundation::Diagnostics::CausalityRelation_Cancel; +const ::Windows::Foundation::Diagnostics::CausalityRelation CausalityRelation_AssignDelegate = ::Windows::Foundation::Diagnostics::CausalityRelation_AssignDelegate ; + +const ::Windows::Foundation::Diagnostics::CausalitySynchronousWork CausalitySynchronousWork_CompletionNotification = ::Windows::Foundation::Diagnostics::CausalitySynchronousWork_CompletionNotification; +const ::Windows::Foundation::Diagnostics::CausalitySynchronousWork CausalitySynchronousWork_ProgressNotification = ::Windows::Foundation::Diagnostics::CausalitySynchronousWork_ProgressNotification; +const ::Windows::Foundation::Diagnostics::CausalitySynchronousWork CausalitySynchronousWork_Execution = ::Windows::Foundation::Diagnostics::CausalitySynchronousWork_Execution; +} +} +} +} +#endif + +#include +#include +#include +#include +#include + +#include + +// Set packing +#include + +#pragma warning(push) +// nonstandard extension used: override specifier 'override' +#pragma warning(disable: 4481) + +// GUID identifying the the Windows Platform for logging purposes +// {C54C95D9-5B6E-41E9-A28D-2DD68F94B500} +extern _declspec(selectany) const GUID GUID_CAUSALITY_WINDOWS_PLATFORM_ID = +{ 0xc54c95d9, 0x5b6e, 0x41e9, { 0xa2, 0x8d, 0x2d, 0xd6, 0x8f, 0x94, 0xb5, 0x0 } }; + +namespace Microsoft { +namespace WRL { + +// Designates the error propagation policy used by FireProgress and FireComplete. If PropagateDelegateError is the mode +// then failures returned from the async completion and progress delegates are propagated. If IgnoreDelegateError +// is the mode, then failures returned from the async completion and progress delegates are converted to successes and +// the errors are swallowed. +enum ErrorPropagationPolicy +{ + PropagateDelegateError = 1, + IgnoreDelegateError = 2 +}; + +namespace Details +{ + // contains states indicating existance or lack of options + struct AsyncOptionsBase + { + static const bool hasCausalityOptions = false; + static const bool hasErrorPropagationPolicy = false; + static const bool hasCausalityOperationName = false; + static const bool isCausalityEnabled = true; + }; + + template < PCWSTR OpName > + struct IsOperationName + { + static const bool Value = true; + }; + + template < > + struct IsOperationName< nullptr > + { + static const bool Value = false; + }; +} + +// Options for error propagation and the defaults are set here. +#if defined(BUILD_WINDOWS) && (NTDDI_VERSION >= NTDDI_WINBLUE) +template < ErrorPropagationPolicy errorPropagationPolicy = PropagateErrorWithWin8Quirk> +#else +template < ErrorPropagationPolicy errorPropagationPolicy = Microsoft::WRL::ErrorPropagationPolicy::IgnoreDelegateError> +#endif +struct ErrorPropagationOptions : public Microsoft::WRL::Details::AsyncOptionsBase +{ + static const ErrorPropagationPolicy PropagationPolicy = errorPropagationPolicy; + static const bool hasErrorPropagationPolicy = true; +}; + +#ifndef _WRL_DISABLE_CAUSALITY_ + +// Options for causality tracing and the needed defaults are set here. The following class may be used as +// a reference to add more options to AsyncBase +#ifdef BUILD_WINDOWS +#define WRL_DEFAULT_CAUSALITY_GUID GUID_CAUSALITY_WINDOWS_PLATFORM_ID +#define WRL_DEFAULT_CAUSALITY_SOURCE ::ABI::Windows::Foundation::Diagnostics::CausalitySource::CausalitySource_System +#else +#define WRL_DEFAULT_CAUSALITY_GUID GUID_NULL +#define WRL_DEFAULT_CAUSALITY_SOURCE ::ABI::Windows::Foundation::Diagnostics::CausalitySource::CausalitySource_Application +#endif //BUILD_WINDOWS + +template < + PCWSTR OpName = nullptr, + const GUID &PlatformId = WRL_DEFAULT_CAUSALITY_GUID, + ::ABI::Windows::Foundation::Diagnostics::CausalitySource CausalitySource = WRL_DEFAULT_CAUSALITY_SOURCE +> +struct AsyncCausalityOptions : public Microsoft::WRL::Details::AsyncOptionsBase +{ + static PCWSTR GetAsyncOperationName() + { + return OpName; + } + + static const GUID GetPlatformId() + { + return PlatformId; + } + + static ::ABI::Windows::Foundation::Diagnostics::CausalitySource GetCausalitySource() + { + return CausalitySource; + } + + static const bool hasCausalityOptions = true; + static const bool hasCausalityOperationName = Microsoft::WRL::Details::IsOperationName::Value; +}; + +// This option type for causality tracing disables just the tracing part. +extern __declspec(selectany) const WCHAR DisableCausalityAsyncOperationName[] = L"Disabled"; +struct DisableCausality : public AsyncCausalityOptions< DisableCausalityAsyncOperationName > +{ + static const bool isCausalityEnabled = false; +}; + +#endif // _WRL_DISABLE_CAUSALITY_ + +namespace Details +{ +// maps internal definitions for AsyncStatus and defines states that are not client visible +enum AsyncStatusInternal +{ + // non-client visible internal states + _Undefined = -2, + _Created = -1, + + // client visible states (must match AsyncStatus exactly) + _Started = static_cast(::ABI::Windows::Foundation::AsyncStatus::Started), + _Completed = static_cast(::ABI::Windows::Foundation::AsyncStatus::Completed), + _Canceled = static_cast(::ABI::Windows::Foundation::AsyncStatus::Canceled), + _Error = static_cast(::ABI::Windows::Foundation::AsyncStatus::Error), + + // non-client visible internal states + _Closed +}; + +template < typename T > +struct DerefHelper; + +template < typename T > +struct DerefHelper +{ + typedef T DerefType; +}; + +#pragma region AsyncOptionsHelper + +#ifndef _WRL_DISABLE_CAUSALITY_ + +// Provides the name for a async operation/action for logging purposes +template < typename TComplete, bool hasName, typename TOptions > +struct CausalityNameHelper +{ + // Provides a default string for the Async Operation/Action name if a name is not provided + // for logging purposes + static PCWSTR GetName() + { + return TOptions::GetAsyncOperationName(); + } +}; + +// Specialization to handle the logging for those classes that implement z_get_rc_name_impl +template < typename TComplete, typename TOptions > +struct CausalityNameHelper< TComplete, false, TOptions > +{ + static PCWSTR GetName() + { + return TComplete::z_get_rc_name_impl(); + } +}; + +// Specialization to handle the logging for IAsyncAction +template +struct CausalityNameHelper< ::ABI::Windows::Foundation::IAsyncActionCompletedHandler, false, TOptions > +{ + // IAsyncActionCompletedHandler is not templatized so it does not implement z_get_rc_name_impl + // hence this specialization + static PCWSTR GetName() + { + return L"Windows.Foundation.IAsyncActionCompletedHandler"; + } +}; + +#endif // _WRL_DISABLE_CAUSALITY_ + +// helper class to switch between default or given options for error +// propagation +template < bool hasValue, typename TOptions > +struct ErrorPropagationOptionsHelper; + +// provides the given options for error propagation +template < typename TOptions > +struct ErrorPropagationOptionsHelper< true , TOptions > +{ + static const Microsoft::WRL::ErrorPropagationPolicy PropagationPolicy = TOptions::PropagationPolicy; +}; + +// provides default options for error propagation +template < typename TOptions > +struct ErrorPropagationOptionsHelper< false , TOptions > +{ + static const Microsoft::WRL::ErrorPropagationPolicy PropagationPolicy = Microsoft::WRL::ErrorPropagationOptions<>::PropagationPolicy; +}; + +#ifndef _WRL_DISABLE_CAUSALITY_ + +// helper class to switch between default or given options for +// Async Causality Options +template < bool hasCausalityOptions, typename TComplete, typename TOptions > +struct AsyncCausalityOptionsHelper; + +// provides the given options for causality tracing +template < typename TComplete, typename TOptions > +struct AsyncCausalityOptionsHelper < true, TComplete, TOptions > +{ +#ifdef BUILD_WINDOWS + static_assert(!(__is_base_of(::ABI::Windows::Foundation::IAsyncActionCompletedHandler, TComplete) && !TOptions::hasCausalityOperationName),"Please add name to Asynchronous Operations for Better Diagnostics: http://winri/BreakingChanges/BreakingChangeForm/Index/1992"); +#endif + static PCWSTR GetAsyncOperationName() + { + return CausalityNameHelper< TComplete, TOptions::hasCausalityOperationName, TOptions >::GetName(); + } + + static const GUID GetPlatformId() + { + return TOptions::GetPlatformId(); + } + + static const ::ABI::Windows::Foundation::Diagnostics::CausalitySource GetCausalitySource() + { + return TOptions::GetCausalitySource(); + } + + static const bool CausalityEnabled = TOptions::isCausalityEnabled; +}; + +// provides the default options for causality tracing +template < typename TComplete, typename TOptions > +struct AsyncCausalityOptionsHelper < false, TComplete, TOptions > +{ +#ifdef BUILD_WINDOWS + static_assert(!(__is_base_of(::ABI::Windows::Foundation::IAsyncActionCompletedHandler, TComplete) && !TOptions::hasCausalityOperationName),"Please add name to Asynchronous Operations for Better Diagnostics: http://winri/BreakingChanges/BreakingChangeForm/Index/1992"); +#endif + static PCWSTR GetAsyncOperationName() + { + return CausalityNameHelper< TComplete, TOptions::hasCausalityOperationName, TOptions >::GetName(); + } + + static const GUID GetPlatformId() + { + return Microsoft::WRL::AsyncCausalityOptions<>::GetPlatformId(); + } + + static ::ABI::Windows::Foundation::Diagnostics::CausalitySource GetCausalitySource() + { + return Microsoft::WRL::AsyncCausalityOptions<>::GetCausalitySource(); + } + + static const bool CausalityEnabled = TOptions::isCausalityEnabled; +}; + +#endif // _WRL_DISABLE_CAUSALITY_ + +// helper calls to accumulate all options +// Future options have to be added here +template < typename TComplete, typename TOptions > +struct AsyncOptionsHelper : +#ifndef _WRL_DISABLE_CAUSALITY_ + public AsyncCausalityOptionsHelper < TOptions::hasCausalityOptions, TComplete, TOptions >, +#endif // _WRL_DISABLE_CAUSALITY_ + public ErrorPropagationOptionsHelper < TOptions::hasErrorPropagationPolicy, TOptions > +{ +}; + +#pragma endregion +// End of AsyncOptionsHelper + +} // Details + +// designates whether the "GetResults" method returns a single result (after complete fires) or multiple results +// (which are progressively consumable between Start state and before Close is called) +enum AsyncResultType +{ + SingleResult = 0x0001, + MultipleResults = 0x0002 +}; + +// indicates how an attempt to transition to a terminal state of Completed or Error should behave with respect to +// the (client-requested) Canceled state. +enum CancelTransitionPolicy +{ + // If the async operation is presently in a (client-requested) Canceled state, this indicates that + // it will stay in the Canceled state as opposed to transitioning to a terminal Completed or Error + // state. + RemainCanceled = 0, + + // If the async operation is presently in a (client-requested) Canceled state, this indicates that + // state should transition from that Canceled state to the terminal state of Completed or Error as + // determined by the call utilizing this flag. + TransitionFromCanceled +}; + +#pragma region AsyncOptions + +template < ErrorPropagationPolicy errorPropagationPolicy > +struct ErrorPropagationPolicyTraits; + +// This error propagation policy passes through all errors +template <> +struct ErrorPropagationPolicyTraits< PropagateDelegateError > +{ + static HRESULT FireCompletionErrorPropagationPolicyFilter(HRESULT hrIn, IUnknown *, void * = nullptr) + { + // Ignore errors if the error is caused by a disconnected object + if (hrIn == RPC_E_DISCONNECTED || hrIn == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) || hrIn == JSCRIPT_E_CANTEXECUTE) + { + ::RoTransformError(hrIn, S_OK, nullptr); + hrIn = S_OK; + } + return hrIn; + } + + static HRESULT FireProgressErrorPropagationPolicyFilter(HRESULT hrIn, IUnknown *, void * = nullptr) + { + // Ignore errors if the error is caused by a disconnected object + if (hrIn == RPC_E_DISCONNECTED || hrIn == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) || hrIn == JSCRIPT_E_CANTEXECUTE) + { + ::RoTransformError(hrIn, S_OK, nullptr); + hrIn = S_OK; + } + return hrIn; + } +}; + +// This error propagation policy ignores all errors and converts them to S_OK +template <> +struct ErrorPropagationPolicyTraits< IgnoreDelegateError > +{ + static HRESULT FireCompletionErrorPropagationPolicyFilter(HRESULT hrIn, IUnknown *, void * = nullptr) + { + if (FAILED(hrIn)) + { + ::RoTransformError(hrIn, S_OK, nullptr); + hrIn = S_OK; + } + return hrIn; + } + + static HRESULT FireProgressErrorPropagationPolicyFilter(HRESULT hrIn, IUnknown *, void * = nullptr) + { + if (FAILED(hrIn)) + { + ::RoTransformError(hrIn, S_OK, nullptr); + hrIn = S_OK; + } + return hrIn; + } +}; + +// All options for the AsyncBase class are accumulated here. This class may be expanded to include +// new options as needed. +template < +#if defined(BUILD_WINDOWS) && (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorPropagationPolicy errorPropagationPolicy = PropagateErrorWithWin8Quirk, +#else + ErrorPropagationPolicy errorPropagationPolicy = ErrorPropagationPolicy::IgnoreDelegateError, +#endif + PCWSTR OpName = nullptr, +#ifdef BUILD_WINDOWS + const GUID &PlatformId = GUID_CAUSALITY_WINDOWS_PLATFORM_ID, + ::ABI::Windows::Foundation::Diagnostics::CausalitySource CausalitySource = ::ABI::Windows::Foundation::Diagnostics::CausalitySource::CausalitySource_System +#else + const GUID &PlatformId = GUID_NULL, + ::ABI::Windows::Foundation::Diagnostics::CausalitySource CausalitySource = ::ABI::Windows::Foundation::Diagnostics::CausalitySource::CausalitySource_Application +#endif //BUILD_WINDOWS +> +struct AsyncOptions : +#ifndef _WRL_DISABLE_CAUSALITY_ + public AsyncCausalityOptions, +#endif // _WRL_DISABLE_CAUSALITY_ + public ErrorPropagationOptions +{ + static const bool hasCausalityOptions = true; + static const bool hasErrorPropagationPolicy = true; + static const bool hasCausalityOperationName = Microsoft::WRL::Details::IsOperationName::Value; + static const bool isCausalityEnabled = true; +}; + +#pragma endregion +// End of AsyncOptions region + +#ifndef _WRL_DISABLE_CAUSALITY_ + _declspec(selectany) INIT_ONCE gCausalityInitOnce = INIT_ONCE_STATIC_INIT; + _declspec(selectany) ::ABI::Windows::Foundation::Diagnostics::IAsyncCausalityTracerStatics* gCausality; +#endif // _WRL_DISABLE_CAUSALITY_ + +// AsyncBase - base class that implements the WinRT Async state machine +// this base class is designed to be used with WRL to implement an async worker object +template < + typename TComplete, + typename TProgress = Details::Nil, + AsyncResultType resultType = SingleResult, + typename TAsyncBaseOptions = AsyncOptions<> +> +class AsyncBase : public AsyncBase< TComplete, Details::Nil, resultType, TAsyncBaseOptions > +{ + typedef typename Details::ArgTraitsHelper< TProgress >::Traits ProgressTraits; + typedef Microsoft::WRL::Details::AsyncOptionsHelper< TComplete, TAsyncBaseOptions > AllOptions; + friend class AsyncBase< TComplete, Details::Nil, resultType, TAsyncBaseOptions >; + +public: + + // since this is designed to be used inside of an RuntimeClass<> template, we can + // only have a default constructor + AsyncBase() : + progressDelegate_(nullptr), + progressDelegateBucketAssist_(nullptr) + { + } + + // Delegate Helpers + STDMETHOD(PutOnProgress)(TProgress* progressHandler) + { + HRESULT hr = this->CheckValidStateForDelegateCall(); + if (SUCCEEDED(hr)) + { + progressDelegate_ = progressHandler; + + if (progressDelegate_ != nullptr) + { + progressDelegateBucketAssist_ = Microsoft::WRL::Details::GetDelegateBucketAssist(progressDelegate_.Get()); + } + + this->TraceDelegateAssigned(); + } + return hr; + } + + STDMETHOD(GetOnProgress)(TProgress** progressHandler) + { + *progressHandler = nullptr; + HRESULT hr = this->CheckValidStateForDelegateCall(); + if (SUCCEEDED(hr)) + { + progressDelegate_.CopyTo(progressHandler); + } + return hr; + } + + HRESULT FireProgress(const typename ProgressTraits::Arg2Type arg) + { + HRESULT hr = S_OK; + ComPtr< ::ABI::Windows::Foundation::IAsyncInfo > asyncInfo = this; + ComPtr::DerefType> operationInterface; + if (progressDelegate_) + { + hr = asyncInfo.As(&operationInterface); + if (SUCCEEDED(hr)) + { + this->TraceProgressNotificationStart(); + + hr = progressDelegate_->Invoke(operationInterface.Get(), arg); + + this->TraceProgressNotificationComplete(); + } + } + + // filter the errors per the Error Propagation Policy + hr = ErrorPropagationPolicyTraits< AllOptions::PropagationPolicy >::FireProgressErrorPropagationPolicyFilter(hr, progressDelegate_.Get(), progressDelegateBucketAssist_); + + return hr; + } + + HRESULT FireCompletion(void) override + { + // "this" may be deleted during the completion call. Remove progress prior to firing completion. + progressDelegate_.Reset(); + return AsyncBase< TComplete, Details::Nil, resultType, TAsyncBaseOptions >::FireCompletion(); + } + +private: + ::Microsoft::WRL::ComPtr progressDelegate_; + void *progressDelegateBucketAssist_; +}; + +template < typename TComplete, AsyncResultType resultType, typename TAsyncBaseOptions > +class AsyncBase< TComplete, Details::Nil, resultType, TAsyncBaseOptions > : public ::Microsoft::WRL::Implements< ::ABI::Windows::Foundation::IAsyncInfo > +{ + typedef typename Details::ArgTraitsHelper< TComplete >::Traits CompleteTraits; + typedef Microsoft::WRL::Details::AsyncOptionsHelper AllOptions; +public: + // since this is designed to be used inside of a RuntimeClass<> template, we can + // only have a default constructor + AsyncBase() : + currentStatus_(Details::AsyncStatusInternal::_Created), + id_(1), + errorCode_(S_OK), + completeDelegate_(nullptr), + completeDelegateBucketAssist_(nullptr), + asyncOperationBucketAssist_(nullptr), + cCompleteDelegateAssigned_(0), + cCallbackMade_(0) + { + } + + // The TraceCompletion in logged if the FireCompletion occurs and the completion call back is assigned + // if the callback was not made then the async operation completion is logged here + virtual ~AsyncBase() + { + if (!cCallbackMade_) + { + TraceOperationComplete(); + } + } + + // IAsyncInfo::put_Id + STDMETHOD(put_Id)(const unsigned int id) + { + if (id == 0) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + WCHAR const pszParamName[] = L"id"; + ::RoOriginateErrorW(E_INVALIDARG, ARRAYSIZE(pszParamName) - 1, pszParamName); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_INVALIDARG; + } + id_ = id; + + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + if (current != Details::_Created) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_METHOD_CALL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_ILLEGAL_METHOD_CALL; + } + + return S_OK; + } + + // IAsyncInfo::get_Id + STDMETHOD(get_Id)(unsigned int *id) override + { + *id = id_; + return CheckValidStateForAsyncInfoCall(); + } + + // IAsyncInfo::get_Status + STDMETHOD(get_Status)(::ABI::Windows::Foundation::AsyncStatus *status) override + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + *status = static_cast< ::ABI::Windows::Foundation::AsyncStatus >(current); + return CheckValidStateForAsyncInfoCall(); + } + + // IAsyncInfo::get_ErrorCode + STDMETHOD(get_ErrorCode)(HRESULT* errorCode) override + { + HRESULT hr = CheckValidStateForAsyncInfoCall(); + if (SUCCEEDED(hr)) + { + ErrorCode(errorCode); + } + else + { + // Do not propagate the error and error info associated with the async error if this call generated + // a specific error itself. + *errorCode = hr; + } + return hr; + } + +protected: + // Start - this is not externally visible since async operations "hot start" before returning to the caller + STDMETHOD(Start)(void) + { + HRESULT hr = S_OK; + if (TransitionToState(Details::_Started)) + { + hr = OnStart(); + +#ifndef _WRL_DISABLE_CAUSALITY_ + if (SUCCEEDED(hr) && + ::InitOnceExecuteOnce(&gCausalityInitOnce, InitCausality, NULL, NULL)) + { + TraceOperationStart(); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + else + { + hr = E_ILLEGAL_STATE_CHANGE; +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_STATE_CHANGE, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + } + return hr; + } + +public: + // IAsyncInfo::Cancel + STDMETHOD(Cancel)(void) + { + if (TransitionToState(Details::_Canceled)) + { + OnCancel(); + + TraceCancellation(); + } + return S_OK; + } + + // IAsyncInfo::Close + STDMETHOD(Close)(void) override + { + HRESULT hr = S_OK; + if (TransitionToState(Details::_Closed)) + { + OnClose(); + } + else // illegal state change + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + if (current == Details::_Closed) + { + hr = S_OK; // Closed => Closed transition is just ignored + } + else + { + hr = E_ILLEGAL_STATE_CHANGE; +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_STATE_CHANGE, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + } + } + return hr; + } + + // Delegate helpers + STDMETHOD(PutOnComplete)(TComplete* completeHandler) + { + HRESULT hr = CheckValidStateForDelegateCall(); + if (SUCCEEDED(hr)) + { + // this delegate property is "write once" + if (InterlockedIncrement(&cCompleteDelegateAssigned_) == 1) + { + if (completeHandler != nullptr) + { + completeDelegateBucketAssist_ = Microsoft::WRL::Details::GetDelegateBucketAssist(completeHandler); + } + + completeDelegate_ = completeHandler; + + // Guarantee that the write of completeDelegate_ is ordered with respect to the read of state below + // as perceived from FireCompletion on another thread. + MemoryBarrier(); + + this->TraceDelegateAssigned(); + + // in the "hot start" case, put_Completed could have been called after the async operation has hit + // a terminal state. If so, fire the completion immediately. + if (IsTerminalState()) + { + FireCompletion(); + } + } + else + { + hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_DELEGATE_ASSIGNMENT, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + } + } + return hr; + } + + STDMETHOD(GetOnComplete)(TComplete** completeHandler) + { + *completeHandler = nullptr; + HRESULT hr = CheckValidStateForDelegateCall(); + if (SUCCEEDED(hr)) + { + completeDelegate_.CopyTo(completeHandler); + } + return hr; + } + + virtual HRESULT FireCompletion() + { + HRESULT hr = S_OK; + // must do this *before* the InterlockedIncrement! + TryTransitionToCompleted(); + + __WRL_ASSERT__(IsTerminalState() && "Must only call FireCompletion when operation is in terminal state"); + + // we guarantee that completion can only ever be fired once + if (completeDelegate_ != nullptr && InterlockedIncrement(&cCallbackMade_) == 1) + { + ComPtr< ::ABI::Windows::Foundation::IAsyncInfo> asyncInfo = this; + ComPtr::DerefType> operationInterface; + + TraceOperationComplete(); + + if (SUCCEEDED(asyncInfo.As(&operationInterface))) + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + TraceCompletionNotificationStart(); + + hr = completeDelegate_->Invoke(operationInterface.Get(), static_cast<::ABI::Windows::Foundation::AsyncStatus>(current)); + // Filter the errors as per the Error Propagation Policy + hr = ErrorPropagationPolicyTraits< AllOptions::PropagationPolicy >::FireCompletionErrorPropagationPolicyFilter(hr, completeDelegate_.Get(), completeDelegateBucketAssist_); + completeDelegate_ = nullptr; + + + TraceCompletionNotificationComplete(); + } + } + + return hr; + } + +protected: + + inline void CurrentStatus(Details::AsyncStatusInternal *status) + { + ::_InterlockedCompareExchange(reinterpret_cast(status), currentStatus_, static_cast(*status)); + __WRL_ASSERT__(*status != Details::_Undefined); + } + + // This method returns the error code stored as a result of a transition into the error state. + // In addition, if there is any restricted error information associated with the error that was captured at the time + // of the error transition, it will be associated with the calling thread via a SetRestrictedErrorInfo call. + inline void ErrorCode(HRESULT *error) + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + // Do not allow visibility of the error until such point as we have had a successful state transition into the error state. + // The error + information is not a single atomic quantity. It is not considered "published" until we are actively in the error state. + if (current != Details::_Error) + { + *error = S_OK; + } + else + { + ::_InterlockedCompareExchange(reinterpret_cast(error), errorCode_, static_cast(*error)); + if (errorInfo_ != nullptr) + { + SetRestrictedErrorInfo(errorInfo_.Get()); + } + } + } + + bool TryTransitionToCompleted(CancelTransitionPolicy cancelBehavior = CancelTransitionPolicy::RemainCanceled) + { + bool bTransition = TransitionToState(Details::AsyncStatusInternal::_Completed); + if (!bTransition && cancelBehavior == CancelTransitionPolicy::TransitionFromCanceled) + { + bTransition = TransitionCanceledToCompleted(); + } + return bTransition; + } + + bool TryTransitionToError(const HRESULT error, CancelTransitionPolicy cancelBehavior = CancelTransitionPolicy::RemainCanceled, _In_opt_ void * bucketAssist = nullptr) + { + // In addition to the result being transitioned to, there might be restricted error information associated with the error. It + // is assumed that such is on the calling thread. If we successfully transition to the error state with "error" as the code, + // we must also capture the error info and funnel it over to callers of GetResults / ErrorCode. Our call to + // GetRestrictedErrorInfo below will capture the error info after which, it is owned by this async operation. + // + // Since there are multiple pieces of information and the capturing of these are not atomic, no one from the outside is allowed + // to view these until the state transition to error is complete. This happens in two parts: + // + // - A successful CAS from S_OK to error (meaning that this error is the one being captures) + // - A successful state change into the error state (via the Transition* call below) + + if (bucketAssist != nullptr) + { + asyncOperationBucketAssist_ = bucketAssist; + } + bool bTransition = false; + if (::_InterlockedCompareExchange(reinterpret_cast(&errorCode_), error, S_OK) == S_OK) + { + (void)GetRestrictedErrorInfo(&errorInfo_); + + // This thread is the "owner" of the rights to transition to the error state. + bTransition = TransitionToState(Details::AsyncStatusInternal::_Error); + if (!bTransition && cancelBehavior == CancelTransitionPolicy::TransitionFromCanceled) + { + bTransition = TransitionCanceledToError(); + } + } + + if (bTransition) + { + TraceError(); + } + + // if we return true, then we did a valid state transition + // queue firing of completed event (cannot be done from this call frame) + // otherwise we are already in a terminal state: error, canceled, completed, or closed + // and we ignore the transition request to the Error state + return bTransition; + } + + // This method checks to see if the delegate properties can be + // modified in the current state and generates the appropriate + // error hr in the case of violation. + inline HRESULT CheckValidStateForDelegateCall() + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + if (current == Details::_Closed) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_METHOD_CALL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_ILLEGAL_METHOD_CALL; + } + return S_OK; + } + + // This method checks to see if results can be collected in the + // current state and generates the appropriate error hr in + // the case of a violation. + inline HRESULT CheckValidStateForResultsCall() + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + if (current == Details::_Error) + { + HRESULT hr; + + // Make sure to propagate any restricted error info associated with the asynchronous failure. + ErrorCode(&hr); + return hr; + } +#pragma warning(push) +#pragma warning(disable: 4127) // Conditional expression is constant + // single result only legal in Completed state + if (resultType == SingleResult) +#pragma warning(pop) + { + if (current != Details::_Completed) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_METHOD_CALL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_ILLEGAL_METHOD_CALL; + } + } + // multiple results can be called after async operation is running (started) and before/after Completed + else if (current != Details::_Started && + current != Details::_Canceled && + current != Details::_Completed) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_METHOD_CALL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_ILLEGAL_METHOD_CALL; + } + return S_OK; + } + + // This method can be called by derived classes periodically to determine + // whether the asynchronous operation should continue processing or should + // be halted. + inline bool ContinueAsyncOperation() + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + return (current == Details::_Started); + } + + // These methods are used to allow the async worker implementation do work on + // state transitions. No real "work" should be done in these methods. In other words + // they should not block for a long time on UI timescales. + virtual HRESULT OnStart(void) = 0; + virtual void OnClose(void) = 0; + virtual void OnCancel(void) = 0; + +private: +#ifndef _WRL_DISABLE_CAUSALITY_ + // This method is used to initialize the Causality tracking + static BOOL WINAPI InitCausality( + _Inout_opt_ PINIT_ONCE InitOnce, + _Inout_opt_ PVOID Parameter, + _Out_opt_ PVOID* Context ) + { + UNREFERENCED_PARAMETER(InitOnce); + UNREFERENCED_PARAMETER(Parameter); + UNREFERENCED_PARAMETER(Context); + +#ifdef _NO_CAUSALITY_DOWNLEVEL_ + // do not attempt to trace causality on OS versions less than 6.2 (Windows 8) + OSVERSIONINFOEX osvi; + DWORDLONG dwlConditionMask = 0; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + osvi.dwMajorVersion = 6; + osvi.dwMinorVersion = 2; + + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + + // if running on Windows 8 or greater + if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask)) + { +#endif + Microsoft::WRL::Wrappers::HStringReference hstrCausalityTraceName(RuntimeClass_Windows_Foundation_Diagnostics_AsyncCausalityTracer); + if (FAILED(::Windows::Foundation::GetActivationFactory(hstrCausalityTraceName.Get(), &gCausality))) + { + return FALSE; + } + return TRUE; +#ifdef _NO_CAUSALITY_DOWNLEVEL_ + } + else + { + gCausality = nullptr; + return FALSE; + } +#endif + } +#endif _WRL_DISABLE_CAUSALITY_ + + // This method is used to check if calls to the AsyncInfo properties + // (id, status, error code) are legal in the current state. It also + // generates the appropriate error hr to return in the case of an + // illegal call. + inline HRESULT CheckValidStateForAsyncInfoCall() + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + if (current == Details::_Closed) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(E_ILLEGAL_METHOD_CALL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_ILLEGAL_METHOD_CALL; + } + else if (current == Details::_Created) // error in async ::ABI object - returned to caller not started + { + // No RoOriginateError needed since this can hit multiple times in expected scenarios + + return E_ASYNC_OPERATION_NOT_STARTED; + } + + return S_OK; + } + + inline bool TransitionToState(const Details::AsyncStatusInternal newState) + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + + // This enforces the valid state transitions of the asynchronous worker object + // state machine. + switch(newState) + { + case Details::_Started: + if (current != Details::_Created) + { + return false; + } + break; + case Details::_Completed: + if (current != Details::_Started) + { + return false; + } + break; + case Details::_Canceled: + if (current != Details::_Started) + { + return false; + } + break; + case Details::_Error: + if (current != Details::_Started) + { + return false; + } + break; + case Details::_Closed: + if (!IsTerminalState(current)) + { + return false; + } + break; + default: + return false; + break; + } + // attempt the transition to the new state + // Note: if currentStatus_ == current, then there was no intervening write + // by the async work object and the swap succeeded. + Details::AsyncStatusInternal retState = static_cast( + ::_InterlockedCompareExchange(reinterpret_cast(¤tStatus_), + newState, + static_cast(current))); + + // ICE returns the former state, if the returned state and the + // state we captured at the beginning of this method are the same, + // the swap succeeded. + return (retState == current); + } + +protected: + + // It is legal for an async operation object to transition from (client-requested) Canceled + // state to Completed if, for example, the operation completed near the time of the cancellation request. + // An operation which is no longer responsive to client requests to cancel and intends to complete + // successfully despite any new incoming requests to cancel should call TryTransitionToCompleted and + // pass TransitionFromCanceled instead of using this method. + inline bool TransitionCanceledToCompleted() + { + // this is somewhat overly pessimistic since the client cannot possibly transition + // the operation out of the canceled state (only the async operation itself can call + // this method) + Details::AsyncStatusInternal retState = static_cast( + ::_InterlockedCompareExchange(reinterpret_cast(¤tStatus_), + Details::AsyncStatusInternal::_Completed, + Details::AsyncStatusInternal::_Canceled)); + return (retState == Details::AsyncStatusInternal::_Canceled); + } + + // It is legal for an async operation object to transition from (client-requested) Canceled + // state to the error state if, for example, the operation encountered an error near the time + // of the cancellation request. An operation which is no longer responsive to client requests to cancel + // and intends to complete with an error despite any new incoming requests to cancel should call + // TryTransitionToError and pass TransitionFromCanceled instead of using this method. + inline bool TransitionCanceledToError() + { + Details::AsyncStatusInternal retState = static_cast( + ::_InterlockedCompareExchange(reinterpret_cast(¤tStatus_), + Details::AsyncStatusInternal::_Error, + Details::AsyncStatusInternal::_Canceled)); + return (retState == Details::AsyncStatusInternal::_Canceled); + } + + inline bool IsTerminalState() + { + Details::AsyncStatusInternal current = Details::_Undefined; + CurrentStatus(¤t); + return IsTerminalState(current); + } + + inline bool IsTerminalState(Details::AsyncStatusInternal status) + { + return (status == Details::_Error || + status == Details::_Canceled || + status == Details::_Completed || + status == Details::_Closed); + } + + long cCallbackMade_; + long cCompleteDelegateAssigned_; + +#pragma region TracingMethods + + void TraceOperationStart() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceOperationCreation( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Required, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + Microsoft::WRL::Wrappers::HStringReference(AllOptions::GetAsyncOperationName()).Get(), + id_); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceOperationComplete() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + Details::AsyncStatusInternal status; + CurrentStatus(&status); + gCausality->TraceOperationCompletion( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Required, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + static_cast< ::ABI::Windows::Foundation::AsyncStatus >(status)); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceProgressNotificationStart() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkStart( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Important, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_ProgressNotification); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceProgressNotificationComplete() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkCompletion( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Important, + AllOptions::GetCausalitySource(), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_ProgressNotification); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceCompletionNotificationStart() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkStart( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Required, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_CompletionNotification); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceCompletionNotificationComplete() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkCompletion( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Required, + AllOptions::GetCausalitySource(), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_CompletionNotification); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceExecutionStart(::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel traceLevel) + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkStart( + traceLevel, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_Execution); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceExecutionComplete(::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel traceLevel) + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceSynchronousWorkCompletion( + traceLevel, + AllOptions::GetCausalitySource(), + ::ABI::Windows::Foundation::Diagnostics::CausalitySynchronousWork_Execution); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceDelegateAssigned() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceOperationRelation( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Verbose, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalityRelation_AssignDelegate); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceError() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceOperationRelation( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Verbose, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalityRelation_Error); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + + void TraceCancellation() + { +#ifndef _WRL_DISABLE_CAUSALITY_ + if (gCausality && AllOptions::CausalityEnabled) + { + // Ignoring HRESULT intentionally. Tracking failure should not change the + // normal flow of AsyncOperations + gCausality->TraceOperationRelation( + ::ABI::Windows::Foundation::Diagnostics::CausalityTraceLevel_Important, + AllOptions::GetCausalitySource(), + AllOptions::GetPlatformId(), + reinterpret_cast< UINT64 >(this), + ::ABI::Windows::Foundation::Diagnostics::CausalityRelation_Cancel); + } +#endif // _WRL_DISABLE_CAUSALITY_ + } + +#pragma endregion + +private: + ::Microsoft::WRL::ComPtr completeDelegate_; + void *completeDelegateBucketAssist_; + + ::Microsoft::WRL::ComPtr errorInfo_; + Details::AsyncStatusInternal volatile currentStatus_; + HRESULT volatile errorCode_; + unsigned int id_; + +protected: + void *asyncOperationBucketAssist_; +}; + +}} // namespace Microsoft::WRL + +#pragma warning(pop) + +#ifndef _WRL_DISABLE_CAUSALITY +#define CAUSALITY_OPTIONS(OpName) \ + Microsoft::WRL::AsyncCausalityOptions< OpName > +#define ASYNCBASE_CAUSALITY_OPTIONS(TComplete, OpName) \ + Microsoft::WRL::AsyncBase< TComplete, Microsoft::WRL::Details::Nil, Microsoft::WRL::AsyncResultType::SingleResult, CAUSALITY_OPTIONS(OpName)> +#define ASYNCBASE_WITH_PROGRESS_CAUSALITY_OPTIONS(TComplete, TProgress, OpName) \ + Microsoft::WRL::AsyncBase< TComplete, TProgress, Microsoft::WRL::AsyncResultType::SingleResult, CAUSALITY_OPTIONS(OpName)> +#define ASYNCBASE_DISABLE_CAUSALITY(TComplete) \ + Microsoft::WRL::AsyncBase< TComplete, Microsoft::WRL::Details::Nil, Microsoft::WRL::AsyncResultType::SingleResult, Microsoft::WRL::DisableCausality> +#define ASYNCBASE_WITH_PROGRESS_DISABLE_CAUSALITY(TComplete, TProgress) \ + Microsoft::WRL::AsyncBase< TComplete, TProgress, Microsoft::WRL::AsyncResultType::SingleResult, Microsoft::WRL::DisableCausality> +#endif // _WRL_DISABLE_CAUSALITY + +// Restore packing +#include + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // _WRL_ASYNC_H_ + +#ifdef BUILD_WINDOWS +#include +#endif diff --git a/Externals/WIL/tests/workarounds/wrl/wrl/implements.h b/Externals/WIL/tests/workarounds/wrl/wrl/implements.h new file mode 100644 index 0000000000..d259f64bc4 --- /dev/null +++ b/Externals/WIL/tests/workarounds/wrl/wrl/implements.h @@ -0,0 +1,2655 @@ +// +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// Code in Details namespace is for internal usage within the library code +// + +#ifndef _WRL_IMPLEMENTS_H_ +#define _WRL_IMPLEMENTS_H_ + +#ifdef _MSC_VER +#pragma once +#endif // _MSC_VER + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpragma-pack" +#pragma clang diagnostic ignored "-Wunused-value" +#pragma clang diagnostic ignored "-Wmicrosoft-sealed" +#pragma clang diagnostic ignored "-Winaccessible-base" +#endif + +#pragma region includes + +#include +#include +#ifdef BUILD_WINDOWS +#include +#endif +#include +#include + +#include +#include +#include // IMarshal +#include // CLSID_StdGlobalInterfaceTable +#include + +#include +#include + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +#include "roerrorapi.h" +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + +// Set packing +#include + +#pragma endregion + +#ifndef __WRL_NO_DEFAULT_LIB__ +#pragma comment(lib, "ole32.lib") // For CoTaskMemAlloc +#endif + +#pragma region disable warnings + +#pragma warning(push) +#pragma warning(disable: 4584) // 'class1' : base-class 'class2' is already a base-class of 'class3' +#pragma warning(disable: 4481) // nonstandard extension used: override specifier 'override' + +#pragma endregion // disable warnings + +namespace Microsoft { +namespace WRL { + +// Indicator for RuntimeClass,Implements and ChainInterfaces that T interface +// will be not accessible on IID list +// Example: +// struct MyRuntimeClass : RuntimeClass> {} +template +struct CloakedIid : T +{ +}; + +enum RuntimeClassType +{ + WinRt = 0x0001, + ClassicCom = 0x0002, + WinRtClassicComMix = WinRt | ClassicCom, + InhibitWeakReference = 0x0004, + Delegate = ClassicCom, + InhibitFtmBase = 0x0008, + InhibitRoOriginateError = 0x0010 +}; + +template +struct RuntimeClassFlags +{ + static const unsigned int value = flags; +}; + +namespace Details +{ +// Empty struct used for validating template parameter types in Implements +struct ImplementsBase +{ +}; + +} // namespace Details + +// MixIn modifier allows to combine QI from +// a class that doesn't have default constructor on it +template +struct MixIn +{ +}; + +// ComposableBase template to allow deriving from a RuntimeClass +// Optionally allows specifying the base factory and statics interface +template +class ComposableBase +{ +}; +// Back-compat indicator for RuntimeClass to not support IWeakReferenceSource +typedef RuntimeClassFlags InhibitWeakReferencePolicy; + +template +struct ErrorHelper +{ + static void OriginateError(HRESULT hr, HSTRING message) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ::RoOriginateError(hr, message); +#else + UNREFERENCED_PARAMETER(hr); + UNREFERENCED_PARAMETER(message); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + } +}; + +template<> +struct ErrorHelper +{ + static void OriginateError(HRESULT hr, HSTRING message) + { + UNREFERENCED_PARAMETER(hr); + UNREFERENCED_PARAMETER(message); + // No-Op + } +}; + +namespace Details +{ + +//Forward declaration +struct CreatorMap; + +// Sections automatically generate a list of pointers to CreatorMap through the linker +// Sections a and z are used as a terminators +#pragma section("minATL$__a", read) +// Section f is used to put com objects to creator map +#pragma section("minATL$__f", read) +// Section m divides COM entries from WinRT entries +#pragma section("minATL$__m", read) +// Section r is used to put WinRT objects to creator map +#pragma section("minATL$__r", read) +#pragma section("minATL$__z", read) + +extern "C" +{ +// Location of the first and last entries for the linker generated list of pointers to CreatorMapEntry +__declspec(selectany) __declspec(allocate("minATL$__a")) const CreatorMap* __pobjectentryfirst = nullptr; +// Section m divides COM objects from WinRT objects +// - sections between a and m we store COM object info +// - sections between m+1 and z we store WinRT object info +__declspec(selectany) __declspec(allocate("minATL$__m")) const CreatorMap* __pobjectentrymid = nullptr; +__declspec(selectany) __declspec(allocate("minATL$__z")) const CreatorMap* __pobjectentrylast = nullptr; +} + +// Base class used by all module classes. +class __declspec(novtable) ModuleBase +{ +private: + // Lock that synchronize access and termination of factories + static void* moduleLock_; + + static_assert(sizeof(moduleLock_) == sizeof(SRWLOCK), "cacheLock must have the same size as SRWLOCK"); +protected: + static volatile unsigned long objectCount_; +public: + static ModuleBase *module_; + + ModuleBase() throw() + { +#ifdef _DEBUG + // WRLs support for activatable classes requires there is only one instance of Module<>, this assert + // ensures there is only one. Since Module<> is templatized, using different template parameters will + // result in multiple instances, avoid this by making sure all code in a component uses the same parameters. + // Note that the C++ CX runtime creates an instance; Module, + // so mixing it with non CX code can result in this assert. + // WRL supports static and dynamically allocated Module<>, choose dynamic by defining __WRL_DISABLE_STATIC_INITIALIZE__ + // and allocate that instance with new but only once, for example in the main() entry point of an application. + __WRL_ASSERT__(::InterlockedCompareExchangePointer(reinterpret_cast(&module_), this, nullptr) == nullptr && + "The module was already instantiated"); + + SRWLOCK initSRWLOCK = SRWLOCK_INIT; + __WRL_ASSERT__(reinterpret_cast(&moduleLock_)->Ptr == initSRWLOCK.Ptr && "Different value for moduleLock_ than SRWLOCK_INIT"); + (initSRWLOCK); +#else + module_ = this; +#endif + } + + ModuleBase(const ModuleBase&) = delete; + ModuleBase& operator=(const ModuleBase&) = delete; + + virtual ~ModuleBase() throw() + { +#ifdef _DEBUG + __WRL_ASSERT__(::InterlockedCompareExchangePointer(reinterpret_cast(&module_), nullptr, this) == this && + "The module was already instantiated"); +#else + module_ = nullptr; +#endif + } + + // Number of active objects in the module + STDMETHOD_(unsigned long, IncrementObjectCount)() = 0; + STDMETHOD_(unsigned long, DecrementObjectCount)() = 0; + + STDMETHOD_(unsigned long, GetObjectCount)() const + { + return objectCount_; + } + + STDMETHOD_(const CreatorMap**, GetFirstEntryPointer)() const + { + return &__pobjectentryfirst; + } + + STDMETHOD_(const CreatorMap**, GetMidEntryPointer)() const + { + return &__pobjectentrymid; + } + + STDMETHOD_(const CreatorMap**, GetLastEntryPointer)() const + { + return &__pobjectentrylast; + } + + STDMETHOD_(SRWLOCK*, GetLock)() const + { + return reinterpret_cast(&moduleLock_); + } + + STDMETHOD(RegisterWinRTObject)(_In_opt_z_ const wchar_t*, _In_z_ const wchar_t** activatableClassIds, _Inout_ RO_REGISTRATION_COOKIE* cookie, unsigned int) = 0; + STDMETHOD(UnregisterWinRTObject)(_In_opt_z_ const wchar_t*, _In_ RO_REGISTRATION_COOKIE) = 0; + STDMETHOD(RegisterCOMObject)(_In_opt_z_ const wchar_t*, _In_ IID*, _In_ IClassFactory**, _Inout_ DWORD*, unsigned int) = 0; + STDMETHOD(UnregisterCOMObject)(_In_opt_z_ const wchar_t*, _Inout_ DWORD*, unsigned int) = 0; +}; + +__declspec(selectany) volatile unsigned long ModuleBase::objectCount_ = 0; +// moduleLock_ value must be equal SRWLOCK_INIT which is nullptr +__declspec(selectany) void* ModuleBase::moduleLock_ = nullptr; +__declspec(selectany) ModuleBase *ModuleBase::module_ = nullptr; + +#pragma region helper types +// Empty struct used as default template parameter +class Nil +{ +}; + +// Used on RuntimeClass to protect it from being constructed with new +class DontUseNewUseMake +{ +private: + void* operator new(size_t) throw() + { + __WRL_ASSERT__(false); + return 0; + } + +public: + void* operator new(size_t, _In_ void* placement) throw() + { + return placement; + } +}; + +// RuntimeClassBase is used for detection of RuntimeClass in Make method +class RuntimeClassBase +{ +}; + +// RuntimeClassBaseT provides helper methods for QI and getting IIDs +template +class RuntimeClassBaseT : private RuntimeClassBase +{ +protected: + template + static HRESULT AsIID(_In_ T* implements, REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) throw() + { + *ppvObject = nullptr; +#pragma warning(push) +// Conditional expression is constant +#pragma warning(disable: 4127) +// Potential comparison of a constant with another constant +#pragma warning(disable: 6326) +// Conditional check using template parameter is constant and can be used to optimize the code + bool isRefDelegated = false; + // Prefer InlineIsEqualGUID over other forms since it's better perf on 4-byte aligned data, which is almost always the case. + if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) || ((RuntimeClassTypeT & WinRt) != 0 && InlineIsEqualGUID(riid, __uuidof(IInspectable)))) +#pragma warning(pop) + { + *ppvObject = implements->CastToUnknown(); + static_cast(*ppvObject)->AddRef(); + return S_OK; + } + + HRESULT hr = implements->CanCastTo(riid, ppvObject, &isRefDelegated); + if (SUCCEEDED(hr) && !isRefDelegated) + { + static_cast(*ppvObject)->AddRef(); + } + +#pragma warning(suppress: 6102) // '*ppvObject' is used but may not be initialized + _Analysis_assume_(SUCCEEDED(hr) || (*ppvObject == nullptr)); + + return hr; + } + template + static HRESULT GetImplementedIIDS( + _In_ T* implements, + _Out_ ULONG *iidCount, + _When_(*iidCount == 0, _At_(*iids, _Post_null_)) + _When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) + _Result_nullonfailure_ IID **iids) throw() + { + *iids = nullptr; + *iidCount = 0; + unsigned long count = implements->GetIidCount(); + + // If there is no iids the CoTaskMemAlloc don't have to be called + if (count == 0) + { + return S_OK; + } + + IID* iidArray = reinterpret_cast(::CoTaskMemAlloc(sizeof(IID) * count)); + if (iidArray == nullptr) + { + return E_OUTOFMEMORY; + } + + unsigned long index = 0; + + // assign the IIDs to the array + implements->FillArrayWithIid(&index, iidArray); + __WRL_ASSERT__(index == count); + + // and return it + *iidCount = count; + *iids = iidArray; + return S_OK; + } + +public: + HRESULT RuntimeClassInitialize() throw() + { + return S_OK; + } +}; + +// Base class required to mark FtmBase +class FtmBaseMarker +{ +}; + +// Verifies that I is derived from specified base +template +struct VerifyInterfaceHelper; + +// Specialization for ClassicCom interface +template +struct VerifyInterfaceHelper +{ + static void Verify() throw() + { +#ifdef __WRL_STRICT__ + // Make sure that your interfaces inherit from IUnknown and are not IUnknown and/or IInspectable based + // The IUnknown is allowed only on RuntimeClass as first template parameter + static_assert(__is_base_of(IUnknown, I) && !__is_base_of(IInspectable, I) && !(doStrictCheck && IsSame::value), + "'I' has to derive from 'IUnknown' and not from 'IInspectable'. 'I' must not be IUnknown."); +#else + static_assert(__is_base_of(IUnknown, I), "'I' has to derive from 'IUnknown'."); +#endif + } +}; + +// Specialization for WinRtClassicComMix interface +template +struct VerifyInterfaceHelper +{ + static void Verify() throw() + { +#ifdef __WRL_STRICT__ + // Make sure that your interfaces inherit from IUnknown and are not IUnknown and/or IInspectable + // except when IInspectable is the first template parameter + static_assert(__is_base_of(IUnknown, I) && + (doStrictCheck ? !(IsSame::value || IsSame::value) : __is_base_of(IInspectable, I)), + "'I' has to derive from 'IUnknown' and must not be IUnknown and/or IInspectable."); +#else + static_assert(__is_base_of(IUnknown, I), "'I' has to derive from 'IUnknown'."); +#endif + } +}; + +// Specialization for WinRt interface +template +struct VerifyInterfaceHelper +{ + static void Verify() throw() + { +#ifdef __WRL_STRICT__ + // IWeakReferenceSource is exception for WinRt and can be used however it cannot be first templated interface + // Make sure that your interfaces inherit from IInspectable and are not IInspectable + // The IInspectable is allowed only on RuntimeClass as first template parameter + static_assert((__is_base_of(IWeakReferenceSource, I) && doStrictCheck) || + (__is_base_of(IInspectable, I) && !(doStrictCheck && IsSame::value)), + "'I' has to derive from 'IWeakReferenceSource' or 'IInspectable' and must not be IInspectable"); +#else + // IWeakReference and IWeakReferneceSource are exceptions for WinRT + static_assert(__is_base_of(IWeakReference, I) || + __is_base_of(IWeakReferenceSource, I) || + __is_base_of(IInspectable, I), "'I' has to derive from 'IWeakReference', 'IWeakReferenceSource' or 'IInspectable'"); +#endif + } +}; + +// Specialization for Implements passed as template parameter +template +struct VerifyInterfaceHelper +{ + static void Verify() throw() + { +#ifdef __WRL_STRICT__ + // Verifies if Implements has correct RuntimeClassFlags setting + // Allow using FtmBase on classes configured with RuntimeClassFlags (Default configuration) + static_assert(I::ClassFlags::value == type || + type == WinRtClassicComMix || + __is_base_of(::Microsoft::WRL::Details::FtmBaseMarker, I), + "Implements class must have the same and/or compatibile flags configuration"); +#endif + } +}; + +// Specialization for Implements passed as first template parameter +template +struct VerifyInterfaceHelper +{ + static void Verify() throw() + { +#ifdef __WRL_STRICT__ + // Verifies if Implements has correct RuntimeClassFlags setting + static_assert(I::ClassFlags::value == type || type == WinRtClassicComMix, + "Implements class must have the same and/or compatible flags configuration." + "If you use WRL::FtmBase it cannot be specified as first template parameter on RuntimeClass"); + + // Besides make sure that the first interface on Implements meet flags requirement + VerifyInterfaceHelper::Verify(); +#endif + } +}; + +// Interface traits provides casting and filling iids methods helpers +template +struct __declspec(novtable) InterfaceTraits +{ + typedef I0 Base; + static const unsigned long IidCount = 1; + + template + static void Verify() throw() + { + VerifyInterfaceHelper::Verify(); + } + + template + static Base* CastToBase(_In_ T* ptr) throw() + { + return static_cast(ptr); + } + + template + static IUnknown* CastToUnknown(_In_ T* ptr) throw() + { + return static_cast(static_cast(ptr)); + } + + template + _Success_(return == true) + static bool CanCastTo(_In_ T* ptr, REFIID riid, _Outptr_ void **ppv) throw() + { + // Prefer InlineIsEqualGUID over other forms since it's better perf on 4-byte aligned data, which is almost always the case. + if (InlineIsEqualGUID(riid, __uuidof(Base))) + { + *ppv = static_cast(ptr); + return true; + } + + return false; + } + + static void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + *(iids + *index) = __uuidof(Base); + (*index)++; + } +}; + +// Specialization of traits for cloaked interface +template +struct __declspec(novtable) InterfaceTraits> +{ + typedef CloakedType Base; + static const unsigned long IidCount = 0; + + template + static void Verify() throw() + { + VerifyInterfaceHelper::Verify(); + } + + template + static Base* CastToBase(_In_ T* ptr) throw() + { + return static_cast(ptr); + } + + template + static IUnknown* CastToUnknown(_In_ T* ptr) throw() + { + return static_cast(static_cast(ptr)); + } + + template + _Success_(return == true) + static bool CanCastTo(_In_ T* ptr, REFIID riid, _Outptr_ void **ppv) throw() + { + // Prefer InlineIsEqualGUID over other forms since it's better perf on 4-byte aligned data, which is almost always the case. + if (InlineIsEqualGUID(riid, __uuidof(Base))) + { + *ppv = static_cast(ptr); + return true; + } + + return false; + } + + // Cloaked specialization makes it always IID list empty + static void FillArrayWithIid(_Inout_ unsigned long*, _Inout_ IID*) throw() + { + } +}; + +// Specialization for Nil parameter +template<> +struct __declspec(novtable) InterfaceTraits +{ + typedef Nil Base; + static const unsigned long IidCount = 0; + + template + static void Verify() throw() + { + } + + static void FillArrayWithIid(_Inout_ unsigned long *, _Inout_ IID*) throw() + { + } + + template + _Success_(return == true) + static bool CanCastTo(_In_ T*, REFIID, _Outptr_ void **) throw() + { + return false; + } +}; + +// Verify inheritance +template +struct VerifyInheritanceHelper +{ + static void Verify() throw() + { + static_assert(Details::IsBaseOfStrict::Base, typename InterfaceTraits::Base>::value, "'I' needs to inherit from 'Base'."); + } +}; + +template +struct VerifyInheritanceHelper +{ + static void Verify() throw() + { + } +}; + +#pragma endregion // helper types + +} // namespace Details + +inline Details::ModuleBase* GetModuleBase() throw() +{ + return Details::ModuleBase::module_; +} + +// ChainInterfaces - template allows specifying a derived COM interface along with its class hierarchy to allow QI for the base interfaces +template +struct ChainInterfaces : I0 +{ +protected: + template + static void Verify() throw() + { + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + } + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw() + { + typename Details::InterfaceTraits::Base* ptr = Details::InterfaceTraits::CastToBase(this); + + return (Details::InterfaceTraits::CanCastTo(this, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv)) ? S_OK : E_NOINTERFACE; + } + + IUnknown* CastToUnknown() throw() + { + return Details::InterfaceTraits::CastToUnknown(this); + } + + static const unsigned long IidCount = + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount; + + static void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + } +}; + +template +struct ChainInterfaces, I1, I2, I3, I4, I5, I6, I7, I8, I9> +{ + static_assert(!hasImplements, "Cannot use ChainInterfaces> to Mix a class implementing interfaces using \"Implements\""); + +protected: + template + static void Verify() throw() + { + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + Details::InterfaceTraits::template Verify(); + + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + Details::VerifyInheritanceHelper::Verify(); + } + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw() + { + BaseType* ptr = static_cast(static_cast(this)); + + return ( + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv) || + Details::InterfaceTraits::CanCastTo(ptr, riid, ppv)) ? S_OK : E_NOINTERFACE; + } + + // It's not possible to cast to IUnknown when Base interface inherit more interfaces + // The RuntimeClass is taking always the first interface as IUnknown thus it's required to + // list IInspectable or IUnknown class before MixIn parameter, such as: + // struct MyRuntimeClass : RuntimeClass, IFoo, IBar>, MyIndependentImplementation {} + IUnknown* CastToUnknown() throw() = delete; + + static const unsigned long IidCount = + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount + + Details::InterfaceTraits::IidCount; + + static void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + Details::InterfaceTraits::FillArrayWithIid(index, iids); + } +}; + +namespace Details +{ + +#pragma region Implements helper templates + +// Helper template used by Implements. This template traverses a list of interfaces and adds them as base class and information +// to enable QI. doStrictCheck is typically false only for the first interface, allowing IInspectable to be explicitly specified +// only as the first interface. +template +struct __declspec(novtable) ImplementsHelper; + +template +struct __declspec(novtable) ImplementsMarker +{}; + +template +struct __declspec(novtable) MarkImplements; + +template +struct __declspec(novtable) MarkImplements +{ + typedef I0 Type; +}; + +template +struct __declspec(novtable) MarkImplements +{ + typedef ImplementsMarker Type; +}; + +template +struct __declspec(novtable) MarkImplements, true> +{ + // Cloaked Implements type will be handled in the nested processing. + // Applying the ImplementsMarker too early will bypass Cloaked behavior. + typedef CloakedIid Type; +}; + +template +struct __declspec(novtable) MarkImplements, true> +{ + // Implements type in mix-ins will be handled in the nested processing. + typedef MixIn Type; +}; + +// AdjustImplements pre-processes the type list for more efficient builds. +template +struct __declspec(novtable) AdjustImplements; + +template +struct __declspec(novtable) AdjustImplements +{ + typedef ImplementsHelper::Type, Bases...> Type; +}; + +// Use AdjustImplements to remove instances of "Details::Nil" from the type list. +template +struct __declspec(novtable) AdjustImplements +{ + typedef typename AdjustImplements::Type Type; +}; + + +template +struct __declspec(novtable) AdjustImplements +{ + typedef ImplementsHelper Type; +}; + + +// Specialization handles unadorned interfaces +template +struct __declspec(novtable) ImplementsHelper : + I0, + AdjustImplements::Type +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + VerifyInterfaceHelper::Verify(); + // Prefer InlineIsEqualGUID over other forms since it's better perf on 4-byte aligned data, which is almost always the case. + if (InlineIsEqualGUID(riid, __uuidof(I0))) + { + *ppv = reinterpret_cast(reinterpret_cast(this)); + return S_OK; + } + return AdjustImplements::Type::CanCastTo(riid, ppv, pRefDelegated); + } + + IUnknown* CastToUnknown() throw() + { + return reinterpret_cast(reinterpret_cast(this)); + } + + unsigned long GetIidCount() throw() + { + return 1 + AdjustImplements::Type::GetIidCount(); + } + + // FillArrayWithIid + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + *(iids + *index) = __uuidof(I0); + (*index)++; + AdjustImplements::Type::FillArrayWithIid(index, iids); + } +}; + + +// Selector is used to "tag" base interfaces to be used in casting, since a runtime class may indirectly derive from +// the same interface or Implements<> template multiple times +template +struct __declspec(novtable) Selector : public base +{ +}; + +// Specialization handles types that derive from ImplementsHelper (e.g. nested Implements). +template +struct __declspec(novtable) ImplementsHelper, TInterfaces...> : + Selector, TInterfaces...>>, + Selector::Type, ImplementsHelper, TInterfaces...>> +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + typedef Selector, TInterfaces...>> CurrentType; + typedef Selector::Type, ImplementsHelper, TInterfaces...>> BaseType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + VerifyInterfaceHelper::Verify(); + HRESULT hr = CurrentType::CanCastTo(riid, ppv); + if (hr == E_NOINTERFACE) + { + hr = BaseType::CanCastTo(riid, ppv, pRefDelegated); + } + return hr; + } + + IUnknown* CastToUnknown() throw() + { + // First in list wins. + return CurrentType::CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + return CurrentType::GetIidCount() + BaseType::GetIidCount(); + } + + // FillArrayWithIid + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + CurrentType::FillArrayWithIid(index, iids); + BaseType::FillArrayWithIid(index, iids); + } +}; + +// CloakedIid instance. Since the first "real" interface should be checked against doStrictCheck, +// pass this through unchanged. Two specializations for cloaked prevent the need to use the Selector +// used in the Implements<> case. The same can't be done there because some type ambiguities are unavoidable. +template +struct __declspec(novtable) ImplementsHelper, I1, TInterfaces...> : + AdjustImplements::Type, + AdjustImplements::Type +{ + template friend struct ImplementsHelper; + template friend class Details::RuntimeClassBaseT; + +protected: + + typedef typename AdjustImplements::Type CurrentType; + typedef typename AdjustImplements::Type BaseType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + VerifyInterfaceHelper::Verify(); + + HRESULT hr = CurrentType::CanCastTo(riid, ppv, pRefDelegated); + if (SUCCEEDED(hr)) + { + return S_OK; + } + return BaseType::CanCastTo(riid, ppv, pRefDelegated); + } + + IUnknown* CastToUnknown() throw() + { + return CurrentType::CastToUnknown(); + } + + // Don't expose the cloaked IID(s), but continue processing the rest of the interfaces + unsigned long GetIidCount() throw() + { + return BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + BaseType::FillArrayWithIid(index, iids); + } +}; + +template +struct __declspec(novtable) ImplementsHelper> : + AdjustImplements::Type +{ + template friend struct ImplementsHelper; + template friend class Details::RuntimeClassBaseT; + +protected: + + typedef typename AdjustImplements::Type CurrentType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + VerifyInterfaceHelper::Verify(); + + return CurrentType::CanCastTo(riid, ppv, pRefDelegated); + } + + IUnknown* CastToUnknown() throw() + { + return CurrentType::CastToUnknown(); + } + + // Don't expose the cloaked IID(s), but continue processing the rest of the interfaces + unsigned long GetIidCount() throw() + { + return 0; + } + + void FillArrayWithIid(_Inout_ unsigned long * /*index*/, _Inout_ IID* /*iids*/) throw() + { + // no-op + } +}; + + +// terminal case specialization. +template +struct __declspec(novtable) ImplementsHelper +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + template friend class Details::RuntimeClassBaseT; + + HRESULT CanCastTo(_In_ REFIID /*riid*/, _Outptr_ void ** /*ppv*/, bool * /*pRefDelegated*/ = nullptr) throw() + { + return E_NOINTERFACE; + } + + // IUnknown* CastToUnknown() throw(); // not defined for terminal case. + + unsigned long GetIidCount() throw() + { + return 0; + } + + void FillArrayWithIid(_Inout_ unsigned long * /*index*/, _Inout_ IID* /*iids*/) throw() + { + } +}; + +// Specialization handles chaining interfaces +template +struct __declspec(novtable) ImplementsHelper, TInterfaces...> : + ChainInterfaces, + AdjustImplements::Type +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + template friend class Details::RuntimeClassBaseT; + typedef typename AdjustImplements::Type BaseType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + ChainInterfaces::template Verify(); + + HRESULT hr = ChainInterfaces::CanCastTo(riid, ppv); + if (FAILED(hr)) + { + hr = BaseType::CanCastTo(riid, ppv, pRefDelegated); + } + + return hr; + } + + IUnknown* CastToUnknown() throw() + { + return ChainInterfaces::CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + return ChainInterfaces::IidCount + BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + ChainInterfaces::FillArrayWithIid(index, iids); + BaseType::FillArrayWithIid(index, iids); + } +}; + + +// Mixin specialization +template +struct __declspec(novtable) ImplementsHelper, TInterfaces...> : + AdjustImplements::Type +{ + static_assert(hasImplements, "Cannot use MixIn to with a class not deriving from \"Implements\""); + + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + template friend class Details::RuntimeClassBaseT; + typedef typename AdjustImplements::Type BaseType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + VerifyInterfaceHelper::Verify(); + + HRESULT hr = static_cast(static_cast(this))->CanCastTo(riid, ppv); + if (FAILED(hr)) + { + hr = BaseType::CanCastTo(riid, ppv, pRefDelegated); + } + + return hr; + } + + IUnknown* CastToUnknown() throw() + { + return static_cast(static_cast(this))->CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + return static_cast(static_cast(this))->GetIidCount() + + BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + static_cast(static_cast(this))->FillArrayWithIid(index, iids); + BaseType::FillArrayWithIid(index, iids); + } +}; + +// Specialization handles inheriting COM objects. ComposableBase must be the last non-nil interface in the list. +// Trailing nil's are allowed for compatibility with some tools that pad out the list. +template +struct AreAllNil +{ + static const bool value = false; +}; + +template +struct AreAllNil +{ + static const bool value = AreAllNil::value; +}; + +template <> +struct AreAllNil +{ + static const bool value = true; +}; + +template +struct __declspec(novtable) ImplementsHelper, TInterfaces...> : + ImplementsHelper> +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + template friend class Details::RuntimeClassBaseT; + + typedef ImplementsHelper> BaseType; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated = nullptr) throw() + { + static_assert(AreAllNil::value, "ComposableBase should be the last template parameter to RuntimeClass"); + return BaseType::CanCastTo(riid, ppv, pRefDelegated); + } + + IUnknown* CastToUnknown() throw() + { + static_assert(AreAllNil::value, "ComposableBase should be the last template parameter to RuntimeClass"); + return BaseType::CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + static_assert(AreAllNil::value, "ComposableBase should be the last template parameter to RuntimeClass"); + return BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + static_assert(AreAllNil::value, "ComposableBase should be the last template parameter to RuntimeClass"); + BaseType::FillArrayWithIid(index, iids); + } +}; + +template +struct __declspec(novtable) ImplementsHelper> +{ + template friend struct ImplementsHelper; + template friend class RuntimeClassBaseT; + +protected: + template friend class Details::RuntimeClassBaseT; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv, bool *pRefDelegated) throw() + { + *pRefDelegated = true; + return composableBase_.CopyTo(riid, ppv); + } + + IUnknown* CastToUnknown() throw() + { + return nullptr; + } + + unsigned long GetIidCount() throw() + { + return iidCount_; + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + for(unsigned long i = 0; i < iidCount_; i++) + { + *(iids + *index) = *(iidsCached_ + i); + (*index)++; + } + } + + ImplementsHelper() throw() : iidsCached_(nullptr), iidCount_(0) + { + } + + ~ImplementsHelper() throw() + { + ::CoTaskMemFree(iidsCached_); + iidsCached_ = nullptr; + iidCount_ = 0; + } + +public: + HRESULT SetComposableBasePointers(_In_ IInspectable* base, _In_opt_ FactoryInterface* baseFactory = nullptr) throw() + { + if (composableBase_ != nullptr) + { +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorHelper::OriginateError(E_UNEXPECTED, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_UNEXPECTED; + } + + HRESULT hr = base->GetIids(&iidCount_, &iidsCached_); + if (SUCCEEDED(hr)) + { + composableBase_ = base; + composableBaseFactory_ = baseFactory; + } + return hr; + } + + ComPtr GetComposableBase() throw() + { + return composableBase_; + } + + ComPtr GetComposableBaseFactory() throw() + { + return composableBaseFactory_; + } + +private: + ComPtr composableBase_; + ComPtr composableBaseFactory_; + IID *iidsCached_; + unsigned long iidCount_; +}; + +#pragma endregion // Implements helper templates + +} // namespace Details + +// Implements - template implementing QI using the information provided through its template parameters +// Each template parameter has to be one of the following: +// * COM Interface +// * A class that implements one or more COM interfaces +// * ChainInterfaces template +template +struct __declspec(novtable) Implements : + Details::AdjustImplements, true, I0, TInterfaces...>::Type, + Details::ImplementsBase +{ +public: + typedef RuntimeClassFlags ClassFlags; + typedef I0 FirstInterface; +protected: + typedef typename Details::AdjustImplements, true, I0, TInterfaces...>::Type BaseType; + template friend struct Details::ImplementsHelper; + template friend class Details::RuntimeClassBaseT; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw() + { + return BaseType::CanCastTo(riid, ppv); + } + + IUnknown* CastToUnknown() throw() + { + return BaseType::CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + return BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + BaseType::FillArrayWithIid(index, iids); + } +}; + +template +struct __declspec(novtable) Implements, I0, TInterfaces...> : + Details::AdjustImplements, true, I0, TInterfaces...>::Type, + Details::ImplementsBase +{ +public: + typedef RuntimeClassFlags ClassFlags; + typedef I0 FirstInterface; +protected: + + typedef typename Details::AdjustImplements, true, I0, TInterfaces...>::Type BaseType; + template friend struct Details::ImplementsHelper; + template friend class Details::RuntimeClassBaseT; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw() + { + return BaseType::CanCastTo(riid, ppv); + } + + IUnknown* CastToUnknown() throw() + { + return BaseType::CastToUnknown(); + } + + unsigned long GetIidCount() throw() + { + return BaseType::GetIidCount(); + } + + void FillArrayWithIid(_Inout_ unsigned long *index, _Inout_ IID* iids) throw() + { + BaseType::FillArrayWithIid(index, iids); + } +}; + +class FtmBase : + public Implements< + ::Microsoft::WRL::RuntimeClassFlags, + ::Microsoft::WRL::CloakedIid< ::IMarshal> >, + // Inheriting from FtmBaseMarker allows using FtmBase on classes configured with RuntimeClassFlags (Default configuration) + private ::Microsoft::WRL::Details::FtmBaseMarker +{ + // defining type 'Super' for other compilers since '__super' is a VC++-specific language extension + using Super = Implements< + ::Microsoft::WRL::RuntimeClassFlags, + ::Microsoft::WRL::CloakedIid< ::IMarshal> >; +protected: + template friend struct Details::ImplementsHelper; + + HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw() + { + // Prefer InlineIsEqualGUID over other forms since it's better perf on 4-byte aligned data, which is almost always the case. + if (InlineIsEqualGUID(riid, __uuidof(::IAgileObject))) + { + + *ppv = Super::CastToUnknown(); + return S_OK; + } + + return Super::CanCastTo(riid, ppv); + } + +public: + FtmBase() throw() + { + ComPtr unknown; + if (SUCCEEDED(::CoCreateFreeThreadedMarshaler(nullptr, &unknown))) + { + unknown.As(&marshaller_); + } + } + + // IMarshal Methods +#pragma warning(suppress: 6101) // PREFast cannot see through the smart-pointer invocation + STDMETHOD(GetUnmarshalClass)(_In_ REFIID riid, + _In_opt_ void *pv, + _In_ DWORD dwDestContext, + _Reserved_ void *pvDestContext, + _In_ DWORD mshlflags, + _Out_ CLSID *pCid) override + { + if (marshaller_) + { + return marshaller_->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlflags, pCid); + } + return E_OUTOFMEMORY; + } + +#pragma warning(suppress: 6101) // PREFast cannot see through the smart-pointer invocation + STDMETHOD(GetMarshalSizeMax)(_In_ REFIID riid, _In_opt_ void *pv, _In_ DWORD dwDestContext, + _Reserved_ void *pvDestContext, _In_ DWORD mshlflags, _Out_ DWORD *pSize) override + { + if (marshaller_) + { + return marshaller_->GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); + } + return E_OUTOFMEMORY; + } + + STDMETHOD(MarshalInterface)(_In_ IStream *pStm, _In_ REFIID riid, _In_opt_ void *pv, _In_ DWORD dwDestContext, + _Reserved_ void *pvDestContext, _In_ DWORD mshlflags) override + { + if (marshaller_) + { + return marshaller_->MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); + } + return E_OUTOFMEMORY; + } + +#pragma warning(suppress: 6101) // PREFast cannot see through the smart-pointer invocation + STDMETHOD(UnmarshalInterface)(_In_ IStream *pStm, _In_ REFIID riid, _Outptr_ void **ppv) override + { + if (marshaller_) + { + return marshaller_->UnmarshalInterface(pStm, riid, ppv); + } + return E_OUTOFMEMORY; + } + + STDMETHOD(ReleaseMarshalData)(_In_ IStream *pStm) override + { + if (marshaller_) + { + return marshaller_->ReleaseMarshalData(pStm); + } + return E_OUTOFMEMORY; + } + + STDMETHOD(DisconnectObject)(_In_ DWORD dwReserved) override + { + if (marshaller_) + { + return marshaller_->DisconnectObject(dwReserved); + } + return E_OUTOFMEMORY; + } + + static HRESULT CreateGlobalInterfaceTable(_Out_ IGlobalInterfaceTable **git) throw() + { + *git = nullptr; + return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IGlobalInterfaceTable), + reinterpret_cast(git)); + } + + ::Microsoft::WRL::ComPtr marshaller_; // Holds a reference to the free threaded marshaler +}; + +namespace Details +{ + +#ifdef _PERF_COUNTERS +class __declspec(novtable) PerfCountersBase +{ +public: + ULONG GetAddRefCount() throw() + { + return addRefCount_; + } + + ULONG GetReleaseCount() throw() + { + return releaseCount_; + } + + ULONG GetQueryInterfaceCount() throw() + { + return queryInterfaceCount_; + } + + void ResetPerfCounters() throw() + { + addRefCount_ = 0; + releaseCount_ = 0; + queryInterfaceCount_ = 0; + } + +protected: + PerfCountersBase() throw() : + addRefCount_(0), + releaseCount_(0), + queryInterfaceCount_(0) + { + } + + void IncrementAddRefCount() throw() + { + InterlockedIncrement(&addRefCount_); + } + + void IncrementReleaseCount() throw() + { + InterlockedIncrement(&releaseCount_); + } + + void IncrementQueryInterfaceCount() throw() + { + InterlockedIncrement(&queryInterfaceCount_); + } + +private: + volatile unsigned long addRefCount_; + volatile unsigned long releaseCount_; + volatile unsigned long queryInterfaceCount_; +}; +#endif + +#if defined(_X86_) || defined(_AMD64_) + +#define UnknownIncrementReference InterlockedIncrement +#define UnknownDecrementReference InterlockedDecrement +#define UnknownBarrierAfterInterlock() +#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer +#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointer +#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointer + +#elif defined(_ARM_) + +#define UnknownIncrementReference InterlockedIncrementNoFence +#define UnknownDecrementReference InterlockedDecrementRelease +#define UnknownBarrierAfterInterlock() __dmb(_ARM_BARRIER_ISH) +#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer +#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence +#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease + +#elif defined(_ARM64_) + +#define UnknownIncrementReference InterlockedIncrementNoFence +#define UnknownDecrementReference InterlockedDecrementRelease +#define UnknownBarrierAfterInterlock() __dmb(_ARM64_BARRIER_ISH) +#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer +#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence +#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease + +#else + +#error Unsupported architecture. + +#endif + +// Since variadic templates can't have a parameter pack after default arguments, provide a convenient helper for defaults. +#define DETAILS_RTCLASS_FLAGS_ARGUMENTS(RuntimeClassFlagsT) \ + RuntimeClassFlagsT, \ + (RuntimeClassFlagsT::value & InhibitWeakReference) == 0, \ + (RuntimeClassFlagsT::value & WinRt) == WinRt, \ + __WRL_IMPLEMENTS_FTM_BASE__(RuntimeClassFlagsT::value) \ + +template +class __declspec(novtable) RuntimeClassImpl; + +#pragma warning(push) +// PREFast cannot see through template instantiation for AsIID() +#pragma warning(disable: 6388) + +template +class __declspec(novtable) RuntimeClassImpl : + public Details::AdjustImplements::Type, + public RuntimeClassBaseT, + protected RuntimeClassFlags, + public DontUseNewUseMake +#ifdef _PERF_COUNTERS + , public PerfCountersBase +#endif +{ +public: + typedef RuntimeClassFlagsT ClassFlags; + + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) + { +#ifdef _PERF_COUNTERS + IncrementQueryInterfaceCount(); +#endif + return Super::AsIID(this, riid, ppvObject); + } + + STDMETHOD_(ULONG, AddRef)() + { + return InternalAddRef(); + } + + STDMETHOD_(ULONG, Release)() + { + ULONG ref = InternalRelease(); + if (ref == 0) + { + delete this; + + auto modulePtr = ::Microsoft::WRL::GetModuleBase(); + if (modulePtr != nullptr) + { + modulePtr->DecrementObjectCount(); + } + } + + return ref; + } + +protected: + using Super = RuntimeClassBaseT; + + RuntimeClassImpl() throw() : refcount_(1) + { + } + + virtual ~RuntimeClassImpl() throw() + { + // Set refcount_ to -(LONG_MAX/2) to protect destruction and + // also catch mismatched Release in debug builds + refcount_ = -(LONG_MAX/2); + } + + unsigned long InternalAddRef() throw() + { +#ifdef _PERF_COUNTERS + IncrementAddRefCount(); +#endif + return UnknownIncrementReference(&refcount_); + } + + unsigned long InternalRelease() throw() + { +#ifdef _PERF_COUNTERS + IncrementReleaseCount(); +#endif + // A release fence is required to ensure all guarded memory accesses are + // complete before any thread can begin destroying the object. + unsigned long newValue = UnknownDecrementReference(&refcount_); + if (newValue == 0) + { + // An acquire fence is required before object destruction to ensure + // that the destructor cannot observe values changing on other threads. + UnknownBarrierAfterInterlock(); + } + return newValue; + } + + unsigned long GetRefCount() const throw() + { + return refcount_; + } + + friend class WeakReferenceImpl; + +private: + volatile long refcount_; +}; + +template +struct HasIInspectable; + +template +struct HasIInspectable +{ + static const bool isIInspectable = __is_base_of(IInspectable, I); +}; + +template +struct HasIInspectable +{ + static const bool isIInspectable = HasIInspectable::isIInspectable; +}; + +#ifdef __WRL_STRICT__ +template +#else +template::isIInspectable> +#endif +struct IInspectableInjector; + +template +struct IInspectableInjector +{ + typedef Details::Nil InspectableIfNeeded; +}; + +template +struct IInspectableInjector +{ + typedef IInspectable InspectableIfNeeded; +}; + +// Implements IInspectable in ILst +template +class __declspec(novtable) RuntimeClassImpl : + public Details::AdjustImplements::InspectableIfNeeded, I0, TInterfaces...>::Type, + public RuntimeClassBaseT, + protected RuntimeClassFlags, + public DontUseNewUseMake +#ifdef _PERF_COUNTERS + , public PerfCountersBase +#endif +{ +public: + typedef RuntimeClassFlagsT ClassFlags; + + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) + { +#ifdef _PERF_COUNTERS + IncrementQueryInterfaceCount(); +#endif + return Super::AsIID(this, riid, ppvObject); + } + + STDMETHOD_(ULONG, AddRef)() + { + return InternalAddRef(); + } + + STDMETHOD_(ULONG, Release)() + { + ULONG ref = InternalRelease(); + if (ref == 0) + { + delete this; + + auto modulePtr = ::Microsoft::WRL::GetModuleBase(); + if (modulePtr != nullptr) + { + modulePtr->DecrementObjectCount(); + } + } + + return ref; + } + + // IInspectable methods + STDMETHOD(GetIids)( + _Out_ ULONG *iidCount, + _When_(*iidCount == 0, _At_(*iids, _Post_null_)) + _When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) + _Result_nullonfailure_ IID **iids) + { + return Super::GetImplementedIIDS(this, iidCount, iids); + } + +#if !defined(__WRL_STRICT__) || !defined(__WRL_FORCE_INSPECTABLE_CLASS_MACRO__) + STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeClassName) + { + *runtimeClassName = nullptr; + + __WRL_ASSERT__(false && "Use InspectableClass macro to set runtime class name and trust level."); + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorHelper::OriginateError(E_NOTIMPL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_NOTIMPL; + } + + STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel*) + { + __WRL_ASSERT__(false && "Use InspectableClass macro to set runtime class name and trust level."); + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorHelper::OriginateError(E_NOTIMPL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_NOTIMPL; + } +#endif // !defined(__WRL_STRICT__) || !defined(__WRL_FORCE_INSPECTABLE_CLASS_MACRO__) + +protected: + using Super = RuntimeClassBaseT; + + RuntimeClassImpl() throw() : refcount_(1) + { + } + + virtual ~RuntimeClassImpl() throw() + { + // Set refcount_ to -(LONG_MAX/2) to protect destruction and + // also catch mismatched Release in debug builds + refcount_ = -(LONG_MAX/2); + } + + unsigned long InternalAddRef() throw() + { +#ifdef _PERF_COUNTERS + IncrementAddRefCount(); +#endif + return UnknownIncrementReference(&refcount_); + } + + unsigned long InternalRelease() throw() + { +#ifdef _PERF_COUNTERS + IncrementReleaseCount(); +#endif + // A release fence is required to ensure all guarded memory accesses are + // complete before any thread can begin destroying the object. + unsigned long newValue = UnknownDecrementReference(&refcount_); + if (newValue == 0) + { + // An acquire fence is required before object destruction to ensure + // that the destructor cannot observe values changing on other threads. + UnknownBarrierAfterInterlock(); + } + return newValue; + } + + unsigned long GetRefCount() const throw() + { + return refcount_; + } +private: + volatile long refcount_; +}; + +class StrongReference +{ +public: + StrongReference(long refCount = 1) throw() : strongRefCount_(refCount) {} + + ~StrongReference() throw() + { + // Set refcount_ to -(LONG_MAX/2) to protect destruction and + // also catch mismatched Release in debug builds + strongRefCount_ = -(LONG_MAX / 2); + } + + unsigned long IncrementStrongReference() throw() + { + return UnknownIncrementReference(&strongRefCount_); + } + + unsigned long DecrementStrongReference() throw() + { + // A release fence is required to ensure all guarded memory accesses are + // complete before any thread can begin destroying the object. + unsigned long newValue = UnknownDecrementReference(&strongRefCount_); + if (newValue == 0) + { + // An acquire fence is required before object destruction to ensure + // that the destructor cannot observe values changing on other threads. + UnknownBarrierAfterInterlock(); + } + return newValue; + } + + unsigned long GetStrongReferenceCount() throw() + { + return strongRefCount_; + } + + void SetStrongReference(unsigned long value) throw() + { + strongRefCount_ = value; + } + + long strongRefCount_; +}; + +// To support storing, encoding and decoding reference-count/pointers regardless of the target platform. +// In a RuntimeClass, the refCount_ member can mean either +// 1. actual reference count +// 2. pointer to the weak reference object which holds the strong count +// The member +// 1. If it is a count, the most significant bit will be OFF +// 2. If it is an encoded pointer to the weak reference, the most significant bit will be turned ON +// To test which mode it is +// 1. Test for negative +// 2. If it is, it is an encoded pointer to the weak reference +// 3. If it is not, it is the actual reference count +// To yield the encoded pointer +// 1. Test the value for negative +// 2. If it is, shift the value to the left and cast it to a WeakReferenceImpl* +// +const UINT_PTR EncodeWeakReferencePointerFlag = static_cast(1) << ((sizeof(UINT_PTR) * 8) - 1); + +union ReferenceCountOrWeakReferencePointer +{ + // Represents the count when it is a count (in practice only the least significant 4 bytes) + UINT_PTR refCount; + // Pointer size, *signed* to help with ease of casting and such + INT_PTR rawValue; + // The hint that this could also be a pointer + void* ifHighBitIsSetThenShiftLeftToYieldPointerToWeakReference; +}; + +// Helper methods to test, decode and decode the different representations of ReferenceCountOrWeakReferencePointer +inline bool IsValueAPointerToWeakReference(INT_PTR value) +{ + return value < 0; +} + +// Forward declaration +class WeakReferenceImpl; +inline INT_PTR EncodeWeakReferencePointer(Microsoft::WRL::Details::WeakReferenceImpl* value); +inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value); + +// Helper functions, originally from winnt.h, needed to get the semantics right when fetching values +// in multi-threaded scenarios. This is in order to guarantee the compiler emits exactly one read +// (compiler may decide to re-fetch the value later on, which in some cases can break code) +#if defined(_ARM_) + +FORCEINLINE +LONG +ReadULongPtrNoFence ( + _In_ _Interlocked_operand_ DWORD const volatile *Source + ) + +{ + LONG Value; + + Value = __iso_volatile_load32((int *)Source); + return Value; +} + +#elif defined(_ARM64_) + +FORCEINLINE +LONG64 +ReadULongPtrNoFence ( + _In_ _Interlocked_operand_ DWORD64 const volatile *Source + ) + +{ + LONG64 Value; + + Value = __iso_volatile_load64((__int64 *)Source); + return Value; +} + +#elif defined(_X86_) + +FORCEINLINE +LONG +ReadULongPtrNoFence ( + _In_ _Interlocked_operand_ DWORD const volatile *Source + ) + +{ + LONG Value; + + Value = *Source; + return Value; +} + +#elif defined(_AMD64_) + +FORCEINLINE +LONG64 +ReadULongPtrNoFence ( + _In_ _Interlocked_operand_ DWORD64 const volatile *Source + ) + +{ + LONG64 Value; + + Value = *Source; + return Value; +} + +#else + +#error Unsupported architecture. + +#endif + +template +inline T ReadValueFromPointerNoFence(_In_ const volatile T* value) +{ + ULONG_PTR currentValue = ReadULongPtrNoFence(reinterpret_cast(value)); + const T* currentPointerToValue = reinterpret_cast(¤tValue); + return *currentPointerToValue; +} + +inline WeakReferenceImpl* CreateWeakReference(_In_ IUnknown*); + +// Implementation of activatable class that implements IWeakReferenceSource +// and delegates reference counting to WeakReferenceImpl object +template +class __declspec(novtable) RuntimeClassImpl : + public Details::AdjustImplements::InspectableIfNeeded, I0, IWeakReferenceSource, TInterfaces...>::Type, + public RuntimeClassBaseT, + public DontUseNewUseMake +#ifdef _PERF_COUNTERS + , public PerfCountersBase +#endif +{ +public: + typedef RuntimeClassFlagsT ClassFlags; + + RuntimeClassImpl() throw() + { + refCount_.rawValue = 1; + } + + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) + { +#ifdef _PERF_COUNTERS + IncrementQueryInterfaceCount(); +#endif + return Super::AsIID(this, riid, ppvObject); + } + + STDMETHOD_(ULONG, AddRef)() + { + return InternalAddRef(); + } + + STDMETHOD_(ULONG, Release)() + { + ULONG ref = InternalRelease(); + if (ref == 0) + { + delete this; + + auto modulePtr = ::Microsoft::WRL::GetModuleBase(); + if (modulePtr != nullptr) + { + modulePtr->DecrementObjectCount(); + } + } + + return ref; + } + + // IInspectable methods + STDMETHOD(GetIids)( + _Out_ ULONG *iidCount, + _When_(*iidCount == 0, _At_(*iids, _Post_null_)) + _When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) + _Result_nullonfailure_ IID **iids) + { + return Super::GetImplementedIIDS(this, iidCount, iids); + } + +#if !defined(__WRL_STRICT__) || !defined(__WRL_FORCE_INSPECTABLE_CLASS_MACRO__) + STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeClassName) + { + *runtimeClassName = nullptr; + + __WRL_ASSERT__(false && "Use InspectableClass macro to set runtime class name and trust level."); + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorHelper::OriginateError(E_NOTIMPL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_NOTIMPL; + } + + STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel*) + { + __WRL_ASSERT__(false && "Use InspectableClass macro to set runtime class name and trust level."); +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + ErrorHelper::OriginateError(E_NOTIMPL, nullptr); +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) + return E_NOTIMPL; + } +#endif // !defined(__WRL_STRICT__) || !defined(__WRL_FORCE_INSPECTABLE_CLASS_MACRO__) + + STDMETHOD(GetWeakReference)(_Outptr_ IWeakReference **weakReference); + + virtual ~RuntimeClassImpl() throw(); + +protected: + template friend class Details::RuntimeClassBaseT; + using ImplementsHelper = typename Details::AdjustImplements::InspectableIfNeeded, I0, IWeakReferenceSource, TInterfaces...>::Type; + using Super = RuntimeClassBaseT; + + unsigned long InternalAddRef() throw(); + + unsigned long InternalRelease() throw(); + + unsigned long GetRefCount() const throw(); + + friend class WeakReferenceImpl; + +#ifdef __WRL_UNITTEST__ +protected: +#else +private: +#endif + ReferenceCountOrWeakReferencePointer refCount_; +}; + +inline INT_PTR EncodeWeakReferencePointer(Microsoft::WRL::Details::WeakReferenceImpl* value) +{ + return ((reinterpret_cast(value) >> 1) | EncodeWeakReferencePointerFlag); +} + +inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value) +{ + return reinterpret_cast(value << 1); +} + +#pragma warning(pop) // C6388 + +template +class __declspec(novtable) RuntimeClassImpl : + public RuntimeClassImpl +{ +}; + +template +class __declspec(novtable) RuntimeClassImpl : + public RuntimeClassImpl +{ +}; + +// To minimize breaks with code written against WRL before variadic support was added, this form is maintained. +template +struct InterfaceListHelper +{ + typedef InterfaceListHelper TypeT; +}; + +template < + typename ILst, + class RuntimeClassFlagsT, + bool implementsWeakReferenceSource = (RuntimeClassFlagsT::value & InhibitWeakReference) == 0, + bool implementsInspectable = (RuntimeClassFlagsT::value & WinRt) == WinRt, + bool implementsFtmBase = __WRL_IMPLEMENTS_FTM_BASE__(RuntimeClassFlagsT::value) +> +class RuntimeClass; + + +template < + typename RuntimeClassFlagsT, + bool implementsWeakReferenceSource, + bool implementsInspectable, + bool implementsFtmBase, + typename ...TInterfaces +> +class RuntimeClass, RuntimeClassFlagsT, implementsWeakReferenceSource, implementsInspectable, implementsFtmBase> : + public RuntimeClassImpl +{ +protected: + HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled) + { + *handled = false; + return S_OK; + } +}; + +} // namespace Details + +// The RuntimeClass IUnknown methods +// It inherits from Details::RuntimeClass that provides helper methods for reference counting and +// collecting IIDs +template +class RuntimeClass : + public Details::RuntimeClassImpl), TInterfaces...> +{ + RuntimeClass(const RuntimeClass&); + RuntimeClass& operator=(const RuntimeClass&); +protected: + HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled) + { + *handled = false; + return S_OK; + } +public: + RuntimeClass() throw() + { + auto modulePtr = ::Microsoft::WRL::GetModuleBase(); + if (modulePtr != nullptr) + { + modulePtr->IncrementObjectCount(); + } + } + typedef RuntimeClass RuntimeClassT; +}; + +template +class RuntimeClass, TInterfaces...> : + public Details::RuntimeClassImpl), TInterfaces...> +{ + RuntimeClass(const RuntimeClass&); + RuntimeClass& operator=(const RuntimeClass&); +protected: + HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled) + { + *handled = false; + return S_OK; + } +public: + RuntimeClass() throw() + { + auto modulePtr = ::Microsoft::WRL::GetModuleBase(); + if (modulePtr != nullptr) + { + modulePtr->IncrementObjectCount(); + } + } + typedef RuntimeClass RuntimeClassT; +}; + +namespace Details +{ +//Weak reference implementation + class WeakReferenceImpl sealed: + public ::Microsoft::WRL::RuntimeClass, IWeakReference>, + public StrongReference + { + public: + WeakReferenceImpl(_In_ IUnknown* unk) throw() : StrongReference(LONG_MAX / 2), unknown_(unk) + { + // Set ref count to 2 to avoid unnecessary interlocked increment operation while returning + // WeakReferenceImpl from GetWeakReference method. One reference is hold by the object the second is hold + // by the caller of GetWeakReference method. + refcount_ = 2; + } + + virtual ~WeakReferenceImpl() throw() + { + } + + STDMETHOD(Resolve)(REFIID riid, _Outptr_result_maybenull_ _Result_nullonfailure_ IInspectable **ppvObject) + { + *ppvObject = nullptr; + + for(;;) + { + long ref = this->strongRefCount_; + if (ref == 0) + { + return S_OK; + } + + // InterlockedCompareExchange calls _InterlockedCompareExchange intrinsic thus we call directly _InterlockedCompareExchange to save the call + if (::_InterlockedCompareExchange(&this->strongRefCount_, ref + 1, ref) == ref) + { +#ifdef _PERF_COUNTERS + // This artificially manipulates the strong ref count via AddRef to account for the resolve + // interlocked operation above when tallying reference counting operations. + unknown_->AddRef(); + ::_InterlockedDecrement(&this->strongRefCount_); +#endif + break; + } + } + + HRESULT hr = unknown_->QueryInterface(riid, reinterpret_cast(ppvObject)); + unknown_->Release(); + return hr; + } + + +private: + IUnknown *unknown_; +}; + +template +RuntimeClassImpl::~RuntimeClassImpl() throw() +{ + if (IsValueAPointerToWeakReference(refCount_.rawValue)) + { + WeakReferenceImpl* weakRef = DecodeWeakReferencePointer(refCount_.rawValue); + weakRef->Release(); + weakRef = nullptr; + } +} + +template +unsigned long RuntimeClassImpl::GetRefCount() const throw() +{ + ReferenceCountOrWeakReferencePointer currentValue = ReadValueFromPointerNoFence(&refCount_); + + if (IsValueAPointerToWeakReference(currentValue.rawValue)) + { + WeakReferenceImpl* weakRef = DecodeWeakReferencePointer(currentValue.rawValue); + return weakRef->GetStrongReferenceCount(); + } + else + { + return static_cast(currentValue.refCount); + } +} + +template +unsigned long RuntimeClassImpl::InternalAddRef() throw() +{ +#ifdef _PERF_COUNTERS + IncrementAddRefCount(); +#endif + + ReferenceCountOrWeakReferencePointer currentValue = ReadValueFromPointerNoFence(&refCount_); + + for (;;) + { + if (!IsValueAPointerToWeakReference(currentValue.rawValue)) + { + UINT_PTR updateValue = currentValue.refCount + 1; + +#ifdef __WRL_UNITTEST__ + OnBeforeInternalAddRefIncrement(); +#endif + + INT_PTR previousValue = reinterpret_cast(UnknownInterlockedCompareExchangePointerForIncrement(reinterpret_cast(&(refCount_.refCount)), reinterpret_cast(updateValue), reinterpret_cast(currentValue.refCount))); + if (previousValue == currentValue.rawValue) + { + return static_cast(updateValue); + } + + currentValue.rawValue = previousValue; + } + else + { + WeakReferenceImpl* weakRef = DecodeWeakReferencePointer(currentValue.rawValue); + return weakRef->IncrementStrongReference(); + } + } +} + +template +unsigned long RuntimeClassImpl::InternalRelease() throw() +{ +#ifdef _PERF_COUNTERS + IncrementReleaseCount(); +#endif + + ReferenceCountOrWeakReferencePointer currentValue = ReadValueFromPointerNoFence(&refCount_); + + for (;;) + { + if (!IsValueAPointerToWeakReference(currentValue.rawValue)) + { + UINT_PTR updateValue = currentValue.refCount - 1; + +#ifdef __WRL_UNITTEST__ + OnBeforeInternalReleaseDecrement(); +#endif + + INT_PTR previousValue = reinterpret_cast(UnknownInterlockedCompareExchangePointerForIncrement(reinterpret_cast(&(refCount_.refCount)), reinterpret_cast(updateValue), reinterpret_cast(currentValue.refCount))); + if (previousValue == currentValue.rawValue) + { + return static_cast(updateValue); + } + + currentValue.rawValue = previousValue; + } + else + { + WeakReferenceImpl* weakRef = DecodeWeakReferencePointer(currentValue.rawValue); + return weakRef->DecrementStrongReference(); + } + } +} + +template +COM_DECLSPEC_NOTHROW HRESULT RuntimeClassImpl::GetWeakReference(_Outptr_ IWeakReference **weakReference) +{ + WeakReferenceImpl* weakRef = nullptr; + INT_PTR encodedWeakRef = 0; + ReferenceCountOrWeakReferencePointer currentValue = ReadValueFromPointerNoFence(&refCount_); + + *weakReference = nullptr; + + if (IsValueAPointerToWeakReference(currentValue.rawValue)) + { + weakRef = DecodeWeakReferencePointer(currentValue.rawValue); + + weakRef->AddRef(); + *weakReference = weakRef; + return S_OK; + } + + // WeakReferenceImpl is created with ref count 2 to avoid interlocked increment + weakRef = CreateWeakReference(ImplementsHelper::CastToUnknown()); + if (weakRef == nullptr) + { + return E_OUTOFMEMORY; + } + + encodedWeakRef = EncodeWeakReferencePointer(weakRef); + + for (;;) + { + INT_PTR previousValue = 0; + + weakRef->SetStrongReference(static_cast(currentValue.refCount)); + +#ifdef __WRL_UNITTEST__ + OnBeforeGetWeakReferenceSwap(); +#endif + + previousValue = reinterpret_cast(UnknownInterlockedCompareExchangePointer(reinterpret_cast(&(this->refCount_.ifHighBitIsSetThenShiftLeftToYieldPointerToWeakReference)), reinterpret_cast(encodedWeakRef), currentValue.ifHighBitIsSetThenShiftLeftToYieldPointerToWeakReference)); + if (previousValue == currentValue.rawValue) + { + // No need to call AddRef in this case, WeakReferenceImpl is created with ref count 2 to avoid interlocked increment + *weakReference = weakRef; + return S_OK; + } + else if (IsValueAPointerToWeakReference(previousValue)) + { + // Another thread beat this call to create the weak reference. + + delete weakRef; + + weakRef = DecodeWeakReferencePointer(previousValue); + weakRef->AddRef(); + *weakReference = weakRef; + return S_OK; + } + + // Another thread won via an AddRef or Release. + // Let's try again + currentValue.rawValue = previousValue; + } +} + +// Memory allocation for object that doesn't support weak references +// It only allocates memory +template +class MakeAllocator +{ +public: + MakeAllocator() throw() : buffer_(nullptr) + { + } + + ~MakeAllocator() throw() + { + if (buffer_ != nullptr) + { + delete buffer_; + } + } + + void* Allocate() throw() + { + __WRL_ASSERT__(buffer_ == nullptr); + // Allocate memory with operator new(size, nothrow) only + // This will allow developer to override one operator only + // to enable different memory allocation model + buffer_ = (char*) (operator new (sizeof(T), std::nothrow)); + return buffer_; + } + + void Detach() throw() + { + buffer_ = nullptr; + } +private: + char* buffer_; +}; + +} //Details + +#pragma region make overloads + +namespace Details { + +// Make and MakeAndInitialize functions must not be marked as throw() as the constructor is allowed to throw exceptions. +template +ComPtr Make(TArgs&&... args) +{ + static_assert(__is_base_of(Details::RuntimeClassBase, T), "Make can only instantiate types that derive from RuntimeClass"); + ComPtr object; + Details::MakeAllocator allocator; + void *buffer = allocator.Allocate(); + if (buffer != nullptr) + { + auto ptr = new (buffer)T(Details::Forward(args)...); + object.Attach(ptr); + allocator.Detach(); + } + return object; +} + +#pragma warning(push) +#pragma warning(disable:6387 6388 28196) // PREFast does not understand call to ComPtr::CopyTo() is safe here + +template +HRESULT MakeAndInitialize(_Outptr_result_nullonfailure_ I** result, TArgs&&... args) +{ + static_assert(__is_base_of(Details::RuntimeClassBase, T), "Make can only instantiate types that derive from RuntimeClass"); + static_assert(__is_base_of(I, T), "The 'T' runtime class doesn't implement 'I' interface"); + *result = nullptr; + Details::MakeAllocator allocator; + void *buffer = allocator.Allocate(); + if (buffer == nullptr) { return E_OUTOFMEMORY; } + auto ptr = new (buffer)T; + ComPtr object; + object.Attach(ptr); + allocator.Detach(); + HRESULT hr = object->RuntimeClassInitialize(Details::Forward(args)...); + if (FAILED(hr)) { return hr; } + return object.CopyTo(result); +} + +#pragma warning(pop) // C6387 C6388 C28196 + +template +HRESULT MakeAndInitialize(_Inout_ ComPtrRef> ppvObject, TArgs&&... args) +{ + return MakeAndInitialize(ppvObject.ReleaseAndGetAddressOf(), Details::Forward(args)...); +} + +} //end of Details + +using Details::MakeAndInitialize; +using Details::Make; + +#pragma endregion // make overloads + +namespace Details +{ + inline WeakReferenceImpl* CreateWeakReference(_In_ IUnknown* unk) + { + return Make(unk).Detach(); + } +} + +#define InspectableClass(runtimeClassName, trustLevel) \ + public: \ + static _Null_terminated_ const wchar_t* STDMETHODCALLTYPE InternalGetRuntimeClassName() throw() \ + { \ + static_assert((RuntimeClassT::ClassFlags::value & ::Microsoft::WRL::WinRtClassicComMix) == ::Microsoft::WRL::WinRt || \ + (RuntimeClassT::ClassFlags::value & ::Microsoft::WRL::WinRtClassicComMix) == ::Microsoft::WRL::WinRtClassicComMix, \ + "'InspectableClass' macro must not be used with ClassicCom clasess."); \ + static_assert(__is_base_of(::Microsoft::WRL::Details::RuntimeClassBase, RuntimeClassT), "'InspectableClass' macro can only be used with ::Windows::WRL::RuntimeClass types"); \ + static_assert(!__is_base_of(IActivationFactory, RuntimeClassT), "Incorrect usage of IActivationFactory interface. Make sure that your RuntimeClass doesn't implement IActivationFactory interface use ::Windows::WRL::ActivationFactory instead or 'InspectableClass' macro is not used on ::Windows::WRL::ActivationFactory"); \ + return runtimeClassName; \ + } \ + static ::TrustLevel STDMETHODCALLTYPE InternalGetTrustLevel() throw() \ + { \ + return trustLevel; \ + } \ + STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) \ + { \ + *runtimeName = nullptr; \ + HRESULT hr = S_OK; \ + auto name = InternalGetRuntimeClassName(); \ + if (name != nullptr) \ + { \ + hr = ::WindowsCreateString(name, static_cast(::wcslen(name)), runtimeName); \ + } \ + return hr; \ + } \ + STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) \ + { \ + *trustLvl = trustLevel; \ + return S_OK; \ + } \ + STDMETHOD(GetIids)(_Out_ ULONG *iidCount, \ + _When_(*iidCount == 0, _At_(*iids, _Post_null_)) \ + _When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) \ + _Result_nullonfailure_ IID **iids) \ + { \ + return RuntimeClassT::GetIids(iidCount, iids); \ + } \ + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) \ + { \ + bool handled = false; \ + HRESULT hr = this->CustomQueryInterface(riid, ppvObject, &handled); \ + if (FAILED(hr) || handled) return hr; \ + return RuntimeClassT::QueryInterface(riid, ppvObject); \ + } \ + STDMETHOD_(ULONG, Release)() \ + { \ + return RuntimeClassT::Release(); \ + } \ + STDMETHOD_(ULONG, AddRef)() \ + { \ + return RuntimeClassT::AddRef(); \ + } \ + private: + +#define MixInHelper() \ + public: \ + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) \ + { \ + static_assert((RuntimeClassT::ClassFlags::value & ::Microsoft::WRL::WinRt) == 0, "'MixInClass' macro must not be used with WinRt clasess."); \ + static_assert(__is_base_of(::Microsoft::WRL::Details::RuntimeClassBase, RuntimeClassT), "'MixInHelper' macro can only be used with ::Windows::WRL::RuntimeClass types"); \ + static_assert(!__is_base_of(IClassFactory, RuntimeClassT), "Incorrect usage of IClassFactory interface. Make sure that your RuntimeClass doesn't implement IClassFactory interface use ::Windows::WRL::ClassFactory instead or 'MixInHelper' macro is not used on ::Windows::WRL::ClassFactory"); \ + return RuntimeClassT::QueryInterface(riid, ppvObject); \ + } \ + STDMETHOD_(ULONG, Release)() \ + { \ + return RuntimeClassT::Release(); \ + } \ + STDMETHOD_(ULONG, AddRef)() \ + { \ + return RuntimeClassT::AddRef(); \ + } \ + private: + +// Please make sure that those macros are in sync with those ones from 'wrl/module.h' +#ifndef WrlCreatorMapIncludePragmaEx +#define WrlCreatorMapIncludePragmaEx(className, group) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'WrlCreatorMapIncludePragmaEx' macro"); +#endif + +#ifndef WrlCreatorMapIncludePragma +#define WrlCreatorMapIncludePragma(className) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'WrlCreatorMapIncludePragma' macro"); +#endif + +#ifndef ActivatableClassWithFactoryEx +#define ActivatableClassWithFactoryEx(className, factory, groupId) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'ActivatableClassWithFactoryEx' macro"); +#endif + +#ifndef ActivatableClassWithFactory +#define ActivatableClassWithFactory(className, factory) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'ActivatableClassWithFactory' macro"); +#endif + +#ifndef ActivatableClass +#define ActivatableClass(className) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'ActivatableClass' macro"); +#endif + +#ifndef ActivatableStaticOnlyFactoryEx +#define ActivatableStaticOnlyFactoryEx(factory, serverName) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'ActivatableStaticOnlyFactoryEx' macro"); +#endif + +#ifndef ActivatableStaticOnlyFactory +#define ActivatableStaticOnlyFactory(factory) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'ActivatableStaticOnlyFactory' macro"); +#endif + +#ifndef CoCreatableClassWithFactoryEx +#define CoCreatableClassWithFactoryEx(className, factory, groupId) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'CoCreatableClassWithFactory' macro"); +#endif + +#ifndef CoCreatableClassWithFactory +#define CoCreatableClassWithFactory(className, factory) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'CoCreatableClassWithFactory' macro"); +#endif + +#ifndef CoCreatableClass +#define CoCreatableClass(className) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'CoCreatableClass' macro"); +#endif + +#ifndef CoCreatableClassWrlCreatorMapInclude +#define CoCreatableClassWrlCreatorMapInclude(className) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'CoCreatableClassWrlCreatorMapInclude' macro"); +#endif + +#ifndef CoCreatableClassWrlCreatorMapIncludeEx +#define CoCreatableClassWrlCreatorMapIncludeEx(className, groupId) static_assert(false, "It's required to include 'wrl/module.h' to be able to use 'CoCreatableClassWrlCreatorMapInclude' macro"); +#endif + +#undef UnknownIncrementReference +#undef UnknownDecrementReference +#undef UnknownBarrierAfterInterlock +#undef UnknownInterlockedCompareExchangePointer +#undef UnknownInterlockedCompareExchangePointerForIncrement +#undef UnknownInterlockedCompareExchangePointerForRelease + +}} // namespace Microsoft::WRL + +#pragma warning(pop) + +// Restore packing +#include + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // _WRL_IMPLEMENTS_H_ diff --git a/Externals/licenses.md b/Externals/licenses.md index 094e5e06dd..3a85373930 100644 --- a/Externals/licenses.md +++ b/Externals/licenses.md @@ -60,6 +60,8 @@ Dolphin includes or links code of the following third-party software projects: [LGPLv2+](http://www.surina.net/soundtouch/license.html) - [TAP-Windows](https://openvpn.net/): header only +- [Windows Implementation Libraries](https://github.com/microsoft/wil): + [MIT](https://github.com/microsoft/wil/blob/master/LICENSE) - [xxHash](https://github.com/Cyan4973/xxHash): [2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE) - [zlib](http://www.zlib.net/): diff --git a/Source/VSProps/Base.props b/Source/VSProps/Base.props index ef85a44f7b..beaee0509e 100644 --- a/Source/VSProps/Base.props +++ b/Source/VSProps/Base.props @@ -53,6 +53,7 @@ $(ExternalsDir)pugixml;%(AdditionalIncludeDirectories) $(ExternalsDir)SFML\include;%(AdditionalIncludeDirectories) $(ExternalsDir)Vulkan\include;%(AdditionalIncludeDirectories) + $(ExternalsDir)WIL\include;%(AdditionalIncludeDirectories) $(ExternalsDir)xxhash;%(AdditionalIncludeDirectories) $(ExternalsDir)zlib;%(AdditionalIncludeDirectories) FMT_HEADER_ONLY=1;%(PreprocessorDefinitions) @@ -62,6 +63,7 @@ USE_ANALYTICS=1;%(PreprocessorDefinitions) USE_DISCORD_PRESENCE;%(PreprocessorDefinitions) CURL_STATICLIB;%(PreprocessorDefinitions) + WIL_SUPPRESS_EXCEPTIONS;%(PreprocessorDefinitions) _ARCH_64=1;_M_X86=1;_M_X86_64=1;%(PreprocessorDefinitions) _ARCH_64=1;_M_ARM_64=1;%(PreprocessorDefinitions) HAVE_FFMPEG;%(PreprocessorDefinitions)