This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3554. `chrono::parse` needs `const charT*` and `basic_string_view<charT>` overloads

Section: 27.13 [time.parse] Status: WP Submitter: Howard Hinnant Opened: 2021-05-22 Last modified: 2021-10-14

Priority: 2

View all other issues in [time.parse].

View all issues with WP status.

Discussion:

The `chrono::parse` functions take `const basic_string<charT, traits, Alloc>&` parameters to specify the format strings for the parse. Due to an oversight on my part in the proposal, overloads taking `const charT*` and `basic_string_view<charT, traits>` were omitted. These are necessary when the supplied arguments is a string literal or `string_view` respectively:

>     in >> parse("%F %T", tp);

These overloads have been implemented in the example implementation.

[2021-05-26; Reflector poll]

Set priority to 2 after reflector poll.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4885.

> 1. Modify 27.2 [time.syn], header `<chrono>` synopsis, as indicated:

>    >     […]
>    >     namespace chrono {
>    >       // 27.13 [time.parse], parsing
>    >       template<class charT, class Parsable>
>    >         unspecified
>    >           parse(const charT* fmt, Parsable& tp);

>    >       template<class charT, class traits, class Parsable>
>    >         unspecified
>    >           parse(basic_string_view<charT, traits> fmt, Parsable& tp);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(const charT* fmt, Parsable& tp,
>    >                 basic_string<charT, traits, Alloc>& abbrev);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(basic_string_view<charT, traits> fmt, Parsable& tp,
>    >                 basic_string<charT, traits, Alloc>& abbrev);

>    >       template<class charT, class Parsable>
>    >         unspecified
>    >           parse(const charT* fmt, Parsable& tp, minutes& offset);

>    >       template<class charT, class traits, class Parsable>
>    >         unspecified
>    >           parse(basic_string_view<charT, traits> fmt, Parsable& tp, minutes& offset);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >       unspecified
>    >         parse(const charT* fmt, Parsable& tp,
>    >               basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(basic_string_view<charT, traits> fmt, Parsable& tp,
>    >                 basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(const basic_string<charT, traits, Alloc>& format, Parsable& tp);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(const basic_string<charT, traits, Alloc>& format, Parsable& tp,
>    >                 basic_string<charT, traits, Alloc>& abbrev);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(const basic_string<charT, traits, Alloc>& format, Parsable& tp,
>    >                 minutes& offset);

>    >       template<class charT, class traits, class Alloc, class Parsable>
>    >         unspecified
>    >           parse(const basic_string<charT, traits, Alloc>& format, Parsable& tp,
>    >                 basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

>    >       […]
>    >     }
>    >     […]

> 2. Modify 27.13 [time.parse] as indicated:

>    >     template<class charT, class Parsable>
>    >       unspecified
>    >         parse(const charT* fmt, Parsable& tp);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT>&>(), fmt, tp)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT>{fmt}, tp);`

>    >     template<class charT, class traits, class Parsable>
>    >       unspecified
>    >         parse(basic_string_view<charT, traits> fmt, Parsable& tp);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.data(), tp)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits>{fmt}, tp);`

>    >     template<class charT, class traits, class Alloc, class Parsable>
>    >       unspecified
>    >         parse(const charT* fmt, Parsable& tp,
>    >               basic_string<charT, traits, Alloc>& abbrev);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt, tp, addressof(abbrev))

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits, Alloc>{fmt}, tp, abbrev);`

>    >     template<class charT, class traits, class Alloc, class Parsable>
>    >       unspecified
>    >         parse(basic_string_view<charT, traits> fmt, Parsable& tp,
>    >               basic_string<charT, traits, Alloc>& abbrev);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.data(), tp, addressof(abbrev))

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits, Alloc>{fmt}, tp, abbrev);`

