First Commit
This commit is contained in:
6702
3rdparty/winwil/include/wil/Tracelogging.h
vendored
Normal file
6702
3rdparty/winwil/include/wil/Tracelogging.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3330
3rdparty/winwil/include/wil/com.h
vendored
Normal file
3330
3rdparty/winwil/include/wil/com.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
489
3rdparty/winwil/include/wil/com_apartment_variable.h
vendored
Normal file
489
3rdparty/winwil/include/wil/com_apartment_variable.h
vendored
Normal file
@@ -0,0 +1,489 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Smart pointers and other thin usability pattern wrappers over COM patterns.
|
||||
#ifndef __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
#define __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
|
||||
#include <any>
|
||||
#include <objidl.h>
|
||||
#include <roapi.h>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
#include "com.h"
|
||||
#include "cppwinrt.h"
|
||||
#include "result_macros.h"
|
||||
#include "win32_helpers.h"
|
||||
|
||||
#ifndef WIL_ENABLE_EXCEPTIONS
|
||||
#error This header requires exceptions
|
||||
#endif
|
||||
|
||||
namespace wil
|
||||
{
|
||||
// Determine if apartment variables are supported in the current process context.
|
||||
// Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier)
|
||||
// failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details.
|
||||
// APIs needed to implement apartment variables did not work in non-packaged processes.
|
||||
inline bool are_apartment_variables_supported()
|
||||
{
|
||||
unsigned long long apartmentId{};
|
||||
return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE);
|
||||
}
|
||||
|
||||
// COM will implicitly rundown the apartment registration when it invokes a handler
|
||||
// and blocks calling unregister when executing the callback. So be careful to release()
|
||||
// this when callback is invoked to avoid a double free of the cookie.
|
||||
using unique_apartment_shutdown_registration =
|
||||
unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&::RoUnregisterForApartmentShutdown), ::RoUnregisterForApartmentShutdown>;
|
||||
|
||||
struct apartment_variable_platform
|
||||
{
|
||||
static unsigned long long GetApartmentId()
|
||||
{
|
||||
unsigned long long apartmentId{};
|
||||
FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId));
|
||||
return apartmentId;
|
||||
}
|
||||
|
||||
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
|
||||
{
|
||||
unsigned long long id{};
|
||||
shutdown_type cookie;
|
||||
THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put()));
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
|
||||
{
|
||||
FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie));
|
||||
}
|
||||
|
||||
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
|
||||
{
|
||||
return wil::CoInitializeEx(coinitFlags);
|
||||
}
|
||||
|
||||
// disable the test hook
|
||||
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE;
|
||||
|
||||
using shutdown_type = wil::unique_apartment_shutdown_registration;
|
||||
};
|
||||
|
||||
enum class apartment_variable_leak_action
|
||||
{
|
||||
fail_fast,
|
||||
ignore
|
||||
};
|
||||
|
||||
// "pins" the current module in memory by incrementing the module reference count and leaking that.
|
||||
inline void ensure_module_stays_loaded()
|
||||
{
|
||||
static INIT_ONCE s_initLeakModule{}; // avoiding magic statics
|
||||
wil::init_once_failfast(s_initLeakModule, []() {
|
||||
HMODULE result{};
|
||||
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result));
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
// For the address of data, you can detect global variables by the ability to resolve the module from the address.
|
||||
inline bool IsGlobalVariable(const void* moduleAddress) noexcept
|
||||
{
|
||||
wil::unique_hmodule moduleHandle;
|
||||
return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<PCWSTR>(moduleAddress), &moduleHandle) != FALSE;
|
||||
}
|
||||
|
||||
struct any_maker_base
|
||||
{
|
||||
std::any (*adapter)(void*);
|
||||
void* inner;
|
||||
|
||||
WI_NODISCARD std::any operator()() const
|
||||
{
|
||||
return adapter(inner);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct any_maker : any_maker_base
|
||||
{
|
||||
any_maker()
|
||||
{
|
||||
adapter = [](auto) -> std::any {
|
||||
return T{};
|
||||
};
|
||||
}
|
||||
|
||||
any_maker(T (*maker)())
|
||||
{
|
||||
adapter = [](auto maker) -> std::any {
|
||||
return reinterpret_cast<T (*)()>(maker)();
|
||||
};
|
||||
inner = reinterpret_cast<void*>(maker);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
any_maker(F&& f)
|
||||
{
|
||||
adapter = [](auto maker) -> std::any {
|
||||
return reinterpret_cast<F*>(maker)[0]();
|
||||
};
|
||||
inner = std::addressof(f);
|
||||
}
|
||||
};
|
||||
|
||||
template <apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast, typename test_hook = apartment_variable_platform>
|
||||
struct apartment_variable_base
|
||||
{
|
||||
inline static winrt::slim_mutex s_lock;
|
||||
|
||||
struct apartment_variable_storage
|
||||
{
|
||||
apartment_variable_storage(apartment_variable_storage&& other) noexcept = default;
|
||||
apartment_variable_storage(const apartment_variable_storage& other) = delete;
|
||||
|
||||
apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_))
|
||||
{
|
||||
}
|
||||
|
||||
winrt::apartment_context context;
|
||||
typename test_hook::shutdown_type cookie;
|
||||
// Variables are stored using the address of the apartment_variable_base<> as the key.
|
||||
std::unordered_map<apartment_variable_base<leak_action, test_hook>*, std::any> variables;
|
||||
};
|
||||
|
||||
// Apartment id -> variables storage.
|
||||
inline static wil::object_without_destructor_on_shutdown<std::unordered_map<unsigned long long, apartment_variable_storage>> s_apartmentStorage;
|
||||
|
||||
constexpr apartment_variable_base() = default;
|
||||
~apartment_variable_base()
|
||||
{
|
||||
// Global variables (object with static storage duration)
|
||||
// are run down when the process is shutting down or when the
|
||||
// dll is unloaded. At these points it is not possible to start
|
||||
// an async operation and the work performed is not needed,
|
||||
// the apartments with variable have been run down already.
|
||||
const auto isGlobal = details::IsGlobalVariable(this);
|
||||
if (!isGlobal)
|
||||
{
|
||||
clear_all_apartments_async();
|
||||
}
|
||||
|
||||
if constexpr (leak_action == apartment_variable_leak_action::fail_fast)
|
||||
{
|
||||
if (isGlobal && !ProcessShutdownInProgress())
|
||||
{
|
||||
// If you hit this fail fast it means the storage in s_apartmentStorage will be leaked.
|
||||
// For apartment variables used in .exes, this is expected and
|
||||
// this fail fast should be disabled using
|
||||
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||
//
|
||||
// For DLLs, if this is expected, disable this fail fast using
|
||||
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||
//
|
||||
// Use of apartment variables in DLLs only loaded by COM will never hit this case
|
||||
// as COM will unload DLLs before apartments are rundown,
|
||||
// providing the opportunity to empty s_apartmentStorage.
|
||||
//
|
||||
// But DLLs loaded and unloaded to call DLL entry points (outside of COM) may
|
||||
// create variable storage that can't be cleaned up as the DLL lifetime is
|
||||
// shorter that the COM lifetime. In these cases either
|
||||
// 1) accept the leaks and disable the fail fast as describe above
|
||||
// 2) disable module unloading by calling wil::ensure_module_stays_loaded
|
||||
// 3) CoCreate an object from this DLL to make COM aware of the DLL
|
||||
FAIL_FAST_IF(!s_apartmentStorage.get().empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non-copyable, non-assignable
|
||||
apartment_variable_base(apartment_variable_base const&) = delete;
|
||||
void operator=(apartment_variable_base const&) = delete;
|
||||
|
||||
// get current value or throw if no value has been set
|
||||
std::any& get_existing()
|
||||
{
|
||||
auto any = get_if();
|
||||
if (!any)
|
||||
{
|
||||
THROW_HR(E_NOT_SET);
|
||||
}
|
||||
|
||||
return *any;
|
||||
}
|
||||
|
||||
static apartment_variable_storage* get_current_apartment_variable_storage()
|
||||
{
|
||||
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||
if (storage != s_apartmentStorage.get().end())
|
||||
{
|
||||
return &storage->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
apartment_variable_storage* ensure_current_apartment_variables()
|
||||
{
|
||||
auto variables = get_current_apartment_variable_storage();
|
||||
if (variables)
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
|
||||
{
|
||||
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
|
||||
{
|
||||
// This code runs at apartment rundown so be careful to avoid deadlocks by
|
||||
// extracting the variables under the lock then release them outside.
|
||||
auto variables = [apartmentId]() {
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
return s_apartmentStorage.get().extract(apartmentId);
|
||||
}();
|
||||
WI_ASSERT(variables.key() == apartmentId);
|
||||
// The system implicitly releases the shutdown observer
|
||||
// after invoking the callback and does not allow calling unregister
|
||||
// in the callback. So release the reference to the registration.
|
||||
variables.mapped().cookie.release();
|
||||
}
|
||||
};
|
||||
auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get());
|
||||
return &s_apartmentStorage.get()
|
||||
.insert({test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration))})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
// get current value or custom-construct one on demand
|
||||
template <typename T>
|
||||
std::any& get_or_create(any_maker<T>&& creator)
|
||||
{
|
||||
apartment_variable_storage* variable_storage = nullptr;
|
||||
|
||||
{ // scope for lock
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
variable_storage = ensure_current_apartment_variables();
|
||||
|
||||
auto variable = variable_storage->variables.find(this);
|
||||
if (variable != variable_storage->variables.end())
|
||||
{
|
||||
return variable->second;
|
||||
}
|
||||
} // drop the lock
|
||||
|
||||
// create the object outside the lock to avoid reentrant deadlock
|
||||
auto value = creator();
|
||||
|
||||
auto insert_lock = winrt::slim_lock_guard(s_lock);
|
||||
// The insertion may fail if creator() recursively caused itself to be created,
|
||||
// in which case we return the existing object and the falsely-created one is discarded.
|
||||
return variable_storage->variables.insert({this, std::move(value)}).first->second;
|
||||
}
|
||||
|
||||
// get pointer to current value or nullptr if no value has been set
|
||||
std::any* get_if()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
auto variable = variable_storage->variables.find(this);
|
||||
if (variable != variable_storage->variables.end())
|
||||
{
|
||||
return &(variable->second);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// replace or create the current value, fail fasts if the value is not already stored
|
||||
void set(std::any value)
|
||||
{
|
||||
// release value, with the swapped value, outside of the lock
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||
FAIL_FAST_IF(storage == s_apartmentStorage.get().end());
|
||||
auto& variable_storage = storage->second;
|
||||
auto variable = variable_storage.variables.find(this);
|
||||
FAIL_FAST_IF(variable == variable_storage.variables.end());
|
||||
variable->second.swap(value);
|
||||
}
|
||||
}
|
||||
|
||||
// remove any current value
|
||||
void clear()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
variable_storage->variables.erase(this);
|
||||
if (variable_storage->variables.size() == 0)
|
||||
{
|
||||
s_apartmentStorage.get().erase(test_hook::GetApartmentId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async()
|
||||
{
|
||||
// gather all the apartments that hold objects we need to destruct
|
||||
// (do not gather the objects themselves, because the apartment might
|
||||
// destruct before we get around to it, and we should let the apartment
|
||||
// destruct the object while it still can).
|
||||
|
||||
std::vector<winrt::apartment_context> contexts;
|
||||
{ // scope for lock
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
for (auto& [id, storage] : s_apartmentStorage.get())
|
||||
{
|
||||
auto variable = storage.variables.find(this);
|
||||
if (variable != storage.variables.end())
|
||||
{
|
||||
contexts.push_back(storage.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contexts.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup
|
||||
FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put()));
|
||||
|
||||
// From a background thread hop into each apartment to run down the object
|
||||
// if it's still there.
|
||||
co_await winrt::resume_background();
|
||||
|
||||
// This hook enables testing the case where execution of this method loses the race with
|
||||
// apartment rundown by other means.
|
||||
if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE)
|
||||
{
|
||||
Sleep(test_hook::AsyncRundownDelayForTestingRaces);
|
||||
}
|
||||
|
||||
for (auto&& context : contexts)
|
||||
{
|
||||
try
|
||||
{
|
||||
co_await context;
|
||||
clear();
|
||||
}
|
||||
catch (winrt::hresult_error const& e)
|
||||
{
|
||||
// Ignore failure if apartment ran down before we could clean it up.
|
||||
// The object already ran down as part of apartment cleanup.
|
||||
if ((e.code() != RPC_E_SERVER_DIED_DNE) && (e.code() != RPC_E_DISCONNECTED))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const auto& storage()
|
||||
{
|
||||
return s_apartmentStorage.get();
|
||||
}
|
||||
|
||||
static size_t current_apartment_variable_count()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
return variable_storage->variables.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
// Apartment variables enable storing COM objects safely in globals
|
||||
// (objects with static storage duration) by creating a unique copy
|
||||
// in each apartment and managing their lifetime based on apartment rundown
|
||||
// notifications.
|
||||
// They can also be used for automatic or dynamic storage duration but those
|
||||
// cases are less common.
|
||||
// This type is also useful for storing references to apartment affine objects.
|
||||
//
|
||||
// Note, that apartment variables hosted in a COM DLL need to integrate with
|
||||
// the DllCanUnloadNow() function to include the ref counts contributed by
|
||||
// C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects
|
||||
// but WRL projects will need to be updated to call winrt::get_module_lock().
|
||||
|
||||
template <typename T, apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast, typename test_hook = wil::apartment_variable_platform>
|
||||
struct apartment_variable : details::apartment_variable_base<leak_action, test_hook>
|
||||
{
|
||||
using base = details::apartment_variable_base<leak_action, test_hook>;
|
||||
|
||||
constexpr apartment_variable() = default;
|
||||
|
||||
// Get current value or throw if no value has been set.
|
||||
T& get_existing()
|
||||
{
|
||||
return std::any_cast<T&>(base::get_existing());
|
||||
}
|
||||
|
||||
// Get current value or default-construct one on demand.
|
||||
T& get_or_create()
|
||||
{
|
||||
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>()));
|
||||
}
|
||||
|
||||
// Get current value or custom-construct one on demand.
|
||||
template <typename F>
|
||||
T& get_or_create(F&& f)
|
||||
{
|
||||
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>(std::forward<F>(f))));
|
||||
}
|
||||
|
||||
// get pointer to current value or nullptr if no value has been set
|
||||
T* get_if()
|
||||
{
|
||||
return std::any_cast<T>(base::get_if());
|
||||
}
|
||||
|
||||
// replace or create the current value, fail fasts if the value is not already stored
|
||||
template <typename V>
|
||||
void set(V&& value)
|
||||
{
|
||||
return base::set(std::forward<V>(value));
|
||||
}
|
||||
|
||||
// Clear the value in the current apartment.
|
||||
using base::clear;
|
||||
|
||||
// Asynchronously clear the value in all apartments it is present in.
|
||||
using base::clear_all_apartments_async;
|
||||
|
||||
// For testing only.
|
||||
// 1) To observe the state of the storage in the debugger assign this to
|
||||
// a temporary variable (const&) and watch its contents.
|
||||
// 2) Use this to test the implementation.
|
||||
using base::storage;
|
||||
// For testing only. The number of variables in the current apartment.
|
||||
using base::current_apartment_variable_count;
|
||||
};
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
898
3rdparty/winwil/include/wil/common.h
vendored
Normal file
898
3rdparty/winwil/include/wil/common.h
vendored
Normal file
@@ -0,0 +1,898 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! WIL Common Helpers: Provides broadly applicable, dependency-free pure C++ helpers, macros and type traits.
|
||||
#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.
|
||||
/// @cond
|
||||
#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
|
||||
/// @endcond
|
||||
|
||||
// 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.
|
||||
/// @cond
|
||||
#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
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
#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
|
||||
/// @endcond
|
||||
|
||||
#include <sal.h>
|
||||
|
||||
// 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
|
||||
|
||||
//! @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
|
||||
// clang-format off
|
||||
#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__
|
||||
// clang-format on
|
||||
/// @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
|
||||
|
||||
#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 "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
|
||||
|
||||
#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !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 or __EXCEPTIONS 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
|
||||
|
||||
/// @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
|
||||
/// @endcond
|
||||
|
||||
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
|
||||
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
#ifndef WIL_ITERATOR_DEBUG_LEVEL
|
||||
// NOTE: See the definition of 'RESULT_DEBUG' for commentary on the use of 'WIL_KERNEL_MODE' below
|
||||
#if (DBG || defined(DEBUG) || defined(_DEBUG)) && (defined(WIL_KERNEL_MODE) || !defined(NDEBUG))
|
||||
#define WIL_ITERATOR_DEBUG_LEVEL 2
|
||||
#else
|
||||
#define WIL_ITERATOR_DEBUG_LEVEL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (WIL_ITERATOR_DEBUG_LEVEL < 0) || (WIL_ITERATOR_DEBUG_LEVEL > 2)
|
||||
#error Invalid value for 'WIL_ITERATOR_DEBUG_LEVEL'; valid values are 0-2
|
||||
#endif
|
||||
|
||||
// To allow code with mis-matching iterator debug levels to link together without fear of ODR issues, we place iterators whose
|
||||
// definitions differ based on the definition of WIL_ITERATOR_DEBUG_LEVEL in different namespaces
|
||||
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
||||
#define __WI_ITR_NAMESPACE WI_PASTE(itr, WIL_ITERATOR_DEBUG_LEVEL)
|
||||
#define __WI_ITR_NAMESPACE_BEGIN \
|
||||
inline namespace __WI_ITR_NAMESPACE \
|
||||
{
|
||||
#define __WI_ITR_NAMESPACE_END }
|
||||
#else
|
||||
#define __WI_ITR_NAMESPACE
|
||||
#define __WI_ITR_NAMESPACE_BEGIN
|
||||
#define __WI_ITR_NAMESPACE_END
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
// 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
|
||||
|
||||
/**This define controls the degree of runtime checking for various iterator types defined by WIL.
|
||||
This option roughly follows the behavior of the MSVC STL's `_ITERATOR_DEBUG_LEVEL` define, with similar available values. The
|
||||
primary difference (besides being two disjoint values) is that `WIL_ITERATOR_DEBUG_LEVEL` will raise a failfast exception when a
|
||||
check fails as opposed to the invalid parameter handler that the STL invokes. There are three definitions allowed:
|
||||
0) This will disable all additional runtime checks for the various iterator types. This is the default when building as 'Release'
|
||||
1) This enables checks only for unsafe iterator use. This includes things like attempting to increment an iterator past the end,
|
||||
dereference an end iterator, dereference invalidated iterators, etc.
|
||||
2) This enables all checks enabled by level 1 plus some additional checks to try and catch invalid iterator use. The specific
|
||||
checks enabled by this level will vary between iterator types. This is the default when building as 'Debug'
|
||||
*/
|
||||
#define WIL_ITERATOR_DEBUG_LEVEL 0
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
#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
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = nullptr
|
||||
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = nullptr
|
||||
/// @endcond
|
||||
|
||||
//! @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<decltype(val)>>(val)
|
||||
//! Validates that exactly ONE bit is set in compile-time constant `flag`
|
||||
#define WI_StaticAssertSingleBitSet(flag) \
|
||||
static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(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<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(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<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(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))
|
||||
//! @} // bitwise inspection macros
|
||||
|
||||
//! @} // group bitwise
|
||||
|
||||
#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_INITIALIZATION_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 `#IFDEF`s within functions would create ODR violations, whereas
|
||||
doing it with global function pointers and header initialization allows a runtime determination. */
|
||||
#define WI_HEADER_INITIALIZATION_FUNCTION(name, fn)
|
||||
#elif defined(_M_IX86)
|
||||
#define WI_HEADER_INITIALIZATION_FUNCTION(name, fn) \
|
||||
extern "C" \
|
||||
{ \
|
||||
__declspec(selectany) unsigned char g_header_init_##name = static_cast<unsigned char>(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_INITIALIZATION_FUNCTION(name, fn) \
|
||||
extern "C" \
|
||||
{ \
|
||||
__declspec(selectany) unsigned char g_header_init_##name = static_cast<unsigned char>(fn()); \
|
||||
} \
|
||||
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
|
||||
#else
|
||||
#error linker pragma must include g_header_init variation
|
||||
#endif
|
||||
|
||||
// Keep the misspelled name for backward compatibility.
|
||||
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) WI_HEADER_INITIALIZATION_FUNCTION(name, fn)
|
||||
|
||||
/** 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 <typename T>
|
||||
class pointer_range
|
||||
{
|
||||
public:
|
||||
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_)
|
||||
{
|
||||
}
|
||||
WI_NODISCARD T begin() const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
WI_NODISCARD T end() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_begin;
|
||||
T m_end;
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/** Enables using range-based for between a begin and end object pointer.
|
||||
~~~~
|
||||
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
|
||||
~~~~ */
|
||||
template <typename T>
|
||||
details::pointer_range<T> make_range(T begin, T end)
|
||||
{
|
||||
return details::pointer_range<T>(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 <typename T>
|
||||
details::pointer_range<T> make_range(T begin, size_t count)
|
||||
{
|
||||
return details::pointer_range<T>(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 <typename T>
|
||||
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 <typename T>
|
||||
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 <unsigned int... Rest>
|
||||
struct FeatureRequiredBy
|
||||
{
|
||||
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
|
||||
};
|
||||
~~~~ */
|
||||
template <bool...>
|
||||
struct variadic_logical_or;
|
||||
/// @cond
|
||||
template <>
|
||||
struct variadic_logical_or<> : wistd::false_type
|
||||
{
|
||||
};
|
||||
template <bool... Rest>
|
||||
struct variadic_logical_or<true, Rest...> : wistd::true_type
|
||||
{
|
||||
};
|
||||
template <bool... Rest>
|
||||
struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type
|
||||
{
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <unsigned long long flag>
|
||||
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;
|
||||
};
|
||||
} // namespace details
|
||||
/// @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 <typename T, __R_ENABLE_IF_IS_CLASS(T)>
|
||||
_Post_satisfies_(return == static_cast<bool>(val)) __forceinline constexpr bool verify_bool(const T& val) WI_NOEXCEPT
|
||||
{
|
||||
return static_cast<bool>(val);
|
||||
}
|
||||
|
||||
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
|
||||
__forceinline constexpr bool verify_bool(T /*val*/) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == val) __forceinline constexpr bool verify_bool<bool>(bool val) WI_NOEXCEPT
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool<int>(int val) WI_NOEXCEPT
|
||||
{
|
||||
return (val != 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool<unsigned char>(unsigned char val) WI_NOEXCEPT
|
||||
{
|
||||
return (val != 0);
|
||||
}
|
||||
|
||||
/** 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 <typename T>
|
||||
_Post_satisfies_(return == val) __forceinline constexpr int verify_BOOL(T val) WI_NOEXCEPT
|
||||
{
|
||||
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
|
||||
static_assert((wistd::is_same<T, int>::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<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
|
||||
~~~~
|
||||
@param hr The HRESULT returning expression
|
||||
@return An HRESULT representing the evaluation of `val`. */
|
||||
template <typename T>
|
||||
_Post_satisfies_(return == hr) inline constexpr long verify_hresult(T hr) WI_NOEXCEPT
|
||||
{
|
||||
// Note: Written in terms of 'long' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
|
||||
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
|
||||
return hr;
|
||||
}
|
||||
|
||||
/** Verify that `status` is an NTSTATUS value.
|
||||
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
|
||||
underlying typedef behind NTSTATUS.
|
||||
//!
|
||||
Note that occasionally you might run into an NTSTATUS which is directly defined with a `#define`, such as:
|
||||
@code
|
||||
#define STATUS_NOT_SUPPORTED 0x1
|
||||
@endcode
|
||||
Though this looks like an `NTSTATUS`, 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 `NTSTATUS` constants are defined in ntstatus.h:
|
||||
@code
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||
@endcode
|
||||
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_ntstatus`, for example:
|
||||
@code
|
||||
NT_RETURN_IF_FALSE(static_cast<NTSTATUS>(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0));
|
||||
@endcode
|
||||
@param status The NTSTATUS returning expression
|
||||
@return An NTSTATUS representing the evaluation of `val`. */
|
||||
template <typename T>
|
||||
_Post_satisfies_(return == status) inline long verify_ntstatus(T status) WI_NOEXCEPT
|
||||
{
|
||||
// Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS
|
||||
static_assert(wistd::is_same<T, long>::value, "Wrong Type: NTSTATUS expected");
|
||||
return status;
|
||||
}
|
||||
|
||||
/** Verify that `error` is a Win32 error code.
|
||||
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is
|
||||
the underlying type used for WIN32 error codes, as well as any `DWORD` (`unsigned long`) value since this is the type
|
||||
commonly used when manipulating Win32 error codes.
|
||||
@param error The Win32 error code returning expression
|
||||
@return An Win32 error code representing the evaluation of `error`. */
|
||||
template <typename T>
|
||||
_Post_satisfies_(return == error) inline T verify_win32(T error) WI_NOEXCEPT
|
||||
{
|
||||
// Note: Win32 error code are defined as 'long' (#define ERROR_SUCCESS 0L), but are more frequently used as DWORD (unsigned
|
||||
// long). This accept both types.
|
||||
static_assert(
|
||||
wistd::is_same<T, long>::value || wistd::is_same<T, unsigned long>::value,
|
||||
"Wrong Type: Win32 error code (long / unsigned long) expected");
|
||||
return error;
|
||||
}
|
||||
/// @} // 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<unsigned char>(val) \
|
||||
: sizeof(val) == 2 ? static_cast<unsigned short>(val) \
|
||||
: sizeof(val) == 4 ? static_cast<unsigned long>(val) \
|
||||
: static_cast<unsigned long long>(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 <typename TVal, typename TFlags>
|
||||
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
|
||||
{
|
||||
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
|
||||
}
|
||||
|
||||
template <typename TVal>
|
||||
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
|
||||
{
|
||||
return __WI_IS_SINGLE_FLAG_SET(val);
|
||||
}
|
||||
|
||||
template <typename TVal>
|
||||
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
|
||||
{
|
||||
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
|
||||
}
|
||||
|
||||
template <typename TVal, typename TMask, typename TFlags>
|
||||
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
|
||||
{
|
||||
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
|
||||
}
|
||||
|
||||
template <long>
|
||||
struct variable_size;
|
||||
|
||||
template <>
|
||||
struct variable_size<1>
|
||||
{
|
||||
using type = unsigned char;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<2>
|
||||
{
|
||||
using type = unsigned short;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<4>
|
||||
{
|
||||
using type = unsigned long;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<8>
|
||||
{
|
||||
using type = unsigned long long;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct variable_size_mapping
|
||||
{
|
||||
using type = typename variable_size<sizeof(T)>::type;
|
||||
};
|
||||
} // namespace 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 <typename T>
|
||||
using integral_from_enum = typename details::variable_size_mapping<T>::type;
|
||||
|
||||
//! Declares a name that intentionally hides a name from an outer scope.
|
||||
//! Use this to prevent accidental use of a parameter or lambda captured variable.
|
||||
using hide_name = void(struct hidden_name);
|
||||
} // namespace wil
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // __WIL_COMMON_INCLUDED
|
||||
935
3rdparty/winwil/include/wil/coroutine.h
vendored
Normal file
935
3rdparty/winwil/include/wil/coroutine.h
vendored
Normal file
@@ -0,0 +1,935 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Types and helpers for using C++ coroutines.
|
||||
#ifndef __WIL_COROUTINE_INCLUDED
|
||||
#define __WIL_COROUTINE_INCLUDED
|
||||
|
||||
/*
|
||||
* A wil::task<T> / com_task<T> is a coroutine with the following characteristics:
|
||||
*
|
||||
* - T must be a copyable object, movable object, reference, or void.
|
||||
* - The coroutine may be awaited at most once. The second await will crash.
|
||||
* - The coroutine may be abandoned (allowed to destruct without co_await),
|
||||
* in which case unobserved exceptions are fatal.
|
||||
* - By default, wil::task resumes on an arbitrary thread.
|
||||
* - By default, wil::com_task resumes in the same COM apartment.
|
||||
* - task.resume_any_thread() allows resumption on any thread.
|
||||
* - task.resume_same_apartment() forces resumption in the same COM apartment.
|
||||
*
|
||||
* The wil::task and wil::com_task are intended to supplement PPL and C++/WinRT,
|
||||
* not to replace them. It provides coroutine implementations for scenarios that PPL
|
||||
* and C++/WinRT do not support, but it does not support everything that PPL and
|
||||
* C++/WinRT do.
|
||||
*
|
||||
* The implementation is optimized on the assumption that the coroutine is
|
||||
* awaited only once, and that the coroutine is discarded after completion.
|
||||
* To ensure proper usage, the task object is move-only, and
|
||||
* co_await takes ownership of the task. See further discussion below.
|
||||
*
|
||||
* Comparison with PPL and C++/WinRT:
|
||||
*
|
||||
* | | PPL | C++/WinRT | wil::*task |
|
||||
* |-----------------------------------------------------|-----------|-----------|---------------|
|
||||
* | T can be non-constructible | No | Yes | Yes |
|
||||
* | T can be void | Yes | Yes | Yes |
|
||||
* | T can be reference | No | No | Yes |
|
||||
* | T can be WinRT object | Yes | Yes | Yes |
|
||||
* | T can be non-WinRT object | Yes | No | Yes |
|
||||
* | T can be move-only | No | No | Yes |
|
||||
* | Coroutine can be cancelled | Yes | Yes | No |
|
||||
* | Coroutine can throw arbitrary exceptions | Yes | No | Yes |
|
||||
* | Can co_await more than once | Yes | No | No |
|
||||
* | Can have multiple clients waiting for completion | Yes | No | No |
|
||||
* | co_await resumes in same COM context | Sometimes | Yes | You choose [1]|
|
||||
* | Can force co_await to resume in same context | Yes | N/A | Yes [1] |
|
||||
* | Can force co_await to resume in any thread | Yes | No | Yes |
|
||||
* | Can change coroutine's resumption model | No | No | Yes |
|
||||
* | Can wait synchronously | Yes | Yes | Yes [2] |
|
||||
* | Can be consumed by non-C++ languages | No | Yes | No |
|
||||
* | Implementation is small and efficient | No | Yes | Yes |
|
||||
* | Can abandon coroutine (fail to co_await) | Yes | Yes | Yes |
|
||||
* | Exception in abandoned coroutine | Crash | Ignored | Crash |
|
||||
* | Coroutine starts automatically | Yes | Yes | Yes |
|
||||
* | Coroutine starts synchronously | No | Yes | Yes |
|
||||
* | Integrates with C++/WinRT coroutine callouts | No | Yes | No |
|
||||
*
|
||||
* [1] Resumption in the same COM apartment requires that you include COM headers.
|
||||
* [2] Synchronous waiting requires that you include <synchapi.h> (usually via <windows.h>).
|
||||
*
|
||||
* You can include the COM headers and/or synchapi.h headers, and then
|
||||
* re-include this header file to activate the features dependent upon
|
||||
* those headers.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Implement a coroutine that returns a move-only non-WinRT type
|
||||
* and which resumes on an arbitrary thread.
|
||||
*
|
||||
* wil::task<wil::unique_cotaskmem_string> GetNameAsync()
|
||||
* {
|
||||
* co_await resume_background(); // do work on BG thread
|
||||
* wil::unique_cotaskmem_string name;
|
||||
* THROW_IF_FAILED(GetNameSlow(&name));
|
||||
* co_return name; // awaiter will resume on arbitrary thread
|
||||
* }
|
||||
*
|
||||
* Consumers:
|
||||
*
|
||||
* winrt::IAsyncAction UpdateNameAsync()
|
||||
* {
|
||||
* // wil::task resumes on an arbitrary thread.
|
||||
* auto name = co_await GetNameAsync();
|
||||
* // could be on any thread now
|
||||
* co_await SendNameAsync(name.get());
|
||||
* }
|
||||
*
|
||||
* winrt::IAsyncAction UpdateNameAsync()
|
||||
* {
|
||||
* // override default behavior of wil::task and
|
||||
* // force it to resume in the same COM apartment.
|
||||
* auto name = co_await GetNameAsync().resume_same_apartment();
|
||||
* // so we are still on the UI thread
|
||||
* NameElement().Text(winrt::hstring(name.get()));
|
||||
* }
|
||||
*
|
||||
* Conversely, a coroutine that returns a
|
||||
* wil::com_task<T> defaults to resuming in the same
|
||||
* COM apartment, but you can allow it to resume on any thread
|
||||
* by doing co_await GetNameAsync().resume_any_thread().
|
||||
*
|
||||
* There is no harm in doing resume_same_apartment() / resume_any_thread() for a
|
||||
* task that already defaults to resuming in that manner. In fact, awaiting the
|
||||
* task directly is just a shorthand for awaiting the corresponding
|
||||
* resume_whatever() method.
|
||||
*
|
||||
* Alternatively, you can just convert between wil::task<T> and wil::com_task<T>
|
||||
* to change the default resumption context.
|
||||
*
|
||||
* co_await wil::com_task(GetNameAsync()); // now defaults to resume_same_apartment();
|
||||
*
|
||||
* You can store the task in a variable, but since it is a move-only
|
||||
* object, you will have to use std::move in order to transfer ownership out of
|
||||
* an lvalue.
|
||||
*
|
||||
* winrt::IAsyncAction SomethingAsync()
|
||||
* {
|
||||
* wil::com_task<int> task;
|
||||
* switch (source)
|
||||
* {
|
||||
* // Some of these might return wil::task<int>,
|
||||
* // but assigning to a wil::com_task<int> will make
|
||||
* // the task resume in the same COM apartment.
|
||||
* case widget: task = GetValueFromWidgetAsync(); break;
|
||||
* case gadget: task = GetValueFromGadgetAsync(); break;
|
||||
* case doodad: task = GetValueFromDoodadAsync(); break;
|
||||
* default: FAIL_FAST(); // unknown source
|
||||
* }
|
||||
* auto value = co_await std::move(task); // **** need std::move
|
||||
* DoSomethingWith(value);
|
||||
* }
|
||||
*
|
||||
* You can wait synchronously by calling get(). The usual caveats
|
||||
* about synchronous waits on STA threads apply.
|
||||
*
|
||||
* auto value = GetValueFromWidgetAsync().get();
|
||||
*
|
||||
* auto task = GetValueFromWidgetAsync();
|
||||
* auto value = std::move(task).get(); // **** need std::move
|
||||
*/
|
||||
|
||||
// Detect which version of the coroutine standard we have.
|
||||
/// @cond
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||
#include <experimental/coroutine>
|
||||
#define __WI_COROUTINE_NAMESPACE ::std::experimental
|
||||
#elif defined(__cpp_impl_coroutine)
|
||||
#include <coroutine>
|
||||
#define __WI_COROUTINE_NAMESPACE ::std
|
||||
#else
|
||||
#error You must compile with C++20 coroutine support to use coroutine.h.
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
#include <wil/wistd_memory.h>
|
||||
#include <wil/wistd_type_traits.h>
|
||||
#include <wil/result_macros.h>
|
||||
|
||||
namespace wil
|
||||
{
|
||||
// There are three general categories of T that you can
|
||||
// use with a task. We give them these names:
|
||||
//
|
||||
// T = void ("void category")
|
||||
// T = some kind of reference ("reference category")
|
||||
// T = non-void non-reference ("object category")
|
||||
//
|
||||
// Take care that the implementation supports all three categories.
|
||||
//
|
||||
// There is a sub-category of object category for move-only types.
|
||||
// We designed our task to be co_awaitable only once, so that
|
||||
// it can contain a move-only type. Any transfer of T as an
|
||||
// object category must be done as an rvalue reference.
|
||||
template <typename T>
|
||||
struct task;
|
||||
|
||||
template <typename T>
|
||||
struct com_task;
|
||||
} // namespace wil
|
||||
|
||||
/// @cond
|
||||
namespace wil::details::coro
|
||||
{
|
||||
// task and com_task are convertible to each other. However, not
|
||||
// all consumers of this header have COM enabled. Support for saving
|
||||
// COM thread-local error information and restoring it on the resuming
|
||||
// thread is enabled using these function pointers. If COM is not
|
||||
// available then they are null and do not get called. If COM is
|
||||
// enabled then they are filled in with valid pointers and get used.
|
||||
__declspec(selectany) void*(__stdcall* g_pfnCaptureRestrictedErrorInformation)() WI_PFN_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall* g_pfnRestoreRestrictedErrorInformation)(void* restricted_error) WI_PFN_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall* g_pfnDestroyRestrictedErrorInformation)(void* restricted_error) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
template <typename T>
|
||||
struct task_promise;
|
||||
|
||||
// Unions may not contain references, C++/CX types, or void.
|
||||
// To work around that, we put everything inside a result_wrapper
|
||||
// struct, and put the struct in the union. For void,
|
||||
// we create a special empty structure.
|
||||
//
|
||||
// get_value returns rvalue reference to T for object
|
||||
// category, or just T itself for void and reference
|
||||
// category.
|
||||
//
|
||||
// We take advantage of the reference collapsing rules
|
||||
// so that T&& = T if T is reference category.
|
||||
|
||||
template <typename T>
|
||||
struct result_wrapper
|
||||
{
|
||||
T value;
|
||||
T get_value()
|
||||
{
|
||||
return wistd::forward<T>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct result_wrapper<void>
|
||||
{
|
||||
void get_value()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// The result_holder is basically a
|
||||
// std::variant<std::monotype, T, std::exception_ptr>
|
||||
// but with these extra quirks:
|
||||
// * The only valid transition is monotype -> something-else.
|
||||
// Consequently, it does not have valueless_by_exception.
|
||||
|
||||
template <typename T>
|
||||
struct result_holder
|
||||
{
|
||||
// The content of the result_holder
|
||||
// depends on the result_status:
|
||||
//
|
||||
// empty: No active member.
|
||||
// value: Active member is wrap.
|
||||
// error: Active member is error.
|
||||
enum class result_status
|
||||
{
|
||||
empty,
|
||||
value,
|
||||
error
|
||||
};
|
||||
|
||||
result_status status{result_status::empty};
|
||||
union variant
|
||||
{
|
||||
variant()
|
||||
{
|
||||
}
|
||||
~variant()
|
||||
{
|
||||
}
|
||||
result_wrapper<T> wrap;
|
||||
std::exception_ptr error;
|
||||
} result;
|
||||
|
||||
// The restricted error information is lit up when COM headers are
|
||||
// included. If COM is not available then this will remain null.
|
||||
// This error information is thread-local so we must save it on suspend
|
||||
// and restore it on resume so that it propagates to the correct
|
||||
// thread. It will then be available if the exception proves fatal.
|
||||
//
|
||||
// This object is non-copyable so we do not need to worry about
|
||||
// supporting AddRef on the restricted error information.
|
||||
void* restricted_error{nullptr};
|
||||
|
||||
// emplace_value will be called with
|
||||
//
|
||||
// * no parameters (void category)
|
||||
// * The reference type T (reference category)
|
||||
// * Some kind of reference to T (object category)
|
||||
//
|
||||
// Set the status after constructing the object.
|
||||
// That way, if object construction throws an exception,
|
||||
// the holder remains empty.
|
||||
template <typename... Args>
|
||||
void emplace_value(Args&&... args)
|
||||
{
|
||||
WI_ASSERT(status == result_status::empty);
|
||||
new (wistd::addressof(result.wrap)) result_wrapper<T>{wistd::forward<Args>(args)...};
|
||||
status = result_status::value;
|
||||
}
|
||||
|
||||
void unhandled_exception() noexcept
|
||||
{
|
||||
if (g_pfnCaptureRestrictedErrorInformation)
|
||||
{
|
||||
WI_ASSERT(restricted_error == nullptr);
|
||||
restricted_error = g_pfnCaptureRestrictedErrorInformation();
|
||||
}
|
||||
|
||||
WI_ASSERT(status == result_status::empty);
|
||||
new (wistd::addressof(result.error)) std::exception_ptr(std::current_exception());
|
||||
status = result_status::error;
|
||||
}
|
||||
|
||||
T get_value()
|
||||
{
|
||||
if (status == result_status::value)
|
||||
{
|
||||
return result.wrap.get_value();
|
||||
}
|
||||
|
||||
WI_ASSERT(status == result_status::error);
|
||||
if (restricted_error && g_pfnRestoreRestrictedErrorInformation)
|
||||
{
|
||||
g_pfnRestoreRestrictedErrorInformation(restricted_error);
|
||||
}
|
||||
std::rethrow_exception(wistd::exchange(result.error, {}));
|
||||
}
|
||||
|
||||
result_holder() = default;
|
||||
result_holder(result_holder const&) = delete;
|
||||
void operator=(result_holder const&) = delete;
|
||||
|
||||
~result_holder() noexcept(false)
|
||||
{
|
||||
if (restricted_error && g_pfnDestroyRestrictedErrorInformation)
|
||||
{
|
||||
g_pfnDestroyRestrictedErrorInformation(restricted_error);
|
||||
restricted_error = nullptr;
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case result_status::value:
|
||||
result.wrap.~result_wrapper();
|
||||
break;
|
||||
case result_status::error:
|
||||
// Rethrow unobserved exception. Delete this line to
|
||||
// discard unobserved exceptions.
|
||||
if (result.error)
|
||||
std::rethrow_exception(result.error);
|
||||
result.error.~exception_ptr();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Most of the work is done in the promise_base,
|
||||
// It is a CRTP-like base class for task_promise<void> and
|
||||
// task_promise<non-void> because the language forbids
|
||||
// a single promise from containing both return_value and
|
||||
// return_void methods (even if one of them is deleted by SFINAE).
|
||||
template <typename T>
|
||||
struct promise_base
|
||||
{
|
||||
// The coroutine state remains alive as long as the coroutine is
|
||||
// still running (hasn't reached final_suspend) or the associated
|
||||
// task has not yet abandoned the coroutine (either finished awaiting
|
||||
// or destructed without awaiting).
|
||||
//
|
||||
// This saves an allocation, but does mean that the local
|
||||
// frame of the coroutine will remain allocated (with the
|
||||
// coroutine's imbound parameters still live) until all
|
||||
// references are destroyed. To force the promise_base to be
|
||||
// destroyed after co_await, we make the promise_base a
|
||||
// move-only object and require co_await to be given an rvalue reference.
|
||||
|
||||
// Special values for m_waiting.
|
||||
static void* running_ptr()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
static void* completed_ptr()
|
||||
{
|
||||
return reinterpret_cast<void*>(1);
|
||||
}
|
||||
static void* abandoned_ptr()
|
||||
{
|
||||
return reinterpret_cast<void*>(2);
|
||||
}
|
||||
|
||||
// The awaiting coroutine is resumed by calling the
|
||||
// m_resumer with the m_waiting. If the resumer is null,
|
||||
// then the m_waiting is assumed to be the address of a
|
||||
// coroutine_handle<>, which is resumed synchronously.
|
||||
// Externalizing the resumer allows unused awaiters to be
|
||||
// removed by the linker and removes a hard dependency on COM.
|
||||
// Using nullptr to represent the default resumer avoids a
|
||||
// CFG check.
|
||||
|
||||
void(__stdcall* m_resumer)(void*);
|
||||
std::atomic<void*> m_waiting{running_ptr()};
|
||||
result_holder<T> m_holder;
|
||||
|
||||
// Make it easier to access our CRTP derived class.
|
||||
using Promise = task_promise<T>;
|
||||
auto as_promise() noexcept
|
||||
{
|
||||
return static_cast<Promise*>(this);
|
||||
}
|
||||
|
||||
// Make it easier to access the coroutine handle.
|
||||
auto as_handle() noexcept
|
||||
{
|
||||
return __WI_COROUTINE_NAMESPACE::coroutine_handle<Promise>::from_promise(*as_promise());
|
||||
}
|
||||
|
||||
auto get_return_object() noexcept
|
||||
{
|
||||
// let the compiler construct the task / com_task from the promise.
|
||||
return as_promise();
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
as_handle().destroy();
|
||||
}
|
||||
|
||||
// The client lost interest in the coroutine, either because they are discarding
|
||||
// the result without awaiting (risky!), or because they have finished awaiting.
|
||||
// Discarding the result without awaiting is risky because any exception in the coroutine
|
||||
// will be unobserved and result in a crash. If you want to disallow it, then
|
||||
// raise an exception if waiting == running_ptr.
|
||||
void abandon()
|
||||
{
|
||||
auto waiting = m_waiting.exchange(abandoned_ptr(), std::memory_order_acq_rel);
|
||||
if (waiting != running_ptr())
|
||||
destroy();
|
||||
}
|
||||
|
||||
__WI_COROUTINE_NAMESPACE::suspend_never initial_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_value(Args&&... args)
|
||||
{
|
||||
m_holder.emplace_value(wistd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void unhandled_exception() noexcept
|
||||
{
|
||||
m_holder.unhandled_exception();
|
||||
}
|
||||
|
||||
void resume_waiting_coroutine(void* waiting) const
|
||||
{
|
||||
if (m_resumer)
|
||||
{
|
||||
m_resumer(waiting);
|
||||
}
|
||||
else
|
||||
{
|
||||
__WI_COROUTINE_NAMESPACE::coroutine_handle<>::from_address(waiting).resume();
|
||||
}
|
||||
}
|
||||
|
||||
auto final_suspend() noexcept
|
||||
{
|
||||
struct awaiter : __WI_COROUTINE_NAMESPACE::suspend_always
|
||||
{
|
||||
promise_base& self;
|
||||
void await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<>) const noexcept
|
||||
{
|
||||
// Need acquire so we can read from m_resumer.
|
||||
// Need release so that the results are published in the case that nobody
|
||||
// is awaiting right now, so that the eventual awaiter (possibly on another thread)
|
||||
// can read the results.
|
||||
auto waiting = self.m_waiting.exchange(completed_ptr(), std::memory_order_acq_rel);
|
||||
if (waiting == abandoned_ptr())
|
||||
{
|
||||
self.destroy();
|
||||
}
|
||||
else if (waiting != running_ptr())
|
||||
{
|
||||
WI_ASSERT(waiting != completed_ptr());
|
||||
self.resume_waiting_coroutine(waiting);
|
||||
}
|
||||
};
|
||||
};
|
||||
return awaiter{{}, *this};
|
||||
}
|
||||
|
||||
// The remaining methods are used by the awaiters.
|
||||
bool client_await_ready()
|
||||
{
|
||||
// Need acquire in case the coroutine has already completed,
|
||||
// so we can read the results. This matches the release in
|
||||
// the final_suspend's await_suspend.
|
||||
auto waiting = m_waiting.load(std::memory_order_acquire);
|
||||
WI_ASSERT((waiting == running_ptr()) || (waiting == completed_ptr()));
|
||||
return waiting != running_ptr();
|
||||
}
|
||||
|
||||
auto client_await_suspend(void* waiting, void(__stdcall* resumer)(void*))
|
||||
{
|
||||
// "waiting" needs to be a pointer to an object. We reserve the first 16
|
||||
// pseudo-pointers as sentinels.
|
||||
WI_ASSERT(reinterpret_cast<uintptr_t>(waiting) > 16);
|
||||
|
||||
m_resumer = resumer;
|
||||
|
||||
// Acquire to ensure that we can read the results of the return value, if the coroutine is completed.
|
||||
// Release to ensure that our resumption state is published, if the coroutine is not completed.
|
||||
auto previous = m_waiting.exchange(waiting, std::memory_order_acq_rel);
|
||||
|
||||
// Suspend if the coroutine is still running.
|
||||
// Otherwise, the coroutine is completed: Nobody will resume us, so we will have to resume ourselves.
|
||||
WI_ASSERT((previous == running_ptr()) || (previous == completed_ptr()));
|
||||
return previous == running_ptr();
|
||||
}
|
||||
|
||||
T client_await_resume()
|
||||
{
|
||||
return m_holder.get_value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct task_promise : promise_base<T>
|
||||
{
|
||||
template <typename U>
|
||||
void return_value(U&& value)
|
||||
{
|
||||
this->emplace_value(wistd::forward<U>(value));
|
||||
}
|
||||
|
||||
template <typename Dummy = void>
|
||||
wistd::enable_if_t<!wistd::is_reference_v<T>, Dummy> return_value(T const& value)
|
||||
{
|
||||
this->emplace_value(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct task_promise<void> : promise_base<void>
|
||||
{
|
||||
void return_void()
|
||||
{
|
||||
this->emplace_value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct promise_deleter
|
||||
{
|
||||
void operator()(promise_base<T>* promise) const noexcept
|
||||
{
|
||||
promise->abandon();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using promise_ptr = wistd::unique_ptr<promise_base<T>, promise_deleter<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct agile_awaiter
|
||||
{
|
||||
agile_awaiter(promise_ptr<T>&& initial) : promise(wistd::move(initial))
|
||||
{
|
||||
}
|
||||
|
||||
promise_ptr<T> promise;
|
||||
|
||||
bool await_ready()
|
||||
{
|
||||
return promise->client_await_ready();
|
||||
}
|
||||
|
||||
auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
|
||||
{
|
||||
// Use the default resumer.
|
||||
return promise->client_await_suspend(handle.address(), nullptr);
|
||||
}
|
||||
|
||||
T await_resume()
|
||||
{
|
||||
return promise->client_await_resume();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct task_base
|
||||
{
|
||||
auto resume_any_thread() && noexcept
|
||||
{
|
||||
return agile_awaiter<T>{wistd::move(promise)};
|
||||
}
|
||||
|
||||
// You must #include <ole2.h> before <wil/coroutine.h> to enable apartment-aware awaiting.
|
||||
auto resume_same_apartment() && noexcept;
|
||||
|
||||
// Compiler error message metaprogramming: Tell people that they
|
||||
// need to use std::move() if they try to co_await an lvalue.
|
||||
struct cannot_await_lvalue_use_std_move
|
||||
{
|
||||
void await_ready()
|
||||
{
|
||||
}
|
||||
};
|
||||
cannot_await_lvalue_use_std_move operator co_await() & = delete;
|
||||
|
||||
// You must #include <synchapi.h> (usually via <windows.h>) to enable synchronous waiting.
|
||||
decltype(auto) get() &&;
|
||||
|
||||
protected:
|
||||
task_base(task_promise<T>* initial = nullptr) noexcept : promise(initial)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
D& assign(D* self, task_base&& other) noexcept
|
||||
{
|
||||
static_cast<task_base&>(*this) = wistd::move(other);
|
||||
return *self;
|
||||
}
|
||||
|
||||
private:
|
||||
promise_ptr<T> promise;
|
||||
|
||||
static void __stdcall wake_by_address(void* completed);
|
||||
};
|
||||
} // namespace wil::details::coro
|
||||
/// @endcond
|
||||
|
||||
namespace wil
|
||||
{
|
||||
// Must write out both classes separately
|
||||
// Cannot use deduction guides with alias template type prior to C++20.
|
||||
template <typename T>
|
||||
struct task : details::coro::task_base<T>
|
||||
{
|
||||
using base = details::coro::task_base<T>;
|
||||
// Constructing from task_promise<T>* cannot be explicit because get_return_object relies on implicit conversion.
|
||||
task(details::coro::task_promise<T>* initial = nullptr) noexcept : base(initial)
|
||||
{
|
||||
}
|
||||
explicit task(base&& other) noexcept : base(wistd::move(other))
|
||||
{
|
||||
}
|
||||
task& operator=(base&& other) noexcept
|
||||
{
|
||||
return base::assign(this, wistd::move(other));
|
||||
}
|
||||
|
||||
using base::operator co_await;
|
||||
|
||||
auto operator co_await() && noexcept
|
||||
{
|
||||
return wistd::move(*this).resume_any_thread();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct com_task : details::coro::task_base<T>
|
||||
{
|
||||
using base = details::coro::task_base<T>;
|
||||
// Constructing from task_promise<T>* cannot be explicit because get_return_object relies on implicit conversion.
|
||||
com_task(details::coro::task_promise<T>* initial = nullptr) noexcept : base(initial)
|
||||
{
|
||||
}
|
||||
explicit com_task(base&& other) noexcept : base(wistd::move(other))
|
||||
{
|
||||
}
|
||||
com_task& operator=(base&& other) noexcept
|
||||
{
|
||||
return base::assign(this, wistd::move(other));
|
||||
}
|
||||
|
||||
using base::operator co_await;
|
||||
|
||||
auto operator co_await() && noexcept
|
||||
{
|
||||
// You must #include <ole2.h> before <wil/coroutine.h> to enable non-agile awaiting.
|
||||
return wistd::move(*this).resume_same_apartment();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
task(com_task<T>&&) -> task<T>;
|
||||
template <typename T>
|
||||
com_task(task<T>&&) -> com_task<T>;
|
||||
} // namespace wil
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct __WI_COROUTINE_NAMESPACE::coroutine_traits<wil::task<T>, Args...>
|
||||
{
|
||||
using promise_type = wil::details::coro::task_promise<T>;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct __WI_COROUTINE_NAMESPACE::coroutine_traits<wil::com_task<T>, Args...>
|
||||
{
|
||||
using promise_type = wil::details::coro::task_promise<T>;
|
||||
};
|
||||
|
||||
#endif // __WIL_COROUTINE_INCLUDED
|
||||
|
||||
// Can re-include this header after including synchapi.h (usually via windows.h) to enable synchronous wait.
|
||||
#if defined(_SYNCHAPI_H_) && !defined(__WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED)
|
||||
#define __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED
|
||||
|
||||
namespace wil::details::coro
|
||||
{
|
||||
template <typename T>
|
||||
decltype(auto) task_base<T>::get() &&
|
||||
{
|
||||
if (!promise->client_await_ready())
|
||||
{
|
||||
bool completed = false;
|
||||
if (promise->client_await_suspend(&completed, wake_by_address))
|
||||
{
|
||||
bool pending = false;
|
||||
while (!completed)
|
||||
{
|
||||
WaitOnAddress(&completed, &pending, sizeof(pending), INFINITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::exchange(promise, {})->client_await_resume();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void __stdcall task_base<T>::wake_by_address(void* completed)
|
||||
{
|
||||
*reinterpret_cast<bool*>(completed) = true;
|
||||
WakeByAddressSingle(completed);
|
||||
}
|
||||
} // namespace wil::details::coro
|
||||
#endif // __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED
|
||||
|
||||
// Can re-include this header after including COM header files to enable non-agile tasks.
|
||||
#if defined(_COMBASEAPI_H_) && defined(_THREADPOOLAPISET_H_) && !defined(__WIL_COROUTINE_NON_AGILE_INCLUDED)
|
||||
#define __WIL_COROUTINE_NON_AGILE_INCLUDED
|
||||
#include <ctxtcall.h>
|
||||
#include <wil/com.h>
|
||||
#include <roerrorapi.h>
|
||||
|
||||
namespace wil::details::coro
|
||||
{
|
||||
inline void* __stdcall CaptureRestrictedErrorInformation() noexcept
|
||||
{
|
||||
IRestrictedErrorInfo* restrictedError = nullptr;
|
||||
(void)GetRestrictedErrorInfo(&restrictedError);
|
||||
return restrictedError; // the returned object includes a strong reference
|
||||
}
|
||||
|
||||
inline void __stdcall RestoreRestrictedErrorInformation(_In_ void* restricted_error) noexcept
|
||||
{
|
||||
(void)SetRestrictedErrorInfo(static_cast<IRestrictedErrorInfo*>(restricted_error));
|
||||
}
|
||||
|
||||
inline void __stdcall DestroyRestrictedErrorInformation(_In_ void* restricted_error) noexcept
|
||||
{
|
||||
static_cast<IUnknown*>(restricted_error)->Release();
|
||||
}
|
||||
|
||||
struct apartment_info
|
||||
{
|
||||
APTTYPE aptType{};
|
||||
APTTYPEQUALIFIER aptTypeQualifier{};
|
||||
|
||||
void load()
|
||||
{
|
||||
if (FAILED(CoGetApartmentType(&aptType, &aptTypeQualifier)))
|
||||
{
|
||||
// If COM is not initialized, then act as if we are running
|
||||
// on the implicit MTA.
|
||||
aptType = APTTYPE_MTA;
|
||||
aptTypeQualifier = APTTYPEQUALIFIER_IMPLICIT_MTA;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// apartment_resumer resumes a coroutine in a captured apartment.
|
||||
struct apartment_resumer
|
||||
{
|
||||
static auto as_self(void* p)
|
||||
{
|
||||
return reinterpret_cast<apartment_resumer*>(p);
|
||||
}
|
||||
|
||||
static bool is_sta()
|
||||
{
|
||||
apartment_info info;
|
||||
info.load();
|
||||
switch (info.aptType)
|
||||
{
|
||||
case APTTYPE_STA:
|
||||
case APTTYPE_MAINSTA:
|
||||
return true;
|
||||
case APTTYPE_NA:
|
||||
return info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA || info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static wil::com_ptr<IContextCallback> current_context()
|
||||
{
|
||||
wil::com_ptr<IContextCallback> context;
|
||||
// This will fail if COM is not initialized. Treat as implicit MTA.
|
||||
// Do not use IID_PPV_ARGS to avoid ambiguity between ::IUnknown and winrt::IUnknown.
|
||||
CoGetObjectContext(__uuidof(IContextCallback), reinterpret_cast<void**>(&context));
|
||||
return context;
|
||||
}
|
||||
|
||||
__WI_COROUTINE_NAMESPACE::coroutine_handle<> waiter;
|
||||
wil::com_ptr<IContextCallback> context{nullptr};
|
||||
apartment_info info{};
|
||||
HRESULT resume_result = S_OK;
|
||||
|
||||
void capture_context(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
|
||||
{
|
||||
waiter = handle;
|
||||
info.load();
|
||||
context = current_context();
|
||||
if (context == nullptr)
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
}
|
||||
|
||||
static void __stdcall resume_in_context(void* parameter)
|
||||
{
|
||||
auto self = as_self(parameter);
|
||||
if (self->context == nullptr || self->context == current_context())
|
||||
{
|
||||
self->context = nullptr; // removes the context cleanup from the resume path
|
||||
self->waiter();
|
||||
}
|
||||
else if (is_sta())
|
||||
{
|
||||
submit_threadpool_callback(resume_context, self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->resume_context_sync();
|
||||
}
|
||||
}
|
||||
|
||||
static void submit_threadpool_callback(PTP_SIMPLE_CALLBACK callback, void* context)
|
||||
{
|
||||
THROW_IF_WIN32_BOOL_FALSE(TrySubmitThreadpoolCallback(callback, context, nullptr));
|
||||
}
|
||||
|
||||
static void CALLBACK resume_context(PTP_CALLBACK_INSTANCE /*instance*/, void* parameter)
|
||||
{
|
||||
as_self(parameter)->resume_context_sync();
|
||||
}
|
||||
|
||||
void resume_context_sync()
|
||||
{
|
||||
ComCallData data{};
|
||||
data.pUserDefined = this;
|
||||
// The call to resume_apartment_callback will destruct the context.
|
||||
// Capture into a local so we don't destruct it while it's in use.
|
||||
// This also removes the context cleanup from the resume path.
|
||||
auto local_context = wistd::move(context);
|
||||
auto result =
|
||||
local_context->ContextCallback(resume_apartment_callback, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr);
|
||||
if (FAILED(result))
|
||||
{
|
||||
// Unable to resume on the correct apartment.
|
||||
// Resume on the wrong apartment, but tell the coroutine why.
|
||||
resume_result = result;
|
||||
waiter();
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT CALLBACK resume_apartment_callback(ComCallData* data) noexcept
|
||||
{
|
||||
as_self(data->pUserDefined)->waiter();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
THROW_IF_FAILED(resume_result);
|
||||
}
|
||||
};
|
||||
|
||||
// The COM awaiter captures the COM context when the co_await begins.
|
||||
// When the co_await completes, it uses that COM context to resume execution.
|
||||
// This follows the same algorithm employed by C++/WinRT, which has features like
|
||||
// avoiding stack buildup and proper handling of the neutral apartment.
|
||||
// It does, however, introduce fail-fast code paths if thread pool tasks cannot
|
||||
// be submitted. (Those fail-fasts could be removed by preallocating the tasks,
|
||||
// but that means paying an up-front cost for something that may never end up used,
|
||||
// as well as introducing extra cleanup code in the fast-path.)
|
||||
template <typename T>
|
||||
struct com_awaiter : agile_awaiter<T>
|
||||
{
|
||||
com_awaiter(promise_ptr<T>&& initial) : agile_awaiter<T>(wistd::move(initial))
|
||||
{
|
||||
}
|
||||
apartment_resumer resumer;
|
||||
|
||||
auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
|
||||
{
|
||||
resumer.capture_context(handle);
|
||||
return this->promise->client_await_suspend(wistd::addressof(resumer), apartment_resumer::resume_in_context);
|
||||
}
|
||||
|
||||
decltype(auto) await_resume()
|
||||
{
|
||||
resumer.check();
|
||||
return agile_awaiter<T>::await_resume();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
auto task_base<T>::resume_same_apartment() && noexcept
|
||||
{
|
||||
return com_awaiter<T>{wistd::move(promise)};
|
||||
}
|
||||
} // namespace wil::details::coro
|
||||
|
||||
// This section is lit up when COM headers are available. Initialize the global function
|
||||
// pointers such that error information can be saved and restored across thread boundaries.
|
||||
WI_HEADER_INITIALIZATION_FUNCTION(CoroutineRestrictedErrorInitialize, [] {
|
||||
::wil::details::coro::g_pfnCaptureRestrictedErrorInformation = ::wil::details::coro::CaptureRestrictedErrorInformation;
|
||||
::wil::details::coro::g_pfnRestoreRestrictedErrorInformation = ::wil::details::coro::RestoreRestrictedErrorInformation;
|
||||
::wil::details::coro::g_pfnDestroyRestrictedErrorInformation = ::wil::details::coro::DestroyRestrictedErrorInformation;
|
||||
return 1;
|
||||
})
|
||||
|
||||
#endif // __WIL_COROUTINE_NON_AGILE_INCLUDED
|
||||
499
3rdparty/winwil/include/wil/cppwinrt.h
vendored
Normal file
499
3rdparty/winwil/include/wil/cppwinrt.h
vendored
Normal file
@@ -0,0 +1,499 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! WIL Error Handling Helpers: support for interoperability between WIL and C++/WinRT exception-to-HRESULT logic.
|
||||
#ifndef __WIL_CPPWINRT_INCLUDED
|
||||
#define __WIL_CPPWINRT_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
#include <windows.h>
|
||||
#include <unknwn.h>
|
||||
#include <inspectable.h>
|
||||
#include <hstring.h>
|
||||
|
||||
// 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...
|
||||
// For example: "2.0.221104.6"
|
||||
inline constexpr int version_from_string(const char* versionString)
|
||||
{
|
||||
int result = 0;
|
||||
while ((*versionString >= '0') && (*versionString <= '9'))
|
||||
{
|
||||
result = result * 10 + (*versionString - '0');
|
||||
++versionString;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr int major_version_from_string(const char* versionString)
|
||||
{
|
||||
return version_from_string(versionString);
|
||||
}
|
||||
|
||||
inline constexpr int minor_version_from_string(const char* versionString)
|
||||
{
|
||||
int dotCount = 0;
|
||||
while ((*versionString != '\0'))
|
||||
{
|
||||
if (*versionString == '.')
|
||||
{
|
||||
++dotCount;
|
||||
}
|
||||
|
||||
++versionString;
|
||||
if (dotCount == 2)
|
||||
{
|
||||
return version_from_string(versionString);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace wil::details
|
||||
/// @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
|
||||
/// @cond
|
||||
#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
|
||||
/// @endcond
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <winrt/base.h>
|
||||
|
||||
#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
|
||||
|
||||
/// @cond
|
||||
// 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;
|
||||
|
||||
// The same is true with this function pointer as well, except that the version must be 2.X or higher.
|
||||
extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept;
|
||||
/// @endcond
|
||||
|
||||
/// @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.to_abi();
|
||||
}
|
||||
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.to_abi();
|
||||
}
|
||||
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;
|
||||
}
|
||||
} // namespace wil::details
|
||||
/// @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<std::int32_t>(
|
||||
details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
|
||||
}
|
||||
|
||||
inline void __stdcall winrt_throw_hresult(
|
||||
uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept
|
||||
{
|
||||
void* callerReturnAddress{nullptr};
|
||||
PCSTR code{nullptr};
|
||||
wil::details::ReportFailure_Hr<FailureType::Log>(__R_FN_CALL_FULL __R_COMMA result);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if constexpr (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)
|
||||
{
|
||||
WI_ASSERT(winrt_throw_hresult_handler == nullptr);
|
||||
winrt_throw_hresult_handler = winrt_throw_hresult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
|
||||
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
|
||||
WI_HEADER_INITIALIZATION_FUNCTION(WilInitialize_CppWinRT, [] {
|
||||
::wil::WilInitialize_CppWinRT();
|
||||
return 1;
|
||||
});
|
||||
#else
|
||||
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
|
||||
#endif
|
||||
} // namespace details
|
||||
/// @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 <typename T>
|
||||
auto get_abi(T const& object) noexcept
|
||||
{
|
||||
return winrt::get_abi(object);
|
||||
}
|
||||
|
||||
inline auto get_abi(winrt::hstring const& object) noexcept
|
||||
{
|
||||
return static_cast<HSTRING>(winrt::get_abi(object));
|
||||
}
|
||||
|
||||
inline auto str_raw_ptr(const winrt::hstring& str) noexcept
|
||||
{
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto put_abi(T& object) noexcept
|
||||
{
|
||||
return winrt::put_abi(object);
|
||||
}
|
||||
|
||||
inline auto put_abi(winrt::hstring& object) noexcept
|
||||
{
|
||||
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
|
||||
}
|
||||
|
||||
inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept
|
||||
{
|
||||
return static_cast<::IUnknown*>(winrt::get_abi(ptr));
|
||||
}
|
||||
|
||||
// Needed to power wil::cx_object_from_abi that requires IInspectable
|
||||
inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept
|
||||
{
|
||||
return static_cast<::IInspectable*>(winrt::get_abi(ptr));
|
||||
}
|
||||
|
||||
// Taken from the docs.microsoft.com article
|
||||
template <typename T>
|
||||
T convert_from_abi(::IUnknown* from)
|
||||
{
|
||||
T to{nullptr}; // `T` is a projected type.
|
||||
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
|
||||
return to;
|
||||
}
|
||||
|
||||
// For obtaining an object from an interop method on the factory. Example:
|
||||
// winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);
|
||||
// If the method produces something different from the factory type:
|
||||
// winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);
|
||||
template <typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
|
||||
auto capture_interop(HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args)
|
||||
{
|
||||
auto interop = winrt::get_activation_factory<WinRTFactory, Interface>();
|
||||
return winrt::capture<WinRTResult>(interop, method, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// For obtaining an object from an interop method on an instance. Example:
|
||||
// winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);
|
||||
template <typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
|
||||
auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args)
|
||||
{
|
||||
return winrt::capture<WinRTResult>(o.as<Interface>(), method, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/** Holds a reference to the host C++/WinRT module to prevent it from being unloaded.
|
||||
Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong
|
||||
reference to a C++/WinRT object hosted in the same module, but if you have neither,
|
||||
you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference.
|
||||
|
||||
This can be used as a base, which permits EBO:
|
||||
@code
|
||||
struct NonWinrtObject : wil::winrt_module_reference
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
// DLL will not be unloaded as long as NonWinrtObject is still alive.
|
||||
auto p = std::make_unique<NonWinrtObject>();
|
||||
@endcode
|
||||
|
||||
Or it can be used as a member (with [[no_unique_address]] to avoid
|
||||
occupying any memory):
|
||||
@code
|
||||
struct NonWinrtObject
|
||||
{
|
||||
int value;
|
||||
|
||||
[[no_unique_address]] wil::winrt_module_reference module_ref;
|
||||
};
|
||||
|
||||
// DLL will not be unloaded as long as NonWinrtObject is still alive.
|
||||
auto p = std::make_unique<NonWinrtObject>();
|
||||
@endcode
|
||||
|
||||
If using it to prevent the host DLL from unloading while a thread
|
||||
or threadpool work item is still running, create the object before
|
||||
starting the thread, and pass it to the thread. This avoids a race
|
||||
condition where the host DLL could get unloaded before the thread starts.
|
||||
@code
|
||||
std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); });
|
||||
|
||||
// Don't do this (race condition)
|
||||
std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG
|
||||
@endcode
|
||||
|
||||
Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves
|
||||
DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong()
|
||||
of its containing WinRT class, then the IAsyncAction or strong reference will itself keep
|
||||
a strong reference to the host module.)
|
||||
@code
|
||||
winrt::fire_and_forget ContinueBackgroundWork()
|
||||
{
|
||||
// prevent DLL from unloading while we are running on a background thread.
|
||||
// Do this before switching to the background thread.
|
||||
wil::winrt_module_reference module_ref;
|
||||
|
||||
co_await winrt::resume_background();
|
||||
do_background_work();
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
struct [[nodiscard]] winrt_module_reference
|
||||
{
|
||||
winrt_module_reference()
|
||||
{
|
||||
++winrt::get_module_lock();
|
||||
}
|
||||
|
||||
winrt_module_reference(winrt_module_reference const&) : winrt_module_reference()
|
||||
{
|
||||
}
|
||||
|
||||
~winrt_module_reference()
|
||||
{
|
||||
--winrt::get_module_lock();
|
||||
}
|
||||
};
|
||||
|
||||
/** Implements a C++/WinRT class where some interfaces are conditionally supported.
|
||||
@code
|
||||
// Assume the existence of a class "Version2" which says whether
|
||||
// the IMyThing2 interface should be supported.
|
||||
struct Version2 { static bool IsEnabled(); };
|
||||
|
||||
// Declare implementation class which conditionally supports IMyThing2.
|
||||
struct MyThing : wil::winrt_conditionally_implements<MyThingT<MyThing>,
|
||||
Version2, IMyThing2>
|
||||
{
|
||||
// implementation goes here
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
If `Version2::IsEnabled()` returns `false`, then the `QueryInterface`
|
||||
for `IMyThing2` will fail.
|
||||
|
||||
Any interface not listed as conditional is assumed to be enabled unconditionally.
|
||||
|
||||
You can add additional Version / Interface pairs to the template parameter list.
|
||||
Interfaces may be conditionalized on at most one Version class. If you need a
|
||||
complex conditional, create a new helper class.
|
||||
|
||||
@code
|
||||
// Helper class for testing two Versions.
|
||||
struct Version2_or_greater {
|
||||
static bool IsEnabled() { return Version2::IsEnabled() || Version3::IsEnabled(); }
|
||||
};
|
||||
|
||||
// This implementation supports IMyThing2 if either Version2 or Version3 is enabled,
|
||||
// and supports IMyThing3 only if Version3 is enabled.
|
||||
struct MyThing : wil::winrt_conditionally_implements<MyThingT<MyThing>,
|
||||
Version2_or_greater, IMyThing2, Version3, IMyThing3>
|
||||
{
|
||||
// implementation goes here
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
template <typename Implements, typename... Rest>
|
||||
struct winrt_conditionally_implements : Implements
|
||||
{
|
||||
using Implements::Implements;
|
||||
|
||||
void* find_interface(winrt::guid const& iid) const noexcept override
|
||||
{
|
||||
static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two");
|
||||
if (is_enabled<0, std::tuple<Rest...>>(iid))
|
||||
{
|
||||
return Implements::find_interface(iid);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t index, typename Tuple>
|
||||
static bool is_enabled(winrt::guid const& iid)
|
||||
{
|
||||
if constexpr (index >= std::tuple_size_v<Tuple>)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
check_no_duplicates<1, index + 1, Tuple>();
|
||||
return (iid == winrt::guid_of<std::tuple_element_t<index + 1, Tuple>>()) ? std::tuple_element_t<index, Tuple>::IsEnabled()
|
||||
: is_enabled<index + 2, Tuple>(iid);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t index, std::size_t upto, typename Tuple>
|
||||
static constexpr void check_no_duplicates()
|
||||
{
|
||||
if constexpr (index < upto)
|
||||
{
|
||||
static_assert(
|
||||
!std::is_same_v<std::tuple_element_t<index, Tuple>, std::tuple_element_t<upto, Tuple>>,
|
||||
"Duplicate interfaces found in winrt_conditionally_implements");
|
||||
check_no_duplicates<index + 2, upto, Tuple>();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_CPPWINRT_INCLUDED
|
||||
334
3rdparty/winwil/include/wil/cppwinrt_authoring.h
vendored
Normal file
334
3rdparty/winwil/include/wil/cppwinrt_authoring.h
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Helpers that make authoring C++/WinRT components easier.
|
||||
|
||||
namespace wil
|
||||
{
|
||||
#ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
|
||||
/// @cond
|
||||
#define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct single_threaded_property_storage
|
||||
{
|
||||
T m_value{};
|
||||
single_threaded_property_storage() = default;
|
||||
single_threaded_property_storage(const T& value) : m_value(value)
|
||||
{
|
||||
}
|
||||
operator T&()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
operator T const&() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
template <typename Q>
|
||||
auto operator=(Q&& q)
|
||||
{
|
||||
m_value = wistd::forward<Q>(q);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
template <typename T>
|
||||
struct single_threaded_property
|
||||
: std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>
|
||||
{
|
||||
single_threaded_property() = default;
|
||||
template <typename... TArgs>
|
||||
single_threaded_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...)
|
||||
{
|
||||
}
|
||||
|
||||
using base_type =
|
||||
std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>;
|
||||
|
||||
T operator()() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
// This is the only setter exposed. We don't expose `operator()(Q&& q)`,
|
||||
// since that is what C++/WinRT uses to implement public setters. Since
|
||||
// single_threaded_property is intended for readonly properties, we
|
||||
// don't want to expose that.
|
||||
//
|
||||
// To set the value of this property *internally* (within your
|
||||
// implementation), use this `operator=`:
|
||||
//
|
||||
// MyProperty = 42;
|
||||
// // MyProperty(42); // won't work
|
||||
//
|
||||
// For settable properties, use single_threaded_rw_property<T> instead.
|
||||
template <typename Q>
|
||||
auto& operator=(Q&& q)
|
||||
{
|
||||
static_cast<base_type&>(*this) = std::forward<Q>(q);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct single_threaded_rw_property : single_threaded_property<T>
|
||||
{
|
||||
using base_type = single_threaded_property<T>;
|
||||
template <typename... TArgs>
|
||||
single_threaded_rw_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...)
|
||||
{
|
||||
}
|
||||
|
||||
using base_type::operator();
|
||||
|
||||
// needed in lieu of deducing-this
|
||||
template <typename Q>
|
||||
auto& operator()(Q&& q)
|
||||
{
|
||||
return *this = std::forward<Q>(q);
|
||||
}
|
||||
|
||||
// needed in lieu of deducing-this
|
||||
template <typename Q>
|
||||
auto& operator=(Q&& q)
|
||||
{
|
||||
base_type::operator=(std::forward<Q>(q));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
|
||||
|
||||
#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)) || \
|
||||
defined(WIL_DOXYGEN) // WinRT / XAML helpers
|
||||
/// @cond
|
||||
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct event_base
|
||||
{
|
||||
winrt::event_token operator()(const T& handler)
|
||||
{
|
||||
return m_handler.add(handler);
|
||||
}
|
||||
|
||||
auto operator()(const winrt::event_token& token) noexcept
|
||||
{
|
||||
return m_handler.remove(token);
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
auto invoke(TArgs&&... args)
|
||||
{
|
||||
return m_handler(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::event<T> m_handler;
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* @brief A default event handler that maps to
|
||||
* [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1).
|
||||
* @tparam T The event data type.
|
||||
*/
|
||||
template <typename T>
|
||||
struct untyped_event : wil::details::event_base<winrt::Windows::Foundation::EventHandler<T>>
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A default event handler that maps to
|
||||
* [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2).
|
||||
* @tparam T The event data type.
|
||||
* @details Usage example:
|
||||
* @code
|
||||
* // In IDL, this corresponds to:
|
||||
* // event Windows.Foundation.TypedEventHandler<ModalPage, String> OkClicked;
|
||||
* wil::typed_event<MarkupSample::ModalPage, winrt::hstring> OkClicked;
|
||||
* @endcode
|
||||
*/
|
||||
template <typename TSender, typename TArgs>
|
||||
struct typed_event : wil::details::event_base<winrt::Windows::Foundation::TypedEventHandler<TSender, TArgs>>
|
||||
{
|
||||
};
|
||||
|
||||
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)
|
||||
|
||||
#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))) || \
|
||||
defined(WIL_DOXYGEN) // INotifyPropertyChanged helpers
|
||||
/// @cond
|
||||
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA
|
||||
namespace details
|
||||
{
|
||||
#ifdef WINRT_Microsoft_UI_Xaml_Data_H
|
||||
using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler;
|
||||
using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs;
|
||||
#elif defined(WINRT_Windows_UI_Xaml_Data_H)
|
||||
using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler;
|
||||
using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs;
|
||||
#endif
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* @brief Helper base class to inherit from to have a simple implementation of
|
||||
* [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged).
|
||||
* @tparam T CRTP type
|
||||
* @details When you declare your class, make this class a base class and pass your class as a template parameter:
|
||||
* @code
|
||||
* struct MyPage : MyPageT<MyPage>, wil::notify_property_changed_base<MyPage>
|
||||
* {
|
||||
* wil::single_threaded_notifying_property<int> MyInt;
|
||||
* MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { }
|
||||
* // or
|
||||
* WIL_NOTIFYING_PROPERTY(int, MyInt, 42);
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
template <typename T, typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler, typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
|
||||
struct notify_property_changed_base
|
||||
{
|
||||
using Type = T;
|
||||
auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value)
|
||||
{
|
||||
return m_propertyChanged.add(value);
|
||||
}
|
||||
|
||||
void PropertyChanged(winrt::event_token const& token)
|
||||
{
|
||||
m_propertyChanged.remove(token);
|
||||
}
|
||||
|
||||
Type& self()
|
||||
{
|
||||
return *static_cast<Type*>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Raises a property change notification event
|
||||
* @param name The name of the property
|
||||
* @return
|
||||
* @details Usage example\n
|
||||
* C++
|
||||
* @code
|
||||
* void MyPage::DoSomething()
|
||||
* {
|
||||
* // modify MyInt
|
||||
* // MyInt = ...
|
||||
*
|
||||
* // now send a notification to update the bound UI elements
|
||||
* RaisePropertyChanged(L"MyInt");
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
auto RaisePropertyChanged(std::wstring_view name)
|
||||
{
|
||||
return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name});
|
||||
}
|
||||
|
||||
protected:
|
||||
winrt::event<Xaml_Data_PropertyChangedEventHandler> m_propertyChanged;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a property type with notifications
|
||||
* @tparam T the property type
|
||||
* @details Use the INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the
|
||||
* right property name, and bind it to the `notify_property_changed_base` implementation.
|
||||
*/
|
||||
template <typename T, typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler, typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
|
||||
struct single_threaded_notifying_property : single_threaded_rw_property<T>
|
||||
{
|
||||
using Type = T;
|
||||
using base_type = single_threaded_rw_property<T>;
|
||||
using base_type::operator();
|
||||
|
||||
template <typename Q>
|
||||
auto& operator()(Q&& q)
|
||||
{
|
||||
return *this = std::forward<Q>(q);
|
||||
}
|
||||
|
||||
template <typename Q>
|
||||
auto& operator=(Q&& q)
|
||||
{
|
||||
if (q != this->operator()())
|
||||
{
|
||||
static_cast<base_type&>(*this) = std::forward<Q>(q);
|
||||
if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr))
|
||||
{
|
||||
(*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{m_name});
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
single_threaded_notifying_property(
|
||||
winrt::event<Xaml_Data_PropertyChangedEventHandler>* npc,
|
||||
const winrt::Windows::Foundation::IInspectable& sender,
|
||||
std::wstring_view name,
|
||||
TArgs&&... args) :
|
||||
single_threaded_rw_property<T>(std::forward<TArgs...>(args)...), m_name(name), m_npc(npc), m_sender(sender)
|
||||
{
|
||||
}
|
||||
|
||||
single_threaded_notifying_property(const single_threaded_notifying_property&) = default;
|
||||
single_threaded_notifying_property(single_threaded_notifying_property&&) = default;
|
||||
std::wstring_view Name() const noexcept
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring_view m_name;
|
||||
winrt::event<Xaml_Data_PropertyChangedEventHandler>* m_npc;
|
||||
winrt::weak_ref<winrt::Windows::Foundation::IInspectable> m_sender;
|
||||
};
|
||||
|
||||
/**
|
||||
* @def WIL_NOTIFYING_PROPERTY
|
||||
* @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage
|
||||
* alternative to wil::single_threaded_notifying_property<T>.
|
||||
* @details You can pass an initializer list for the initial property value in the variadic arguments to this macro.
|
||||
*/
|
||||
#define WIL_NOTIFYING_PROPERTY(type, name, ...) \
|
||||
type m_##name{__VA_ARGS__}; \
|
||||
auto name() const noexcept \
|
||||
{ \
|
||||
return m_##name; \
|
||||
} \
|
||||
auto& name(type value) \
|
||||
{ \
|
||||
if (m_##name != value) \
|
||||
{ \
|
||||
m_##name = std::move(value); \
|
||||
RaisePropertyChanged(L"" #name); \
|
||||
} \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @def INIT_NOTIFYING_PROPERTY
|
||||
* @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor.
|
||||
*/
|
||||
#define INIT_NOTIFYING_PROPERTY(NAME, VALUE) NAME(&m_propertyChanged, *this, L"" #NAME, VALUE)
|
||||
|
||||
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))
|
||||
} // namespace wil
|
||||
393
3rdparty/winwil/include/wil/cppwinrt_helpers.h
vendored
Normal file
393
3rdparty/winwil/include/wil/cppwinrt_helpers.h
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Helpers for common patterns and tasks when using C++/WinRT.
|
||||
|
||||
#ifndef __WIL_CPPWINRT_HELPERS_DEFINED
|
||||
#define __WIL_CPPWINRT_HELPERS_DEFINED
|
||||
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
struct dispatcher_RunAsync
|
||||
{
|
||||
template <typename Dispatcher, typename... Args>
|
||||
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
||||
{
|
||||
dispatcher.RunAsync(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
struct dispatcher_TryEnqueue
|
||||
{
|
||||
template <typename Dispatcher, typename... Args>
|
||||
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
||||
{
|
||||
dispatcher.TryEnqueue(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dispatcher>
|
||||
struct dispatcher_traits;
|
||||
} // namespace wil::details
|
||||
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||
#include <experimental/coroutine>
|
||||
namespace wil::details
|
||||
{
|
||||
template <typename T = void>
|
||||
using coroutine_handle = std::experimental::coroutine_handle<T>;
|
||||
}
|
||||
#elif defined(__cpp_impl_coroutine)
|
||||
#include <coroutine>
|
||||
#if (__cpp_lib_coroutine >= 201902L)
|
||||
namespace wil::details
|
||||
{
|
||||
template <typename T = void>
|
||||
using coroutine_handle = std::coroutine_handle<T>;
|
||||
}
|
||||
#endif // __cpp_lib_coroutine
|
||||
#endif // __cpp_impl_coroutine
|
||||
/// @endcond
|
||||
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || \
|
||||
(defined(__cpp_impl_coroutine) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L))
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
struct dispatched_handler_state
|
||||
{
|
||||
details::coroutine_handle<> handle{};
|
||||
bool orphaned = false;
|
||||
};
|
||||
|
||||
struct dispatcher_handler
|
||||
{
|
||||
dispatcher_handler(dispatched_handler_state* state) : m_state(state)
|
||||
{
|
||||
}
|
||||
dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {}))
|
||||
{
|
||||
}
|
||||
|
||||
~dispatcher_handler()
|
||||
{
|
||||
if (m_state && m_state->handle)
|
||||
{
|
||||
m_state->orphaned = true;
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
void operator()()
|
||||
{
|
||||
Complete();
|
||||
}
|
||||
|
||||
void Complete()
|
||||
{
|
||||
auto state = std::exchange(m_state, nullptr);
|
||||
std::exchange(state->handle, {}).resume();
|
||||
}
|
||||
|
||||
dispatched_handler_state* m_state;
|
||||
};
|
||||
} // namespace wil::details
|
||||
/// @endcond
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! Resumes coroutine execution on the thread associated with the dispatcher, or throws
|
||||
//! an exception (from an arbitrary thread) if unable. Supported dispatchers are
|
||||
//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue,
|
||||
//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher,
|
||||
//! but you must include the corresponding <winrt/Namespace.h> header before including
|
||||
//! wil/cppwinrt_helpers.h. It is okay to include wil/cppwinrt_helpers.h multiple times:
|
||||
//! support will be enabled for any winrt/Namespace.h headers that were included since
|
||||
//! the previous inclusion of wil/cppwinrt_headers.h.
|
||||
template <typename Dispatcher>
|
||||
[[nodiscard]] auto resume_foreground(
|
||||
Dispatcher const& dispatcher,
|
||||
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
|
||||
{
|
||||
using Traits = details::dispatcher_traits<Dispatcher>;
|
||||
using Priority = typename Traits::Priority;
|
||||
using Handler = typename Traits::Handler;
|
||||
|
||||
struct awaitable
|
||||
{
|
||||
awaitable(Dispatcher const& dispatcher, Priority priority) noexcept : m_dispatcher(dispatcher), m_priority(priority)
|
||||
{
|
||||
}
|
||||
bool await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void await_suspend(details::coroutine_handle<> handle)
|
||||
{
|
||||
m_state.handle = handle;
|
||||
Handler handler{details::dispatcher_handler(&m_state)};
|
||||
try
|
||||
{
|
||||
// The return value of Schedule is not reliable. Use the dispatcher_handler destructor
|
||||
// to detect whether the work item failed to run.
|
||||
Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void await_resume() const
|
||||
{
|
||||
if (m_state.orphaned)
|
||||
{
|
||||
throw winrt::hresult_error(static_cast<winrt::hresult>(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Dispatcher const& m_dispatcher;
|
||||
Priority const m_priority;
|
||||
details::dispatched_handler_state m_state;
|
||||
};
|
||||
return awaitable{dispatcher, priority};
|
||||
}
|
||||
} // namespace wil
|
||||
#endif // Coroutines are supported
|
||||
|
||||
#endif // __WIL_CPPWINRT_HELPERS_DEFINED
|
||||
|
||||
/// @cond
|
||||
#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template <>
|
||||
struct dispatcher_traits<winrt::Windows::UI::Core::CoreDispatcher>
|
||||
{
|
||||
using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority;
|
||||
using Handler = winrt::Windows::UI::Core::DispatchedHandler;
|
||||
using Scheduler = dispatcher_RunAsync;
|
||||
};
|
||||
} // namespace wil::details
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
||||
|
||||
#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template <>
|
||||
struct dispatcher_traits<winrt::Windows::System::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Windows::System::DispatcherQueuePriority;
|
||||
using Handler = winrt::Windows::System::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
} // namespace wil::details
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
||||
|
||||
#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS)
|
||||
#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template <>
|
||||
struct dispatcher_traits<winrt::Microsoft::System::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Microsoft::System::DispatcherQueuePriority;
|
||||
using Handler = winrt::Microsoft::System::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
} // namespace wil::details
|
||||
#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
||||
|
||||
#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS)
|
||||
#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template <>
|
||||
struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority;
|
||||
using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
} // namespace wil::details
|
||||
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
||||
/// @endcond
|
||||
|
||||
#if (defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)) || \
|
||||
defined(WIL_DOXYGEN)
|
||||
/// @cond
|
||||
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
|
||||
/// @endcond
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct is_winrt_vector_like
|
||||
{
|
||||
private:
|
||||
template <
|
||||
typename U,
|
||||
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(), winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
|
||||
static constexpr bool get_value(int)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template <typename>
|
||||
static constexpr bool get_value(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr bool value = get_value<T>(0);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_winrt_iterator_like
|
||||
{
|
||||
private:
|
||||
template <typename U, typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
|
||||
static constexpr bool get_value(int)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template <typename>
|
||||
static constexpr bool get_value(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr bool value = get_value<T>(0);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr T empty() noexcept
|
||||
{
|
||||
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the
|
||||
collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively
|
||||
processing the collection.
|
||||
@code
|
||||
winrt::IVector<winrt::hstring> collection = GetCollection();
|
||||
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
|
||||
for (winrt::hstring const& item : allData)
|
||||
{
|
||||
// use item
|
||||
}
|
||||
@endcode
|
||||
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
|
||||
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, etc.)
|
||||
Iterable-only types fetch content in units of 64. When used with an iterator, the returned
|
||||
vector contains the iterator's current position and any others after it.
|
||||
*/
|
||||
template <typename TSrc>
|
||||
auto to_vector(TSrc const& src)
|
||||
{
|
||||
if constexpr (details::is_winrt_vector_like<TSrc>::value)
|
||||
{
|
||||
using T = decltype(src.GetAt(0));
|
||||
std::vector<T> result;
|
||||
if (auto expected = src.Size())
|
||||
{
|
||||
result.resize(expected + 1, details::empty<T>());
|
||||
auto actual = src.GetMany(0, result);
|
||||
if (actual > expected)
|
||||
{
|
||||
throw winrt::hresult_changed_state();
|
||||
}
|
||||
result.resize(actual, details::empty<T>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if constexpr (details::is_winrt_iterator_like<TSrc>::value)
|
||||
{
|
||||
using T = decltype(src.Current());
|
||||
std::vector<T> result;
|
||||
constexpr uint32_t chunkSize = 64;
|
||||
while (true)
|
||||
{
|
||||
auto const lastSize = result.size();
|
||||
result.resize(lastSize + chunkSize, details::empty<T>());
|
||||
auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize});
|
||||
if (fetched < chunkSize)
|
||||
{
|
||||
result.resize(lastSize + fetched, details::empty<T>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return to_vector(src.First());
|
||||
}
|
||||
}
|
||||
} // namespace wil
|
||||
#endif
|
||||
|
||||
#if (defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)) || \
|
||||
defined(WIL_DOXYGEN)
|
||||
/// @cond
|
||||
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|
||||
/// @endcond
|
||||
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
||||
#pragma push_macro("ABI")
|
||||
#undef ABI
|
||||
#define ABI
|
||||
#endif
|
||||
|
||||
namespace wil
|
||||
{
|
||||
#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)
|
||||
//! The following methods require that you include both <winrt/Windows.UI.h>
|
||||
//! <Windows.UI.Interop.h> before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION
|
||||
//! is at least NTDDI_WIN10_CU. It is okay to include wil/cppwinrt_helpers.h multiple times:
|
||||
//! support will be enabled for any headers that were included since the previous inclusion
|
||||
//! of wil/cppwinrt_headers.h.
|
||||
inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd)
|
||||
{
|
||||
ABI::Windows::UI::WindowId abiWindowId;
|
||||
winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId));
|
||||
return winrt::Windows::UI::WindowId{abiWindowId.Value};
|
||||
}
|
||||
|
||||
inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId)
|
||||
{
|
||||
HWND hwnd;
|
||||
winrt::check_hresult(::GetWindowFromWindowId({windowId.Value}, &hwnd));
|
||||
return hwnd;
|
||||
}
|
||||
#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/
|
||||
} // namespace wil
|
||||
|
||||
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
||||
#pragma pop_macro("ABI")
|
||||
#endif
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|
||||
80
3rdparty/winwil/include/wil/cppwinrt_wrl.h
vendored
Normal file
80
3rdparty/winwil/include/wil/cppwinrt_wrl.h
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Provides interoperability between C++/WinRT types and the WRL Module system.
|
||||
#ifndef __WIL_CPPWINRT_WRL_INCLUDED
|
||||
#define __WIL_CPPWINRT_WRL_INCLUDED
|
||||
|
||||
#include "cppwinrt.h"
|
||||
#include <winrt/base.h>
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <wrl/module.h>
|
||||
|
||||
// wil::wrl_factory_for_winrt_com_class provides interopability between a
|
||||
// C++/WinRT class and the WRL Module system, allowing the winrt class to be
|
||||
// CoCreatable.
|
||||
//
|
||||
// Usage:
|
||||
// - In your cpp, add:
|
||||
// CoCreatableCppWinRtClass(className)
|
||||
//
|
||||
// - In the dll.cpp (or equivalent) for the module containing your class, add:
|
||||
// CoCreatableClassWrlCreatorMapInclude(className)
|
||||
//
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <typename TCppWinRTClass>
|
||||
class module_count_wrapper : public TCppWinRTClass
|
||||
{
|
||||
public:
|
||||
module_count_wrapper()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->IncrementObjectCount();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~module_count_wrapper()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->DecrementObjectCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
template <typename TCppWinRTClass>
|
||||
class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<>
|
||||
{
|
||||
public:
|
||||
IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept
|
||||
try
|
||||
{
|
||||
*object = nullptr;
|
||||
RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr);
|
||||
|
||||
return winrt::make<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
|
||||
}
|
||||
CATCH_RETURN()
|
||||
};
|
||||
} // namespace wil
|
||||
|
||||
#define CoCreatableCppWinRtClass(className) \
|
||||
CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)
|
||||
|
||||
#endif // __WIL_CPPWINRT_WRL_INCLUDED
|
||||
1329
3rdparty/winwil/include/wil/filesystem.h
vendored
Normal file
1329
3rdparty/winwil/include/wil/filesystem.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
233
3rdparty/winwil/include/wil/nt_result_macros.h
vendored
Normal file
233
3rdparty/winwil/include/wil/nt_result_macros.h
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors
|
||||
//! across return codes, fail fast, exceptions and logging for NTSTATUS values.
|
||||
#ifndef __WIL_NT_RESULTMACROS_INCLUDED
|
||||
#define __WIL_NT_RESULTMACROS_INCLUDED
|
||||
|
||||
#include "result_macros.h"
|
||||
|
||||
// Helpers for return macros
|
||||
/// @cond
|
||||
#define __NT_RETURN_NTSTATUS(status, str) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
NTSTATUS __status = (status); \
|
||||
if (FAILED_NTSTATUS(__status)) \
|
||||
{ \
|
||||
__R_FN(Return_NtStatus)(__R_INFO(str) __status); \
|
||||
} \
|
||||
return __status; \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
NTSTATUS __status = (status); \
|
||||
if (FAILED_NTSTATUS(__status)) \
|
||||
{ \
|
||||
__R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)); \
|
||||
} \
|
||||
return __status; \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
/// @endcond
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros for returning failures as NTSTATUS
|
||||
//*****************************************************************************
|
||||
|
||||
// Always returns a known result (NTSTATUS) - always logs failures
|
||||
#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status)
|
||||
|
||||
// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure
|
||||
#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) \
|
||||
__NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__)
|
||||
|
||||
// Conditionally returns failures (NTSTATUS) - always logs failures
|
||||
#define NT_RETURN_IF_NTSTATUS_FAILED(status) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
const auto __statusRet = wil::verify_ntstatus(status); \
|
||||
if (FAILED_NTSTATUS(__statusRet)) \
|
||||
{ \
|
||||
__NT_RETURN_NTSTATUS(__statusRet, #status); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure
|
||||
#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
const auto __statusRet = wil::verify_ntstatus(status); \
|
||||
if (FAILED_NTSTATUS(__statusRet)) \
|
||||
{ \
|
||||
__NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros to catch and convert exceptions on failure
|
||||
//*****************************************************************************
|
||||
|
||||
// Use these macros *within* a catch (...) block to handle exceptions
|
||||
#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr))
|
||||
#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) \
|
||||
return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__))
|
||||
|
||||
// Use these macros in place of a catch block to handle exceptions
|
||||
#define NT_CATCH_RETURN() \
|
||||
catch (...) \
|
||||
{ \
|
||||
NT_RETURN_CAUGHT_EXCEPTION(); \
|
||||
}
|
||||
#define NT_CATCH_RETURN_MSG(fmt, ...) \
|
||||
catch (...) \
|
||||
{ \
|
||||
NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//*****************************************************************************
|
||||
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
|
||||
//*****************************************************************************
|
||||
|
||||
// StatusFromCaughtException 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 NTSTATUS. If an exception is of an unrecognized type
|
||||
// the function will fail fast.
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// // Code
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// status = wil::StatusFromCaughtException();
|
||||
// }
|
||||
_Always_(_Post_satisfies_(return < 0)) __declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT
|
||||
{
|
||||
bool isNormalized = false;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
if (details::g_pfnResultFromCaughtExceptionInternal)
|
||||
{
|
||||
status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status;
|
||||
}
|
||||
if (FAILED_NTSTATUS(status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
// Caller bug: an unknown exception was thrown
|
||||
__WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions);
|
||||
return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <FailureType>
|
||||
__declspec(noinline) inline NTSTATUS
|
||||
ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
|
||||
template <FailureType>
|
||||
__declspec(noinline) inline NTSTATUS
|
||||
ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList);
|
||||
|
||||
namespace __R_NS_NAME
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||
{
|
||||
__R_FN_LOCALS;
|
||||
return wil::details::ReportStatus_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY);
|
||||
}
|
||||
|
||||
__R_DIRECT_METHOD(NTSTATUS, Nt_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::ReportStatus_CaughtExceptionMsg<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
|
||||
}
|
||||
#endif
|
||||
} // namespace __R_NS_NAME
|
||||
|
||||
template <FailureType T>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
|
||||
}
|
||||
|
||||
template <>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
RESULT_NORETURN_RESULT(
|
||||
ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||
}
|
||||
|
||||
template <>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
RESULT_NORETURN_RESULT(
|
||||
ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||
}
|
||||
|
||||
template <FailureType T>
|
||||
__declspec(noinline) inline NTSTATUS
|
||||
ReportStatus_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<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
|
||||
}
|
||||
|
||||
template <>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(
|
||||
__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" -- ");
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(
|
||||
__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default)
|
||||
.status);
|
||||
}
|
||||
|
||||
template <>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(
|
||||
__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" -- ");
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(
|
||||
__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default)
|
||||
.status);
|
||||
}
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_NT_RESULTMACROS_INCLUDED
|
||||
3323
3rdparty/winwil/include/wil/registry.h
vendored
Normal file
3323
3rdparty/winwil/include/wil/registry.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1951
3rdparty/winwil/include/wil/registry_helpers.h
vendored
Normal file
1951
3rdparty/winwil/include/wil/registry_helpers.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7654
3rdparty/winwil/include/wil/resource.h
vendored
Normal file
7654
3rdparty/winwil/include/wil/resource.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1293
3rdparty/winwil/include/wil/result.h
vendored
Normal file
1293
3rdparty/winwil/include/wil/result.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7338
3rdparty/winwil/include/wil/result_macros.h
vendored
Normal file
7338
3rdparty/winwil/include/wil/result_macros.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
131
3rdparty/winwil/include/wil/result_originate.h
vendored
Normal file
131
3rdparty/winwil/include/wil/result_originate.h
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! WIL Error Handling Helpers: supporting file enabling the originating of errors to produce better crash dumps
|
||||
|
||||
// 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 <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
|
||||
#include <winstring.h>
|
||||
#include "resource.h"
|
||||
#include "com.h"
|
||||
#include <roerrorapi.h>
|
||||
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
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<IRestrictedErrorInfo> 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<decltype(&::RoOriginateErrorW)>(GetProcAddress(errorModule.get(), "RoOriginateErrorW"));
|
||||
if (pfn != nullptr)
|
||||
{
|
||||
pfn(failure.hr, 0, failure.pszMessage);
|
||||
}
|
||||
}
|
||||
#else // DESKTOP | SYSTEM
|
||||
::RoOriginateErrorW(failure.hr, 0, failure.pszMessage);
|
||||
#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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This method will check for the presence of stowed exception data on the current thread. If such data exists, and the
|
||||
// HRESULT matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this
|
||||
// situation will result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we
|
||||
// just return and the calling method fails fast the same way it always has.
|
||||
inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
|
||||
{
|
||||
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
|
||||
{
|
||||
wil::unique_bstr descriptionUnused;
|
||||
HRESULT existingHr = failure.hr;
|
||||
wil::unique_bstr restrictedDescriptionUnused;
|
||||
wil::unique_bstr capabilitySidUnused;
|
||||
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(
|
||||
&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
|
||||
(existingHr == failure.hr))
|
||||
{
|
||||
// GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for
|
||||
// RoFailFastWithErrorContext so we must restore it via SetRestrictedErrorInfo first.
|
||||
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||
RoFailFastWithErrorContext(existingHr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing
|
||||
// fast in this method, so it is available in the debugger just-in-case.
|
||||
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
} // namespace wil
|
||||
|
||||
// Automatically call RoOriginateError upon error origination by including this file
|
||||
WI_HEADER_INITIALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] {
|
||||
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
||||
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
|
||||
return 1;
|
||||
})
|
||||
|
||||
#endif // __WIL_RESULT_ORIGINATE_INCLUDED
|
||||
219
3rdparty/winwil/include/wil/rpc_helpers.h
vendored
Normal file
219
3rdparty/winwil/include/wil/rpc_helpers.h
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Helpers for invoking RPC functions and translating structured exceptions to HRESULTs or C++ exceptions
|
||||
#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 <typename TReturnType>
|
||||
struct call_adapter
|
||||
{
|
||||
template <typename... TArgs>
|
||||
static HRESULT call(TArgs&&... args)
|
||||
{
|
||||
return wistd::invoke(wistd::forward<TArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct call_adapter<void>
|
||||
{
|
||||
template <typename... TArgs>
|
||||
static HRESULT call(TArgs&&... args)
|
||||
{
|
||||
wistd::invoke(wistd::forward<TArgs>(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);
|
||||
}
|
||||
} // namespace details
|
||||
/// @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<KittenState> state;
|
||||
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||
RETURN_IF_FAILED(hr);
|
||||
~~~
|
||||
*/
|
||||
template <typename... TCall>
|
||||
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<void, result_t>)'
|
||||
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(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 <typename TResult, typename... TCall>
|
||||
HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
|
||||
{
|
||||
RpcTryExcept
|
||||
{
|
||||
result = wistd::invoke(wistd::forward<TCall>(args)...);
|
||||
return S_OK;
|
||||
}
|
||||
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
|
||||
{
|
||||
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
|
||||
}
|
||||
RpcEndExcept
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
// Provides an adapter around calling the context-handle-close method on an
|
||||
// RPC interface, which itself is an RPC call.
|
||||
template <typename TStorage, typename close_fn_t, close_fn_t close_fn>
|
||||
struct rpc_closer_t
|
||||
{
|
||||
static void Close(TStorage arg) WI_NOEXCEPT
|
||||
{
|
||||
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/** 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.
|
||||
@code
|
||||
// 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<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
|
||||
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();
|
||||
@endcode
|
||||
*/
|
||||
template <typename TContext, typename close_fn_t, close_fn_t close_fn>
|
||||
using unique_rpc_context_handle =
|
||||
unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::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:
|
||||
@code
|
||||
wil::unique_midl_ptr<KittenState> state;
|
||||
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||
// use 'state'
|
||||
@endcode
|
||||
*/
|
||||
template <typename... TCall>
|
||||
void invoke_rpc(TCall&&... args)
|
||||
{
|
||||
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(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:
|
||||
@code
|
||||
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
|
||||
// use 'id'
|
||||
@endcode
|
||||
*/
|
||||
template <typename... TCall>
|
||||
auto invoke_rpc_result(TCall&&... args)
|
||||
{
|
||||
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||
result_t result{};
|
||||
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
} // namespace wil
|
||||
|
||||
#endif
|
||||
379
3rdparty/winwil/include/wil/safecast.h
vendored
Normal file
379
3rdparty/winwil/include/wil/safecast.h
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Type independent wrappers around the various intsafe.h functions
|
||||
#ifndef __WIL_SAFECAST_INCLUDED
|
||||
#define __WIL_SAFECAST_INCLUDED
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <intsafe.h>
|
||||
#include "wistd_config.h"
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
// Default error case for undefined conversions in intsafe.h
|
||||
template <typename OldT, typename NewT>
|
||||
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 <typename NewT, typename OldT>
|
||||
constexpr bool is_known_safe_static_cast_v =
|
||||
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
|
||||
(sizeof(NewT) == sizeof(OldT) &&
|
||||
((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
|
||||
|
||||
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
|
||||
// operation only supports conversions between integral types.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::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 <typename NewT, typename OldT>
|
||||
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Check to see if the cast is a conversion to native wchar_t
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Check to see if the cast is a conversion from native wchar_t
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
|
||||
|
||||
// True when the conversion is between integral types and can be handled by static_cast
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, does not involve native wchar, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_no_wchar_v =
|
||||
both_integral_v<NewT, OldT> && !is_known_safe_static_cast_v<NewT, OldT> && neither_native_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<NewT, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, is a cast to native wchar_t, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_to_wchar_v =
|
||||
both_integral_v<NewT, OldT> && !is_known_safe_static_cast_v<NewT, OldT> && is_cast_to_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<unsigned short, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, is a cast from native wchar_t, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_from_wchar_v =
|
||||
both_integral_v<NewT, OldT> && !is_known_safe_static_cast_v<NewT, OldT> && is_cast_from_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<NewT, unsigned short>;
|
||||
|
||||
// True when the conversion is supported and unsafe, and may or may not involve
|
||||
// native wchar_t.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_v =
|
||||
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> || is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
|
||||
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
|
||||
|
||||
// True when T is any one of the primitive types that the variably sized types are defined as.
|
||||
template <typename T>
|
||||
constexpr bool is_potentially_variably_sized_type_v =
|
||||
wistd::is_same<T, int>::value || wistd::is_same<T, unsigned int>::value || wistd::is_same<T, long>::value ||
|
||||
wistd::is_same<T, unsigned long>::value || wistd::is_same<T, __int64>::value || wistd::is_same<T, unsigned __int64>::value;
|
||||
|
||||
// True when either type is potentially variably sized (e.g. size_t, ptrdiff_t)
|
||||
template <typename OldT, typename NewT>
|
||||
constexpr bool is_potentially_variably_sized_cast_v =
|
||||
is_potentially_variably_sized_type_v<OldT> || is_potentially_variably_sized_type_v<NewT>;
|
||||
|
||||
// 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 <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, char> = IntToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, short> = IntToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, char> = LongToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, int> = LongToInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, short> = LongToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, char> = ShortToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
|
||||
template <>
|
||||
__WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
unsigned short newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(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<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(var);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <typename NewT, typename OldT, wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
unsigned short newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(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<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(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<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_nothrow(const OldT /*var*/)
|
||||
{
|
||||
static_assert(!wistd::is_same_v<NewT, NewT>, "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<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0>
|
||||
NewT safe_cast_nothrow(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(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<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<OldT, NewT>(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<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(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<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short*>(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<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
static_assert(
|
||||
details::is_potentially_variably_sized_cast_v<OldT, NewT>,
|
||||
"This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
|
||||
*newTResult = static_cast<NewT>(var);
|
||||
return S_OK;
|
||||
}
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_SAFECAST_INCLUDED
|
||||
233
3rdparty/winwil/include/wil/stl.h
vendored
Normal file
233
3rdparty/winwil/include/wil/stl.h
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Windows STL helpers: custom allocators for STL containers
|
||||
#ifndef __WIL_STL_INCLUDED
|
||||
#define __WIL_STL_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
#include "resource.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#if _HAS_CXX17
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
#ifndef WI_STL_FAIL_FAST_IF
|
||||
#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
#if defined(WIL_ENABLE_EXCEPTIONS)
|
||||
|
||||
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 <typename T>
|
||||
struct secure_allocator : public std::allocator<T>
|
||||
{
|
||||
template <typename Other>
|
||||
struct rebind
|
||||
{
|
||||
using other = secure_allocator<Other>;
|
||||
};
|
||||
|
||||
secure_allocator() : std::allocator<T>()
|
||||
{
|
||||
}
|
||||
|
||||
~secure_allocator() = default;
|
||||
|
||||
secure_allocator(const secure_allocator& a) : std::allocator<T>(a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class U>
|
||||
secure_allocator(const secure_allocator<U>& a) : std::allocator<T>(a)
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(size_t n)
|
||||
{
|
||||
return std::allocator<T>::allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n)
|
||||
{
|
||||
SecureZeroMemory(p, sizeof(T) * n);
|
||||
std::allocator<T>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
//! `wil::secure_vector` will be securely zeroed before deallocation.
|
||||
template <typename Type>
|
||||
using secure_vector = std::vector<Type, secure_allocator<Type>>;
|
||||
//! `wil::secure_wstring` will be securely zeroed before deallocation.
|
||||
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
|
||||
//! `wil::secure_string` will be securely zeroed before deallocation.
|
||||
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <>
|
||||
struct string_maker<std::wstring>
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
HRESULT trim_at_existing_null(size_t length)
|
||||
{
|
||||
m_value.erase(length);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
} // namespace details
|
||||
/// @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();
|
||||
}
|
||||
|
||||
#if _HAS_CXX17
|
||||
/**
|
||||
zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty).
|
||||
* zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated.
|
||||
* A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated
|
||||
string_view as a plain string view.
|
||||
* A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated.
|
||||
*/
|
||||
template <class TChar>
|
||||
class basic_zstring_view : public std::basic_string_view<TChar>
|
||||
{
|
||||
using size_type = typename std::basic_string_view<TChar>::size_type;
|
||||
|
||||
public:
|
||||
constexpr basic_zstring_view() noexcept = default;
|
||||
constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default;
|
||||
constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default;
|
||||
|
||||
constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept :
|
||||
std::basic_string_view<TChar>(pStringData, stringLength)
|
||||
{
|
||||
if (pStringData[stringLength] != 0)
|
||||
{
|
||||
WI_STL_FAIL_FAST_IF(true);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t stringArrayLength>
|
||||
constexpr basic_zstring_view(const TChar (&stringArray)[stringArrayLength]) noexcept :
|
||||
std::basic_string_view<TChar>(&stringArray[0], length_n(&stringArray[0], stringArrayLength))
|
||||
{
|
||||
}
|
||||
|
||||
// Construct from nul-terminated char ptr. To prevent this from overshadowing array construction,
|
||||
// we disable this constructor if the value is an array (including string literal).
|
||||
template <typename TPtr, std::enable_if_t<std::is_convertible<TPtr, const TChar*>::value && !std::is_array<TPtr>::value>* = nullptr>
|
||||
constexpr basic_zstring_view(TPtr&& pStr) noexcept : std::basic_string_view<TChar>(std::forward<TPtr>(pStr))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr basic_zstring_view(const std::basic_string<TChar>& str) noexcept :
|
||||
std::basic_string_view<TChar>(&str[0], str.size())
|
||||
{
|
||||
}
|
||||
|
||||
// basic_string_view [] precondition won't let us read view[view.size()]; so we define our own.
|
||||
WI_NODISCARD constexpr const TChar& operator[](size_type idx) const noexcept
|
||||
{
|
||||
WI_ASSERT(idx <= this->size() && this->data() != nullptr);
|
||||
return this->data()[idx];
|
||||
}
|
||||
|
||||
WI_NODISCARD constexpr const TChar* c_str() const noexcept
|
||||
{
|
||||
WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0);
|
||||
return this->data();
|
||||
}
|
||||
|
||||
private:
|
||||
// Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator.
|
||||
static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept
|
||||
{
|
||||
const std::basic_string_view<TChar> view(str, buf_size);
|
||||
auto pos = view.find_first_of(TChar());
|
||||
if (pos == view.npos)
|
||||
{
|
||||
WI_STL_FAIL_FAST_IF(true);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// The following basic_string_view methods must not be allowed because they break the nul-termination.
|
||||
using std::basic_string_view<TChar>::swap;
|
||||
using std::basic_string_view<TChar>::remove_suffix;
|
||||
};
|
||||
|
||||
using zstring_view = basic_zstring_view<char>;
|
||||
using zwstring_view = basic_zstring_view<wchar_t>;
|
||||
|
||||
inline namespace literals
|
||||
{
|
||||
constexpr zstring_view operator"" _zv(const char* str, std::size_t len) noexcept
|
||||
{
|
||||
return zstring_view(str, len);
|
||||
}
|
||||
|
||||
constexpr zwstring_view operator"" _zv(const wchar_t* str, std::size_t len) noexcept
|
||||
{
|
||||
return zwstring_view(str, len);
|
||||
}
|
||||
} // namespace literals
|
||||
|
||||
#endif // _HAS_CXX17
|
||||
|
||||
} // namespace wil
|
||||
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
#endif // __WIL_STL_INCLUDED
|
||||
712
3rdparty/winwil/include/wil/token_helpers.h
vendored
Normal file
712
3rdparty/winwil/include/wil/token_helpers.h
vendored
Normal file
@@ -0,0 +1,712 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Helpers for using tokens and impersonation
|
||||
#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 <new>
|
||||
#include <lmcons.h> // for UNLEN and DNLEN
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
// for GetUserNameEx()
|
||||
/// @cond
|
||||
#ifndef SECURITY_WIN32
|
||||
#define SECURITY_WIN32
|
||||
#endif
|
||||
/// @endcond
|
||||
#include <Security.h>
|
||||
|
||||
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 <typename T>
|
||||
struct MapTokenStructToInfoClass;
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_OWNER>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOwner;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_USER>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenUser;
|
||||
static constexpr bool FixedSize = false;
|
||||
};
|
||||
|
||||
// fixed size cases
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevationType;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_ORIGIN>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOrigin;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_SOURCE>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenSource;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_STATISTICS>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenStatistics;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_TYPE>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenType;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
template <>
|
||||
struct MapTokenStructToInfoClass<TOKEN_ELEVATION>
|
||||
{
|
||||
static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevation;
|
||||
static constexpr bool FixedSize = true;
|
||||
};
|
||||
|
||||
struct token_info_deleter
|
||||
{
|
||||
template <typename T>
|
||||
void operator()(T* p) const
|
||||
{
|
||||
static_assert(wistd::is_trivially_destructible_v<T>, "do not use with nontrivial types");
|
||||
::operator delete(p);
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @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, OpenThreadTokenAs::Self));
|
||||
~~~~
|
||||
@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 openAs Current to use current thread security context, or Self to use process security context.
|
||||
*/
|
||||
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
|
||||
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||
|
||||
// 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 wil::unique_tokeninfo_ptr<T> (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.
|
||||
@code
|
||||
// Retrieve the TOKEN_USER structure for the current process
|
||||
wil::unique_tokeninfo_ptr<TOKEN_USER> user;
|
||||
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
|
||||
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
|
||||
@endcode
|
||||
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
|
||||
@code
|
||||
wil::unique_tokeninfo_ptr<TOKEN_PRIVILEGES> 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));
|
||||
}
|
||||
@endcode
|
||||
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
|
||||
type. The type of `<T>` 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 <typename Q>
|
||||
using unique_tokeninfo_ptr = wistd::unique_ptr<Q, details::token_info_deleter>;
|
||||
|
||||
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
inline HRESULT get_token_information_nothrow(unique_tokeninfo_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
|
||||
{
|
||||
tokenInfo.reset();
|
||||
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||
|
||||
DWORD tokenInfoSize = 0;
|
||||
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
|
||||
RETURN_LAST_ERROR_IF(
|
||||
!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
|
||||
unique_tokeninfo_ptr<T> tokenInfoClose{static_cast<T*>(::operator new(tokenInfoSize, std::nothrow))};
|
||||
RETURN_IF_NULL_ALLOC(tokenInfoClose);
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
|
||||
tokenInfo = wistd::move(tokenInfoClose);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::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<T>::infoClass;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
unique_tokeninfo_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
|
||||
{
|
||||
unique_tokeninfo_ptr<T> temp;
|
||||
policy::HResult(get_token_information_nothrow(temp, token));
|
||||
return temp;
|
||||
}
|
||||
|
||||
template <typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
T GetTokenInfoWrap(HANDLE token = nullptr)
|
||||
{
|
||||
T temp{};
|
||||
policy::HResult(get_token_information_nothrow(&temp, token));
|
||||
return temp;
|
||||
}
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
|
||||
template <typename T>
|
||||
inline auto get_token_information_failfast(HANDLE token = nullptr)
|
||||
{
|
||||
return details::GetTokenInfoWrap<T, err_failfast_policy>(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<TOKEN_USER>(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.
|
||||
@code
|
||||
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
|
||||
ConsumeSid(user->User.Sid);
|
||||
@endcode
|
||||
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
|
||||
@code
|
||||
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
|
||||
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
|
||||
{
|
||||
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
|
||||
`<T>` 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 <typename T>
|
||||
inline auto get_token_information(HANDLE token = nullptr)
|
||||
{
|
||||
return details::GetTokenInfoWrap<T, err_exception_policy>(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<TOKEN_USER>(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
|
||||
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken)
|
||||
{
|
||||
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
|
||||
|
||||
if (oldToken)
|
||||
{
|
||||
::CloseHandle(oldToken);
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
/// @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.
|
||||
@param reverter An RAII object that, on success, will revert the impersonation when it goes out of scope.
|
||||
*/
|
||||
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
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <size_t AuthorityCount>
|
||||
struct static_sid_t
|
||||
{
|
||||
BYTE Revision;
|
||||
BYTE SubAuthorityCount;
|
||||
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
|
||||
DWORD SubAuthority[AuthorityCount];
|
||||
|
||||
PSID get()
|
||||
{
|
||||
return reinterpret_cast<PSID>(this);
|
||||
}
|
||||
|
||||
template <size_t other>
|
||||
static_sid_t& operator=(const static_sid_t<other>& 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;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
/** 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()"
|
||||
@code
|
||||
// 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));
|
||||
@endcode
|
||||
*/
|
||||
template <typename... Ts>
|
||||
constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
|
||||
{
|
||||
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
|
||||
|
||||
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<DWORD>(subAuthorities)...}};
|
||||
}
|
||||
|
||||
//! Variant of static_sid that defaults to the NT authority
|
||||
template <typename... Ts>
|
||||
constexpr auto make_static_nt_sid(Ts&&... subAuthorities)
|
||||
{
|
||||
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(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 <typename... Ts>
|
||||
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<Ts>(subAuthorities)...);
|
||||
BOOL isMember;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
|
||||
|
||||
*result = (isMember != FALSE);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||
/** 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.
|
||||
@param value The result of the operation; `true` if the token represents an app container, `false` otherwise.
|
||||
*/
|
||||
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
|
||||
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||
|
||||
template <typename... Ts>
|
||||
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<Ts>(subAuthorities)...));
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename... Ts>
|
||||
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<Ts>(subAuthorities)...));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_TOKEN_HELPERS_INCLUDED
|
||||
73
3rdparty/winwil/include/wil/traceloggingconfig.h
vendored
Normal file
73
3rdparty/winwil/include/wil/traceloggingconfig.h
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Various definitions for use in conjunction with TraceLogging APIs
|
||||
|
||||
#ifndef __WIL_TRACELOGGING_CONFIG_H
|
||||
/// @cond
|
||||
#define __WIL_TRACELOGGING_CONFIG_H
|
||||
/// @endcond
|
||||
|
||||
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||
#define TraceLoggingOptionMicrosoftTelemetry() // Empty definition for TraceLoggingOptionMicrosoftTelemetry
|
||||
|
||||
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||
#define TraceLoggingOptionWindowsCoreTelemetry() // Empty definition for TraceLoggingOptionWindowsCoreTelemetry
|
||||
|
||||
// Event privacy tags. Use the PDT macro values for the tag parameter, e.g.:
|
||||
// TraceLoggingWrite(...,
|
||||
// TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage),
|
||||
// ...);
|
||||
#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags")
|
||||
#define PDT_BrowsingHistory 0x0000000000000002u
|
||||
#define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u
|
||||
#define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u
|
||||
#define PDT_ProductAndServicePerformance 0x0000000001000000u
|
||||
#define PDT_ProductAndServiceUsage 0x0000000002000000u
|
||||
#define PDT_SoftwareSetupAndInventory 0x0000000080000000u
|
||||
|
||||
// Event categories specified via keywords, e.g.:
|
||||
// TraceLoggingWrite(...,
|
||||
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
// ...);
|
||||
#define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47
|
||||
#define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46
|
||||
#define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45
|
||||
#define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment)
|
||||
|
||||
// Event categories specified via event tags, e.g.:
|
||||
// TraceLoggingWrite(...,
|
||||
// TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY),
|
||||
// ...);
|
||||
#define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000
|
||||
#define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000
|
||||
#define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000
|
||||
#define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000
|
||||
#define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000
|
||||
#define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000
|
||||
#define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000
|
||||
#define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000
|
||||
#define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000
|
||||
#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000
|
||||
#define MICROSOFT_EVENTTAG_DROP_PII 0x02000000
|
||||
#define MICROSOFT_EVENTTAG_HASH_PII 0x04000000
|
||||
#define MICROSOFT_EVENTTAG_MARK_PII 0x08000000
|
||||
|
||||
// Field categories specified via field tags, e.g.:
|
||||
// TraceLoggingWrite(...,
|
||||
// TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII),
|
||||
// ...);
|
||||
#define MICROSOFT_FIELDTAG_DROP_PII 0x04000000
|
||||
#define MICROSOFT_FIELDTAG_HASH_PII 0x08000000
|
||||
#endif // __WIL_TRACELOGGING_CONFIG_H
|
||||
913
3rdparty/winwil/include/wil/win32_helpers.h
vendored
Normal file
913
3rdparty/winwil/include/wil/win32_helpers.h
vendored
Normal file
@@ -0,0 +1,913 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Various types and helpers for interfacing with various Win32 APIs
|
||||
#ifndef __WIL_WIN32_HELPERS_INCLUDED
|
||||
#define __WIL_WIN32_HELPERS_INCLUDED
|
||||
|
||||
#include <minwindef.h> // FILETIME, HINSTANCE
|
||||
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
|
||||
#include <libloaderapi.h> // GetProcAddress
|
||||
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
|
||||
#include <winreg.h>
|
||||
#include <objbase.h>
|
||||
|
||||
// detect std::bit_cast
|
||||
#ifdef __has_include
|
||||
#if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include(<bit>)
|
||||
#include <bit>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
#if __cpp_lib_bit_cast >= 201806L
|
||||
#define __WI_CONSTEXPR_BIT_CAST constexpr
|
||||
#else
|
||||
#define __WI_CONSTEXPR_BIT_CAST inline
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
#include "result.h"
|
||||
#include "resource.h"
|
||||
#include "wistd_functional.h"
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
/// @cond
|
||||
#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_)
|
||||
// If we're using c++20, then <compare> must be included to use the string ordinal functions
|
||||
#define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
|
||||
#elif !_HAS_CXX20 && defined(_STRING_VIEW_)
|
||||
#define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
namespace wistd
|
||||
{
|
||||
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||
|
||||
#if _HAS_CXX20
|
||||
|
||||
using weak_ordering = std::weak_ordering;
|
||||
|
||||
#else // _HAS_CXX20
|
||||
|
||||
struct weak_ordering
|
||||
{
|
||||
static const weak_ordering less;
|
||||
static const weak_ordering equivalent;
|
||||
static const weak_ordering greater;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value > 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value <= 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept
|
||||
{
|
||||
return left.m_value >= 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right > 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right >= 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept
|
||||
{
|
||||
return right <= 0;
|
||||
}
|
||||
|
||||
signed char m_value;
|
||||
};
|
||||
|
||||
inline constexpr weak_ordering weak_ordering::less{static_cast<signed char>(-1)};
|
||||
inline constexpr weak_ordering weak_ordering::equivalent{static_cast<signed char>(0)};
|
||||
inline constexpr weak_ordering weak_ordering::greater{static_cast<signed char>(1)};
|
||||
|
||||
#endif // !_HAS_CXX20
|
||||
|
||||
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||
} // namespace wistd
|
||||
/// @endcond
|
||||
|
||||
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.
|
||||
constexpr size_t max_path_segment_length = 255;
|
||||
|
||||
//! Character length not including the null, MAX_PATH (260) includes the null.
|
||||
constexpr size_t 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.
|
||||
constexpr size_t max_extended_path_length = 0x7FFF - 24;
|
||||
|
||||
//! For {guid} string form. Includes space for the null terminator.
|
||||
constexpr size_t guid_string_buffer_length = 39;
|
||||
|
||||
//! For {guid} string form. Not including the null terminator.
|
||||
constexpr size_t guid_string_length = 38;
|
||||
|
||||
#pragma region String and identifier comparisons
|
||||
// Using CompareStringOrdinal functions:
|
||||
//
|
||||
// Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names,
|
||||
// etc). DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT).
|
||||
|
||||
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) || defined(WIL_DOXYGEN)
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
[[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
|
||||
{
|
||||
// Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative,
|
||||
// merely truncating the string. CompareStringOrdinal should be resilient to negatives.
|
||||
return ::CompareStringOrdinal(
|
||||
left.data(), static_cast<int>(left.size()), right.data(), static_cast<int>(right.size()), caseInsensitive);
|
||||
}
|
||||
} // namespace details
|
||||
/// @endcond
|
||||
|
||||
[[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
|
||||
{
|
||||
switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive))
|
||||
{
|
||||
case CSTR_LESS_THAN:
|
||||
return wistd::weak_ordering::less;
|
||||
case CSTR_GREATER_THAN:
|
||||
return wistd::weak_ordering::greater;
|
||||
default:
|
||||
return wistd::weak_ordering::equivalent;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#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_duration
|
||||
|
||||
namespace filetime
|
||||
{
|
||||
constexpr unsigned long long to_int64(const FILETIME& ft) WI_NOEXCEPT
|
||||
{
|
||||
#if __cpp_lib_bit_cast >= 201806L
|
||||
return std::bit_cast<unsigned long long>(ft);
|
||||
#else
|
||||
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
||||
// due to alignment differences.
|
||||
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||
#endif
|
||||
}
|
||||
|
||||
__WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
|
||||
{
|
||||
#if __cpp_lib_bit_cast >= 201806L
|
||||
return std::bit_cast<FILETIME>(i64);
|
||||
#else
|
||||
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<FILETIME*>(&i64);
|
||||
#endif
|
||||
}
|
||||
|
||||
__WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const& ft, long long delta100ns) WI_NOEXCEPT
|
||||
{
|
||||
return from_int64(to_int64(ft) + delta100ns);
|
||||
}
|
||||
|
||||
constexpr bool is_empty(const FILETIME& ft) WI_NOEXCEPT
|
||||
{
|
||||
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
||||
}
|
||||
|
||||
inline FILETIME get_system_time() WI_NOEXCEPT
|
||||
{
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
return ft;
|
||||
}
|
||||
|
||||
/// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
|
||||
constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT
|
||||
{
|
||||
return time100ns / filetime_duration::one_millisecond;
|
||||
}
|
||||
|
||||
/// Convert time as milliseconds to units of 100 nanoseconds.
|
||||
constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT
|
||||
{
|
||||
return timeMsec * filetime_duration::one_millisecond;
|
||||
}
|
||||
|
||||
#if (defined(_APISETREALTIME_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)) || defined(WIL_DOXYGEN)
|
||||
/// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not
|
||||
/// include time the system spends in sleep or hibernation.
|
||||
///
|
||||
/// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
|
||||
///
|
||||
/// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't
|
||||
/// include time the system spends in sleep or hibernation.
|
||||
/// For example
|
||||
///
|
||||
/// start = GetTickCount64();
|
||||
/// hibernate();
|
||||
/// ...wake from hibernation 30 minutes later...;
|
||||
/// elapsed = GetTickCount64() - start;
|
||||
/// // elapsed = 30min
|
||||
///
|
||||
/// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
|
||||
///
|
||||
/// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out
|
||||
/// parameter).
|
||||
/// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
|
||||
inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT
|
||||
{
|
||||
ULONGLONG now{};
|
||||
QueryUnbiasedInterruptTime(&now);
|
||||
return now;
|
||||
}
|
||||
|
||||
/// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not
|
||||
/// include time the system spends in sleep or hibernation.
|
||||
/// @see QueryUnbiasedInterruptTimeAs100ns
|
||||
inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT
|
||||
{
|
||||
return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
|
||||
}
|
||||
#endif // _APISETREALTIME_
|
||||
} // namespace filetime
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RECT helpers
|
||||
template <typename rect_type>
|
||||
constexpr auto rect_width(rect_type const& rect)
|
||||
{
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
template <typename rect_type>
|
||||
constexpr auto rect_height(rect_type const& rect)
|
||||
{
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
template <typename rect_type>
|
||||
constexpr auto rect_is_empty(rect_type const& rect)
|
||||
{
|
||||
return (rect.left >= rect.right) || (rect.top >= rect.bottom);
|
||||
}
|
||||
|
||||
template <typename rect_type, typename point_type>
|
||||
constexpr auto rect_contains_point(rect_type const& rect, point_type const& point)
|
||||
{
|
||||
return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom);
|
||||
}
|
||||
|
||||
template <typename rect_type, typename length_type>
|
||||
constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height)
|
||||
{
|
||||
rect_type rect;
|
||||
rect.left = x;
|
||||
rect.top = y;
|
||||
rect.right = x + width;
|
||||
rect.bottom = y + height;
|
||||
return rect;
|
||||
}
|
||||
#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 <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback) WI_NOEXCEPT
|
||||
{
|
||||
details::string_maker<string_type> maker;
|
||||
|
||||
wchar_t value[stackBufferLength]{};
|
||||
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.
|
||||
// May need to loop more than once if external conditions cause the value to change.
|
||||
size_t bufferLength;
|
||||
do
|
||||
{
|
||||
bufferLength = valueLengthNeededWithNull;
|
||||
// bufferLength includes the null so subtract that as make() will add space for it.
|
||||
RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
|
||||
|
||||
RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
|
||||
WI_ASSERT(valueLengthNeededWithNull > 0);
|
||||
|
||||
// If the value shrunk, then adjust the string to trim off the excess buffer.
|
||||
if (valueLengthNeededWithNull < bufferLength)
|
||||
{
|
||||
RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
|
||||
}
|
||||
} while (valueLengthNeededWithNull > bufferLength);
|
||||
}
|
||||
result = maker.release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(
|
||||
result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(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 <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(
|
||||
result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(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;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(
|
||||
result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
DWORD lengthToUse = static_cast<DWORD>(valueLength);
|
||||
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
|
||||
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
|
||||
|
||||
// On success, return the amount used; on failure, try doubling
|
||||
*valueLengthNeededWithNul = success ? (static_cast<size_t>(lengthToUse) + 1) : (static_cast<size_t>(lengthToUse) * 2);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
/** Expands environment strings and checks path existence with SearchPathW */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
wil::unique_cotaskmem_string expandedName;
|
||||
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
|
||||
|
||||
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
|
||||
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(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. */
|
||||
template <typename string_type, size_t initialBufferLength = 128>
|
||||
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(
|
||||
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<DWORD>(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. */
|
||||
template <typename string_type, size_t initialBufferLength = 128>
|
||||
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(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 <typename string_type, size_t initialBufferLength = 128>
|
||||
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT
|
||||
{
|
||||
auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
DWORD copiedCount{};
|
||||
size_t valueUsedWithNul{};
|
||||
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. The
|
||||
// count copied to the buffer includes the nul-character as well.
|
||||
copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
|
||||
valueUsedWithNul = static_cast<size_t>(copiedCount) + 1;
|
||||
copyFailed = (0 == copiedCount);
|
||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
||||
// and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
|
||||
// the nul-character
|
||||
copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
|
||||
valueUsedWithNul = static_cast<size_t>(copiedCount) + 1;
|
||||
copyFailed = (0 == copiedCount);
|
||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
|
||||
}
|
||||
|
||||
RETURN_LAST_ERROR_IF(copyFailed);
|
||||
|
||||
// When the copy truncated, request another try with more space.
|
||||
*valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
|
||||
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
|
||||
}
|
||||
|
||||
/** 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 <typename string_type, size_t initialBufferLength = 128>
|
||||
HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT
|
||||
{
|
||||
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
|
||||
}
|
||||
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(
|
||||
result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||||
if (*valueLengthNeededWithNul < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNul)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetWindowsDirectoryW(string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(
|
||||
result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT {
|
||||
*valueLengthNeededWithNul = ::GetWindowsDirectoryW(value, static_cast<DWORD>(valueLength));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||||
if (*valueLengthNeededWithNul < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNul)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(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 <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
|
||||
{
|
||||
string_type result{};
|
||||
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(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. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Looks up the environment variable 'key' and returns null if it is not found. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||
string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(process, module, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetWindowsDirectoryW()
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((wil::GetWindowsDirectoryW<string_type, stackBufferLength>(result)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetSystemDirectoryW()
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((wil::GetSystemDirectoryW<string_type, stackBufferLength>(result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0)
|
||||
{
|
||||
string_type result{};
|
||||
THROW_IF_FAILED((wil::QueryFullProcessImageNameW<string_type, stackBufferLength>(processHandle, flags, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
|
||||
// Lookup a DWORD value under HKLM\...\Image File Execution Options\<current process name>
|
||||
inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0)
|
||||
{
|
||||
auto filePath = wil::GetModuleFileNameW<wil::unique_cotaskmem_string>();
|
||||
if (auto lastSlash = wcsrchr(filePath.get(), L'\\'))
|
||||
{
|
||||
const auto fileName = lastSlash + 1;
|
||||
auto keyPath = wil::str_concat<wil::unique_cotaskmem_string>(
|
||||
LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)", fileName);
|
||||
DWORD value{}, sizeofValue = sizeof(value);
|
||||
if (::RegGetValueW(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
keyPath.get(),
|
||||
valueName,
|
||||
#ifdef RRF_SUBKEY_WOW6464KEY
|
||||
RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
|
||||
#else
|
||||
RRF_RT_REG_DWORD,
|
||||
#endif
|
||||
nullptr,
|
||||
&value,
|
||||
&sizeofValue) == ERROR_SUCCESS)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Waits for a debugger to attach to the current process based on registry configuration.
|
||||
//
|
||||
// Example:
|
||||
// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe
|
||||
// WaitForDebuggerPresent=1
|
||||
//
|
||||
// REG_DWORD value of
|
||||
// missing or 0 -> don't break
|
||||
// 1 -> wait for the debugger, continue execution once it is attached
|
||||
// 2 -> wait for the debugger, break here once attached.
|
||||
inline void WaitForDebuggerPresent(bool checkRegistryConfig = true)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1;
|
||||
if (configValue == 0)
|
||||
{
|
||||
return; // not configured, don't wait
|
||||
}
|
||||
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
if (configValue == 2)
|
||||
{
|
||||
DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller
|
||||
}
|
||||
return; // debugger now attached, continue executing
|
||||
}
|
||||
Sleep(500);
|
||||
}
|
||||
}
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
|
||||
#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() WI_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
}
|
||||
|
||||
// GetModuleHandleExW was added to the app partition in version 22000 of the SDK
|
||||
#if defined(NTDDI_WIN10_CO) ? WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) \
|
||||
: WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
// Use this in threads that can outlive the object or API call that created them.
|
||||
// Without this COM, or the API caller, can unload the DLL, resulting in a crash.
|
||||
// It is very important that this be the first object created in the thread proc
|
||||
// as when this runs down the thread exits and no destructors of objects created before
|
||||
// it will run.
|
||||
[[nodiscard]] inline auto get_module_reference_for_thread() noexcept
|
||||
{
|
||||
HMODULE thisModule{};
|
||||
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule));
|
||||
return wil::scope_exit([thisModule] {
|
||||
FreeLibraryAndExitThread(thisModule, 0);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @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) WI_NOEXCEPT : m_once(once)
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // https://github.com/Microsoft/wil/issues/2
|
||||
void success() WI_NOEXCEPT
|
||||
{
|
||||
m_flags = 0;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
~init_once_completer() WI_NOEXCEPT
|
||||
{
|
||||
::InitOnceComplete(&m_once, m_flags, nullptr);
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
/// @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<IFoo> g_foo;
|
||||
HRESULT MyMethod()
|
||||
{
|
||||
bool winner = false;
|
||||
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
|
||||
{
|
||||
ComPtr<IFoo> 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 <typename T>
|
||||
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 <typename T>
|
||||
bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
|
||||
{
|
||||
bool callerCompleted;
|
||||
|
||||
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(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<IFoo> g_foo;
|
||||
void MyMethod()
|
||||
{
|
||||
bool winner = wil::init_once(g_init, []
|
||||
{
|
||||
ComPtr<IFoo> 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 <typename T>
|
||||
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
|
||||
} // namespace wil
|
||||
|
||||
// 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<decltype(::fn)*>(GetProcAddress(hinst, #fn))
|
||||
|
||||
#endif // __WIL_WIN32_HELPERS_INCLUDED
|
||||
205
3rdparty/winwil/include/wil/win32_result_macros.h
vendored
Normal file
205
3rdparty/winwil/include/wil/win32_result_macros.h
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors
|
||||
//! across return codes, fail fast, exceptions and logging for Win32 error codes.
|
||||
#ifndef __WIL_WIN32_RESULTMACROS_INCLUDED
|
||||
#define __WIL_WIN32_RESULTMACROS_INCLUDED
|
||||
|
||||
#include "result_macros.h"
|
||||
|
||||
// Helpers for return macros
|
||||
/// @cond
|
||||
#define __WIN32_RETURN_WIN32(error, str) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
const auto __error = (error); \
|
||||
if (FAILED_WIN32(__error)) \
|
||||
{ \
|
||||
__R_FN(Return_Win32)(__R_INFO(str) __error); \
|
||||
} \
|
||||
return __error; \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str))
|
||||
|
||||
FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr)
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros for returning failures as WIN32 error codes
|
||||
//*****************************************************************************
|
||||
|
||||
// Always returns a known result (WIN32 error code) - always logs failures
|
||||
#define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error)
|
||||
#define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr)
|
||||
|
||||
// Conditionally returns failures (WIN32 error code) - always logs failures
|
||||
#define WIN32_RETURN_IF_WIN32_ERROR(error) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
const auto __errorRet = wil::verify_win32(error); \
|
||||
if (FAILED_WIN32(__errorRet)) \
|
||||
{ \
|
||||
__WIN32_RETURN_WIN32(__errorRet, #error); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_WIN32_IF(error, condition) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if (wil::verify_bool(condition)) \
|
||||
{ \
|
||||
__WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_WIN32_IF_NULL(error, ptr) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if ((ptr) == nullptr) \
|
||||
{ \
|
||||
__WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_LAST_ERROR_IF(condition) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if (wil::verify_bool(condition)) \
|
||||
{ \
|
||||
__WIN32_RETURN_GLE_FAIL(#condition); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if ((ptr) == nullptr) \
|
||||
{ \
|
||||
__WIN32_RETURN_GLE_FAIL(#ptr); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
// Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
|
||||
#define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
const auto __errorRet = wil::verify_win32(error); \
|
||||
if (FAILED_WIN32(__errorRet)) \
|
||||
{ \
|
||||
return __errorRet; \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if (wil::verify_bool(condition)) \
|
||||
{ \
|
||||
return wil::verify_win32(error); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if ((ptr) == nullptr) \
|
||||
{ \
|
||||
return wil::verify_win32(error); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if (wil::verify_bool(condition)) \
|
||||
{ \
|
||||
return wil::verify_win32(wil::details::GetLastErrorFail()); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) \
|
||||
__WI_SUPPRESS_4127_S do \
|
||||
{ \
|
||||
if ((ptr) == nullptr) \
|
||||
{ \
|
||||
return wil::verify_win32(wil::details::GetLastErrorFail()); \
|
||||
} \
|
||||
} \
|
||||
__WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros to catch and convert exceptions on failure
|
||||
//*****************************************************************************
|
||||
|
||||
// Use these macros *within* a catch (...) block to handle exceptions
|
||||
#define WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr))
|
||||
|
||||
// Use these macros in place of a catch block to handle exceptions
|
||||
#define WIN32_CATCH_RETURN() \
|
||||
catch (...) \
|
||||
{ \
|
||||
WIN32_RETURN_CAUGHT_EXCEPTION(); \
|
||||
}
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//*****************************************************************************
|
||||
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
|
||||
//*****************************************************************************
|
||||
|
||||
// Win32ErrorFromCaughtException 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 a WIN32 error code. If an exception is of an unrecognized type
|
||||
// the function will fail fast.
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// // Code
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// status = wil::Win32ErrorFromCaughtException();
|
||||
// }
|
||||
_Always_(_Post_satisfies_(return > 0)) __declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT
|
||||
{
|
||||
return __WIN32_FROM_HRESULT(ResultFromCaughtException());
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details::__R_NS_NAME
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
__R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||
{
|
||||
__R_FN_LOCALS;
|
||||
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
|
||||
}
|
||||
#endif
|
||||
|
||||
__R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||
{
|
||||
__R_FN_LOCALS;
|
||||
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
|
||||
}
|
||||
} // namespace details::__R_NS_NAME
|
||||
/// @endcond
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_WIN32_RESULTMACROS_INCLUDED
|
||||
173
3rdparty/winwil/include/wil/windowing.h
vendored
Normal file
173
3rdparty/winwil/include/wil/windowing.h
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_WINDOWING_INCLUDED
|
||||
#define __WIL_WINDOWING_INCLUDED
|
||||
|
||||
#include <WinUser.h>
|
||||
#include <exception>
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
namespace wil
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct always_false : wistd::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename TCallback>
|
||||
BOOL __stdcall EnumWindowsCallbackNoThrow(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
auto pCallback = reinterpret_cast<TCallback*>(lParam);
|
||||
#ifdef __cpp_if_constexpr
|
||||
using result_t = decltype((*pCallback)(hwnd));
|
||||
if constexpr (wistd::is_void_v<result_t>)
|
||||
{
|
||||
(*pCallback)(hwnd);
|
||||
return TRUE;
|
||||
}
|
||||
else if constexpr (wistd::is_same_v<result_t, HRESULT>)
|
||||
{
|
||||
// NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0
|
||||
return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, bool>)
|
||||
{
|
||||
return (*pCallback)(hwnd) ? TRUE : FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(details::always_false<TCallback>::value, "Callback must return void, bool, or HRESULT");
|
||||
}
|
||||
#else
|
||||
return (*pCallback)(hwnd);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename TEnumApi, typename TCallback>
|
||||
void DoEnumWindowsNoThrow(TEnumApi&& enumApi, TCallback&& callback) noexcept
|
||||
{
|
||||
enumApi(EnumWindowsCallbackNoThrow<TCallback>, reinterpret_cast<LPARAM>(&callback));
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TCallback>
|
||||
struct EnumWindowsCallbackData
|
||||
{
|
||||
std::exception_ptr exception;
|
||||
TCallback* pCallback;
|
||||
};
|
||||
|
||||
template <typename TCallback>
|
||||
BOOL __stdcall EnumWindowsCallback(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
auto pCallbackData = reinterpret_cast<EnumWindowsCallbackData<TCallback>*>(lParam);
|
||||
try
|
||||
{
|
||||
auto pCallback = pCallbackData->pCallback;
|
||||
#ifdef __cpp_if_constexpr
|
||||
using result_t = decltype((*pCallback)(hwnd));
|
||||
if constexpr (std::is_void_v<result_t>)
|
||||
{
|
||||
(*pCallback)(hwnd);
|
||||
return TRUE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, HRESULT>)
|
||||
{
|
||||
// NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0
|
||||
return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, bool>)
|
||||
{
|
||||
return (*pCallback)(hwnd) ? TRUE : FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(details::always_false<TCallback>::value, "Callback must return void, bool, or HRESULT");
|
||||
}
|
||||
#else
|
||||
return (*pCallback)(hwnd);
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pCallbackData->exception = std::current_exception();
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TEnumApi, typename TCallback>
|
||||
void DoEnumWindows(TEnumApi&& enumApi, TCallback&& callback)
|
||||
{
|
||||
EnumWindowsCallbackData<TCallback> callbackData = {nullptr, &callback};
|
||||
enumApi(EnumWindowsCallback<TCallback>, reinterpret_cast<LPARAM>(&callbackData));
|
||||
if (callbackData.exception)
|
||||
{
|
||||
std::rethrow_exception(callbackData.exception);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace details
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_window_nothrow(TCallback&& callback) noexcept
|
||||
{
|
||||
details::DoEnumWindowsNoThrow(&EnumWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_thread_window_nothrow(_In_ DWORD threadId, TCallback&& callback) noexcept
|
||||
{
|
||||
auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumThreadWindows(threadId, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindowsNoThrow(boundEnumThreadWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_child_window_nothrow(_In_ HWND hwndParent, TCallback&& callback) noexcept
|
||||
{
|
||||
auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumChildWindows(hwndParent, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindowsNoThrow(boundEnumChildWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TCallback>
|
||||
void for_each_window(TCallback&& callback)
|
||||
{
|
||||
details::DoEnumWindows(&EnumWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_thread_window(_In_ DWORD threadId, TCallback&& callback)
|
||||
{
|
||||
auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumThreadWindows(threadId, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindows(boundEnumThreadWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_child_window(_In_ HWND hwndParent, TCallback&& callback)
|
||||
{
|
||||
auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumChildWindows(hwndParent, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindows(boundEnumChildWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace wil
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#endif // __WIL_WINDOWING_INCLUDED
|
||||
2520
3rdparty/winwil/include/wil/winrt.h
vendored
Normal file
2520
3rdparty/winwil/include/wil/winrt.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
582
3rdparty/winwil/include/wil/wistd_config.h
vendored
Normal file
582
3rdparty/winwil/include/wil/wistd_config.h
vendored
Normal file
@@ -0,0 +1,582 @@
|
||||
// -*- 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 <new>) 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 compatibility 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 <cstddef> // 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
|
||||
|
||||
#if defined(__WI_LIBCPP_COMPILER_MSVC)
|
||||
#define __WI_PUSH_WARNINGS __pragma(warning(push))
|
||||
#define __WI_POP_WARNINGS __pragma(warning(pop))
|
||||
#elif defined(__WI_LIBCPP_COMPILER_CLANG)
|
||||
#define __WI_PUSH_WARNINGS __pragma(clang diagnostic push)
|
||||
#define __WI_POP_WARNINGS __pragma(clang diagnostic pop)
|
||||
#else
|
||||
#define __WI_PUSH_WARNINGS
|
||||
#define __WI_POP_WARNINGS
|
||||
#endif
|
||||
|
||||
#ifdef __WI_LIBCPP_COMPILER_MSVC
|
||||
#define __WI_MSVC_DISABLE_WARNING(id) __pragma(warning(disable : id))
|
||||
#else
|
||||
#define __WI_MSVC_DISABLE_WARNING(id)
|
||||
#endif
|
||||
|
||||
#ifdef __WI_LIBCPP_COMPILER_CLANG
|
||||
#define __WI_CLANG_DISABLE_WARNING(warning) __pragma(clang diagnostic ignored #warning)
|
||||
#else
|
||||
#define __WI_CLANG_DISABLE_WARNING(warning)
|
||||
#endif
|
||||
|
||||
// NOTE: MSVC, which is what we primarily target, is severely 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 <endian.h>
|
||||
#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)
|
||||
{
|
||||
using nullptr_t = decltype(__nullptr);
|
||||
|
||||
template <class _T1, class _T2 = _T1>
|
||||
struct __less
|
||||
{
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T1& __x, const _T1& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T1& __x, const _T2& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T2& __x, const _T1& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T2& __x, const _T2& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<_T1, _T1>
|
||||
{
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T1& __x, const _T1& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<const _T1, _T1>
|
||||
{
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()(
|
||||
const _T1& __x, const _T1& __y) const
|
||||
{
|
||||
return __x < __y;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<_T1, const _T1>
|
||||
{
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __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 <class _Tp, class _Compare>
|
||||
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 <class _Tp>
|
||||
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 <class _Tp, class _Compare>
|
||||
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 <class _Tp>
|
||||
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 <class _Arg, class _Result>
|
||||
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
|
||||
{
|
||||
using argument_type = _Arg;
|
||||
using result_type = _Result;
|
||||
};
|
||||
|
||||
template <class _Arg1, class _Arg2, class _Result>
|
||||
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
|
||||
{
|
||||
using first_argument_type = _Arg1;
|
||||
using second_argument_type = _Arg2;
|
||||
using result_type = _Result;
|
||||
};
|
||||
} // namespace wistd
|
||||
/// @endcond
|
||||
|
||||
#endif // _WISTD_CONFIG_H_
|
||||
568
3rdparty/winwil/include/wil/wistd_functional.h
vendored
Normal file
568
3rdparty/winwil/include/wil/wistd_functional.h
vendored
Normal file
@@ -0,0 +1,568 @@
|
||||
// -*- 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 <new>) 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 <intrin.h> // For __fastfail
|
||||
#include <new.h> // For placement new
|
||||
|
||||
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#pragma warning(disable : 4800)
|
||||
|
||||
/// @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 <class _Ret>
|
||||
struct __invoke_void_return_wrapper
|
||||
{
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
template <class... _Args>
|
||||
static _Ret __call(_Args&&... __args)
|
||||
{
|
||||
return __invoke(wistd::forward<_Args>(__args)...);
|
||||
}
|
||||
#else
|
||||
template <class _Fn>
|
||||
static _Ret __call(_Fn __f)
|
||||
{
|
||||
return __invoke(__f);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0>
|
||||
static _Ret __call(_Fn __f, _A0& __a0)
|
||||
{
|
||||
return __invoke(__f, __a0);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1>
|
||||
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1)
|
||||
{
|
||||
return __invoke(__f, __a0, __a1);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1, class _A2>
|
||||
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2)
|
||||
{
|
||||
return __invoke(__f, __a0, __a1, __a2);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <>
|
||||
struct __invoke_void_return_wrapper<void>
|
||||
{
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
template <class... _Args>
|
||||
static void __call(_Args&&... __args)
|
||||
{
|
||||
(void)__invoke(wistd::forward<_Args>(__args)...);
|
||||
}
|
||||
#else
|
||||
template <class _Fn>
|
||||
static void __call(_Fn __f)
|
||||
{
|
||||
__invoke(__f);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0>
|
||||
static void __call(_Fn __f, _A0& __a0)
|
||||
{
|
||||
__invoke(__f, __a0);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1>
|
||||
static void __call(_Fn __f, _A0& __a0, _A1& __a1)
|
||||
{
|
||||
__invoke(__f, __a0, __a1);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1, class _A2>
|
||||
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 _Fp>
|
||||
class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
|
||||
|
||||
namespace __function
|
||||
{
|
||||
|
||||
template <class _Rp>
|
||||
struct __maybe_derive_from_unary_function
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Rp, class _A1>
|
||||
struct __maybe_derive_from_unary_function<_Rp(_A1)> : public unary_function<_A1, _Rp>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Rp>
|
||||
struct __maybe_derive_from_binary_function
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Rp, class _A1, class _A2>
|
||||
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)> : public binary_function<_A1, _A2, _Rp>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY bool __not_null(_Fp const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY bool __not_null(_Fp* __ptr)
|
||||
{
|
||||
return __ptr;
|
||||
}
|
||||
|
||||
template <class _Ret, class _Class>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY bool __not_null(_Ret _Class::*__ptr)
|
||||
{
|
||||
return __ptr;
|
||||
}
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY bool __not_null(function<_Fp> const& __f)
|
||||
{
|
||||
return !!__f;
|
||||
}
|
||||
|
||||
} // namespace __function
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
|
||||
namespace __function
|
||||
{
|
||||
|
||||
template <class _Fp>
|
||||
class __base;
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
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 _FD, class _FB>
|
||||
class __func;
|
||||
|
||||
template <class _Fp, class _Rp, class... _ArgTypes>
|
||||
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 <class _Fp, class _Rp, class... _ArgTypes>
|
||||
void __func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
|
||||
{
|
||||
::new (__p) __func(__f_);
|
||||
}
|
||||
|
||||
template <class _Fp, class _Rp, class... _ArgTypes>
|
||||
void __func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
|
||||
{
|
||||
::new (__p) __func(wistd::move(__f_));
|
||||
}
|
||||
|
||||
template <class _Fp, class _Rp, class... _ArgTypes>
|
||||
void __func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
|
||||
{
|
||||
__f_.~_Fp();
|
||||
}
|
||||
|
||||
template <class _Fp, class _Rp, class... _ArgTypes>
|
||||
_Rp __func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&&... __arg)
|
||||
{
|
||||
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
|
||||
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
|
||||
}
|
||||
|
||||
// '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).
|
||||
constexpr const size_t __buffer_size = 13 * sizeof(void*);
|
||||
|
||||
} // namespace __function
|
||||
|
||||
// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in
|
||||
// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type) function<_Rp(_ArgTypes...)>
|
||||
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
|
||||
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
|
||||
{
|
||||
using __base = __function::__base<_Rp(_ArgTypes...)>;
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
typename aligned_storage<__function::__buffer_size>::type __buf_;
|
||||
__base* __f_;
|
||||
|
||||
__WI_LIBCPP_NO_CFI static __base* __as_base(void* p)
|
||||
{
|
||||
return static_cast<__base*>(p);
|
||||
}
|
||||
|
||||
template <class _Fp, bool>
|
||||
struct __callable_imp
|
||||
{
|
||||
static const bool value = is_same<void, _Rp>::value || is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type, _Rp>::value;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
struct __callable_imp<_Fp, false>
|
||||
{
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
struct __callable
|
||||
{
|
||||
static const bool value =
|
||||
__callable_imp<_Fp, __lazy_and<integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>, __invokable<_Fp&, _ArgTypes...>>::value>::value;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
|
||||
|
||||
public:
|
||||
using result_type = _Rp;
|
||||
|
||||
// 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 <class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function(_Fp);
|
||||
|
||||
function& operator=(const function&);
|
||||
function& operator=(function&&);
|
||||
function& operator=(nullptr_t) WI_NOEXCEPT;
|
||||
template <class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function& operator=(_Fp&&);
|
||||
|
||||
~function();
|
||||
|
||||
// function modifiers:
|
||||
void swap(function&);
|
||||
|
||||
// function capacity:
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT
|
||||
{
|
||||
return __f_;
|
||||
}
|
||||
|
||||
// deleted overloads close possible hole in the type system
|
||||
template <class _R2, class... _ArgTypes2>
|
||||
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
|
||||
template <class _R2, class... _ArgTypes2>
|
||||
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 <class _Rp, class... _ArgTypes>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS function<_Rp(_ArgTypes...)>::function(const function& __f)
|
||||
{
|
||||
if (__f.__f_ == nullptr)
|
||||
__f_ = 0;
|
||||
else
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__clone(__f_);
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS function<_Rp(_ArgTypes...)>::function(function&& __f)
|
||||
{
|
||||
if (__f.__f_ == nullptr)
|
||||
__f_ = 0;
|
||||
else
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__move(__f_);
|
||||
__f.__f_->destroy();
|
||||
__f.__f_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
template <class _Fp, class>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(nullptr)
|
||||
{
|
||||
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 (static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
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 <class _Rp, class... _ArgTypes>
|
||||
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 <class _Rp, class... _ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
__base* __t = __f_;
|
||||
__f_ = 0;
|
||||
if (__t)
|
||||
__t->destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
template <class _Fp, class>
|
||||
function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
|
||||
{
|
||||
*this = nullptr;
|
||||
if (__function::__not_null(__f))
|
||||
{
|
||||
typedef __function::__func<typename decay<_Fp>::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 (static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>::~function()
|
||||
{
|
||||
if (__f_)
|
||||
__f_->destroy();
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
void function<_Rp(_ArgTypes...)>::swap(function& __f)
|
||||
{
|
||||
if (wistd::addressof(__f) == this)
|
||||
return;
|
||||
if (__f_ && __f.__f_)
|
||||
{
|
||||
typename aligned_storage<sizeof(__buf_)>::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 <class _Rp, class... _ArgTypes>
|
||||
_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
|
||||
{
|
||||
if (__f_ == nullptr)
|
||||
__throw_bad_function_call();
|
||||
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
return !__f;
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT
|
||||
{
|
||||
return !__f;
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
return (bool)__f;
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
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 <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY void swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{
|
||||
return __x.swap(__y);
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY void swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{
|
||||
return __x.swap(__y);
|
||||
}
|
||||
|
||||
// std::invoke
|
||||
template <class _Fn, class... _Args>
|
||||
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
|
||||
} // namespace wistd
|
||||
/// @endcond
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif // _WISTD_FUNCTIONAL_H_
|
||||
992
3rdparty/winwil/include/wil/wistd_memory.h
vendored
Normal file
992
3rdparty/winwil/include/wil/wistd_memory.h
vendored
Normal file
@@ -0,0 +1,992 @@
|
||||
// -*- 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 <new>) 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 <class _Tp, class = void>
|
||||
struct __has_pointer_type : false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct __has_pointer_type<_Tp, typename __void_t<typename _Tp::pointer>::type> : true_type
|
||||
{
|
||||
};
|
||||
|
||||
namespace __pointer_type_imp
|
||||
{
|
||||
|
||||
template <class _Tp, class _Dp, bool = __has_pointer_type<_Dp>::value>
|
||||
struct __pointer_type
|
||||
{
|
||||
using type = typename _Dp::pointer;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Dp>
|
||||
struct __pointer_type<_Tp, _Dp, false>
|
||||
{
|
||||
using type = _Tp*;
|
||||
};
|
||||
|
||||
} // namespace __pointer_type_imp
|
||||
|
||||
template <class _Tp, class _Dp>
|
||||
struct __pointer_type
|
||||
{
|
||||
using type = typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type;
|
||||
};
|
||||
|
||||
template <class _Tp, int _Idx, bool _CanBeEmptyBase = is_empty<_Tp>::value && !__libcpp_is_final<_Tp>::value>
|
||||
struct __compressed_pair_elem
|
||||
{
|
||||
using _ParamT = _Tp;
|
||||
using reference = _Tp&;
|
||||
using const_reference = const _Tp&;
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_()
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Up, class = typename enable_if<!is_same<__compressed_pair_elem, typename decay<_Up>::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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY const_reference __get() const WI_NOEXCEPT
|
||||
{
|
||||
return __value_;
|
||||
}
|
||||
|
||||
private:
|
||||
_Tp __value_;
|
||||
};
|
||||
|
||||
template <class _Tp, int _Idx>
|
||||
struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp
|
||||
{
|
||||
using _ParamT = _Tp;
|
||||
using reference = _Tp&;
|
||||
using const_reference = const _Tp&;
|
||||
using __value_type = _Tp;
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default;
|
||||
|
||||
template <class _Up, class = typename enable_if<!is_same<__compressed_pair_elem, typename decay<_Up>::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_NODISCARD_ATTRIBUTE __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 _T1, class _T2>
|
||||
class __declspec(empty_bases) __compressed_pair : private __compressed_pair_elem<_T1, 0>, private __compressed_pair_elem<_T2, 1>
|
||||
{
|
||||
using _Base1 = __compressed_pair_elem<_T1, 0>;
|
||||
using _Base2 = __compressed_pair_elem<_T2, 1>;
|
||||
|
||||
// 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 <
|
||||
bool _Dummy = true,
|
||||
class = typename enable_if<__dependent_type<is_default_constructible<_T1>, _Dummy>::value && __dependent_type<is_default_constructible<_T2>, _Dummy>::value>::type>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair()
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Tp, typename enable_if<!is_same<typename decay<_Tp>::type, __compressed_pair>::value, bool>::type = true>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr explicit __compressed_pair(_Tp&& __t) : _Base1(wistd::forward<_Tp>(__t)), _Base2()
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair(__second_tag, _Tp&& __t) :
|
||||
_Base1(), _Base2(wistd::forward<_Tp>(__t))
|
||||
{
|
||||
}
|
||||
|
||||
template <class _U1, class _U2>
|
||||
__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_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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 <class _T1, class _T2>
|
||||
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 <class _T1, class _T2>
|
||||
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 <class _Tp>
|
||||
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 <class _Up>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY default_delete(
|
||||
const default_delete<_Up>&, typename enable_if<is_convertible<_Up*, _Tp*>::value>::type* = nullptr) 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 <class _Tp>
|
||||
struct __WI_LIBCPP_TEMPLATE_VIS default_delete<_Tp[]>
|
||||
{
|
||||
private:
|
||||
template <class _Up>
|
||||
struct _EnableIfConvertible : enable_if<is_convertible<_Up (*)[], _Tp (*)[]>::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 <class _Up>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY default_delete(const default_delete<_Up[]>&, typename _EnableIfConvertible<_Up>::type* = nullptr) WI_NOEXCEPT
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Up>
|
||||
__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 <class _Deleter>
|
||||
struct __unique_ptr_deleter_sfinae
|
||||
{
|
||||
static_assert(!is_reference<_Deleter>::value, "incorrect specialization");
|
||||
using __lval_ref_type = const _Deleter&;
|
||||
using __good_rval_ref_type = _Deleter&&;
|
||||
using __enable_rval_overload = true_type;
|
||||
};
|
||||
|
||||
template <class _Deleter>
|
||||
struct __unique_ptr_deleter_sfinae<_Deleter const&>
|
||||
{
|
||||
using __lval_ref_type = const _Deleter&;
|
||||
using __bad_rval_ref_type = const _Deleter&&;
|
||||
using __enable_rval_overload = false_type;
|
||||
};
|
||||
|
||||
template <class _Deleter>
|
||||
struct __unique_ptr_deleter_sfinae<_Deleter&>
|
||||
{
|
||||
using __lval_ref_type = _Deleter&;
|
||||
using __bad_rval_ref_type = _Deleter&&;
|
||||
using __enable_rval_overload = false_type;
|
||||
};
|
||||
#endif // !defined(__WI_LIBCPP_CXX03_LANG)
|
||||
|
||||
template <class _Tp, class _Dp = default_delete<_Tp>>
|
||||
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = _Tp;
|
||||
using deleter_type = _Dp;
|
||||
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
|
||||
|
||||
static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
|
||||
|
||||
private:
|
||||
__compressed_pair<pointer, deleter_type> __ptr_;
|
||||
|
||||
struct __nat
|
||||
{
|
||||
int __for_bool_;
|
||||
};
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _LValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _GoodRValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _BadRValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type;
|
||||
|
||||
template <bool _Dummy, class _Deleter = typename __dependent_type<__identity<deleter_type>, _Dummy>::type>
|
||||
using _EnableIfDeleterDefaultConstructible =
|
||||
typename enable_if<is_default_constructible<_Deleter>::value && !is_pointer<_Deleter>::value>::type;
|
||||
|
||||
template <class _ArgType>
|
||||
using _EnableIfDeleterConstructible = typename enable_if<is_constructible<deleter_type, _ArgType>::value>::type;
|
||||
|
||||
template <class _UPtr, class _Up>
|
||||
using _EnableIfMoveConvertible =
|
||||
typename enable_if<is_convertible<typename _UPtr::pointer, pointer>::value && !is_array<_Up>::value>::type;
|
||||
|
||||
template <class _UDel>
|
||||
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 <class _UDel>
|
||||
using _EnableIfDeleterAssignable = typename enable_if<is_assignable<_Dp&, _UDel&&>::value>::type;
|
||||
|
||||
public:
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer())
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer())
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY explicit unique_ptr(pointer __p) WI_NOEXCEPT : __ptr_(__p)
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_LValRefType<_Dummy>>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY unique_ptr(pointer __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT : __ptr_(__p, __d)
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_GoodRValRefType<_Dummy>>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY unique_ptr(pointer __p, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT : __ptr_(__p, wistd::move(__d))
|
||||
{
|
||||
static_assert(!is_reference<deleter_type>::value, "rvalue deleter bound to reference");
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_BadRValRefType<_Dummy>>>
|
||||
__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<deleter_type>(__u.get_deleter()))
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Up, class _Ep, class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _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<deleter_type>(__u.get_deleter());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Up, class _Ep, class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _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 <class _Up, class _Ep>
|
||||
unique_ptr(unique_ptr<_Up, _Ep>&);
|
||||
|
||||
unique_ptr& operator=(unique_ptr&);
|
||||
template <class _Up, class _Ep>
|
||||
unique_ptr& operator=(unique_ptr<_Up, _Ep>&);
|
||||
|
||||
public:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr() : __ptr_(pointer())
|
||||
{
|
||||
static_assert(!is_pointer<deleter_type>::value, "unique_ptr constructed with null function pointer deleter");
|
||||
static_assert(is_default_constructible<deleter_type>::value, "unique_ptr::deleter_type is not default constructible");
|
||||
}
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(nullptr_t) : __ptr_(pointer())
|
||||
{
|
||||
static_assert(!is_pointer<deleter_type>::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<deleter_type>::value, "unique_ptr constructed with null function pointer deleter");
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
operator __rv<unique_ptr>()
|
||||
{
|
||||
return __rv<unique_ptr>(*this);
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(__rv<unique_ptr> __u) : __ptr_(__u->release(), wistd::forward<deleter_type>(__u->get_deleter()))
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Up, class _Ep>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY typename enable_if<
|
||||
!is_array<_Up>::value && is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>::value && is_assignable<deleter_type&, _Ep&>::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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY typename add_lvalue_reference<_Tp>::type operator*() const
|
||||
{
|
||||
return *__ptr_.first();
|
||||
}
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY pointer operator->() const WI_NOEXCEPT
|
||||
{
|
||||
return __ptr_.first();
|
||||
}
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY const deleter_type& get_deleter() const WI_NOEXCEPT
|
||||
{
|
||||
return __ptr_.second();
|
||||
}
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __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 _Tp, class _Dp>
|
||||
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
|
||||
{
|
||||
public:
|
||||
using element_type = _Tp;
|
||||
using deleter_type = _Dp;
|
||||
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
|
||||
|
||||
private:
|
||||
__compressed_pair<pointer, deleter_type> __ptr_;
|
||||
|
||||
template <class _From>
|
||||
struct _CheckArrayPointerConversion : is_same<_From, pointer>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _FromElem>
|
||||
struct _CheckArrayPointerConversion<_FromElem*>
|
||||
: integral_constant<
|
||||
bool,
|
||||
is_same<_FromElem*, pointer>::value ||
|
||||
(is_same<pointer, element_type*>::value && is_convertible<_FromElem (*)[], element_type (*)[]>::value)>
|
||||
{
|
||||
};
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _LValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _GoodRValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
|
||||
|
||||
template <bool _Dummy>
|
||||
using _BadRValRefType = typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type;
|
||||
|
||||
template <bool _Dummy, class _Deleter = typename __dependent_type<__identity<deleter_type>, _Dummy>::type>
|
||||
using _EnableIfDeleterDefaultConstructible =
|
||||
typename enable_if<is_default_constructible<_Deleter>::value && !is_pointer<_Deleter>::value>::type;
|
||||
|
||||
template <class _ArgType>
|
||||
using _EnableIfDeleterConstructible = typename enable_if<is_constructible<deleter_type, _ArgType>::value>::type;
|
||||
|
||||
template <class _Pp>
|
||||
using _EnableIfPointerConvertible = typename enable_if<_CheckArrayPointerConversion<_Pp>::value>::type;
|
||||
|
||||
template <class _UPtr, class _Up, class _ElemT = typename _UPtr::element_type>
|
||||
using _EnableIfMoveConvertible = typename enable_if<
|
||||
is_array<_Up>::value && is_same<pointer, element_type*>::value && is_same<typename _UPtr::pointer, _ElemT*>::value &&
|
||||
is_convertible<_ElemT (*)[], element_type (*)[]>::value>::type;
|
||||
|
||||
template <class _UDel>
|
||||
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 <class _UDel>
|
||||
using _EnableIfDeleterAssignable = typename enable_if<is_assignable<_Dp&, _UDel&&>::value>::type;
|
||||
|
||||
public:
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer())
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer())
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Pp, bool _Dummy = true, class = _EnableIfDeleterDefaultConstructible<_Dummy>, class = _EnableIfPointerConvertible<_Pp>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY explicit unique_ptr(_Pp __p) WI_NOEXCEPT : __ptr_(__p)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Pp, bool _Dummy = true, class = _EnableIfDeleterConstructible<_LValRefType<_Dummy>>, class = _EnableIfPointerConvertible<_Pp>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY unique_ptr(_Pp __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT : __ptr_(__p, __d)
|
||||
{
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_LValRefType<_Dummy>>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY unique_ptr(nullptr_t, _LValRefType<_Dummy> __d) WI_NOEXCEPT : __ptr_(nullptr, __d)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _Pp, bool _Dummy = true, class = _EnableIfDeleterConstructible<_GoodRValRefType<_Dummy>>, 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<deleter_type>::value, "rvalue deleter bound to reference");
|
||||
}
|
||||
|
||||
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_GoodRValRefType<_Dummy>>>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY unique_ptr(nullptr_t, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT : __ptr_(nullptr, wistd::move(__d))
|
||||
{
|
||||
static_assert(!is_reference<deleter_type>::value, "rvalue deleter bound to reference");
|
||||
}
|
||||
|
||||
template <class _Pp, bool _Dummy = true, class = _EnableIfDeleterConstructible<_BadRValRefType<_Dummy>>, 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<deleter_type>(__u.get_deleter()))
|
||||
{
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr& operator=(unique_ptr&& __u) WI_NOEXCEPT
|
||||
{
|
||||
reset(__u.release());
|
||||
__ptr_.second() = wistd::forward<deleter_type>(__u.get_deleter());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Up, class _Ep, class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _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 <class _Up, class _Ep, class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _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 <class _Up>
|
||||
explicit unique_ptr(_Up);
|
||||
|
||||
unique_ptr(unique_ptr&);
|
||||
template <class _Up>
|
||||
unique_ptr(unique_ptr<_Up>&);
|
||||
|
||||
unique_ptr& operator=(unique_ptr&);
|
||||
template <class _Up>
|
||||
unique_ptr& operator=(unique_ptr<_Up>&);
|
||||
|
||||
template <class _Up>
|
||||
unique_ptr(
|
||||
_Up __u,
|
||||
typename conditional<is_reference<deleter_type>::value, deleter_type, typename add_lvalue_reference<const deleter_type>::type>::type,
|
||||
typename enable_if<is_convertible<_Up, pointer>::value, __nat>::type = __nat());
|
||||
|
||||
public:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr() : __ptr_(pointer())
|
||||
{
|
||||
static_assert(!is_pointer<deleter_type>::value, "unique_ptr constructed with null function pointer deleter");
|
||||
}
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(nullptr_t) : __ptr_(pointer())
|
||||
{
|
||||
static_assert(!is_pointer<deleter_type>::value, "unique_ptr constructed with null function pointer deleter");
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
explicit unique_ptr(pointer __p) : __ptr_(__p)
|
||||
{
|
||||
static_assert(!is_pointer<deleter_type>::value, "unique_ptr constructed with null function pointer deleter");
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(pointer __p, deleter_type __d) : __ptr_(__p, wistd::forward<deleter_type>(__d))
|
||||
{
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(nullptr_t, deleter_type __d) : __ptr_(pointer(), wistd::forward<deleter_type>(__d))
|
||||
{
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
operator __rv<unique_ptr>()
|
||||
{
|
||||
return __rv<unique_ptr>(*this);
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr(__rv<unique_ptr> __u) : __ptr_(__u->release(), wistd::forward<deleter_type>(__u->get_deleter()))
|
||||
{
|
||||
}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
unique_ptr& operator=(__rv<unique_ptr> __u)
|
||||
{
|
||||
reset(__u->release());
|
||||
__ptr_.second() = wistd::forward<deleter_type>(__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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY typename add_lvalue_reference<_Tp>::type operator[](size_t __i) const
|
||||
{
|
||||
return __ptr_.first()[__i];
|
||||
}
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY const deleter_type& get_deleter() const WI_NOEXCEPT
|
||||
{
|
||||
return __ptr_.second();
|
||||
}
|
||||
__WI_LIBCPP_NODISCARD_ATTRIBUTE __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 <class _Pp>
|
||||
__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 <class _Tp, class _Dp>
|
||||
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 <class _Tp, class _Dp>
|
||||
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 <class _T1, class _D1, class _T2, class _D2>
|
||||
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 <class _T1, class _D1, class _T2, class _D2>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator!=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y)
|
||||
{
|
||||
return !(__x == __y);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
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 <class _T1, class _D1, class _T2, class _D2>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y)
|
||||
{
|
||||
return __y < __x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator<=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y)
|
||||
{
|
||||
return !(__y < __x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator>=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y)
|
||||
{
|
||||
return !(__x < __y);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
return !__x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator==(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT
|
||||
{
|
||||
return !__x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator!=(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
return static_cast<bool>(__x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator!=(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT
|
||||
{
|
||||
return static_cast<bool>(__x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
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 <class _T1, class _D1>
|
||||
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 <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator>(const unique_ptr<_T1, _D1>& __x, nullptr_t)
|
||||
{
|
||||
return nullptr < __x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator>(nullptr_t, const unique_ptr<_T1, _D1>& __x)
|
||||
{
|
||||
return __x < nullptr;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator<=(const unique_ptr<_T1, _D1>& __x, nullptr_t)
|
||||
{
|
||||
return !(nullptr < __x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator<=(nullptr_t, const unique_ptr<_T1, _D1>& __x)
|
||||
{
|
||||
return !(__x < nullptr);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY bool operator>=(const unique_ptr<_T1, _D1>& __x, nullptr_t)
|
||||
{
|
||||
return !(__x < nullptr);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
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 <class _Tp, class _Dp>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY unique_ptr<_Tp, _Dp> move(unique_ptr<_Tp, _Dp>& __t)
|
||||
{
|
||||
return unique_ptr<_Tp, _Dp>(__rv<unique_ptr<_Tp, _Dp>>(__t));
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace wistd
|
||||
/// @endcond
|
||||
|
||||
#endif // _WISTD_MEMORY_H_
|
||||
4969
3rdparty/winwil/include/wil/wistd_type_traits.h
vendored
Normal file
4969
3rdparty/winwil/include/wil/wistd_type_traits.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
131
3rdparty/winwil/include/wil/wrl.h
vendored
Normal file
131
3rdparty/winwil/include/wil/wrl.h
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
//! @file
|
||||
//! Windows Runtime Library Helpers: helpers for constructing RuntimeClass based objects and agile WRL Callback objects
|
||||
#ifndef __WIL_WRL_INCLUDED
|
||||
#define __WIL_WRL_INCLUDED
|
||||
|
||||
#include <wrl.h>
|
||||
#include "result.h"
|
||||
#include "common.h" // wistd type_traits helpers
|
||||
#include <libloaderapi.h> // GetModuleHandleW
|
||||
|
||||
/// @cond
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
/// @endcond
|
||||
|
||||
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.
|
||||
@code
|
||||
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
|
||||
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
|
||||
@endcode
|
||||
*/
|
||||
template <typename T, typename... TArgs>
|
||||
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<T> obj;
|
||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
|
||||
not require 2 phase construction).
|
||||
@code
|
||||
// SomeClass uses exceptions for error handling in its constructor.
|
||||
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
|
||||
@endcode
|
||||
*/
|
||||
template <typename T, typename... TArgs>
|
||||
Microsoft::WRL::ComPtr<T> 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<decltype(&T::RuntimeClassInitialize)>::value,
|
||||
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
|
||||
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(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 <typename TDelegateInterface, typename... Args>
|
||||
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TDelegateInterface, typename... Args>
|
||||
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
|
||||
{
|
||||
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
|
||||
THROW_IF_NULL_ALLOC(result);
|
||||
return result;
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
/** Holds a reference to the host WRL module to prevent it from being unloaded.
|
||||
Normally, the reference is held implicitly because you are a member function
|
||||
of a DLL-hosted COM object, or because you retain a strong reference
|
||||
to some DLL-hosted COM object, but if those do not apply to you, then you
|
||||
will need to hold a reference explicitly. For examples (and for the C++/WinRT
|
||||
equivalent), see winrt_module_reference.
|
||||
*/
|
||||
struct [[nodiscard]] wrl_module_reference
|
||||
{
|
||||
wrl_module_reference()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->IncrementObjectCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GET_MODULE_HANDLE_EX_FLAG_PIN
|
||||
// If this assertion fails, then you are using wrl_module_reference
|
||||
// from a DLL that does not host WRL objects, and the module reference
|
||||
// has no effect.
|
||||
WI_ASSERT(reinterpret_cast<HMODULE>(&__ImageBase) == GetModuleHandleW(nullptr));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
wrl_module_reference(wrl_module_reference const&) : wrl_module_reference()
|
||||
{
|
||||
}
|
||||
|
||||
~wrl_module_reference()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->DecrementObjectCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_WRL_INCLUDED
|
||||
Reference in New Issue
Block a user