<div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Mon, Oct 8, 2018 at 7:45 PM Tom Honermann &lt;<a href="mailto:tom@honermann.net">tom@honermann.net</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
  
    
  
  <div text="#000000" bgcolor="#FFFFFF">
    <div class="m_-4949948655659317562moz-cite-prefix">On 10/08/2018 12:38 PM, Markus Scherer
      wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">&gt; <span style="color:rgb(0,0,0);font-family:sans-serif;font-size:medium">ICU
          supports customization of its internal code unit type, but </span><code class="m_-4949948655659317562gmail-highlight"><span style="color:rgb(153,0,85)">char16_t</span></code><span style="color:rgb(0,0,0);font-family:sans-serif;font-size:medium"> is
          used by default, following ICU’s adoption of C++11.</span><br>
        <div><br>
        </div>
        <div>Not quite... ICU supports customization of its code unit
          type <u><i>for C APIs</i></u>. Internally, and in C++ APIs,
          we switched to char16_t. And because that broke call sites, we
          mitigated where we could with overloads and shim classes.</div>
      </div>
    </blockquote>
    <br>
    Ah, thank you for the correction.  If we end up submitting a
    revision of the paper, I&#39;ll include this correction.  I had checked
    the ICU sources (<tt>include/unicode/umachine.h</tt>) and verified
    that the <tt>UChar</tt> typedef was configurable, but I didn&#39;t
    realize that configuration was limited to C code.<br></div></blockquote><div><br></div><div>We limited it to C API by doing s/UChar/char16_t/g in C++ API, except where we replaced a raw pointer with a shim class. So you won&#39;t see &quot;UChar&quot; in C++ API any more at all.</div><div><br></div><div>Internally to compiling ICU itself, we kept UChar in existing code (so that we didn&#39;t have to change tens of thousands of lines) but fixed it to be a typedef for char16_t.</div><div><br></div><div>Unfortunately, if UChar is configured != char16_t, you need casts or cast helpers for using C APIs from C++ code.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">It would be interesting to get more perspective on how and why ICU
    evolved like it did.  What was the motivation for ICU to switch to <tt>char16_t</tt>? 
    Were the anticipated benefits realized despite the perhaps
    unanticipated complexities?</div></blockquote><div><br></div><div>We assumed that C++ code was going to adopt char16_t and maybe std::u16string, and we wanted it to be easy for ICU to work with those types.</div><div><br></div><div>In particular, the string literals weighed heavily. For the most part, we can ignore the standard library when it comes to Unicode, but the previous lack of real UTF-16 string literals was extremely inconvenient. We used to have all kinds of static const UChar arrays with numeric intializer lists, or init-once code for setting up string &quot;constants&quot;, even when they contained only ASCII characters.</div><div><br></div><div>Now that we can use u&quot;literals&quot; we managed to clean up some of our code, and new library code and especially new unit test code benefits greatly.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">If Windows were to suddenly sprout
    Win32 interfaces defined in terms of <tt>char16_t</tt>, would the
    pain be substantially relieved?</div></blockquote><div><br></div><div>No. Many if not most of our users are not on Windows, or at least not only on Windows. UTF-16 is fairly widely used.</div><div><br></div><div>Anyway, I doubt that Windows will do that. Operating systems want to never break code like this, and these would all be duplicates.</div><div>Although I suppose they could do it as a header-only shim.</div><div><br></div><div>Microsoft was pretty unhappy with this change in ICU. They went with it because they were early in their integration of ICU into Windows.</div><div><br></div><div>They also have some fewer problems: I believe they concluded that the aliasing trick was so developer-hostile that they decided never to optimize based on it, at least for the types involved. I don&#39;t think our aliasing barrier is defined on Windows.</div><div><br></div><div>If u&quot;literals&quot; had just been uint16_t* without a new type, then we could have used string literals without changing API and breaking call sites, on most platforms anyway. And if uint16_t==wchar_t on Windows, then that would have been fine, too.</div><div><br></div><div>Note: Of course there are places where we use uint16_t* binary data, but there is never any confusion whether a function works with binary data vs. a string. You just wouldn&#39;t use the same function or name for unrelated operations.</div><div><br></div><div>Note also: While most of ICU works with UTF-16, we do have some UTF-8 functions. We distinguish the two with different function names, such as in <a href="http://icu-project.org/apiref/icu4c/classicu_1_1CaseMap.html">class CaseMap</a> (toLower() vs. utf8ToLower()).</div><div><br></div><div>If we had operations that worked on both UTF-8 and some other charset, we would also use different names.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">Are code bases that use ICU on
    non-Windows platforms (slowly) migrating from <tt>uint16_t</tt> to
    <tt>char16_t</tt>?<br></div></blockquote><div><br></div><div>I don&#39;t remember what Chromium and Android ended up doing. You could take a look at their code.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF"><blockquote type="cite"><div dir="ltr"><div>If you do want a distinct type, why not just standardize on
          uint8_t? Why does it need to be a new type that is distinct
          from that, too?<br></div>
      </div>
    </blockquote>
    Lyberta provided one example; we do need to be able to overload or
    specialize on character vs integer types.</div></blockquote><div><br></div><div>I don&#39;t find the examples so far convincing. Overloading on primitive types to distinguish between UTF-8 vs. one or more legacy charsets seems both unnecessary and like bad practice. Explicit naming of things that are different is good.</div><div><br></div><div>What makes sense to me is that &quot;char&quot; can be signed, and that&#39;s bad for dealing with non-ASCII characters.</div><div>In ICU, when I get to actual UTF-8 processing, I tend to either cast each byte to uint8_t or cast the whole pointer to uint8_t* and call an internal worker function.</div><div>Somewhat ironically, the fastest way to test for a UTF-8 trail byte is via the opposite cast, testing if (int8_t)b&lt;-0x40.</div><div><br></div><div>This is why I said it would be much simpler if the &quot;char&quot; default could be changed to be unsigned.</div><div>I realize that non-portable code that assumes a signed char type would then need the opposite command-line option that people now use to force it to unsigned.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">Since <tt>uint8_t</tt>
    is conditionally supported, we can&#39;t rely on its existence within
    the standard (we&#39;d have to use <tt>unsigned char</tt> or <tt>uint_least8_t</tt>
    instead).<br></div></blockquote><div><br></div><div>I seriously doubt that there is a platform that keeps up with modern C++ and does not have a real uint8_t.</div><div><br></div><div>ICU is one of the more widely portable libraries (or was, until we adopted C++11 and left some behind) and would likely fail royally if the uint8_t and uint16_t types we are using were actually wider than advertised and revealed larger values etc. Since ICU is also widely used, that would break a lot of systems. But no one has ever reported a bug (or request for porting patches) related to non-power-of-2 integer types.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text="#000000" bgcolor="#FFFFFF">I think there is value in maintaining consistency with <tt>char16_t</tt>
    and <tt>char32_t</tt>.  <tt>char8_t</tt> provides the missing
    piece needed to enable a clean, type safe, external vs internal
    encoding model that allows use of any of UTF-8, UTF-16, or UTF-32 as
    the internal encoding, that is easy to teach, and that facilitates
    generic libraries like text_view that work seamlessly with any of
    these encodings.<br></div></blockquote><div><br></div><div>Maybe. I don&#39;t see the need to use the same function names for a variety of legacy charsets vs. UTF-8.<br></div><div><br></div><div>20 years ago I wrote a set of macros that looked the same but had versions for UTF-8, UTF-16, and UTF-32. I briefly thought we could make (some of?) ICU essentially switchable between UTFs. I quickly learned that any real, non-trivial code you would want to write for either of them wants to be specific to that UTF, especially when people want text processing to be fast. (You can see remnants of this youthful folly in ICU&#39;s unicode/utf_old.h header file.)</div><div><br></div><div>Best regards,</div><div>markus</div></div></div>