2181. Exceptions from seed sequence operations

Section: 29.6.1.2 [rand.req.seedseq], 29.6.3 [rand.eng], 29.6.4 [rand.adapt] Status: C++17 Submitter: Daniel Krügler Opened: 2012-08-18 Last modified: 2017-07-30

Priority: 3

View all other issues in [rand.req.seedseq].

View all issues with C++17 status.

Discussion:

LWG issue 2180 points out some deficiences in regard to the specification of the library-provided type std::seed_seq regarding exceptions, but there is another specification problem in regard to general types satisfying the seed sequence constraints (named SSeq) as described in 29.6.1.2 [rand.req.seedseq].

29.6.3 [rand.eng] p3 and 29.6.4.1 [rand.adapt.general] p3 say upfront:

Except where specified otherwise, no function described in this section 29.6.3 [rand.eng]/29.6.4 [rand.adapt] throws an exception.

This constraint causes problems, because the described templates in these sub-clauses depend on operations of SSeq::generate() which is a function template, that depends both on operations provided by the implementor of SSeq (e.g. of std::seed_seq), and those of the random access iterator type provided by the caller. With class template linear_congruential_engine we have just one example for a user of SSeq::generate() via:

template<class Sseq> 
linear_congruential_engine<>::linear_congruential_engine(Sseq& q);

template<class Sseq> 
void linear_congruential_engine<>::seed(Sseq& q);

None of these operations has an exclusion rule for exceptions.

As described in 2180 the wording for std::seed_seq should and can be fixed to ensure that operations of seed_seq::generate() won't throw except from operations of the provided iterator range, but there is no corresponding "safety belt" for user-provided SSeq types, since 29.6.1.2 [rand.req.seedseq] does not impose no-throw requirements onto operations of seed sequences.

  1. A quite radical step to fix this problem would be to impose general no-throw requirements on the expression q.generate(rb,re) from Table 115, but this is not as simple as it looks initially, because this function again depends on general types that are mutable random access iterators. Typically, we do not impose no-throw requirements on iterator operations and this would restrict general seed sequences where exceptions are not a problem. Furthermore, we do not impose comparable constraints for other expressions, like that of the expression g() in Table 116 for good reasons, e.g. random_device::operator() explicitly states when it throws exceptions.

  2. A less radical variant of the previous suggestion would be to add a normative requirement on the expression q.generate(rb,re) from Table 115 that says: "Throws nothing if operations of rb and re do not throw exceptions". Nevertheless we typically do not describe conditional Throws elements in proper requirement sets elsewhere (Container requirements excluded, they just describe the containers from Clause 23) and this may exclude resonable implementations of seed sequences that could throw exceptions under rare situations.

  3. The iterator arguments provided to SSeq::generate() for operations in templates of 29.6.3 [rand.eng] and 29.6.4 [rand.adapt] are under control of implementations, so we could impose stricter exceptions requirements on SSeq::generate() for SSeq types that are used to instantiate member templates in 29.6.3 [rand.eng] and 29.6.4 [rand.adapt] solely.

  4. We simply add extra wording to the introductive parts of 29.6.3 [rand.eng] and 29.6.4 [rand.adapt] that specify that operations of the engine (adaptor) templates that depend on a template parameter SSeq throw no exception unless SSeq::generate() throws an exception.

Given these options I would suggest to apply the variant described in the fourth bullet.

The proposed resolution attempts to reduce a lot of the redundancies of requirements in the introductory paragraphs of 29.6.3 [rand.eng] and 29.6.4 [rand.adapt] by introducing a new intermediate sub-clause "Engine and engine adaptor class templates" following sub-clause 29.6.2 [rand.synopsis]. This approach also solves the problem that currently 29.6.3 [rand.eng] also describes requirements that apply for 29.6.4 [rand.adapt] (Constrained templates involving the Sseq parameters).

[2013-04-20, Bristol]

Remove the first bullet point:

?- Throughout this sub-clause general requirements and conventions are described that apply to every class template specified in sub-clause 29.6.3 [rand.eng] and 29.6.4 [rand.adapt]. Phrases of the form "in those sub-clauses" shall be interpreted as equivalent to "in sub-clauses 29.6.3 [rand.eng] and 29.6.4 [rand.adapt]".

Replace "in those sub-clauses" with "in sub-clauses 29.6.3 [rand.eng] and 29.6.4 [rand.adapt]".

Find another place for the wording.

Daniel: These are requirements on the implementation not on the types. I'm not comfortable in moving it to another place without double checking.

Improve the text (there are 4 "for"s): for copy constructors, for copy assignment operators, for streaming operators, and for equality and inequality operators are not shown in the synopses.

Move the information of this paragraph to the paragraphs it refers to:

"-?- Descriptions are provided in those sub-clauses only for engine operations that are not described in 29.6.1.4 [rand.req.eng], for adaptor operations that are not described in 29.6.1.5 [rand.req.adapt], or for operations where there is additional semantic information. In particular, declarations for copy constructors, for copy assignment operators, for streaming operators, and for equality and inequality operators are not shown in the synopses."

