Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ if (BUILD_TESTING)
)
FetchContent_MakeAvailable(fmt)

if(MSVC)
set(PROXY_WARNING_FLAGS /W4)
set(PROXY_STRICT_WARNING_FLAGS ${PROXY_WARNING_FLAGS} /WX)
else()
set(PROXY_WARNING_FLAGS -Wall -Wextra -Wpedantic $<$<CXX_COMPILER_ID:Clang>:-Wno-c++2b-extensions>)
set(PROXY_STRICT_WARNING_FLAGS ${PROXY_WARNING_FLAGS} -Werror)
endif()

add_subdirectory(tests)
add_subdirectory(benchmarks)
add_subdirectory(docs)
Expand Down
7 changes: 1 addition & 6 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,4 @@ add_executable(msft_proxy_benchmarks
)
target_include_directories(msft_proxy_benchmarks PRIVATE .)
target_link_libraries(msft_proxy_benchmarks PRIVATE msft_proxy4::proxy benchmark::benchmark benchmark::benchmark_main)

if (MSVC)
target_compile_options(msft_proxy_benchmarks PRIVATE /W4)
else()
target_compile_options(msft_proxy_benchmarks PRIVATE -Wall -Wextra -Wpedantic $<$<CXX_COMPILER_ID:Clang>:-Wno-c++2b-extensions>)
endif()
target_compile_options(msft_proxy_benchmarks PRIVATE ${PROXY_WARNING_FLAGS})
6 changes: 1 addition & 5 deletions docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ foreach(SOURCE ${EXAMPLE_SOURCES})
get_filename_component(EXECUTABLE_NAME ${SOURCE} NAME_WE)
add_executable(${EXECUTABLE_NAME} ${SOURCE})
target_link_libraries(${EXECUTABLE_NAME} PRIVATE msft_proxy4::proxy)
if (MSVC)
target_compile_options(${EXECUTABLE_NAME} PRIVATE /W4)
else()
target_compile_options(${EXECUTABLE_NAME} PRIVATE -Wall -Wextra -Wpedantic $<$<CXX_COMPILER_ID:Clang>:-Wno-c++2b-extensions>)
endif()
target_compile_options(${EXECUTABLE_NAME} PRIVATE ${PROXY_WARNING_FLAGS})
endforeach()

target_link_libraries(example_spec_skills_fmt_format PRIVATE fmt::fmt)
156 changes: 82 additions & 74 deletions include/proxy/v4/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include <version>

#if __STDC_HOSTED__
#include <atomic>
#if __has_include(<format>)

