Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fmt::format_to + FMT_STRING does not support user types #1567

Closed
refnum opened this issue Feb 28, 2020 · 2 comments
Closed

fmt::format_to + FMT_STRING does not support user types #1567

refnum opened this issue Feb 28, 2020 · 2 comments

Comments

@refnum
Copy link
Contributor

refnum commented Feb 28, 2020

fmt::format_to can use FMT_STRING to validate the format string, while formatting to a fmt::memory_buffer to avoid allocating a std::string.

This works successfully for built-in types but fails to compile for user-defined types with a 'call to deleted constructor of' error (Xcode 11.3.1).

struct MyPoint
{
    int32_t x;
    int32_t y;
};

template <>
struct fmt::formatter<MyPoint>
{
    constexpr auto parse(format_parse_context& ctx)
    {
        return ctx.end();
    }

    template <typename FormatContext>
    auto format(const MyPoint& point, FormatContext& ctx)
    {
        return format_to(ctx.out(), "{}-{}", point.x, point.y);
    }

};



static void TestFunc()
{
    
    int32_t x = 111;
    int32_t y = 222;
    MyPoint thePoint{333, 444};


    // OK
    fmt::memory_buffer out1;
    fmt::format_to(out1, FMT_STRING("Hello {} and {}"), x, y);


    // Fails
    std::vector<char> out2;
    fmt::format_to(std::back_inserter(out2), FMT_STRING("Hello {}"), thePoint);


    // Fails
    fmt::memory_buffer out3;
    fmt::format_to(out3, FMT_STRING("Hello {}"), thePoint);

/*
 /tmp/fmt_2020_02_18/include/fmt/format.h:2582:12: error: call to deleted constructor of 'conditional_t<has_formatter<mapped_type, context>::value, formatter<mapped_type, char_type>, internal::fallback_formatter<MyPoint &, char_type> >' (aka 'fmt::v6::internal::fallback_formatter<MyPoint &, char, void>')
   auto f = conditional_t<has_formatter<mapped_type, context>::value,
            ^
 /tmp/fmt_2020_02_18/include/fmt/format.h:2595:23: note: in instantiation of function template specialization 'fmt::v6::internal::parse_format_specs<MyPoint &, fmt::v6::basic_format_parse_context<char, fmt::v6::internal::error_handler> >' requested here
         parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
                       ^
 /tmp/fmt_2020_02_18/include/fmt/format.h:2642:54: note: in instantiation of member function 'fmt::v6::internal::format_string_checker<char, fmt::v6::internal::error_handler, MyPoint &>::format_string_checker' requested here
   format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
                                                      ^
 /tmp/fmt_2020_02_18/include/fmt/format.h:2651:17: note: in instantiation of function template specialization 'fmt::v6::internal::do_check_format_string<char, fmt::v6::internal::error_handler, MyPoint &>' requested here
       internal::do_check_format_string<typename S::char_type,
                 ^
 /tmp/fmt_2020_02_18/include/fmt/format.h:3286:13: note: in instantiation of function template specialization 'fmt::v6::internal::check_format_string<MyPoint &, FMT_STRING, 0>' requested here
   internal::check_format_string<Args...>(format_str);
             ^
 /tmp/test.cpp100:2: note: in instantiation of function template specialization 'fmt::v6::format_to<FMT_STRING, MyPoint &, 500, char>' requested here
         format_to(out3, FMT_STRING("Hello {}"), thePoint);
         ^
 /tmp/fmt_2020_02_18/include/fmt/core.h:709:3: note: 'fallback_formatter' has been explicitly marked deleted here
   fallback_formatter() = delete;
   ^
 1 error generated.
*/
}
@refnum
Copy link
Contributor Author

refnum commented Feb 29, 2020

Attached is an example test for format-test.cc which reproduces the issue.

The UserDate class is the same as the 'date' example given in the docs, with two additions to silence C++14 compatibility warnings when built as a test.

These are:

  • Add a -> decltype(ctx.begin()) to parse and format to avoid a "deduced return types are a C++14 extension" warning from clang.
  • Declare parse as const to avoid a "constexpr non-static member function will not be implicitly 'const' in C++14" warning from clang.

If the user-defined type is constructed as an argument then both fmt::format and fmt::format_to succeed with or without FMT_STRING validation.

If an existing instance of the user-defined type is passed as an argument then fmt::format succeeds with or without FMT_STRING validation.

If an existing instance of the user-defined type is passed as an argument then fmt::format_to succeeds without FMT_STRING validation.

If an existing instance of the user-defined type is passed as an argument then fmt::format_to fails to compile with FMT_STRING validation.

The error case is #if'd out. Turn off and the test will compile and pass, turn on and it will fail to compile.

This is using Xcode 11.3.1, where clang reports itself as:

$ clang --version
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0
Thread model: posix

format_to_FMT_STRING.cpp.zip

@vitaut
Copy link
Contributor

vitaut commented Mar 1, 2020

Fixed in 1e84931. Thanks for reporting.

@vitaut vitaut closed this as completed Mar 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants