Title
Incorrect formatting of nested ranges and tuples
Status
wp
Section
[format.range.formatter][format.tuple]
Submitter
Victor Zverovich

Created on 2023-02-20.00:00:00 last changed 11 months ago

Messages

Date: 2023-11-13.14:08:10

Proposed resolution:

This wording is relative to N4928.

  1. Modify [format.string.general] p1 as indicated:

    […]
    format-specifier:
    : format-spec
    format-spec:
    as specified by the formatter specialization for the argument type; cannot start with }
  2. Modify [formatter.requirements] as indicated:

    -3- Given character type charT, output iterator type Out, and formatting argument type T, in Table 74 [tab:formatter.basic] and Table 75 [tab:formatter]:

    […]

    pc.begin() points to the beginning of the format-spec ([format.string]) of the replacement field being formatted in the format string. If format-spec is not present or empty then either pc.begin() == pc.end() or *pc.begin() == '}'.

  3. Modify [format.range.formatter] as indicated:

    template<class ParseContext>
      constexpr typename ParseContext::iterator
        parse(ParseContext& ctx);
    

    -9- Effects: Parses the format specifiers as a range-format-spec and stores the parsed specifiers in *this. Calls underlying_.parse(ctx) to parse format-spec in range-format-spec or, if the latter is not present, an empty format-spec. The values of opening-bracket_, closing-bracket_, and separator_ are modified if and only if required by the range-type or the n option, if present. If:

    1. (9.1) — the range-type is neither s nor ?s,

    2. (9.2) — underlying_.set_debug_format() is a valid expression, and

    3. (9.3) — there is no range-underlying-spec,

    then calls underlying_.set_debug_format().

  4. Modify [format.tuple] as indicated:

    template<class ParseContext>
      constexpr typename ParseContext::iterator
        parse(ParseContext& ctx);
    

    -7- Effects: Parses the format specifiers as a tuple-format-spec and stores the parsed specifiers in *this. The values of opening-bracket_, closing-bracket_, and separator_ are modified if and only if required by the tuple-type, if present. For each element e in underlying_, calls e.parse(ctx) to parse an empty format-spec and, if e.set_debug_format() is a valid expression, calls e.set_debug_format().

Date: 2023-11-11.00:00:00

[ 2023-11-11 Approved at November 2023 meeting in Kona. Status changed: Voting → WP. ]

Date: 2023-06-16.09:31:14

[ Varna 2023-06-16; Move to Ready ]

This would allow resolving LWG 3776 as NAD.

Date: 2023-06-16.09:31:14

[ Varna 2023-06-16; Jonathan provides tweaked wording ]

Add "an" in two places.

Date: 2023-03-15.00:00:00

[ 2023-03-22; Reflector poll ]

Set priority to 2 after reflector poll.

This wording is relative to N4928.

  1. Modify [format.string.general] p1 as indicated:

    […]
    format-specifier:
    : format-spec
    format-spec:
    as specified by the formatter specialization for the argument type; cannot start with }
  2. Modify [formatter.requirements] as indicated:

    -3- Given character type charT, output iterator type Out, and formatting argument type T, in Table 74 [tab:formatter.basic] and Table 75 [tab:formatter]:

    […]

    pc.begin() points to the beginning of the format-spec ([format.string]) of the replacement field being formatted in the format string. If format-spec is not present or empty then either pc.begin() == pc.end() or *pc.begin() == '}'.

  3. Modify [format.range.formatter] as indicated:

    template<class ParseContext>
      constexpr typename ParseContext::iterator
        parse(ParseContext& ctx);
    

    -9- Effects: Parses the format specifiers as a range-format-spec and stores the parsed specifiers in *this. Calls underlying_.parse(ctx) to parse format-spec in range-format-spec or, if the latter is not present, empty format-spec. The values of opening-bracket_, closing-bracket_, and separator_ are modified if and only if required by the range-type or the n option, if present. If:

    1. (9.1) — the range-type is neither s nor ?s,

    2. (9.2) — underlying_.set_debug_format() is a valid expression, and

    3. (9.3) — there is no range-underlying-spec,

    then calls underlying_.set_debug_format().

  4. Modify [format.tuple] as indicated:

    template<class ParseContext>
      constexpr typename ParseContext::iterator
        parse(ParseContext& ctx);
    

    -7- Effects: Parses the format specifiers as a tuple-format-spec and stores the parsed specifiers in *this. The values of opening-bracket_, closing-bracket_, and separator_ are modified if and only if required by the tuple-type, if present. For each element e in underlying_, calls e.parse(ctx) to parse empty format-spec and, if e.set_debug_format() is a valid expression, calls e.set_debug_format().

Date: 2023-02-20.00:00:00

formatter specializations for ranges and tuples set debug format for underlying element formatters in their parse functions e.g. [format.range.formatter] p9:

template<class ParseContext>
  constexpr typename ParseContext::iterator
    parse(ParseContext& ctx);

Effects: Parses the format specifier as a range-format-spec and stores the parsed specifiers in *this. The values of opening-bracket_, closing-bracket_, and separator_ are modified if and only if required by the range-type or the n option, if present. If:

  1. — the range-type is neither s nor ?s,

  2. underlying_.set_debug_format() is a valid expression, and

  3. — there is no range-underlying-spec,

then calls underlying_.set_debug_format().

However, they don't say anything about calling parse functions of those formatters. As as result, formatting of nested ranges can be incorrect, e.g.

std::string s = std::format("{}", std::vector<std::vector<std::string>>{{"a, b", "c"}});

With the current specification s is [[a, b, c]] instead of [["a, b", "c"]], i.e. strings in the output are not correctly escaped. The same is true for nested tuples and combinations of tuples and ranges.

The fix approved by LEWG as part of P2733 (which was trying to address a different issue) is to always call parse for underlying formatter. Additionally the standard should clarify that format-spec cannot start with '}' because that's the implicit assumption in range formatting and what happens when format-spec is not present.

History
Date User Action Args
2023-11-13 14:08:10adminsetmessages: + msg13840
2023-11-13 14:08:10adminsetstatus: voting -> wp
2023-11-07 21:41:54adminsetstatus: ready -> voting
2023-06-16 09:31:14adminsetmessages: + msg13639
2023-06-16 09:31:14adminsetmessages: + msg13638
2023-06-16 09:31:14adminsetstatus: new -> ready
2023-03-22 22:40:39adminsetmessages: + msg13479
2023-02-25 14:23:38adminsetmessages: + msg13430
2023-02-20 00:00:00admincreate