Alisdair: I prefer duplication here than consolidation/reference to these paragraphs.

The room showed weakly favjust or for duplication.

Previous resolution from Daniel [SUPERSEDED]:

  1. Add a new sub-clause titled "Engine and engine adaptor class templates" following sub-clause 29.6.2 [rand.synopsis] (but at the same level) and add one further sub-clause "General" as child of the new sub-clause as follows:

    Engine and engine adaptor class templates [rand.engadapt]

    General [rand.engadapt.general]

    -?- Throughout this sub-clause general requirements and conventions are described that apply to every class template specified in sub-clause 29.6.3 [rand.eng] and 29.6.4 [rand.adapt]. Phrases of the form "in those sub-clauses" shall be interpreted as equivalent to "in sub-clauses 29.6.3 [rand.eng] and 29.6.4 [rand.adapt]".

    -?- Except where specified otherwise, the complexity of each function specified in those sub-clauses is constant.

    -?- Except where specified otherwise, no function described in those sub-clauses throws an exception.

    -?- Every function described in those sub-clauses that has a function parameter q of type SSeq& for a template type parameter named SSeq that is different from type std::seed_seq throws what and when the invocation of q.generate throws.

    -?- Descriptions are provided in those sub-clauses only for engine operations that are not described in 29.6.1.4 [rand.req.eng], for adaptor operations that are not described in 29.6.1.5 [rand.req.adapt], or for operations where there is additional semantic information. In particular, declarations for copy constructors, for copy assignment operators, for streaming operators, and for equality and inequality operators are not shown in the synopses.

    -?- Each template specified in those sub-clauses requires one or more relationships, involving the value(s) of its non-type template parameter(s), to hold. A program instantiating any of these templates is ill-formed if any such required relationship fails to hold.

    -?- For every random number engine and for every random number engine adaptor X defined in those sub-clauses:

    • if the constructor

      template <class Sseq> explicit X(Sseq& q);
      

      is called with a type Sseq that does not qualify as a seed sequence, then this constructor shall not participate in overload resolution;

    • if the member function

      template <class Sseq> void seed(Sseq& q);
      

      is called with a type Sseq that does not qualify as a seed sequence, then this function shall not participate in overload resolution;

    The extent to which an implementation determines that a type cannot be a seed sequence is unspecified, except that as a minimum a type shall not qualify as a seed sequence if it is implicitly convertible to X::result_type.

  2. Edit the contents of sub-clause 29.6.3 [rand.eng] as indicated:

    -1- Each type instantiated from a class template specified in this section 29.6.3 [rand.eng] satisfies the requirements of a random number engine (29.6.1.4 [rand.req.eng]) type and the general implementation requirements specified in sub-clause [rand.engadapt.general].

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.3 [rand.eng] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.3 [rand.eng] throws an exception.

    -4- Descriptions are provided in this section 29.6.3 [rand.eng] only for engine operations that are not described in 29.6.1.4 [rand.req.eng] […]

    -5- Each template specified in this section 29.6.3 [rand.eng] requires one or more relationships, involving the value(s) of its non-type template parameter(s), to hold. […]

    -6- For every random number engine and for every random number engine adaptor X defined in this subclause (29.6.3 [rand.eng]) and in sub-clause 29.6.3 [rand.eng]: […]

  3. Edit the contents of sub-clause 29.6.4.1 [rand.adapt.general] as indicated:

    -1- Each type instantiated from a class template specified in this section 29.6.3 [rand.eng]29.6.4 [rand.adapt] satisfies the requirements of a random number engine adaptor (29.6.1.5 [rand.req.adapt]) type and the general implementation requirements specified in sub-clause [rand.engadapt.general].

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.4 [rand.adapt] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.4 [rand.adapt] throws an exception.

    -4- Descriptions are provided in this section 29.6.4 [rand.adapt] only for engine operations that are not described in 29.6.1.5 [rand.req.adapt] […]

    -5- Each template specified in this section 29.6.4 [rand.adapt] requires one or more relationships, involving the value(s) of its non-type template parameter(s), to hold. […]

[2014-02-09, Daniel provides alternative resolution]

[Lenexa 2015-05-07: Move to Ready]

LWG 2181 exceptions from seed sequence operations

STL: Daniel explained that I was confused. I said, oh, seed_seq says it can throw if the RanIt throws. Daniel says the RanIts are provided by the engine. Therefore if you give a seed_seq to an engine, it cannot throw, as implied by the current normative text. So what Daniel has in the PR is correct, if slightly unnecessary. It's okay to have explicitly non-overlapping Standardese even if overlapping would be okay.

Marshall: And this is a case where the std:: on seed_seq is a good thing.

STL: Meh.

STL: And that was my only concern with this PR. I like the latest PR much better than the previous.

Marshall: Yes. There's a drive-by fix for referencing the wrong section. Other than that, the two are the same.

STL: Alisdair wanted the repetition instead of centralization, and I agree.

Marshall: Any other opinions?

Jonathan: I'll buy it.

STL: For a dollar?

