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

Formatting a std::string with "{:x}" doesn't throw a fmt::format_error with version 8.0.0 #2402

Closed
neo1973 opened this issue Jun 29, 2021 · 3 comments

Comments

@neo1973
Copy link

neo1973 commented Jun 29, 2021

This code throws an exception when compiled with fmt 7.1.3 but not with 8.0.0:

#include <fmt/format.h>
#include <string>

int main() {
    fmt::print( "{:x}", std::string( "test" ) );
}

Was this change in behaviour intentionally or is this a regression?

When compiling in C++20 mode the code creates a compiler error with 8.0.0 which is inconsistent compared to the runtime behaviour:

In file included from /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:44,
                 from <source>:2:
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h: In function 'int main()':
<source>:7:15:   in 'constexpr' expansion of 'fmt::v8::basic_format_string<char, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >("{:x}")'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2857:40:   in 'constexpr' expansion of 'fmt::v8::detail::parse_format_string<true, char, fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(((fmt::v8::basic_format_string<char, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*)this)->fmt::v8::basic_format_string<char, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::str_, fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(fmt::v8::basic_string_view<char>(((const char*)s)), (fmt::v8::detail::error_handler(), fmt::v8::detail::error_handler())))'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2408:44:   in 'constexpr' expansion of 'fmt::v8::detail::parse_replacement_field<char, fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>((p + -1), end, (* & handler))'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2383:38:   in 'constexpr' expansion of '(& handler)->fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::on_format_specs(adapter.fmt::v8::detail::parse_replacement_field<char, fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&>(const char*, const char*, fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)::id_adapter::arg_id, (begin + 1), end)'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2726:51:   in 'constexpr' expansion of '((fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*)this)->fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::parse_funcs_[id](((fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*)this)->fmt::v8::detail::format_string_checker<char, fmt::v8::detail::error_handler, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::context_)'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2457:17:   in 'constexpr' expansion of 'f.fmt::v8::formatter<fmt::v8::basic_string_view<char>, char, void>::parse<fmt::v8::detail::compile_parse_context<char, fmt::v8::detail::error_handler> >((* & ctx))'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2819:37:   in 'constexpr' expansion of 'fmt::v8::detail::check_string_type_spec<char, fmt::v8::detail::error_handler&>(((int)((fmt::v8::formatter<fmt::v8::basic_string_view<char>, char, void>*)this)->fmt::v8::formatter<fmt::v8::basic_string_view<char>, char, void>::specs_.fmt::v8::detail::dynamic_format_specs<char>::<anonymous>.fmt::v8::basic_format_specs<char>::type), eh)'
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:2594:44: error: call to non-'constexpr' function 'void fmt::v8::detail::error_handler::on_error(const char*)'
 2594 |   if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
      |                                 ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:2825,
                 from <source>:2:
/opt/compiler-explorer/libs/fmt/trunk/include/fmt/format-inl.h:2553:15: note: 'void fmt::v8::detail::error_handler::on_error(const char*)' declared here
 2553 | FMT_FUNC void detail::error_handler::on_error(const char* message) {
      |               ^~~~~~

Godbolt

@alexezeder
Copy link
Contributor

This is done intentionally, you can check out the changelog for 8.0.0 release:

Enabled compile-time format string check by default.
For example (godbolt):

 #include <fmt/core.h>

 int main() {
   fmt::print("{:d}", "I am not a number");
 }

gives a compile-time error on compilers with C++20 consteval support
(gcc 10+, clang 11+) because d is not a valid format specifier for a
string.

To pass a runtime string wrap it in fmt::runtime:

fmt::print(fmt::runtime("{:d}"), "I am not a number");

If you want to handle an exception at runtime, then just wrap format string in fmt::runtime as above.

@neo1973
Copy link
Author

neo1973 commented Jun 29, 2021

Thanks for the comment @alexezeder. I'm aware of fmt::runtime to disable compile time checks, my problem is that this code doesn't throw an exception with 8.0.0 when compiled in C++17 mode. It did throw with 7.1.3:

#include <fmt/format.h>
#include <string>

int main()
{
    fmt::print( "{:x}", std::string( "test" ) );
}

Output 7.1.3:

terminate called after throwing an instance of 'fmt::v7::format_error'
  what():  invalid type specifier

Output 8.0.0:

test

@vitaut
Copy link
Contributor

vitaut commented Jul 2, 2021

Fixed in 1d73845. Thanks for reporting.

@vitaut vitaut closed this as completed Jul 2, 2021
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

3 participants