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 9d4b98e23..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); @@ -179,7 +179,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 258feafc6..a02d37bf1 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 dfe1488ee..e4a27a26d 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 eab187d6a..8d8a57c7f 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -296,7 +296,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_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_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 3c309256c..4fec2fb5c 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -677,9 +677,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 ae77512d7..483923fe9 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)) @@ -50,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 : @@ -67,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 @@ -702,9 +718,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_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_includes.h b/strings/base_includes.h index 14d992fc9..2c954d4af 100644 --- a/strings/base_includes.h +++ b/strings/base_includes.h @@ -29,29 +29,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 1b96e240c..f375b3c5f 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_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_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/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 diff --git a/test/test_cpp20/async.cpp b/test/test_cpp20/async.cpp new file mode 100644 index 000000000..b3a582ceb --- /dev/null +++ b/test/test_cpp20/async.cpp @@ -0,0 +1,92 @@ +#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 + // + + IAsyncAction Action() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + + IAsyncActionWithProgress ActionWithProgress() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + + 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; + } + + 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); + Check(Operation); + Check(OperationWithProgress); +} 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") { 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 bd678407c..000000000 --- a/test/test_cpp20/pch.h +++ /dev/null @@ -1,14 +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.Data.Json.h" -#include "winrt/Windows.Foundation.h" -#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 778faa5f0..7701247b5 100644 --- a/test/test_cpp20/test_cpp20.vcxproj +++ b/test/test_cpp20/test_cpp20.vcxproj @@ -131,6 +131,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + stdcpplatest + NotUsing + /bigobj Console @@ -152,6 +155,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + stdcpplatest + NotUsing + /bigobj Console @@ -171,6 +177,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + stdcpplatest + NotUsing + /bigobj Console @@ -190,6 +199,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + stdcpplatest + NotUsing + /bigobj Console @@ -209,6 +221,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + stdcpplatest + NotUsing + /bigobj Console @@ -230,6 +245,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + stdcpplatest + NotUsing + /bigobj Console @@ -253,6 +271,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + stdcpplatest + NotUsing + /bigobj Console @@ -276,6 +297,9 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + stdcpplatest + NotUsing + /bigobj Console @@ -292,18 +316,12 @@ - - - + + - - NotUsing - - - Create - +