Hwrd: I'll buy that for a nickel.

Marshall: Any objections to Ready? I don't see a point in Immediate.

Jonathan: Absolutely agree.

Marshall: 7 for ready, 0 opposed, 0 abstain.

[2014-05-22, Daniel syncs with recent WP]

[2015-10-31, Daniel comments and simplifies suggested wording changes]

Upon Walter Brown's suggestion the revised wording does not contain any wording changes that could be considered as editorial.

Previous resolution from Daniel [SUPERSEDED]:

This wording is relative to N3936.

  1. Edit the contents of sub-clause 29.6.3 [rand.eng] as indicated:

    -1- Each type instantiated from a class template specified in this section 29.6.3 [rand.eng] satisfies the requirements of a random number engine (29.6.1.4 [rand.req.eng]) type.

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.3 [rand.eng] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.3 [rand.eng] throws an exception.

    -?- Every function described in this section 29.6.3 [rand.eng] that has a function parameter q of type Sseq& for a template type parameter named Sseq that is different from type std::seed_seq throws what and when the invocation of q.generate throws.

    -4- Descriptions are provided in this section 29.6.3 [rand.eng] only for engine operations that are not described in 29.6.1.4 [rand.req.eng] or for operations where there is additional semantic information. In particular, declarations for copy constructors, for copy assignment operators, for streaming operators, and for equality operators, and inequality operators are not shown in the synopses.

    -5- Each template specified in this section 29.6.3 [rand.eng] requires one or more relationships, involving the value(s) of its non-type template parameter(s), to hold. A program instantiating any of these templates is ill-formed if any such required relationship fails to hold.

    -6- For every random number engine and for every random number engine adaptor X defined in this subclause (29.6.3 [rand.eng]) and in sub-clause 29.6.4 [rand.adapt]:

    • if the constructor

      template <class Sseq> explicit X(Sseq& q);
      

      is called with a type Sseq that does not qualify as a seed sequence, then this constructor shall not participate in overload resolution;

    • if the member function

      template <class Sseq> void seed(Sseq& q);
      

      is called with a type Sseq that does not qualify as a seed sequence, then this function shall not participate in overload resolution;

    The extent to which an implementation determines that a type cannot be a seed sequence is unspecified, except that as a minimum a type shall not qualify as a seed sequence if it is implicitly convertible to X::result_type.

  2. Edit the contents of sub-clause 29.6.4.1 [rand.adapt.general] as indicated:

    -1- Each type instantiated from a class template specified in this section 29.6.3 [rand.eng]29.6.4 [rand.adapt] satisfies the requirements of a random number engine adaptor (29.6.1.5 [rand.req.adapt]) type.

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.4 [rand.adapt] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.4 [rand.adapt] throws an exception.

    -?- Every function described in this section 29.6.4 [rand.adapt] that has a function parameter q of type Sseq& for a template type parameter named Sseq that is different from type std::seed_seq throws what and when the invocation of q.generate throws.

    -4- Descriptions are provided in this section 29.6.4 [rand.adapt] only for adaptor operations that are not described in section 29.6.1.5 [rand.req.adapt] or for operations where there is additional semantic information. In particular, declarations for copy constructors, for copy assignment operators, for streaming operators, and for equality operators, and inequality operators are not shown in the synopses.

    -5- Each template specified in this section 29.6.4 [rand.adapt] requires one or more relationships, involving the value(s) of its non-type template parameter(s), to hold. A program instantiating any of these templates is ill-formed if any such required relationship fails to hold.

  3. Edit the contents of sub-clause 29.6.8.1 [rand.dist.general] p2 as indicated: [Drafting note: These editorial changes are just for consistency with those applied to 29.6.3 [rand.eng] and 29.6.4.1 [rand.adapt.general]end drafting note]

    -2- Descriptions are provided in this section 29.6.8 [rand.dist] only for distribution operations that are not described in 29.6.1.6 [rand.req.dist] or for operations where there is additional semantic information. In particular, declarations for copy constructors, for copy assignment operators, for streaming operators, and for equality operators, and inequality operators are not shown in the synopses.

Proposed resolution:

This wording is relative to N4527.

  1. Edit the contents of sub-clause 29.6.3 [rand.eng] as indicated:

    -1- […]

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.3 [rand.eng] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.3 [rand.eng] throws an exception.

    -?- Every function described in this section 29.6.3 [rand.eng] that has a function parameter q of type Sseq& for a template type parameter named Sseq that is different from type std::seed_seq throws what and when the invocation of q.generate throws.

    […]

  2. Edit the contents of sub-clause 29.6.4.1 [rand.adapt.general] as indicated:

    -1- […]

    -2- Except where specified otherwise, the complexity of each function specified in this section 29.6.4 [rand.adapt] is constant.

    -3- Except where specified otherwise, no function described in this section 29.6.4 [rand.adapt] throws an exception.

    -?- Every function described in this section 29.6.4 [rand.adapt] that has a function parameter q of type Sseq& for a template type parameter named Sseq that is different from type std::seed_seq throws what and when the invocation of q.generate throws.