diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index e07bdabf5..1a4f55640 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -115,6 +115,16 @@ namespace cppwinrt } } + [[nodiscard]] static finish_with wrap_ifdef(writer& w, std::string_view macro) + { + auto format = R"(#ifdef % +)"; + + 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('.'); @@ -3216,6 +3226,18 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable type); } + static void write_std_formatter(writer& w, TypeDef const& type) + { + if (implements_interface(type, "Windows.Foundation.IStringable")) + { + auto generics = type.GenericParam(); + + w.write(" template<%> struct formatter<%, wchar_t> : formatter {};\n", + bind(generics), + type); + } + } + static void write_namespace_special(writer& w, std::string_view const& namespace_name) { if (namespace_name == "Windows.Foundation") @@ -3259,6 +3281,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (namespace_name == "Windows.Foundation") { w.write(strings::base_reference_produce_1); + w.write(strings::base_stringable_format); } } } diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index d4768df66..71272d14f 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -81,6 +81,7 @@ + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 26c1fdafd..bfd56434a 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -169,6 +169,9 @@ strings + + strings + diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index 8dfd6f09d..5168d8d7f 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -197,9 +197,17 @@ namespace cppwinrt } { auto wrap_std = wrap_std_namespace(w); - auto wrap_lean = wrap_lean_and_mean(w); - w.write_each(members.interfaces); - w.write_each(members.classes); + + { + auto wrap_lean = wrap_lean_and_mean(w); + w.write_each(members.interfaces); + w.write_each(members.classes); + } + { + auto wrap_format = wrap_ifdef(w, "__cpp_lib_format"); + w.write_each(members.interfaces); + w.write_each(members.classes); + } } write_namespace_special(w, ns); diff --git a/cppwinrt/helpers.h b/cppwinrt/helpers.h index 7250bcbc8..22854c48e 100644 --- a/cppwinrt/helpers.h +++ b/cppwinrt/helpers.h @@ -498,6 +498,27 @@ namespace cppwinrt return result; } + static bool implements_interface(TypeDef const& type, std::string_view const& name) + { + for (auto&& impl : type.InterfaceImpl()) + { + const auto iface = impl.Interface(); + if (iface.type() != TypeDefOrRef::TypeSpec && type_name(iface) == name) + { + return true; + } + } + + if (auto base = get_base_class(type)) + { + return implements_interface(base, name); + } + else + { + return false; + } + } + bool has_fastabi_tearoffs(writer& w, TypeDef const& type) { for (auto&& [name, info] : get_interfaces(w, type)) diff --git a/strings/base_includes.h b/strings/base_includes.h index 7354a304c..14d992fc9 100644 --- a/strings/base_includes.h +++ b/strings/base_includes.h @@ -24,6 +24,10 @@ #include #endif +#ifdef __cpp_lib_format +#include +#endif + #ifdef __cpp_lib_coroutine #include diff --git a/strings/base_string.h b/strings/base_string.h index f50403c41..244f81e99 100644 --- a/strings/base_string.h +++ b/strings/base_string.h @@ -327,7 +327,7 @@ WINRT_EXPORT namespace winrt return rend(); } -#if __cpp_lib_starts_ends_with +#ifdef __cpp_lib_starts_ends_with bool starts_with(wchar_t const value) const noexcept { return operator std::wstring_view().starts_with(value); @@ -437,6 +437,11 @@ WINRT_EXPORT namespace winrt } } +#ifdef __cpp_lib_format +template<> +struct std::formatter : std::formatter {}; +#endif + namespace winrt::impl { template <> struct abi diff --git a/strings/base_stringable_format.h b/strings/base_stringable_format.h new file mode 100644 index 000000000..41a54dfd4 --- /dev/null +++ b/strings/base_stringable_format.h @@ -0,0 +1,12 @@ + +#ifdef __cpp_lib_format +template<> +struct std::formatter : std::formatter +{ + template + auto format(winrt::Windows::Foundation::IStringable const& obj, FormatContext& fc) + { + return std::formatter::format(obj.ToString(), fc); + } +}; +#endif diff --git a/test/test_cpp20/format.cpp b/test/test_cpp20/format.cpp new file mode 100644 index 000000000..b3119a77e --- /dev/null +++ b/test/test_cpp20/format.cpp @@ -0,0 +1,28 @@ +#include "pch.h" +#include + +struct stringable : winrt::implements +{ + winrt::hstring ToString() + { + return L"a stringable object"; + } +}; + +TEST_CASE("format") +{ + { + winrt::hstring str = L"World"; + REQUIRE(std::format(L"Hello {}", str) == L"Hello World"); + } + + { + winrt::Windows::Foundation::IStringable obj = winrt::make(); + REQUIRE(std::format(L"This is {}", obj) == L"This is a stringable object"); + } + + { + winrt::Windows::Data::Json::JsonArray jsonArray; + REQUIRE(std::format(L"The contents of the array are: {}", jsonArray) == L"The contents of the array are: []"); + } +} diff --git a/test/test_cpp20/hstring.cpp b/test/test_cpp20/hstring.cpp index 2f2033185..57d145fbb 100644 --- a/test/test_cpp20/hstring.cpp +++ b/test/test_cpp20/hstring.cpp @@ -10,4 +10,4 @@ TEST_CASE("hstring") REQUIRE(text.ends_with(L"rocks!")); REQUIRE(textView.ends_with(L"rocks!")); REQUIRE(text.ends_with(L"rocks!") == textView.ends_with(L"rocks!")); -} \ No newline at end of file +} diff --git a/test/test_cpp20/pch.h b/test/test_cpp20/pch.h index ea74230cc..bd678407c 100644 --- a/test/test_cpp20/pch.h +++ b/test/test_cpp20/pch.h @@ -4,6 +4,8 @@ #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 diff --git a/test/test_cpp20/test_cpp20.vcxproj b/test/test_cpp20/test_cpp20.vcxproj index 00035dc2e..e66cce714 100644 --- a/test/test_cpp20/test_cpp20.vcxproj +++ b/test/test_cpp20/test_cpp20.vcxproj @@ -302,6 +302,7 @@ + NotUsing