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/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index e24cfd302..60db08ad8 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -145,6 +145,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 +335,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,7 +2845,12 @@ 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) { auto format = R"( struct % { @@ -2829,7 +2867,7 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable if (types.empty()) { - return false; + return {}; } struct complex_struct @@ -2895,7 +2933,7 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable } } - bool promote = false; + write_structs_result result; auto cpp_namespace = w.write_temp("@", w.type_namespace); for (auto&& type : structs) @@ -2921,14 +2959,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..0825ea849 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -65,10 +65,12 @@ + + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 96129ab20..dfb12f27b 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -106,6 +106,9 @@ strings + + strings + strings @@ -187,6 +190,9 @@ strings + + strings + diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index ed9386b4e..50d2050dd 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -11,6 +11,7 @@ namespace cppwinrt auto wrap_file_guard = wrap_open_file_guard(w, "BASE"); w.write(strings::base_includes); + w.write_root_include("shared"); w.write(strings::base_macros); w.write(strings::base_types); w.write(strings::base_extern); @@ -46,6 +47,26 @@ namespace cppwinrt 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.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(strings::base_includes); + ixx.write_root_include("shared"); + ixx.write(strings::base_module); + 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 +86,48 @@ 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_root_include("shared"); + w.write("#define WINRT_EXPORT export\n"); + w.write("#define WINRT_IMPL_MODULES\n"); + 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 +182,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 +202,17 @@ namespace cppwinrt write_preamble(w); write_open_file_guard(w, ns, '1'); + w.write("#ifndef WINRT_IMPL_MODULES\n"); for (auto&& depends : w.depends) { w.write_depends(depends.first, '0'); } w.write_depends(w.type_namespace, '0'); + w.write("#endif\n"); 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 +220,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 +234,19 @@ 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'; + w.write("#ifndef WINRT_IMPL_MODULES\n"); for (auto&& depends : w.depends) { w.write_depends(depends.first, impl); } w.write_depends(w.type_namespace, '1'); + w.write("#endif\n"); 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,6 +292,7 @@ namespace cppwinrt w.swap(); write_preamble(w); write_open_file_guard(w, ns); + w.write("#ifndef WINRT_IMPL_MODULES\n"); write_version_assert(w); write_parent_depends(w, c, ns); @@ -228,7 +302,10 @@ namespace cppwinrt } w.write_depends(w.type_namespace, '2'); + w.write("#endif\n"); 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 0fa3e5f8d..a80602222 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..090544429 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -11,6 +11,7 @@ #include #include #include "base_includes.h" +#include "base_shared.h" #include "base_macros.h" #include "base_types.h" #include "base_extern.h" diff --git a/strings/base_macros.h b/strings/base_macros.h index 3dc01fa2d..47b120fc2 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,10 +7,6 @@ #pragma warning(disable : 4268) #endif -#if defined(__cpp_lib_coroutine) -#define WINRT_IMPL_COROUTINES -#endif - #ifndef WINRT_EXPORT #define WINRT_EXPORT #endif @@ -35,6 +15,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_ @@ -49,30 +31,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_module.h b/strings/base_module.h new file mode 100644 index 000000000..41216db01 --- /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 + +#define WINRT_EXPORT export + +export module winrt:base; diff --git a/strings/base_security.h b/strings/base_security.h index 8160f11ad..3b3507f75 100644 --- a/strings/base_security.h +++ b/strings/base_security.h @@ -69,7 +69,7 @@ WINRT_EXPORT namespace winrt check_bool(WINRT_IMPL_SetThreadToken(nullptr, get())); } - auto operator()() const + /*TODO: figure out why compiler ICEs auto operator()() const { struct guard { @@ -94,6 +94,6 @@ WINRT_EXPORT namespace winrt }; return guard(impersonate()); - } + }*/ }; } diff --git a/strings/base_shared.h b/strings/base_shared.h new file mode 100644 index 000000000..7e144c4a6 --- /dev/null +++ b/strings/base_shared.h @@ -0,0 +1,47 @@ +#include + +#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 diff --git a/strings/base_xaml_typename.h b/strings/base_xaml_typename.h index 4a782fc72..215f27074 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/test_cpp20/async.cpp b/test/test_cpp20/async.cpp new file mode 100644 index 000000000..41b3e4cad --- /dev/null +++ b/test/test_cpp20/async.cpp @@ -0,0 +1,92 @@ +#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 + // + // TODO: make a new project for this + + 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 3ae64e25e..37c42e99f 100644 --- a/test/test_cpp20/await_completed.cpp +++ b/test/test_cpp20/await_completed.cpp @@ -1,4 +1,9 @@ -#include "pch.h" +#include +#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 d16193328..977dad9b7 100644 --- a/test/test_cpp20/format.cpp +++ b/test/test_cpp20/format.cpp @@ -1,8 +1,10 @@ -#include "pch.h" - #ifdef __cpp_lib_format #include +#include "catch.hpp" + +import winrt; + struct stringable : winrt::implements { winrt::hstring ToString() 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 30687e00c..b3e937de6 100644 --- a/test/test_cpp20/main.cpp +++ b/test/test_cpp20/main.cpp @@ -5,7 +5,8 @@ #define CATCH_CONFIG_WINDOWS_SEH #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 c1a8f5ff3..000000000 --- a/test/test_cpp20/pch.h +++ /dev/null @@ -1,17 +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 "winrt/Windows.Storage.Streams.h" -#include -#include "catch.hpp" - -#include - -using namespace std::literals; diff --git a/test/test_cpp20/ranges.cpp b/test/test_cpp20/ranges.cpp index 9df542696..81b434c43 100644 --- a/test/test_cpp20/ranges.cpp +++ b/test/test_cpp20/ranges.cpp @@ -1,9 +1,11 @@ -#include "pch.h" - #ifdef __cpp_lib_ranges #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 4eaee0a18..4e90b64d1 100644 --- a/test/test_cpp20/test_cpp20.vcxproj +++ b/test/test_cpp20/test_cpp20.vcxproj @@ -93,6 +93,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + NotUsing Level4 true %(AdditionalOptions) -O3 -flto -fwhole-program-vtables @@ -117,6 +118,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + NotUsing Level4 true %(AdditionalOptions) -flto -fwhole-program-vtables @@ -139,6 +141,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + NotUsing Level4 true %(AdditionalOptions) -flto -fwhole-program-vtables @@ -161,6 +164,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreadedDebug + NotUsing Level4 true %(AdditionalOptions) -flto -fwhole-program-vtables @@ -185,6 +189,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + NotUsing Level4 true %(AdditionalOptions) -O3 -flto -fwhole-program-vtables @@ -211,6 +216,7 @@ $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) MultiThreaded + NotUsing Level4 true %(AdditionalOptions) -O3 -flto -fwhole-program-vtables @@ -230,21 +236,15 @@ - - - + + - - NotUsing - - - Create - +