Skip to content
New issue

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

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

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants