Skip to content

Commit

Permalink
fix custom types formatting at compile-time, add test
Browse files Browse the repository at this point in the history
  • Loading branch information
alexezeder authored and vitaut committed Jun 1, 2021
1 parent 8c1b22b commit 9976869
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 14 deletions.
2 changes: 1 addition & 1 deletion include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ template <typename Context> class value {
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {}

template <typename T> FMT_INLINE value(const T& val) {
template <typename T> FMT_CONSTEXPR FMT_INLINE value(const T& val) {
custom.value = &val;
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
Expand Down
9 changes: 5 additions & 4 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1935,10 +1935,11 @@ OutputIt write(OutputIt out, const T* value,
}

template <typename Char, typename OutputIt, typename T>
auto write(OutputIt out, const T& value) -> typename std::enable_if<
mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
type::custom_type,
OutputIt>::type {
FMT_CONSTEXPR auto write(OutputIt out, const T& value) ->
typename std::enable_if<
mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
type::custom_type,
OutputIt>::type {
using context_type = basic_format_context<OutputIt, Char>;
using formatter_type =
conditional_t<has_formatter<T, context_type>::value,
Expand Down
33 changes: 24 additions & 9 deletions test/compile-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,34 @@ TEST(iterator_test, truncating_back_inserter) {
EXPECT_EQ(buffer, "42");
}

TEST(compile_test, compile_fallback) {
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
// not available.
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
}

#ifdef __cpp_if_constexpr
struct test_formattable {};

FMT_BEGIN_NAMESPACE
template <> struct formatter<test_formattable> : formatter<const char*> {
char word_spec = 'f';
constexpr auto parse(format_parse_context& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
if (it != end && (*it == 'f' || *it == 'b')) word_spec = *it++;
if (it != end && *it != '}') throw format_error("invalid format");
return it;
}
template <typename FormatContext>
auto format(test_formattable, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx);
constexpr auto format(test_formattable, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<const char*>::format(word_spec == 'f' ? "foo" : "bar",
ctx);
}
};
FMT_END_NAMESPACE

TEST(compile_test, compile_fallback) {
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
// not available.
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
}

#ifdef __cpp_if_constexpr
TEST(compile_test, format_default) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
Expand Down Expand Up @@ -336,6 +346,11 @@ TEST(compile_time_formatting_test, combination) {
EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4));
}

TEST(compile_time_formatting_test, custom_type) {
EXPECT_EQ("foo", test_format<4>(FMT_COMPILE("{}"), test_formattable()));
EXPECT_EQ("bar", test_format<4>(FMT_COMPILE("{:b}"), test_formattable()));
}

TEST(compile_time_formatting_test, multibyte_fill) {
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
}
Expand Down

0 comments on commit 9976869

Please sign in to comment.