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

Fix std::byte formatting with compile-time API #2072

Merged

Conversation

alexezeder
Copy link
Contributor

@alexezeder alexezeder commented Dec 22, 2020

Right now formatting of std::byte using compile-time API like so:

fmt::format(FMT_COMPILE("{}"), std::byte{42})

ends up with compilation error, proof on Compiler Explorer. Because there are 2 write functions which satisfy for the passed type std::byte:

fmt/include/fmt/format.h

Lines 2114 to 2120 in 5a37e18

template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_enum<T>::value &&
!std::is_same<T, Char>::value)>
FMT_CONSTEXPR OutputIt write(OutputIt out, T value) {
return write<Char>(
out, static_cast<typename std::underlying_type<T>::type>(value));
}

fmt/include/fmt/format.h

Lines 2150 to 2162 in 5a37e18

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 {
using context_type = basic_format_context<OutputIt, Char>;
using formatter_type =
conditional_t<has_formatter<T, context_type>::value,
typename context_type::template formatter_type<T>,
fallback_formatter<T, Char>>;
context_type ctx(out, {}, {});
return formatter_type().format(value, ctx);
}

Also, I found __cpp_lib_byte feature macro, so all new checks are based on this macro instead of __cplusplus >= 201703L and I changed old checks accordingly.

Copy link
Contributor

@vitaut vitaut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR.

include/fmt/format.h Outdated Show resolved Hide resolved
@alexezeder
Copy link
Contributor Author

alexezeder commented Dec 23, 2020

Using some tricks I was able to get a precise location of the line that fails format-test on MSVC, here it is:

fmt/test/format-test.cc

Lines 1992 to 1993 in 0446614

enum test_enum : unsigned char { test_value };
EXPECT_EQ("0", fmt::to_string(test_value));

This check fails with these values:

Value of: fmt::to_string(test_value)
Actual: "false"
Expected: "0"

I don't have MSVC to check what is going on there - why a value of enum converted as bool. Also, I was able to get on Github Actions that this function is actually called 😐:

fmt/include/fmt/format.h

Lines 2125 to 2128 in 0446614

template <typename Char, typename OutputIt>
constexpr OutputIt write(OutputIt out, bool value) {
return write<Char>(out, string_view(value ? "true" : "false"));
}

from here:

fmt/include/fmt/format.h

Lines 3813 to 3818 in 0446614

template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline std::string to_string(const T& value) {
std::string result;
detail::write<char>(std::back_inserter(result), value);
return result;
}

while all other compilers invoke the following function from to_string above:

fmt/include/fmt/format.h

Lines 2114 to 2123 in 0446614

template <
typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(
std::is_enum<T>::value && !std::is_same<T, Char>::value &&
mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
type::custom_type)>
FMT_CONSTEXPR OutputIt write(OutputIt out, T value) {
return write<Char>(
out, static_cast<typename std::underlying_type<T>::type>(value));
}

I'm pretty confused about that so I think it's better if someone who has MSVC would check that because I just don't see any reason why this should happen. So probably this PR can be closed because I'm unable to fix that.

@vitaut
Copy link
Contributor

vitaut commented Dec 24, 2020

So probably this PR can be closed because I'm unable to fix that.

OK, closing then until someone comes up with an msvc workaround.

@alexezeder
Copy link
Contributor Author

alexezeder commented Dec 24, 2020

I found 2 workarounds for this problem, both of them you can see here - https://godbolt.org/z/dvcTjj. I even tested one of them here alexezeder@a3b1c07 and it works.
Also, while searching for the current problem I found out that there was a similar problem with MSVC with almost the same code - https://godbolt.org/z/3qE5Gr, but as you can see it was fixed in v19.20.
IMHO it's definitely an MSVC bug, so I should probably report this bug to them, but I won't because I didn't find a way to do that without signing in using a Microsoft account.

@alexezeder
Copy link
Contributor Author

In case one of the proposed workarounds is okay, then this PR can be reopened, and then I can add the selected workaround here.
And probably an issue can be closed in this case.

@vitaut vitaut reopened this Dec 25, 2020
@vitaut
Copy link
Contributor

vitaut commented Dec 25, 2020

In case one of the proposed workarounds is okay, then this PR can be reopened, and then I can add the selected workaround here.

Sure, thanks for looking into this. I suggest going with the first workaround to avoid introducing unnecessary symbols.

@alexezeder alexezeder changed the title Fix byte formatting with compile-time API Fix std::byte formatting with compile-time API Dec 25, 2020
@vitaut vitaut merged commit d09b5c1 into fmtlib:master Dec 27, 2020
@vitaut
Copy link
Contributor

vitaut commented Dec 27, 2020

Thank you!

@alexezeder alexezeder deleted the fix/byte_formatting_with_compile_time_api branch January 15, 2021 21:15
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

Successfully merging this pull request may close these issues.

2 participants