// LLVM libc++ 17 has usable <format> despite lacking __cpp_lib_format until 19.
#if __cpp_lib_format >= 201907L || _LIBCPP_VERSION >= 170000
#include <format>
#endif // __has_include(<format>)
#if __cpp_lib_format >= 201907L || \
(defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 170000)
#define PRO4D_HAS_FORMAT
#endif // __cpp_lib_format || _LIBCPP_VERSION >= 170000
#endif // __STDC_HOSTED__
Expand Down Expand Up @@ -714,9 +714,6 @@ struct facade_conv_traits_impl {
template <class P>
static constexpr bool conv_applicable_ptr =
(conv_traits<Cs, F>::template applicable_ptr<P> && ...);
template <bool IsDirect, class D, class O>
static constexpr bool is_invocable =
std::is_base_of_v<invocation_meta<F, IsDirect, D, O>, conv_meta>;
};
template <class F, class... Rs>
struct facade_refl_traits_impl {
Expand Down Expand Up @@ -2148,6 +2145,11 @@ class bad_proxy_cast : public std::bad_cast {

namespace details {

template <template <class...> class TT, class... Ctx>
struct enabled_t {};
template <class T, template <class...> class TT, class... Ctx>
concept enabled_for = std::is_base_of_v<enabled_t<TT, Ctx...>, T>;

struct view_conversion_dispatch : cast_dispatch_base<false, true> {
template <class T>
PRO4D_STATIC_CALL(auto, T& value) noexcept
Expand Down Expand Up @@ -2179,47 +2181,74 @@ struct weak_conversion_dispatch : cast_dispatch_base<false, true> {
template <class F>
using weak_conversion_overload = weak_proxy<F>() const noexcept;

template <template <class...> class Formatter,
template <class...> class StringView,
template <class...> class ParseContext,
template <class...> class FormatContext>
struct format_traits {
template <class CharT>
using overload = typename FormatContext<CharT>::iterator(
StringView<CharT> spec, FormatContext<CharT>& fc) const;

struct dispatch {
template <class T, class CharT>
PRO4D_STATIC_CALL(auto, const T& self, StringView<CharT> spec,
FormatContext<CharT>& fc)
requires(std::is_default_constructible_v<Formatter<T, CharT>>)
{
Formatter<T, CharT> impl;
{
ParseContext<CharT> pc{spec};
impl.parse(pc);
}
return impl.format(self, fc);
}

template <class P, class D, class... Os>
struct PRO4D_ENFORCE_EBO accessor : accessor<P, D, Os>... {};
template <class P, class D>
struct accessor<P, D, overload<char>> : enabled_t<Formatter, char> {};
template <class P, class D>
struct accessor<P, D, overload<wchar_t>> : enabled_t<Formatter, wchar_t> {};
};

template <class CharT>
struct formatter {
constexpr auto parse(ParseContext<CharT>& pc) {
for (auto it = pc.begin(); it != pc.end(); ++it) {
if (*it == '}') {
spec_ = StringView<CharT>{pc.begin(), it + 1};
return it;
}
}
return pc.end();
}

template <class P, class CompatibleFormatContext>
auto format(const P& p, CompatibleFormatContext& fc) const ->
typename CompatibleFormatContext::iterator {
return invoke<dispatch, overload<CharT>>(p, spec_, fc);
}

private:
StringView<CharT> spec_;
};
};

#ifdef PRO4D_HAS_FORMAT
template <class CharT>
struct format_overload_traits;
struct std_format_context_traits;
template <>
struct format_overload_traits<char>
: std::type_identity<std::format_context::iterator(
std::string_view spec, std::format_context& fc) const> {};
struct std_format_context_traits<char>
: std::type_identity<std::format_context> {};
template <>
struct format_overload_traits<wchar_t>
: std::type_identity<std::wformat_context::iterator(
std::wstring_view spec, std::wformat_context& fc) const> {};
struct std_format_context_traits<wchar_t>
: std::type_identity<std::wformat_context> {};
template <class CharT>
using format_overload_t = typename format_overload_traits<CharT>::type;

struct format_dispatch {
// Note: This function requires std::formatter<T, CharT> to be well-formed.
// However, the standard did not provide such facility before C++23. In the
// "required" clause of this function, std::formattable (C++23) is preferred
// when available. Otherwise, when building with C++20, we simply check
// whether std::formatter<T, CharT> is a disabled specialization of
// std::formatter by std::is_default_constructible_v as per
// [format.formatter.spec].
template <class T, class CharT, class OutIt>
PRO4D_STATIC_CALL(OutIt, const T& self, std::basic_string_view<CharT> spec,
std::basic_format_context<OutIt, CharT>& fc)
requires(
#if __cpp_lib_format_ranges >= 202207L
std::formattable<T, CharT>
#else
std::is_default_constructible_v<std::formatter<T, CharT>>
#endif // __cpp_lib_format_ranges >= 202207L
)
{
std::formatter<T, CharT> impl;
{
std::basic_format_parse_context<CharT> pc{spec};
impl.parse(pc);
}
return impl.format(self, fc);
}
};
using std_format_context = typename std_format_context_traits<CharT>::type;
struct std_format_traits
: format_traits<std::formatter, std::basic_string_view,
std::basic_format_parse_context, std_format_context> {};
#endif // PRO4D_HAS_FORMAT

#if __cpp_rtti >= 199711L
Expand Down Expand Up @@ -2329,14 +2358,14 @@ namespace skills {

#ifdef PRO4D_HAS_FORMAT
template <class FB>
using format =
typename FB::template add_convention<details::format_dispatch,
details::format_overload_t<char>>;
using format = typename FB::template add_convention<
details::std_format_traits::dispatch,
details::std_format_traits::overload<char>>;

template <class FB>
using wformat =
typename FB::template add_convention<details::format_dispatch,
details::format_overload_t<wchar_t>>;
using wformat = typename FB::template add_convention<
details::std_format_traits::dispatch,
details::std_format_traits::overload<wchar_t>>;
#endif // PRO4D_HAS_FORMAT

#if __cpp_rtti >= 199711L
Expand Down Expand Up @@ -2676,31 +2705,10 @@ struct weak_dispatch : D {
#ifdef PRO4D_HAS_FORMAT
namespace std {

template <pro::v4::facade F, class CharT>
requires(pro::v4::details::facade_traits<F>::template is_invocable<
false, pro::v4::details::format_dispatch,
pro::v4::details::format_overload_t<CharT>>)
struct formatter<pro::v4::proxy_indirect_accessor<F>, CharT> {
constexpr auto parse(basic_format_parse_context<CharT>& pc) {
for (auto it = pc.begin(); it != pc.end(); ++it) {
if (*it == '}') {
spec_ = basic_string_view<CharT>{pc.begin(), it + 1};
return it;
}
}
return pc.end();
}

template <class OutIt>
OutIt format(const pro::v4::proxy_indirect_accessor<F>& p,
basic_format_context<OutIt, CharT>& fc) const {
return invoke<pro::v4::details::format_dispatch,
pro::v4::details::format_overload_t<CharT>>(p, spec_, fc);
}

private:
basic_string_view<CharT> spec_;
};
template <class T, class CharT>
requires(pro::v4::details::enabled_for<T, std::formatter, CharT>)
struct formatter<T, CharT>
: pro::v4::details::std_format_traits::formatter<CharT> {};

} // namespace std
#endif // PRO4D_HAS_FORMAT
Expand Down
59 changes: 12 additions & 47 deletions include/proxy/v4/proxy_fmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,69 +28,34 @@ using fmt_buffered_context = fmt::buffered_context<CharT>;
using fmt_buffered_context = fmt::buffer_context<CharT>;
#endif // FMT_VERSION

template <class CharT>
using fmt_format_overload_t = fmt_buffered_context<CharT>::iterator(
std::basic_string_view<CharT> spec, fmt_buffered_context<CharT>& fc) const;

struct fmt_format_dispatch {
template <class T, class CharT, class FormatContext>
PRO4D_STATIC_CALL(auto, const T& self, std::basic_string_view<CharT> spec,
FormatContext& fc)
requires(std::is_default_constructible_v<fmt::formatter<T, CharT>>)
{
fmt::formatter<T, CharT> impl;
{
fmt::basic_format_parse_context<CharT> pc{spec};
impl.parse(pc);
}
return impl.format(self, fc);
}
};
struct fmt_format_traits
: format_traits<fmt::formatter, std::basic_string_view,
fmt::basic_format_parse_context, fmt_buffered_context> {};

} // namespace details

namespace skills {

template <class FB>
using fmt_format =
typename FB::template add_convention<details::fmt_format_dispatch,
details::fmt_format_overload_t<char>>;
using fmt_format = typename FB::template add_convention<
details::fmt_format_traits::dispatch,
details::fmt_format_traits::overload<char>>;

template <class FB>
using fmt_wformat = typename FB::template add_convention<
details::fmt_format_dispatch, details::fmt_format_overload_t<wchar_t>>;
details::fmt_format_traits::dispatch,
details::fmt_format_traits::overload<wchar_t>>;

} // namespace skills

} // namespace pro::inline v4

namespace fmt {

template <pro::v4::facade F, class CharT>
requires(pro::v4::details::facade_traits<F>::template is_invocable<
false, pro::v4::details::fmt_format_dispatch,
pro::v4::details::fmt_format_overload_t<CharT>>)
struct formatter<pro::v4::proxy_indirect_accessor<F>, CharT> {
constexpr auto parse(basic_format_parse_context<CharT>& pc) {
for (auto it = pc.begin(); it != pc.end(); ++it) {
if (*it == '}') {
spec_ = std::basic_string_view<CharT>{pc.begin(), it + 1};
return it;
}
}
return pc.end();
}

template <class FormatContext>
auto format(const pro::v4::proxy_indirect_accessor<F>& p,
FormatContext& fc) const -> typename FormatContext::iterator {
return invoke<pro::v4::details::fmt_format_dispatch,
pro::v4::details::fmt_format_overload_t<CharT>>(p, spec_, fc);
}

private:
std::basic_string_view<CharT> spec_;
};
template <class T, class CharT>
requires(pro::v4::details::enabled_for<T, fmt::formatter, CharT>)
struct formatter<T, CharT>
: pro::v4::details::fmt_format_traits::formatter<CharT> {};

} // namespace fmt

Expand Down
11 changes: 3 additions & 8 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_executable(msft_proxy_tests
proxy_creation_tests.cpp
proxy_details_tests.cpp
proxy_dispatch_tests.cpp
proxy_fmt_format_no_wchar_tests.cpp
proxy_fmt_format_tests.cpp
proxy_format_tests.cpp
proxy_integration_tests.cpp
Expand All @@ -27,18 +28,12 @@ add_executable(msft_proxy_tests
)
target_include_directories(msft_proxy_tests PRIVATE .)
target_link_libraries(msft_proxy_tests PRIVATE msft_proxy4::proxy gtest_main fmt::fmt)

if(MSVC)
target_compile_options(msft_proxy_tests PRIVATE /W4 /WX)
else()
target_compile_options(msft_proxy_tests PRIVATE -Wall -Wextra -Wpedantic -Werror $<$<CXX_COMPILER_ID:Clang>:-Wno-c++2b-extensions>)
endif()

target_compile_options(msft_proxy_tests PRIVATE ${PROXY_STRICT_WARNING_FLAGS})
gtest_discover_tests(msft_proxy_tests)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_executable(msft_proxy_freestanding_tests freestanding/proxy_freestanding_tests.cpp)
target_compile_options(msft_proxy_freestanding_tests PRIVATE -ffreestanding -fno-exceptions -fno-rtti -Wall -Wextra -Wpedantic -Werror $<$<CXX_COMPILER_ID:Clang>:-Wno-c++2b-extensions>)
target_compile_options(msft_proxy_freestanding_tests PRIVATE -ffreestanding -fno-exceptions -fno-rtti ${PROXY_STRICT_WARNING_FLAGS})
target_link_options(msft_proxy_freestanding_tests PRIVATE -nodefaultlibs -lc)
target_link_libraries(msft_proxy_freestanding_tests PRIVATE msft_proxy4::proxy)
add_test(NAME ProxyFreestandingTests COMMAND msft_proxy_freestanding_tests)
Expand Down
35 changes: 35 additions & 0 deletions tests/proxy_fmt_format_no_wchar_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2022-2026 Microsoft Corporation.
// Copyright (c) 2026-Present Next Gen C++ Foundation.
// Licensed under the MIT License.

#include <gtest/gtest.h>

#include <fmt/format.h> // <fmt/xchar.h> is intentionally not included in this TU
#include <proxy/proxy.h>
#include <proxy/proxy_fmt.h>

namespace proxy_fmt_format_no_wchar_tests_details {

struct NonFormattable : pro::facade_builder::build {};

static_assert(
!std::is_default_constructible_v<
fmt::formatter<pro::proxy_indirect_accessor<NonFormattable>, char>>);

struct Formattable : pro::facade_builder //
::add_skill<pro::skills::fmt_format> //
::build {};

static_assert(std::is_default_constructible_v<
fmt::formatter<pro::proxy_indirect_accessor<Formattable>, char>>);

} // namespace proxy_fmt_format_no_wchar_tests_details

namespace details = proxy_fmt_format_no_wchar_tests_details;

TEST(ProxyFmtFormatNoWcharTests, TestFormat) {
int v = 123;
pro::proxy<details::Formattable> p = &v;
ASSERT_EQ(fmt::format("{}", *p), "123");
ASSERT_EQ(fmt::format("{:*<6}", *p), "123***");
}
Loading
Loading