2338. §[re.traits]/7 expects of locale facets something not guaranteed by [locale.facet]/4

Section: 31.7 [re.traits], 25.3.1.1.2 [locale.facet] Status: Open Submitter: Sergey Zubkov Opened: 2013-10-15 Last modified: 2016-02-10

Priority: 3

View all other issues in [re.traits].

View all issues with Open status.

Discussion:

31.7 [re.traits]/7, begins with "if typeid(use_facet<collate<charT> >) == typeid(collate_byname<charT>)", which appears to be pseudocode with the intention to convey that the collate facet has not been replaced by the user. Cf. the wording in N1429 "there is no portable way to implement transform_primary in terms of std::locale, since even if the sort key format returned by std::collate_byname<>::transform is known and can be converted into a primary sort key, the user can still install their own custom std::collate implementation into the locale object used, and that can use any sort key format they see fit.".

Taken literally, 31.7 [re.traits]/7 appears to imply that named locales are required to hold their collate facets with dynamic type std::collate_byname<charT>, which is in fact true in some implementations (e.g libc++), but not others (e.g. libstdc++). This does not follow from the description of _byname in 25.3.1.1.2 [locale.facet]/4, which is only required to provide equivalent semantics, to the named locale's facet, not to actually be one.

[2015-05-06 Lenexa: Move to Open]

MC, RP: Consequence of failing to follow the rule is UB.

MC: Tightening of requirements.

RP: It should be this way, we just didn't impose it before.

MC: Second change is a bug fix, original code didn't work.

TK: Doesn't seem to make things worse.

Bring up in larger group tomorrow.

JW arrives.

JW: libstdc++ violates this due to two std::string ABIs.

JW: This prevents installing a type derived from Facet_byname, constrains the implementor from using a smarter derived class version.

JW: Can't look at facet id to detect replacement, because replacements have the same id.

RP: Can you give it multiple ids through multiple inheritance?

JW: No, the facet mechanism wouldn't like that.

JW: We should also ask Martin Sebor, he's implemented this stuff recently.

MC: Sounds like this resolution doesn't work, need a better solution.

JW: Write in words "if the facet has not been replaced by the user", the implementation knows how to detect that, but not like this.

RP: User RE traits need to detect this too.

JW: =(

Move to Open, JW will invite Martin Sebor to join LWG for discussion.

Later ...

JW: This is not needed for user specializations after all.

MC: Agree, [re.traits]/7 only applies to the stdlib traits.

NM: Effects: doesn't make sense.

JW, NM, Martin Sebor to come up with new wording.

Proposed resolution:

This wording is relative to N3691.

  1. Modify 25.3.1.1.2 [locale.facet]/4 as indicated:

    For some standard facets a standard "..._byname" class, derived from it, implements the virtual function semantics equivalent toprovided by that facet of the locale constructed by locale(const char*) with the same name. Each such facet provides a constructor that takes a const char* argument, which names the locale, and a refs argument, which is passed to the base class constructor. Each such facet also provides a constructor that takes a string argument str and a refs argument, which has the same effect as calling the first constructor with the two arguments str.c_str() and refs. If there is no "..._byname" version of a facet, the base class implements named locale semantics itself by reference to other facets. For any locale loc constructed by locale(const char*) and facet Facet that has a corresponding standard Facet_byname class, typeid(use_facet<Facet>(loc)) == typeid(Facet_byname).

  2. Modify 31.7 [re.traits]/7 as indicated:

    template <class ForwardIterator>
      string_type transform_primary(ForwardIterator first, ForwardIterator last) const;
    

    -7- Effects: if typeid(use_facet<collate<charT> >(getloc())) == typeid(collate_byname<charT>) and the form of the sort key returned by collate_byname<charT>::transform(first, last) is known and can be converted into a primary sort key then returns that key, otherwise returns an empty string.