diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a42f2c5b7..5137d11d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: compiler: [MSVC, clang-cl] arch: [x86, x64, arm64] config: [Debug, Release] - test_exe: [test, test_nocoro, test_cpp20, test_cpp20_no_sourcelocation, test_fast, test_slow, test_old, test_module_lock_custom, test_module_lock_none] + test_exe: [test, test_nocoro, test_cppmodules test_cpp20, test_cpp20_no_sourcelocation, test_fast, test_slow, test_old, test_module_lock_custom, test_module_lock_none] exclude: - arch: arm64 config: Debug diff --git a/.pipelines/jobs/OneBranchTest.yml b/.pipelines/jobs/OneBranchTest.yml index 2fb3a4bd1..76e7099c0 100644 --- a/.pipelines/jobs/OneBranchTest.yml +++ b/.pipelines/jobs/OneBranchTest.yml @@ -21,6 +21,10 @@ jobs: TestExe: 'test_nocoro' TestProject: 'test_nocoro' BuildPlatform: 'x86' + test_cppmodules.x86: + TestExe: 'test_cppmodules' + TestProject: 'test_cppmodules' + BuildPlatform: 'x86' test_cpp20.x86: TestExe: 'test_cpp20' TestProject: 'test_cpp20' diff --git a/Directory.Build.Props b/Directory.Build.Props index 5f530da91..046271359 100644 --- a/Directory.Build.Props +++ b/Directory.Build.Props @@ -52,8 +52,9 @@ true true true - stdcpp17 + stdcpp17 stdcpp20 + stdcpplatest Use pch.h CPPWINRT_VERSION_STRING="$(CppWinRTBuildVersion)";%(PreprocessorDefinitions) diff --git a/build_test_all.cmd b/build_test_all.cmd index 649f4dc69..1f268160b 100644 --- a/build_test_all.cmd +++ b/build_test_all.cmd @@ -29,6 +29,7 @@ call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%, call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_nocoro +call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cppmodules call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20 call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20_no_sourcelocation call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_fast diff --git a/cppwinrt.sln b/cppwinrt.sln index 3bcfb33bc..1356b11f6 100644 --- a/cppwinrt.sln +++ b/cppwinrt.sln @@ -124,6 +124,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_nocoro", "test\test_no {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cppmodules", "test\test_cppmodules\test_cppmodules.vcxproj", "{4A135922-BE13-4FEC-8280-69028D1CB28D}" + ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + EndProjectSection +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D15C8430-A7CD-4616-BD84-243B26A9F1C2}" ProjectSection(SolutionItems) = preProject build_nuget.cmd = build_nuget.cmd @@ -411,6 +417,18 @@ Global {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x64.Build.0 = Release|x64 {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.ActiveCfg = Release|Win32 {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.Build.0 = Release|Win32 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|ARM64.Build.0 = Debug|ARM64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|x64.ActiveCfg = Debug|x64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|x64.Build.0 = Debug|x64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|x86.ActiveCfg = Debug|Win32 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Debug|x86.Build.0 = Debug|Win32 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|ARM64.ActiveCfg = Release|ARM64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|ARM64.Build.0 = Release|ARM64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|x64.ActiveCfg = Release|x64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|x64.Build.0 = Release|x64 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|x86.ActiveCfg = Release|Win32 + {4A135922-BE13-4FEC-8280-69028D1CB28D}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -435,6 +453,7 @@ Global {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {9E392830-805A-4AAF-932D-C493143EFACA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {4A135922-BE13-4FEC-8280-69028D1CB28D} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2783B8FD-EA3B-4D6B-9F81-662D289E02AA} diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index fc5081bef..d3e066058 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -37,7 +37,6 @@ 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."); #define CPPWINRT_VERSION "%" )"; @@ -123,6 +122,16 @@ namespace cppwinrt return { w, write_endif }; } + [[nodiscard]] static finish_with wrap_ifndef(writer& w, std::string_view macro) + { + auto format = R"(#ifndef % +)"; + + w.write(format, macro); + + return { w, write_endif }; + } + static void write_parent_depends(writer& w, cache const& c, std::string_view const& type_namespace) { auto pos = type_namespace.rfind('.'); @@ -145,6 +154,28 @@ namespace cppwinrt } } + static void write_parent_imports(writer& w, cache const& c, std::string_view const& type_namespace) + { + auto pos = type_namespace.rfind('.'); + + if (pos == std::string::npos) + { + return; + } + + auto parent = type_namespace.substr(0, pos); + auto found = c.namespaces().find(parent); + + if (found != c.namespaces().end() && has_projected_types(found->second)) + { + w.write_import(parent); + } + else + { + write_parent_imports(w, c, parent); + } + } + static void write_pch(writer& w) { auto format = R"(#include "%" @@ -313,11 +344,22 @@ namespace cppwinrt return; } - if (type_name == "Windows.Foundation.DateTime" || - type_name == "Windows.Foundation.TimeSpan") + if (type_name.name_space == "Windows.Foundation") { - // Don't forward declare these since they're not structs. - return; + if (type_name.name == "DateTime" || + type_name.name == "TimeSpan") + { + // Don't forward declare these since they're not structs. + return; + } + + if (type_name.name == "Point" || + type_name.name == "Rect" || + type_name.name == "Size") + { + // Don't forward declare these since they're already defined in base.h. + return; + } } if (type_name.name_space == "Windows.Foundation.Numerics") @@ -2812,8 +2854,15 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable } } - static bool write_structs(writer& w, std::vector const& types) + struct write_structs_result + { + bool promote = false; + }; + + static write_structs_result write_structs(writer& w, std::vector const& types) { + write_structs_result result{}; + auto format = R"( struct % { % }; @@ -2829,7 +2878,7 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable if (types.empty()) { - return false; + return result; } struct complex_struct @@ -2895,7 +2944,6 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable } } - bool promote = false; auto cpp_namespace = w.write_temp("@", w.type_namespace); for (auto&& type : structs) @@ -2921,14 +2969,14 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable continue; } - if (!starts_with(field.second, cpp_namespace)) + if (!result.promote && !starts_with(field.second, cpp_namespace)) { - promote = true; + result.promote = true; } } } - return promote; + return result; } static void write_class_requires(writer& w, TypeDef const& type) diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index b8beed890..b1768568a 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -46,7 +46,6 @@ - @@ -56,20 +55,23 @@ + - + + + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 96129ab20..52c8ae197 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -157,9 +157,6 @@ - - strings - strings @@ -178,15 +175,24 @@ strings - - strings - strings strings + + strings + + + strings + + + strings + + + strings + diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index ed9386b4e..d0f066119 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -6,11 +6,18 @@ namespace cppwinrt { writer w; write_preamble(w); - w.write(strings::base_version_odr, CPPWINRT_VERSION_STRING); { auto wrap_file_guard = wrap_open_file_guard(w, "BASE"); - w.write(strings::base_includes); + { + // ifdef out things that are handled by the module + auto wrap_modules_guard = wrap_ifndef(w, "WINRT_IMPL_MODULES"); + w.write(strings::base_global_fragment); + w.write("#include \"./shared.h\"\n"); + w.write(strings::base_std_includes); + w.write(strings::base_numerics); + } + write_version_assert(w); w.write(strings::base_macros); w.write(strings::base_types); w.write(strings::base_extern); @@ -41,11 +48,43 @@ namespace cppwinrt w.write(strings::base_iterator); w.write(strings::base_coroutine_threadpool); w.write(strings::base_natvis); - w.write(strings::base_version); } w.flush_to_file(settings.output_folder + "winrt/base.h"); } + static void write_shared_h() + { + writer w; + write_preamble(w); + w.write(strings::base_shared); + w.write(strings::base_version_odr, CPPWINRT_VERSION_STRING); + w.write(strings::base_version); + w.flush_to_file(settings.output_folder + "winrt/shared.h"); + } + + static void write_base_ixx() + { + writer ixx; + write_preamble(ixx); + ixx.write("module;\n"); + ixx.write("#define WINRT_IMPL_MODULES\n"); + ixx.write(strings::base_global_fragment); + ixx.write_root_include("shared"); + // 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. + ixx.write("#include \n"); + ixx.write("export module winrt:base;\n"); + // windowsnumerics.impl imports std headers. + // include-then-import is the prefered order for mixing headers and modules + // so we have to put it before the import. + ixx.write(strings::base_numerics); + ixx.write("import std;\n"); + ixx.write_root_include("base"); + ixx.flush_to_file(settings.output_folder + "winrt/ixx/base.ixx"); + } + static void write_fast_forward_h(std::vector const& classes) { writer w; @@ -65,6 +104,47 @@ namespace cppwinrt w.flush_to_file(settings.output_folder + "winrt/fast_forward.h"); } + static void write_namespace_ixx(cache const& c, writer const& header_writer, std::string_view const& ns, char import_impl, char impl = 0) + { + writer w; + w.type_namespace = ns; + write_preamble(w); + w.write("module;\n"); + w.write("#define WINRT_IMPL_MODULES\n"); + w.write_root_include("shared"); + if (impl) + { + w.write("export module winrt:%.%;\n", ns, module_friendly_impl(impl)); + } + else + { + w.write("export module winrt:%;\n", ns); + } + + w.write("import std;\n"); + + w.write_import("base"); + if (!impl) + { + write_parent_imports(w, c, ns); + } + + for (auto&& depends : header_writer.depends) + { + w.write_import(depends.first, import_impl); + } + + if (impl != '0') + { + w.write("export "); + w.write_import(ns, impl ? impl - 1 : '2'); + } + + w.write_depends(ns, impl); + + w.save_module(impl); + } + static void write_namespace_0_h(std::string_view const& ns, cache::namespace_members const& members) { writer w; @@ -119,6 +199,8 @@ namespace cppwinrt } w.save_header('0'); + + write_namespace_ixx({}, {}, ns, 0, '0'); } static void write_namespace_1_h(std::string_view const& ns, cache::namespace_members const& members) @@ -137,13 +219,18 @@ namespace cppwinrt write_preamble(w); write_open_file_guard(w, ns, '1'); - for (auto&& depends : w.depends) { - w.write_depends(depends.first, '0'); - } + auto wrap_modules_guard = wrap_ifndef(w, "WINRT_IMPL_MODULES"); + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, '0'); + } - w.write_depends(w.type_namespace, '0'); + w.write_depends(w.type_namespace, '0'); + } w.save_header('1'); + + write_namespace_ixx({}, w, ns, '0', '1'); } static void write_namespace_2_h(std::string_view const& ns, cache::namespace_members const& members) @@ -151,11 +238,11 @@ namespace cppwinrt writer w; w.type_namespace = ns; - bool promote; + write_structs_result structs_info; { auto wrap_type = wrap_type_namespace(w, ns); w.write_each(members.delegates); - promote = write_structs(w, members.structs); + structs_info = write_structs(w, members.structs); w.write_each(members.classes); w.write_each(members.classes); } @@ -165,15 +252,20 @@ namespace cppwinrt write_preamble(w); write_open_file_guard(w, ns, '2'); - char const impl = promote ? '2' : '1'; + char const impl = structs_info.promote ? '2' : '1'; - for (auto&& depends : w.depends) { - w.write_depends(depends.first, impl); - } + auto wrap_modules_guard = wrap_ifndef(w, "WINRT_IMPL_MODULES"); + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, impl); + } - w.write_depends(w.type_namespace, '1'); + w.write_depends(w.type_namespace, '1'); + } w.save_header('2'); + + write_namespace_ixx({}, w, ns, impl, '2'); } static void write_namespace_h(cache const& c, std::string_view const& ns, cache::namespace_members const& members) @@ -219,16 +311,23 @@ namespace cppwinrt w.swap(); write_preamble(w); write_open_file_guard(w, ns); - write_version_assert(w); - write_parent_depends(w, c, ns); - - for (auto&& depends : w.depends) + { - w.write_depends(depends.first, '2'); - } + auto wrap_modules_guard = wrap_ifndef(w, "WINRT_IMPL_MODULES"); + w.write_root_include("base"); + write_parent_depends(w, c, ns); + + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, '2'); + } - w.write_depends(w.type_namespace, '2'); + w.write_depends(w.type_namespace, '2'); + } + write_version_assert(w); w.save_header(); + + write_namespace_ixx(c, w, ns, '2'); } static void write_module_g_cpp(std::vector const& classes) diff --git a/cppwinrt/main.cpp b/cppwinrt/main.cpp index 70a55b076..1990e33f6 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -97,6 +97,7 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder path output_folder = args.value("output", "."); create_directories(output_folder / "winrt/impl"); + create_directories(output_folder / "winrt/ixx"); settings.output_folder = canonical(output_folder).string(); settings.output_folder += std::filesystem::path::preferred_separator; @@ -344,9 +345,9 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder group.synchronous(args.exists("synchronous")); writer ixx; 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("export module winrt;\n"); + ixx.write("export "); + ixx.write_import("base"); for (auto&&[ns, members] : c.namespaces()) { @@ -355,7 +356,8 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder continue; } - ixx.write("#include \"winrt/%.h\"\n", ns); + ixx.write("export "); + ixx.write_import(ns); group.add([&, &ns = ns, &members = members] { @@ -366,10 +368,14 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder }); } + ixx.flush_to_file(settings.output_folder + "winrt/ixx/winrt.ixx"); + + write_shared_h(); + if (settings.base) { write_base_h(); - ixx.flush_to_file(settings.output_folder + "winrt/winrt.ixx"); + write_base_ixx(); } if (settings.component) diff --git a/cppwinrt/type_writers.h b/cppwinrt/type_writers.h index df17450f1..ad7ddca4c 100644 --- a/cppwinrt/type_writers.h +++ b/cppwinrt/type_writers.h @@ -104,6 +104,22 @@ namespace cppwinrt return false; } + std::string_view module_friendly_impl(char impl) + { + if (impl == '2') + { + return "two"; + } + else if (impl == '1') + { + return "one"; + } + else + { + return "zero"; + } + } + struct writer : writer_base { using writer_base::write; @@ -576,6 +592,18 @@ namespace cppwinrt } } + void write_import(std::string_view const& ns, char impl = 0) + { + if (impl) + { + write("import :%.%;\n", ns, module_friendly_impl(impl)); + } + else + { + write("import :%;\n", ns); + } + } + void save_header(char impl = 0) { auto filename{ settings.output_folder + "winrt/" }; @@ -596,5 +624,21 @@ namespace cppwinrt filename += ".h"; flush_to_file(filename); } + + void save_module(char impl = 0) + { + auto filename{ settings.output_folder + "winrt/ixx/" }; + + filename += type_namespace; + + if (impl) + { + filename += '.'; + filename += module_friendly_impl(impl); + } + + filename += ".ixx"; + flush_to_file(filename); + } }; } diff --git a/natvis/pch.h b/natvis/pch.h index 95e561971..4965781f6 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -10,7 +10,10 @@ #pragma warning(pop) #include #include -#include "base_includes.h" +#include "base_global_fragment.h" +#include "base_shared.h" +#include "base_std_includes.h" +#include "base_numerics.h" #include "base_macros.h" #include "base_types.h" #include "base_extern.h" diff --git a/run_tests.cmd b/run_tests.cmd index 58e3c5524..b467a73ae 100644 --- a/run_tests.cmd +++ b/run_tests.cmd @@ -10,6 +10,7 @@ if "%target_configuration%"=="" set target_configuration=Debug call :run_test test call :run_test test_nocoro +call :run_test test_cppmodules call :run_test test_cpp20 call :run_test test_cpp20_no_sourcelocation call :run_test test_fast diff --git a/strings/base_extern.h b/strings/base_extern.h index 84e2943c4..5c5c9e2cf 100644 --- a/strings/base_extern.h +++ b/strings/base_extern.h @@ -1,8 +1,8 @@ -__declspec(selectany) std::int32_t(__stdcall* winrt_to_hresult_handler)(void* address) noexcept {}; -__declspec(selectany) winrt::hstring(__stdcall* winrt_to_message_handler)(void* address) {}; -__declspec(selectany) void(__stdcall* winrt_throw_hresult_handler)(std::uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept {}; -__declspec(selectany) std::int32_t(__stdcall* winrt_activation_handler)(void* classId, winrt::guid const& iid, void** factory) noexcept {}; +WINRT_EXPORT extern "C++" __declspec(selectany) std::int32_t(__stdcall* winrt_to_hresult_handler)(void* address) noexcept {}; +WINRT_EXPORT extern "C++" __declspec(selectany) winrt::hstring(__stdcall* winrt_to_message_handler)(void* address) {}; +WINRT_EXPORT extern "C++" __declspec(selectany) void(__stdcall* winrt_throw_hresult_handler)(std::uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept {}; +WINRT_EXPORT extern "C++" __declspec(selectany) std::int32_t(__stdcall* winrt_activation_handler)(void* classId, winrt::guid const& iid, void** factory) noexcept {}; #if defined(_MSC_VER) #ifdef _M_HYBRID diff --git a/strings/base_global_fragment.h b/strings/base_global_fragment.h new file mode 100644 index 000000000..3ad027849 --- /dev/null +++ b/strings/base_global_fragment.h @@ -0,0 +1,7 @@ + +#include + +#if __has_include() +#define WINRT_IMPL_NUMERICS +#include +#endif diff --git a/strings/base_macros.h b/strings/base_macros.h index 3dc01fa2d..60609e78b 100644 --- a/strings/base_macros.h +++ b/strings/base_macros.h @@ -1,20 +1,4 @@ -#ifdef _DEBUG - -#define WINRT_ASSERT _ASSERTE -#define WINRT_VERIFY WINRT_ASSERT -#define WINRT_VERIFY_(result, expression) WINRT_ASSERT(result == expression) - -#else - -#define WINRT_ASSERT(expression) ((void)0) -#define WINRT_VERIFY(expression) (void)(expression) -#define WINRT_VERIFY_(result, expression) (void)(expression) - -#endif - -#define WINRT_IMPL_SHIM(...) (*(abi_t<__VA_ARGS__>**)&static_cast<__VA_ARGS__ const&>(static_cast(*this))) - #ifdef _MSC_VER // Note: this is a workaround for a false-positive warning produced by the Visual C++ 15.9 compiler. #pragma warning(disable : 5046) @@ -23,24 +7,6 @@ #pragma warning(disable : 4268) #endif -#if defined(__cpp_lib_coroutine) -#define WINRT_IMPL_COROUTINES -#endif - -#ifndef WINRT_EXPORT -#define WINRT_EXPORT -#endif - -#ifdef WINRT_IMPL_NUMERICS -#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_ -#include -#undef _WINDOWS_NUMERICS_NAMESPACE_ -#undef _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ -#undef _WINDOWS_NUMERICS_END_NAMESPACE_ -#endif - #if defined(_MSC_VER) #define WINRT_IMPL_NOINLINE __declspec(noinline) #elif defined(__GNUC__) @@ -49,30 +15,6 @@ #define WINRT_IMPL_NOINLINE #endif -#if defined(_MSC_VER) -#define WINRT_IMPL_EMPTY_BASES __declspec(empty_bases) -#else -#define WINRT_IMPL_EMPTY_BASES -#endif - -#if defined(_MSC_VER) -#define WINRT_IMPL_NOVTABLE __declspec(novtable) -#else -#define WINRT_IMPL_NOVTABLE -#endif - -#if defined(__clang__) && defined(__has_attribute) -#if __has_attribute(__lto_visibility_public__) -#define WINRT_IMPL_PUBLIC __attribute__((lto_visibility_public)) -#else -#define WINRT_IMPL_PUBLIC -#endif // __has_attribute(__lto_visibility_public__) -#else -#define WINRT_IMPL_PUBLIC -#endif - -#define WINRT_IMPL_ABI_DECL WINRT_IMPL_NOVTABLE WINRT_IMPL_PUBLIC - #if defined(__clang__) #define WINRT_IMPL_HAS_DECLSPEC_UUID __has_declspec_attribute(uuid) #elif defined(_MSC_VER) diff --git a/strings/base_numerics.h b/strings/base_numerics.h new file mode 100644 index 000000000..f8afc5837 --- /dev/null +++ b/strings/base_numerics.h @@ -0,0 +1,12 @@ + +#ifdef WINRT_IMPL_NUMERICS +#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_ +#undef _WINDOWS_NUMERICS_END_NAMESPACE_ +#endif diff --git a/strings/base_security.h b/strings/base_security.h index a912487e4..50ca356f8 100644 --- a/strings/base_security.h +++ b/strings/base_security.h @@ -69,31 +69,32 @@ WINRT_EXPORT namespace winrt check_bool(WINRT_IMPL_SetThreadToken(nullptr, get())); } - auto operator()() const - { - struct guard - { - guard(access_token&& previous) noexcept : m_previous(std::move(previous)) - { - } - - ~guard() - { - m_previous.revert(); - } - - guard(guard const&) - { - // A Visual C++ compiler bug (550631) requires the copy constructor even though it is never called. - WINRT_ASSERT(false); - } - - private: + auto operator()() const; + }; +} - access_token const m_previous; - }; +namespace winrt::impl +{ + struct access_token_guard + { + access_token_guard(access_token&& previous) noexcept : m_previous(std::move(previous)) + { + } - return guard(impersonate()); + ~access_token_guard() + { + m_previous.revert(); } + + private: + access_token const m_previous; }; } + +WINRT_EXPORT namespace winrt +{ + inline auto access_token::operator()() const + { + return impl::access_token_guard(impersonate()); + } +} diff --git a/strings/base_shared.h b/strings/base_shared.h new file mode 100644 index 000000000..15c39ae8b --- /dev/null +++ b/strings/base_shared.h @@ -0,0 +1,57 @@ +#pragma once + +#if __has_include() +#include +#endif + +#ifdef _DEBUG + +#include + +#define WINRT_ASSERT _ASSERTE +#define WINRT_VERIFY WINRT_ASSERT +#define WINRT_VERIFY_(result, expression) WINRT_ASSERT(result == expression) + +#else + +#define WINRT_ASSERT(expression) ((void)0) +#define WINRT_VERIFY(expression) (void)(expression) +#define WINRT_VERIFY_(result, expression) (void)(expression) + +#endif + +#define WINRT_IMPL_SHIM(...) (*(abi_t<__VA_ARGS__>**)&static_cast<__VA_ARGS__ const&>(static_cast(*this))) + +#if defined(__cpp_lib_coroutine) +#define WINRT_IMPL_COROUTINES +#endif + +#if defined(_MSC_VER) +#define WINRT_IMPL_EMPTY_BASES __declspec(empty_bases) +#else +#define WINRT_IMPL_EMPTY_BASES +#endif + +#if defined(_MSC_VER) +#define WINRT_IMPL_NOVTABLE __declspec(novtable) +#else +#define WINRT_IMPL_NOVTABLE +#endif + +#if defined(__clang__) && defined(__has_attribute) +#if __has_attribute(__lto_visibility_public__) +#define WINRT_IMPL_PUBLIC __attribute__((lto_visibility_public)) +#else +#define WINRT_IMPL_PUBLIC +#endif // __has_attribute(__lto_visibility_public__) +#else +#define WINRT_IMPL_PUBLIC +#endif + +#define WINRT_IMPL_ABI_DECL WINRT_IMPL_NOVTABLE WINRT_IMPL_PUBLIC + +#ifdef WINRT_IMPL_MODULES +#define WINRT_EXPORT export +#else +#define WINRT_EXPORT +#endif diff --git a/strings/base_includes.h b/strings/base_std_includes.h similarity index 84% rename from strings/base_includes.h rename to strings/base_std_includes.h index d6808792b..19a69b9fc 100644 --- a/strings/base_includes.h +++ b/strings/base_std_includes.h @@ -1,5 +1,4 @@ -#include #include #include #include @@ -27,15 +26,6 @@ #include #include -#if __has_include() -#include -#endif - -#if __has_include() -#define WINRT_IMPL_NUMERICS -#include -#endif - #ifndef WINRT_LEAN_AND_MEAN #include #endif diff --git a/strings/base_version.h b/strings/base_version.h index 4a6b68e66..c1362b67d 100644 --- a/strings/base_version.h +++ b/strings/base_version.h @@ -14,6 +14,8 @@ char const * const WINRT_version = "C++/WinRT version:" CPPWINRT_VERSION; #pragma detect_mismatch("C++/WinRT version", CPPWINRT_VERSION) #endif +#include + WINRT_EXPORT namespace winrt { template diff --git a/strings/base_xaml_typename.h b/strings/base_xaml_typename.h index b7b3a954a..5fa169618 100644 --- a/strings/base_xaml_typename.h +++ b/strings/base_xaml_typename.h @@ -1,6 +1,8 @@ namespace winrt::impl { + using namespace std::literals; + template struct xaml_typename_name { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e5ca6e0fb..f128934c9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -106,6 +106,7 @@ set(SKIP_LARGE_PCH FALSE CACHE BOOL "Skip building large precompiled headers.") add_subdirectory(test) add_subdirectory(test_nocoro) +add_subdirectory(test_cppmodules) add_subdirectory(test_cpp20) add_subdirectory(test_cpp20_no_sourcelocation) diff --git a/test/test_cpp20/array_span.cpp b/test/test_cpp20/array_span.cpp index 8cebc4a1e..2d6d3cc4f 100644 --- a/test/test_cpp20/array_span.cpp +++ b/test/test_cpp20/array_span.cpp @@ -10,7 +10,7 @@ using namespace Windows::Data::Json; // // This is a helper to create a data reader for use in testing arrays. // -static IAsyncOperation CreateDataReader(std::initializer_list /*values*/) +static IAsyncOperation CreateDataReader(std::initializer_list /*values*/) { InMemoryRandomAccessStream stream; DataWriter writer(stream); @@ -32,8 +32,8 @@ TEST_CASE("array,DataReader,std::span") { auto reader = CreateDataReader({ 1, 2, 3 }).get(); - std::array a{}; - std::span sp(a); + std::array a{}; + std::span sp(a); reader.ReadBytes(sp); // FillArray pattern REQUIRE(a.size() == 3); @@ -49,7 +49,7 @@ TEST_CASE("array,DataReader,std::span,direct") { auto reader = CreateDataReader({ 1, 2, 3 }).get(); - std::array a{}; + std::array a{}; reader.ReadBytes(a); // FillArray pattern REQUIRE(a.size() == 3); diff --git a/test/test_cppmodules/CMakeLists.txt b/test/test_cppmodules/CMakeLists.txt new file mode 100644 index 000000000..e82b7307e --- /dev/null +++ b/test/test_cppmodules/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB TEST_SRCS + LIST_DIRECTORIES false + CONFIGURE_DEPENDS + *.cpp +) +list(FILTER TEST_SRCS EXCLUDE REGEX "/(main|pch)\\.cpp") + + +list(APPEND BROKEN_TESTS + # No broken tests. +) + +# Exclude broken tests +foreach(TEST_SRCS_EXCLUDE_ITEM IN LISTS BROKEN_TESTS) + list(FILTER TEST_SRCS EXCLUDE REGEX "/${TEST_SRCS_EXCLUDE_ITEM}\\.cpp") +endforeach() + +add_executable(test_cppmodules main.cpp ${TEST_SRCS}) + +add_dependencies(test_cppmodules build-cppwinrt-projection) + +add_test( + NAME test_cppmodules + COMMAND "$" ${TEST_COLOR_ARG} +) diff --git a/test/test_cppmodules/async.cpp b/test/test_cppmodules/async.cpp new file mode 100644 index 000000000..757e3218d --- /dev/null +++ b/test/test_cppmodules/async.cpp @@ -0,0 +1,91 @@ +#include +#include "catch.hpp" + +import std; +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_cppmodules/main.cpp b/test/test_cppmodules/main.cpp new file mode 100644 index 000000000..b3e937de6 --- /dev/null +++ b/test/test_cppmodules/main.cpp @@ -0,0 +1,27 @@ +#include +#define CATCH_CONFIG_RUNNER + +// Force reportFatal to be available on mingw-w64 +#define CATCH_CONFIG_WINDOWS_SEH + +#include "catch.hpp" + +import winrt; + +using namespace winrt; + +int main(int const argc, char** argv) +{ + init_apartment(); + std::set_terminate([] { reportFatal("Abnormal termination"); ExitProcess(1); }); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + (void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + (void)_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + return Catch::Session().run(argc, argv); +} + +CATCH_TRANSLATE_EXCEPTION(hresult_error const& e) +{ + return to_string(e.message()); +} diff --git a/test/test_cppmodules/test_cppmodules.vcxproj b/test/test_cppmodules/test_cppmodules.vcxproj new file mode 100644 index 000000000..0edb43149 --- /dev/null +++ b/test/test_cppmodules/test_cppmodules.vcxproj @@ -0,0 +1,252 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {4A135922-BE13-4FEC-8280-69028D1CB28D} + unittests + test_cppmodules + 20 + + + + Application + true + + + Application + true + + + Application + false + true + + + Application + false + true + + + Application + true + + + Application + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + true + %(AdditionalOptions) -O3 -flto -fwhole-program-vtables + true + NotUsing + + + Console + true + true + + + + + + + + + + + + + Disabled + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + Level4 + true + %(AdditionalOptions) -flto -fwhole-program-vtables + true + NotUsing + + + Console + + + + + + + + + + + + + Disabled + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + Level4 + true + %(AdditionalOptions) -flto -fwhole-program-vtables + true + NotUsing + + + Console + + + + + + + + + + + + + Disabled + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + Level4 + true + %(AdditionalOptions) -flto -fwhole-program-vtables + true + NotUsing + + + Console + + + + + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + true + %(AdditionalOptions) -O3 -flto -fwhole-program-vtables + true + NotUsing + + + Console + true + true + + + + + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..\ + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + true + %(AdditionalOptions) -O3 -flto -fwhole-program-vtables + true + NotUsing + + + Console + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file