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 format a long value with formatter #2768

Merged
merged 9 commits into from
Jun 12, 2022
Merged

Conversation

fsb4000
Copy link
Contributor

@fsb4000 fsb4000 commented Jun 7, 2022

Fixes #2765

Although @cpplearner wrote everything in the issue, I was able to write

typename _Format_arg_traits<_FormatContext>::template _Storage_type<_Ty>

correctly only the third time :)

@fsb4000 fsb4000 requested a review from a team as a code owner June 7, 2022 21:05
Co-authored-by: S. B. Tam <cpplearner@outlook.com>
@fsb4000 fsb4000 changed the title allow format a long value with formatter allow format a long value with custom formatter Jun 7, 2022
@fsb4000 fsb4000 changed the title allow format a long value with custom formatter fix format a long value with formatter Jun 7, 2022
@StephanTLavavej StephanTLavavej added bug Something isn't working format C++20/23 format labels Jun 7, 2022
@StephanTLavavej

This comment was marked as resolved.

@cpplearner
Copy link
Contributor

P0355R7_calendars_and_time_zones_formatting is failing with:

D:\build\out\inc\format(3148) : Assertion failed: The custom handler should be structurally unreachable for _Arg_formatter

This seems to be because std::string gets stored as a std::basic_format_arg::handle, which means this function is unused:

STL/stl/inc/format

Lines 1778 to 1780 in 17fde2c

template <class _Traits, class _Alloc>
static auto _Phony_basic_format_arg_constructor(const basic_string<_Char_type, _Traits, _Alloc>&)
-> basic_string_view<_Char_type>; // not defined

Maybe the constrained overload is considered to be more specialized?

STL/stl/inc/format

Lines 1744 to 1745 in 17fde2c

template <_Has_formatter<_Context> _Ty>
static auto _Phony_basic_format_arg_constructor(_Ty&&) {

@StephanTLavavej
Copy link
Member

Thanks for fixing this bug! I pushed minor stylistic changes and a small test coverage increase - please meow if you disagree.

Co-authored-by: S. B. Tam <cpplearner@outlook.com>
@StephanTLavavej
Copy link
Member

Thanks @cpplearner for noticing that P0645R10_text_formatting_custom_formatting already has machinery that's identical to Box<T>:

template <class T>
struct custom_formattable_type {
T val;
};
template <class T, class charT>
struct std::formatter<custom_formattable_type<T>, charT> : std::formatter<T, charT> {
template <class FC>
auto format(const custom_formattable_type<T>& val, FC& format_ctx) const {
return std::formatter<T, charT>::format(val.val, format_ctx);
}
};

I verified that adding test coverage for long here will fail without @fsb4000's fix. To be comprehensive, I also added coverage for unsigned long and long double.

@fsb4000
Copy link
Contributor Author

fsb4000 commented Jun 8, 2022

This seems to be because std::string gets stored as a std::basic_format_arg::handle, which means this function is unused:

No, std::string is ok, I have debugged the test.
The error is from the asserts:

test_information_classes<wchar_t>();

test_information_classes<char>(); works OK. (strange)

and inside test_information_classes

const local_info nonexistent2 = la_tz->get_info(LA::Std_1.local_end());
// N4885 [time.zone.info.sys]/7: "Effects: Streams out the sys_info object r in an unspecified format."
// N4885 [time.zone.info.local]/3: "Effects: Streams out the local_info object r in an unspecified format."
empty_braces_helper(sys1,
STR("begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, "
"offset: 36000s, save: 0min, abbrev: AEST"),
STR("begin: 2020-04-04 16:00:00, end: 2020-10-03 16:00:00, "
"offset: 36000s, save: 0min, abbrev: GMT+10"));

All empty_braces_helper tests with local_info and sys_info are failed

void empty_braces_helper(
const Arg& val, const CharT* const expected, const same_as<const CharT*> auto... alternatives) {
// N4885 [time.format]/6: "If the chrono-specs is omitted, the chrono object is formatted
// as if by streaming it to std::ostringstream os and copying os.str() through the output iterator
// of the context with additional padding and adjustments as specified by the format specifiers."
const auto result = format(STR("{}"), val);

So format local_info is stored as a std::basic_format_arg::handle

@cpplearner
Copy link
Contributor

  • format(STR("{}"), val) calls operator<<;
  • operator<< calls _Widen_string<_CharT>(_Val.abbrev);
  • _Widen_string returns a const string& when CharT is char, but returns a wstring when CharT is wchar_t.

That const probably makes a difference here.

#include <format>
#include <string>

using namespace std;

using A = _Format_arg_traits<format_context>::_Storage_type<string>;
using B = _Format_arg_traits<format_context>::_Storage_type<const string>;

static_assert(is_same_v<A, basic_format_arg<format_context>::handle>);
static_assert(is_same_v<B, string_view>);

@fsb4000
Copy link
Contributor Author

fsb4000 commented Jun 8, 2022

well done @cpplearner !
So can we change

template <class _Traits, class _Alloc> 
 static auto _Phony_basic_format_arg_constructor(const basic_string<_Char_type, _Traits, _Alloc>&) 
     -> basic_string_view<_Char_type>; // not defined 

to

template <class _Traits, class _Alloc> 
 static auto _Phony_basic_format_arg_constructor(basic_string<_Char_type, _Traits, _Alloc>) 
     -> basic_string_view<_Char_type>; // not defined 

?

Co-authored-by: S. B. Tam <cpplearner@outlook.com>
stl/inc/format Show resolved Hide resolved
stl/inc/format Show resolved Hide resolved
stl/inc/format Show resolved Hide resolved
@StephanTLavavej StephanTLavavej removed their assignment Jun 8, 2022
stl/inc/format Outdated Show resolved Hide resolved
@StephanTLavavej StephanTLavavej self-requested a review June 9, 2022 00:06
@fsb4000
Copy link
Contributor Author

fsb4000 commented Jun 9, 2022

Yes, I did it intentionally because we don't modify basic_format_arg.
I would store char* as const char* if I don't plan to modify it.
But now I am unsure, maybe the const is not needed...

@StephanTLavavej StephanTLavavej self-assigned this Jun 9, 2022
stl/inc/format Show resolved Hide resolved
Copy link
Member

@barcharcraz barcharcraz left a comment

Choose a reason for hiding this comment

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

Approved, despite this not doing what the standard says for basic_string&

Agree with Casey that we need to reformulate how this overload set works, it's been a very consistent source of bugs in both our implementation and the standard.

@barcharcraz barcharcraz removed their assignment Jun 10, 2022
@StephanTLavavej StephanTLavavej self-assigned this Jun 10, 2022
@StephanTLavavej
Copy link
Member

I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.

@StephanTLavavej StephanTLavavej merged commit 1385898 into microsoft:main Jun 12, 2022
@StephanTLavavej
Copy link
Member

Thanks for the bugfix and targeted overhaul here, improving our implementation beyond the Standard itself! 😹 🐞 🎉

@fsb4000 fsb4000 deleted the fix2765 branch June 12, 2022 13:20
fsb4000 added a commit to fsb4000/STL that referenced this pull request Aug 13, 2022
Co-authored-by: S. B. Tam <cpplearner@outlook.com>
Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working format C++20/23 format
Projects
None yet
Development

Successfully merging this pull request may close these issues.

<format>: Cannot format a long value with formatter
6 participants