>    >     template<class charT, class Parsable>
>    >       unspecified
>    >         parse(const charT* fmt, Parsable& tp, minutes& offset);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT>&>(), fmt, tp,
>    > > >                 declval<basic_string<charT>*>(),
>    > > >                 &offset)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT>{fmt}, tp, offset);`

>    >     template<class charT, class traits, class Parsable>
>    >       unspecified
>    >         parse(basic_string_view<charT, traits> fmt, Parsable& tp, minutes& offset);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.data(), tp,
>    > > >                 declval<basic_string<charT, traits>*>(),
>    > > >                 &offset)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits>{fmt}, tp, offset);`

>    >     template<class charT, class traits, class Alloc, class Parsable>
>    >     unspecified
>    >       parse(const charT* fmt, Parsable& tp,
>    >             basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt, tp, addressof(abbrev), &offset)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits, Alloc>{fmt}, tp, abbrev, offset);`

>    >     template<class charT, class traits, class Alloc, class Parsable>
>    >       unspecified
>    >         parse(basic_string_view<charT, traits> fmt, Parsable& tp,
>    >               basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

>    > > -?- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.data(), tp, addressof(abbrev), &offset)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -?- Effects: Equivalent to `return parse(basic_string<charT, traits, Alloc>{fmt}, tp, abbrev, offset);`

>    >     template<class charT, class traits, class Alloc, class Parsable>
>    >       unspecified
>    >         parse(const basic_string<charT, traits, Alloc>& format, Parsable& tp);

>    > > -2- Constraints: The expression

>    > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.c_str(), tp)

>    > > is well-formed when treated as an unevaluated operand.

>    > > -3- Returns: A manipulator such that the expression `is >> parse(fmt, tp)` has type `I`, has value `is`, and calls `from_stream(is, fmt.c_str(), tp)`.

[2021-06-02 Tim comments and provides updated wording]

The current specification suggests that `parse` takes a reference to the format string (stream extraction on the resulting manipulator is specified to call `from_stream` on `fmt.c_str()`, not some copy), so we can't call it with a temporary string.

Additionally, since the underlying API (`from_stream`) requires a null-terminated string, the usual practice is to avoid providing a `string_view` overload (see, e.g., LWG 3430). The wording below therefore only provides `const charT*` overloads. It does not use "Equivalent to" to avoid requiring that the `basic_string` overload and the `const charT*` overload return the same type. As the manipulator is intended to be immediately consumed, the wording also adds normative encouragement to make misuse more difficult.

[2021-06-23; Reflector poll]

Set status to Tentatively Ready after six votes in favour during reflector poll.

[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

1. Modify 27.2 [time.syn], header `<chrono>` synopsis, as indicated:

   >     […]
   >     namespace chrono {
   >       // 27.13 [time.parse], parsing
   >       template<class charT, class Parsable>
   >         unspecified
   >           parse(const charT* fmt, Parsable& tp);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const basic_string<charT, traits, Alloc>& formatfmt, Parsable& tp);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const charT* fmt, Parsable& tp,
   >                 basic_string<charT, traits, Alloc>& abbrev);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const basic_string<charT, traits, Alloc>& formatfmt, Parsable& tp,
   >                 basic_string<charT, traits, Alloc>& abbrev);

   >       template<class charT, class Parsable>
   >         unspecified
   >           parse(const charT* fmt, Parsable& tp, minutes& offset);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const basic_string<charT, traits, Alloc>& formatfmt, Parsable& tp,
   >                 minutes& offset);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const charT* fmt, Parsable& tp,
   >                 basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

   >       template<class charT, class traits, class Alloc, class Parsable>
   >         unspecified
   >           parse(const basic_string<charT, traits, Alloc>& formatfmt, Parsable& tp,
   >                 basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

   >       […]
   >     }
   >     […]

2. Modify 27.13 [time.parse] as indicated:

   -1- Each `parse` overload specified in this subclause calls `from_stream` unqualified, so as to enable argument dependent lookup (6.5.4 [basic.lookup.argdep]). In the following paragraphs, let `is` denote an object of type `basic_istream<charT, traits>` and let `I` be `basic_istream<charT, traits>&`, where `charT` and `traits` are template parameters in that context.

   -?- Recommended practice: Implementations should make it difficult to accidentally store or use a manipulator that may contain a dangling reference to a format string, for example by making the manipulators produced by `parse` immovable and preventing stream extraction into an lvalue of such a manipulator type.

   >     template<class charT, class Parsable>
   >       unspecified
   >         parse(const charT* fmt, Parsable& tp);
   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const basic_string<charT, traits, Alloc>& fmt, Parsable& tp);

   > > -?- Let F be `fmt` for the first overload and `fmt.c_str()` for the second overload. Let `traits` be `char_traits<charT>` for the first overload.

   > > -2- Constraints: The expression

   > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.c_str()F, tp)

   > > is well-formed when treated as an unevaluated operand.

   > > -3- Returns: A manipulator such that the expression `is >> parse(fmt, tp)` has type `I`, has value `is`, and calls `from_stream(is, fmt.c_str()F, tp)`.

   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const charT* fmt, Parsable& tp,
   >               basic_string<charT, traits, Alloc>& abbrev);
   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const basic_string<charT, traits, Alloc>& fmt, Parsable& tp,
   >               basic_string<charT, traits, Alloc>& abbrev);

   > > -?- Let F be `fmt` for the first overload and `fmt.c_str()` for the second overload.

   > > -4- Constraints: The expression

   > > >     from_stream(declval<basic_istream<charT, traits>&>(), fmt.c_str()F, tp, addressof(abbrev))

   > > is well-formed when treated as an unevaluated operand.

   > > -5- Returns: A manipulator such that the expression `is >> parse(fmt, tp, abbrev)` has type `I`, has value `is`, and calls `from_stream(is, fmt.c_str()F, tp, addressof(abbrev))`.

   >     template<class charT, class Parsable>
   >       unspecified
   >         parse(const charT* fmt, Parsable& tp, minutes& offset);

   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const basic_string<charT, traits, Alloc>& fmt, Parsable& tp,
   >               minutes& offset);

   > > -?- Let F be `fmt` for the first overload and `fmt.c_str()` for the second overload. Let `traits` be `char_traits<charT>` and `Alloc` be `allocator<charT>` for the first overload.

   > > -6- Constraints: The expression

   > > >     from_stream(declval<basic_istream<charT, traits>&>(),
   > > >                 fmt.c_str()F, tp,
   > > >                 declval<basic_string<charT, traits, Alloc>*>(),
   > > >                 &offset)

   > > is well-formed when treated as an unevaluated operand.

   > > -7- Returns: A manipulator such that the expression `is >> parse(fmt, tp, offset)` has type `I`, has value `is`, and calls:

   > > >     from_stream(is,
   > > >                 fmt.c_str()F, tp,
   > > >                 static_cast<basic_string<charT, traits, Alloc>*>(nullptr),
   > > >                 &offset)

   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const charT* fmt, Parsable& tp,
   >               basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

   >     template<class charT, class traits, class Alloc, class Parsable>
   >       unspecified
   >         parse(const basic_string<charT, traits, Alloc>& fmt, Parsable& tp,
   >               basic_string<charT, traits, Alloc>& abbrev, minutes& offset);

   > > -?- Let F be `fmt` for the first overload and `fmt.c_str()` for the second overload.

   > > -8- Constraints: The expression

   > > >     from_stream(declval<basic_istream<charT, traits>&>(),
   > > >                 fmt.c_str()F, tp, addressof(abbrev), &offset)

   > > is well-formed when treated as an unevaluated operand.

   > > -9- Returns: A manipulator such that the expression `is >> parse(fmt, tp, abbrev, offset)` has type `I`, has value `is`, and calls `from_stream(is, fmt.c_str()F, tp, addressof(abbrev), &offset)`.