diff --git a/proxy.h b/proxy.h index 31692a6..a88ede7 100644 --- a/proxy.h +++ b/proxy.h @@ -17,6 +17,10 @@ #include #include +#if __STDC_HOSTED__ +#include +#endif // __STDC_HOSTED__ + #ifdef __cpp_rtti #include #include @@ -1575,6 +1579,33 @@ struct sign { template sign(const char (&str)[N]) -> sign; +#if __STDC_HOSTED__ +template struct format_overload_traits; +template <> +struct format_overload_traits + : std::type_identity {}; +template <> +struct format_overload_traits + : std::type_identity {}; +template +using format_overload_t = typename format_overload_traits::type; + +struct format_dispatch { + template + OutIt operator()(const T& self, std::basic_string_view spec, + std::basic_format_context& fc) { + std::formatter impl; + { + std::basic_format_parse_context pc{spec}; + impl.parse(pc); + } + return impl.format(self, fc); + } +}; +#endif // __STDC_HOSTED__ + #ifdef __cpp_rtti struct proxy_cast_context { const std::type_info* type_ptr; @@ -1737,6 +1768,12 @@ struct basic_facade_builder { template using support_destruction = basic_facade_builder< Cs, Rs, details::make_destructible(C, CL)>; +#if __STDC_HOSTED__ + using support_format = add_convention< + details::format_dispatch, details::format_overload_t>; + using support_wformat = add_convention< + details::format_dispatch, details::format_overload_t>; +#endif // __STDC_HOSTED__ #ifdef __cpp_rtti using support_indirect_rtti = basic_facade_builder< details::add_conv_t +struct formatter, CharT> { + constexpr auto parse(basic_format_parse_context& pc) { + auto it = pc.begin(); + while (it < pc.end() && *it != '}') { ++it; } + spec_ = basic_string_view{pc.begin(), it + 1}; + return it; + } + + template + OutIt format(const pro::proxy_indirect_accessor& ia, + basic_format_context& fc) const { + auto& p = pro::access_proxy(ia); + if (!p.has_value()) { ___PRO_THROW(format_error{"null proxy"}); } + return pro::proxy_invoke>(p, spec_, fc); + } + + private: + basic_string_view spec_; +}; + +} // namespace std +#endif // __STDC_HOSTED__ + #undef ___PRO_THROW #undef ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 49a6987..d5070c9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,7 @@ FetchContent_MakeAvailable(googletest) add_executable(msft_proxy_tests proxy_creation_tests.cpp proxy_dispatch_tests.cpp + proxy_format_tests.cpp proxy_integration_tests.cpp proxy_invocation_tests.cpp proxy_lifetime_tests.cpp diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp new file mode 100644 index 0000000..fe108eb --- /dev/null +++ b/tests/proxy_format_tests.cpp @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include "proxy.h" + +namespace proxy_format_tests_details { + +struct TestFacade : pro::facade_builder + ::support_format + ::support_wformat + ::build {}; + +} // namespace proxy_format_tests_details + +namespace details = proxy_format_tests_details; + +TEST(ProxyFormatTests, TestFormat_Null) { + pro::proxy p; + bool exception_thrown = false; + try { + std::ignore = std::format("{}", *p); + } catch (const std::format_error&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyFormatTests, TestFormat_Value) { + int v = 123; + pro::proxy p = &v; + ASSERT_EQ(std::format("{}", *p), "123"); + ASSERT_EQ(std::format("{:*<6}", *p), "123***"); +} + +TEST(ProxyFormatTests, TestWformat_Null) { + pro::proxy p; + bool exception_thrown = false; + try { + std::ignore = std::format(L"{}", *p); + } catch (const std::format_error&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyFormatTests, TestWformat_Value) { + int v = 123; + pro::proxy p = &v; + ASSERT_EQ(std::format(L"{}", *p), L"123"); + ASSERT_EQ(std::format(L"{:*<6}", *p), L"123***"); +}