Skip to content

Commit

Permalink
Improve compile-time formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyVH committed Aug 19, 2024
1 parent fb07b37 commit d883061
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 17 deletions.
2 changes: 2 additions & 0 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
// Check that all types are formattable.
[[maybe_unused]] auto formattable_check = fmt::make_format_args(args...);
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
Expand Down
42 changes: 28 additions & 14 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,13 @@ template <typename Char, size_t N> struct fixed_string {
}
Char data[N] = {};
};
#endif

template <typename Char, size_t N>
constexpr auto format_as(fixed_string<Char, N> const& value)
-> basic_string_view<Char> {
return {value.data, N - 1};
}
#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS

// Converts a compile-time string to basic_string_view.
template <typename Char, size_t N>
Expand Down Expand Up @@ -3624,11 +3630,13 @@ FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<

template <typename Char, typename OutputIt, typename T,
typename Context = basic_format_context<OutputIt, Char>>
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
-> enable_if_t<mapped_type_constant<T, Context>::value ==
type::custom_type &&
!std::is_fundamental<T>::value,
OutputIt> {
FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
mapped_type_constant<T, Context>::value == type::custom_type &&
!std::is_fundamental<T>::value &&
!std::is_same<
unformattable,
remove_cvref_t<decltype(arg_mapper<Context>().map(value))>>::value,
OutputIt> {
auto formatter = typename Context::template formatter_type<T>();
auto parse_ctx = typename Context::parse_context_type({});
formatter.parse(parse_ctx);
Expand Down Expand Up @@ -3879,19 +3887,21 @@ template <typename T, typename Char>
struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
: formatter<detail::format_as_t<T>, Char> {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto&& val = format_as(value); // Make an lvalue reference for format.
return formatter<detail::format_as_t<T>, Char>::format(val, ctx);
}
};

#define FMT_FORMAT_AS(Type, Base) \
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
auto format(Type value, FormatContext& ctx) const -> decltype(ctx.out()) { \
return formatter<Base, Char>::format(value, ctx); \
} \
#define FMT_FORMAT_AS(Type, Base) \
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \
-> decltype(ctx.out()) { \
return formatter<Base, Char>::format(value, ctx); \
} \
}

FMT_FORMAT_AS(signed char, int);
Expand Down Expand Up @@ -4235,6 +4245,10 @@ inline namespace literals {
* fmt::print("The answer is {answer}.", "answer"_a=42);
*/
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <detail_exported::fixed_string Str> constexpr auto operator""_fs() {
return Str;
}

template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
Expand Down
8 changes: 5 additions & 3 deletions test/compile-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,9 @@ TEST(compile_test, compile_format_string_literal) {
// (compiler file
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
// line 8635)
#if FMT_USE_CONSTEVAL && \
(!FMT_MSC_VERSION || \
(FMT_MSC_VERSION >= 1928 && FMT_MSC_VERSION < 1930)) && \
// Can't support MSVC 19.28 & 19.29, because C++20 constexpr is required for
// fmt::v11::detail::buffer<T>::append.
#if FMT_USE_CONSTEVAL && (!FMT_MSC_VERSION || (FMT_MSC_VERSION >= 1939)) && \
defined(__cpp_lib_is_constant_evaluated)
template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
Expand All @@ -302,6 +302,7 @@ template <size_t max_string_length, typename Char = char> struct test_string {
template <size_t max_string_length, typename Char = char, typename... Args>
consteval auto test_format(auto format, const Args&... args) {
test_string<max_string_length, Char> string{};
fmt::detail::ignore_unused(fmt::formatted_size(format, args...));
fmt::format_to(string.buffer, format, args...);
return string;
}
Expand Down Expand Up @@ -334,6 +335,7 @@ TEST(compile_time_formatting_test, integer) {
EXPECT_EQ("0X4A", test_format<5>(FMT_COMPILE("{:#X}"), 0x4a));

EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42l));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ll));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ull));

Expand Down

0 comments on commit d883061

Please sign in to comment.