From 56ce4234b0350d54a78c5a709e4684e755e42745 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 2 Jun 2021 00:17:42 -0400 Subject: [PATCH 1/4] Improve C++20 modules support --- cppwinrt/code_writers.h | 2 +- cppwinrt/cppwinrt.vcxproj | 1 + cppwinrt/cppwinrt.vcxproj.filters | 3 + cppwinrt/main.cpp | 2 +- strings/base_collections_map.h | 2 +- strings/base_coroutine_foundation.h | 4 +- strings/base_coroutine_threadpool.h | 18 +++++- strings/base_includes.h | 22 ------- strings/base_macros.h | 2 + strings/base_module.h | 10 +++ strings/base_std_hash.h | 2 +- test/test_cpp20/async.cpp | 98 +++++++++++++++++++++++++++++ test/test_cpp20/main.cpp | 3 +- test/test_cpp20/pch.cpp | 1 - test/test_cpp20/pch.h | 12 ---- test/test_cpp20/ranges.cpp | 5 +- test/test_cpp20/test_cpp20.vcxproj | 28 ++++++--- 17 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 strings/base_module.h create mode 100644 test/test_cpp20/async.cpp delete mode 100644 test/test_cpp20/pch.cpp delete mode 100644 test/test_cpp20/pch.h diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index 4b4fa8229..e24b462cb 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -169,7 +169,7 @@ namespace cppwinrt [[nodiscard]] static finish_with wrap_std_namespace(writer& w) { - w.write(R"(namespace std + w.write(R"(WINRT_EXPORT namespace std { )"); diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index 0f8d398fb..f4ed40fb9 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -73,6 +73,7 @@ + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 26c1fdafd..fe2b4ea95 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -109,6 +109,9 @@ strings + + strings + strings diff --git a/cppwinrt/main.cpp b/cppwinrt/main.cpp index 1b26659a3..fc5cc1cb4 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -294,7 +294,7 @@ Where is one or more of: write_preamble(ixx); ixx.write("module;\n"); ixx.write(strings::base_includes); - ixx.write("\nexport module winrt;\n#define WINRT_EXPORT export\n\n"); + ixx.write(strings::base_module); for (auto&&[ns, members] : c.namespaces()) { diff --git a/strings/base_collections_map.h b/strings/base_collections_map.h index f4bd74baf..1a0fe4c1e 100644 --- a/strings/base_collections_map.h +++ b/strings/base_collections_map.h @@ -116,7 +116,7 @@ WINRT_EXPORT namespace winrt } } -namespace std +WINRT_EXPORT namespace std { template struct tuple_size> diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 943e640ed..3e4198eef 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -655,9 +655,9 @@ namespace winrt::impl } #ifdef __cpp_lib_coroutine -namespace std +WINRT_EXPORT namespace std #else -namespace std::experimental +WINRT_EXPORT namespace std::experimental #endif { template diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index 4ea37bf75..81e5cee6d 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -1,6 +1,20 @@ namespace winrt::impl { +#ifdef __cpp_lib_coroutine + template + using coroutine_handle = std::coroutine_handle; + + using suspend_always = std::suspend_always; + using suspend_never = std::suspend_never; +#else + template + using coroutine_handle = std::experimental::coroutine_handle; + + using suspend_always = std::experimental::suspend_always; + using suspend_never = std::experimental::suspend_never; +#endif + inline auto submit_threadpool_callback(void(__stdcall* callback)(void*, void* context), void* context) { if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, context, nullptr)) @@ -676,9 +690,9 @@ WINRT_EXPORT namespace winrt } #ifdef __cpp_lib_coroutine -namespace std +WINRT_EXPORT namespace std #else -namespace std::experimental +WINRT_EXPORT namespace std::experimental #endif { template diff --git a/strings/base_includes.h b/strings/base_includes.h index 7354a304c..307f06ba2 100644 --- a/strings/base_includes.h +++ b/strings/base_includes.h @@ -25,29 +25,7 @@ #endif #ifdef __cpp_lib_coroutine - #include - -namespace winrt::impl -{ - template - using coroutine_handle = std::coroutine_handle; - - using suspend_always = std::suspend_always; - using suspend_never = std::suspend_never; -} - #else - #include - -namespace winrt::impl -{ - template - using coroutine_handle = std::experimental::coroutine_handle; - - using suspend_always = std::experimental::suspend_always; - using suspend_never = std::experimental::suspend_never; -} - #endif diff --git a/strings/base_macros.h b/strings/base_macros.h index 92f746699..d78961f86 100644 --- a/strings/base_macros.h +++ b/strings/base_macros.h @@ -39,6 +39,8 @@ #define _WINDOWS_NUMERICS_NAMESPACE_ winrt::Windows::Foundation::Numerics #define _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ WINRT_EXPORT namespace winrt::Windows::Foundation::Numerics #define _WINDOWS_NUMERICS_END_NAMESPACE_ +// the include in purview of a module is intentional, we want to export the numeric types as part of the module +#pragma warning(suppress: 5244) #include #undef _WINDOWS_NUMERICS_NAMESPACE_ #undef _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ diff --git a/strings/base_module.h b/strings/base_module.h new file mode 100644 index 000000000..2dc1e4c93 --- /dev/null +++ b/strings/base_module.h @@ -0,0 +1,10 @@ + +// Since modules don't result in global symbol pollution, +// we can always enable the classic COM support. +// Users will have to include headers declaring these interfaces +// to make use of it. +#include +#undef GetCurrentTime // Get rid of this evil macro + +export module winrt; +#define WINRT_EXPORT export diff --git a/strings/base_std_hash.h b/strings/base_std_hash.h index eb97db0e9..9767e11cf 100644 --- a/strings/base_std_hash.h +++ b/strings/base_std_hash.h @@ -32,7 +32,7 @@ namespace winrt::impl }; } -namespace std +WINRT_EXPORT namespace std { template<> struct hash { diff --git a/test/test_cpp20/async.cpp b/test/test_cpp20/async.cpp new file mode 100644 index 000000000..129e71080 --- /dev/null +++ b/test/test_cpp20/async.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "catch.hpp" + +import winrt; + +using namespace winrt; +using namespace Windows::Foundation; +using namespace std::chrono_literals; + +namespace +{ + // + // Just some quick tests to make sure that coroutines compile and work with C++20 modules. + // Taken from async_throw in test + // IAsyncOperation and IAsyncOperationWithProgress are affected by compiler bugs so disabled for now: + // https://developercommunity.visualstudio.com/t/identifier-not-found-with-default-membe/1376824 + // + + IAsyncAction Action() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + + IAsyncActionWithProgress ActionWithProgress() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + +#if 0 + IAsyncOperation Operation() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + co_return 1; + } +#endif + + template + void Check(F make) + { + try + { + make().get(); + REQUIRE(false); + } + catch (hresult_invalid_argument const& e) + { + REQUIRE(e.message() == L"Async"); + } + + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + auto async = make(); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Error); + SetEvent(completed.get()); + }); + + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + REQUIRE(async.Status() == AsyncStatus::Error); + + hresult_error e(async.ErrorCode(), take_ownership_from_abi); + REQUIRE(e.message() == L"Async"); + + try + { + async.GetResults(); + REQUIRE(false); + } + catch (hresult_invalid_argument const& e) + { + REQUIRE(e.message() == L"Async"); + } + } +} + +TEST_CASE("async_throw") +{ + Check(Action); + Check(ActionWithProgress); +#if 0 + Check(Operation); + Check(OperationWithProgress); +#endif +} diff --git a/test/test_cpp20/main.cpp b/test/test_cpp20/main.cpp index 7873e4ee7..4b9f3f8d5 100644 --- a/test/test_cpp20/main.cpp +++ b/test/test_cpp20/main.cpp @@ -1,6 +1,7 @@ #define CATCH_CONFIG_RUNNER #include "catch.hpp" -#include "winrt/base.h" + +import winrt; using namespace winrt; diff --git a/test/test_cpp20/pch.cpp b/test/test_cpp20/pch.cpp deleted file mode 100644 index 1d9f38c57..000000000 --- a/test/test_cpp20/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/test/test_cpp20/pch.h b/test/test_cpp20/pch.h deleted file mode 100644 index ea74230cc..000000000 --- a/test/test_cpp20/pch.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#pragma warning(4: 4458) // ensure we compile clean with this warning enabled - -#define WINRT_LEAN_AND_MEAN -#include -#include "winrt/Windows.Foundation.Collections.h" -#include "winrt/Windows.Foundation.Numerics.h" -#include -#include "catch.hpp" - -using namespace std::literals; diff --git a/test/test_cpp20/ranges.cpp b/test/test_cpp20/ranges.cpp index 9bf88a579..a53fb00fe 100644 --- a/test/test_cpp20/ranges.cpp +++ b/test/test_cpp20/ranges.cpp @@ -1,7 +1,10 @@ -#include "pch.h" #include #include +#include "catch.hpp" + +import winrt; + TEST_CASE("ranges") { { diff --git a/test/test_cpp20/test_cpp20.vcxproj b/test/test_cpp20/test_cpp20.vcxproj index 485218a0f..828d45acf 100644 --- a/test/test_cpp20/test_cpp20.vcxproj +++ b/test/test_cpp20/test_cpp20.vcxproj @@ -131,6 +131,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded stdcpplatest + NotUsing + /bigobj Console @@ -153,6 +155,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug stdcpplatest + NotUsing + /bigobj Console @@ -173,6 +177,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug stdcpplatest + NotUsing + /bigobj Console @@ -193,6 +199,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug stdcpplatest + NotUsing + /bigobj Console @@ -213,6 +221,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug stdcpplatest + NotUsing + /bigobj Console @@ -235,6 +245,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded stdcpplatest + NotUsing + /bigobj Console @@ -259,6 +271,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded stdcpplatest + NotUsing + /bigobj Console @@ -283,6 +297,8 @@ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded stdcpplatest + NotUsing + /bigobj Console @@ -299,15 +315,9 @@ - - - - - NotUsing - - - Create - + + + From 3430aad80c04c28487167c2250bd97dc2101900d Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Sat, 31 Jul 2021 04:03:50 -0400 Subject: [PATCH 2/4] IAsyncOperation started working, so enable it back --- test/test_cpp20/async.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/test_cpp20/async.cpp b/test/test_cpp20/async.cpp index 129e71080..b3a582ceb 100644 --- a/test/test_cpp20/async.cpp +++ b/test/test_cpp20/async.cpp @@ -14,8 +14,6 @@ namespace // // Just some quick tests to make sure that coroutines compile and work with C++20 modules. // Taken from async_throw in test - // IAsyncOperation and IAsyncOperationWithProgress are affected by compiler bugs so disabled for now: - // https://developercommunity.visualstudio.com/t/identifier-not-found-with-default-membe/1376824 // IAsyncAction Action() @@ -30,7 +28,6 @@ namespace throw hresult_invalid_argument(L"Async"); } -#if 0 IAsyncOperation Operation() { co_await 10ms; @@ -44,7 +41,6 @@ namespace throw hresult_invalid_argument(L"Async"); co_return 1; } -#endif template void Check(F make) @@ -91,8 +87,6 @@ TEST_CASE("async_throw") { Check(Action); Check(ActionWithProgress); -#if 0 Check(Operation); Check(OperationWithProgress); -#endif } From ac0b44d1f30a932f5cc5da830bdef03290a9c41b Mon Sep 17 00:00:00 2001 From: Scott Jones Date: Thu, 9 Dec 2021 10:22:04 -0800 Subject: [PATCH 3/4] merge fixes, workaround --- strings/base_coroutine_threadpool.h | 8 +++++--- test/test_cpp20/await_completed.cpp | 5 ++++- test/test_cpp20/format.cpp | 6 +++++- test/test_cpp20/hstring.cpp | 4 +++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index e538394d1..483923fe9 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -64,7 +64,9 @@ namespace winrt::impl struct resume_apartment_context { - resume_apartment_context() = default; + resume_apartment_context() : + m_context(try_capture(WINRT_IMPL_CoGetObjectContext)), + m_context_type(get_apartment_type().first) {} resume_apartment_context(std::nullptr_t) : m_context(nullptr), m_context_type(-1) {} resume_apartment_context(resume_apartment_context const&) = default; resume_apartment_context(resume_apartment_context&& other) noexcept : @@ -81,8 +83,8 @@ namespace winrt::impl return m_context_type >= 0; } - com_ptr m_context = try_capture(WINRT_IMPL_CoGetObjectContext); - int32_t m_context_type = get_apartment_type().first; + com_ptr m_context; + int32_t m_context_type; }; inline int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept diff --git a/test/test_cpp20/await_completed.cpp b/test/test_cpp20/await_completed.cpp index 2448a3f88..a46b0c261 100644 --- a/test/test_cpp20/await_completed.cpp +++ b/test/test_cpp20/await_completed.cpp @@ -1,4 +1,7 @@ -#include "pch.h" +#include +#include "catch.hpp" + +import winrt; using namespace winrt; using namespace Windows::Foundation; diff --git a/test/test_cpp20/format.cpp b/test/test_cpp20/format.cpp index b3119a77e..91b56b224 100644 --- a/test/test_cpp20/format.cpp +++ b/test/test_cpp20/format.cpp @@ -1,5 +1,9 @@ -#include "pch.h" +#include "winrt/Windows.Data.Json.h" +#include "winrt/Windows.Foundation.h" #include +#include "catch.hpp" + +import winrt; struct stringable : winrt::implements { diff --git a/test/test_cpp20/hstring.cpp b/test/test_cpp20/hstring.cpp index 57d145fbb..a37c28959 100644 --- a/test/test_cpp20/hstring.cpp +++ b/test/test_cpp20/hstring.cpp @@ -1,4 +1,6 @@ -#include "pch.h" +#include "catch.hpp" + +import winrt; TEST_CASE("hstring") { From 97d694cfce1c5048f94c381f5b560029bc4e8b80 Mon Sep 17 00:00:00 2001 From: Scott Jones Date: Mon, 16 Mar 2026 16:43:54 -0400 Subject: [PATCH 4/4] old experiments --- cppwinrt.props | 2 ++ cppwinrt/code_writers.h | 2 +- strings/base_abi.h | 1 + strings/base_activation.h | 1 + strings/base_identity.h | 1 + strings/base_implements.h | 3 +++ strings/base_meta.h | 33 ++++++++++++++++++++++----------- strings/base_version.h | 3 +++ 8 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cppwinrt.props b/cppwinrt.props index 943cbc823..8bf999342 100644 --- a/cppwinrt.props +++ b/cppwinrt.props @@ -48,6 +48,8 @@ $(CmakeOutDir)\ $(SolutionDir)_build\x86\$(Configuration)\ $(CmakeOutDir)\ + + PreventSdkUapPropsAssignment diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index f4782ac05..4a06a839e 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -40,7 +40,7 @@ namespace cppwinrt static void write_version_assert(writer& w) { w.write_root_include("base"); - auto format = R"(static_assert(winrt::check_version(CPPWINRT_VERSION, "%"), "Mismatched C++/WinRT headers."); + auto format = R"(static_assert(winrt::check_version(WINRT_version_number, "%"), "Mismatched C++/WinRT headers."); #define CPPWINRT_VERSION "%" )"; w.write(format, CPPWINRT_VERSION_STRING, CPPWINRT_VERSION_STRING); diff --git a/strings/base_abi.h b/strings/base_abi.h index b14e8d85f..6cc1240ae 100644 --- a/strings/base_abi.h +++ b/strings/base_abi.h @@ -23,6 +23,7 @@ namespace winrt::impl }; }; + WINRT_EXPORT using inspectable_abi = abi_t; template <> struct abi diff --git a/strings/base_activation.h b/strings/base_activation.h index 8d8eb5e92..5102c8ac3 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -396,6 +396,7 @@ namespace winrt::impl return factory.call(callback); } + WINRT_EXPORT template auto call_factory_cast(F&& callback) { diff --git a/strings/base_identity.h b/strings/base_identity.h index 5bf7bbaa8..943458d24 100644 --- a/strings/base_identity.h +++ b/strings/base_identity.h @@ -457,6 +457,7 @@ namespace winrt::impl static constexpr guid value{ generate_guid(signature::data) }; }; + WINRT_EXPORT template #ifdef __clang__ inline static const auto name_v diff --git a/strings/base_implements.h b/strings/base_implements.h index b2300edc5..68096e6b9 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -190,10 +190,12 @@ namespace winrt::impl template inline constexpr bool has_static_lifetime_v = has_static_lifetime::value; + WINRT_EXPORT template void clear_abi(T*) noexcept {} + WINRT_EXPORT template void clear_abi(T** value) noexcept { @@ -1250,6 +1252,7 @@ namespace winrt::impl } } + WINRT_EXPORT template auto detach_from(T&& object) noexcept { diff --git a/strings/base_meta.h b/strings/base_meta.h index f951c4db0..d13505422 100644 --- a/strings/base_meta.h +++ b/strings/base_meta.h @@ -54,42 +54,49 @@ namespace winrt::impl using type = T; }; + WINRT_EXPORT template struct abi { using type = T; }; - template + WINRT_EXPORT + template struct abi>> { using type = std::underlying_type_t; }; - template + WINRT_EXPORT + template using abi_t = typename abi::type; + WINRT_EXPORT template struct consume; - template + WINRT_EXPORT + template using consume_t = typename consume::template type; template struct delegate; + WINRT_EXPORT template > struct default_interface { using type = T; }; - struct basic_category; - struct interface_category; - struct delegate_category; - struct enum_category; - struct class_category; + WINRT_EXPORT struct basic_category; + WINRT_EXPORT struct interface_category; + WINRT_EXPORT struct delegate_category; + WINRT_EXPORT struct enum_category; + WINRT_EXPORT struct class_category; + WINRT_EXPORT template struct category { @@ -117,6 +124,7 @@ namespace winrt::impl static constexpr auto data{ category_signature, T>::data }; }; + WINRT_EXPORT template #if defined(__clang__) #if __has_declspec_attribute(uuid) @@ -202,13 +210,16 @@ namespace winrt::impl template using arg_out = arg_in*; - template + WINRT_EXPORT + template struct produce_base; - template + WINRT_EXPORT + template struct produce; - template + WINRT_EXPORT + template struct produce : produce_base { }; diff --git a/strings/base_version.h b/strings/base_version.h index 227f10402..b2396039b 100644 --- a/strings/base_version.h +++ b/strings/base_version.h @@ -4,6 +4,9 @@ extern "C" __declspec(selectany) char const * const WINRT_version = "C++/WinRT version:" CPPWINRT_VERSION; +WINRT_EXPORT +constexpr char const WINRT_version_number[] = CPPWINRT_VERSION; + #ifdef _M_IX86 #pragma comment(linker, "/include:_WINRT_version") #else