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