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

Section: 30.13 [time.parse] Status: C++23 Submitter: Howard Hinnant Opened: 2021-05-22 Last modified: 2023-11-22

Priority: 2

View other active issues in [time.parse].

View all other issues in [time.parse].

View all issues with C++23 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 30.2 [time.syn], header <chrono> synopsis, as indicated:

    […]
    namespace chrono {
      // 30.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 30.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 30.2 [time.syn], header <chrono> synopsis, as indicated:

    […]
    namespace chrono {
      // 30.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 30.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).