[HN Gopher] Integer Conversions and Safe Comparisons in C++20
       ___________________________________________________________________
        
       Integer Conversions and Safe Comparisons in C++20
        
       Author : ibobev
       Score  : 45 points
       Date   : 2022-09-12 09:51 UTC (1 days ago)
        
 (HTM) web link (www.cppstories.com)
 (TXT) w3m dump (www.cppstories.com)
        
       | kwant_kiddo wrote:
       | Once again the comments prove that people are more interested in
       | trash-talking C++ than than solving real problems.
       | 
       | C++20 IS fixing many complaints or design-choices made in the
       | past.
       | 
       | In terms of the article it casually mentions in the end using
       | 
       | '''-Werror -Wall -Wextra''' The unsigned signed comparisons will
       | fail to compile. Most serious projects use these flags anyway.
       | 
       | I wonder if clang-tidy will also catch this?
        
         | [deleted]
        
         | synergy20 wrote:
         | I'm fully aware of pitfalls of c++ (and c), but I keep coming
         | back to them as all the rest alternatives failed to provide the
         | same speed, performance, and small size.
         | 
         | every language has its dark corner, none is perfect, same as C
         | and C++. C and C++ are popular for decades is a fact, like it
         | or not, data does not lie.
        
         | anonymoushn wrote:
         | > Most serious projects use these flags anyway.
         | 
         | Most serious projects are opting in to being unable to compile
         | on future compilers whenever new warnings are added?
        
         | phoe-krk wrote:
         | As a person working on big C++ telco codebases for a living,
         | I'm fully aware that C++ is solving real problems and that
         | -Weverything makes it somewhat possible to work with the
         | language. At the same time, I'm appalled by the negative
         | consequences of the "let's fix problems with the old and broken
         | comparison operator by introducing a new, better comparison
         | operator, since we need to maintain almost complete backwards
         | compatibility" approach that C++ takes here.
         | 
         | I'm interested in solving real problems, but C++ deserves trash
         | talking for this reason alone.
        
           | rictic wrote:
           | What's the alternative? Change the semantics of the
           | comparison operator so that code looks cleaner? That means
           | that when you read a comparison in source you have to know
           | how it's going to be compiled in order to tell if the code is
           | correct or not.
        
             | lifthrasiir wrote:
             | You can opt in a new behavior, attribute syntax added in
             | C++11 should make this much more bearable. In fact, I'm
             | honestly surprised that this mode of language evolution
             | didn't seem to be frequently discussed in C++ WG at all;
             | ECMAScript had `use strict` which is very much the same
             | thing and well received, and if ECMAScript can do it with a
             | lot of legacy code, there is no real reason C++ can do the
             | same.
        
               | titzer wrote:
               | This isn't that, though. C/C++ continue to leave
               | undefined behavior in _specifically_ for performance. It
               | would, AFAICT, be standard-compliant behavior for a
               | compiler to handled mixed-sign comparisons involving
               | negative numbers in the intuitive number-line way, but
               | the language specification doesn 't require this because
               | it means another comparison[1]. Because performance, it's
               | the programmer's responsibility to avoid having negative
               | numbers in such comparisons. And woe to they who have
               | bugs in their programs, the compiler may even exploit
               | that UB to assume the code is never reached. The default
               | is all wrong because the priorities are all wrong.
               | 
               | [1] It's non-obvious to me why an implicit conversion
               | with potential UB would _ever_ be a good idea. If a
               | programmer really did want a UB-having single-machine
               | compare between a signed (but non-negative) int and an
               | unsigned int, they could write an explicit static_cast
               | and then compare. Such a static_cast would be the UB-
               | having nop that unlocked a non-UB comparison. You 'd get
               | the exact machine code that you wanted, but you had to
               | opt into UB. Bad default that the short, intuitive-
               | looking code has UB.
        
               | anonymoushn wrote:
               | I don't think this is correct. Comparison operators are
               | defined to perform the usual arithmetic conversions
               | before they are applied, and the usual arithmetic
               | conversions for converting to unsigned integer types are
               | defined like so:
               | 
               | > If the destination type is unsigned, the resulting
               | value is the least unsigned integer congruent to the
               | source integer (modulo 2n where n is the number of bits
               | used to represent the unsigned type). [Note: In a two's
               | complement representation, this conversion is conceptual
               | and there is no change in the bit pattern (if there is no
               | truncation). ]
        
               | titzer wrote:
               | Converting a signed negative number to unsigned is UB.
        
               | anonymoushn wrote:
               | Can you provide a citation? I have above quoted the part
               | of the standard that defines the value of the result of
               | the conversion.
        
               | sharikous wrote:
               | I don't know why they don't do that either. Perhaps
               | because a transition like that will take multiple decades
               | of consistent and steady guidance to succeed, given the
               | nature and size of C++ codebases and nobody will take the
               | responsibility for it?
        
             | bruce343434 wrote:
             | Welcome to every language that has the balls to say "yeah
             | you know what, that didn't work, here's a revision". Stuff
             | is deprecated all the time.
        
       | lultimouomo wrote:
       | The usual arithmetic conversions linked from the article [0] are
       | a bit different from the ones I know. In particular, if int can
       | represent all values of all operands, both of them will be
       | converted to int.
       | 
       | Thus comparing unsigned short and short will usually (if short is
       | narrower than int) do the right thing. C++Insights (which I
       | didn't know! This is the real pearl of this article.) agrees with
       | me [1].
       | 
       | [0]
       | https://en.cppreference.com/w/cpp/language/operator_arithmet...
       | 
       | [1]
       | https://cppinsights.io/lnk?code=I2luY2x1ZGUgPGNzdGRpbz4KCmlu...
        
         | anonymoushn wrote:
         | The relevant part of your first link seems to be right at the
         | beginning:
         | 
         | > If the operand passed to an arithmetic operator is integral
         | or unscoped enumeration type, then before any other action (but
         | after lvalue-to-rvalue conversion, if applicable), the operand
         | undergoes integral promotion.
         | 
         | Clicking "integral promotion" leads here:
         | https://en.cppreference.com/w/cpp/language/implicit_conversi...
        
       | titzer wrote:
       | Virgil has a family of completely well-defined (i.e. no UB)
       | fixed-size integer types with some hard-fought rules that I
       | eventually got around to documenting here:
       | 
       | https://github.com/titzer/virgil/blob/master/doc/tutorial/Fi...
       | 
       | One of the key things is that values are never silently truncated
       | (other than 2's-complement wrap-around that is built-in to
       | arithmetic) or values changed; only promotions. The only sane
       | semantics for over-shifts (shifts larger than the size of the
       | type) is to shift the bits out, like a window.
       | 
       | The upshot of all that is that Virgil has a pretty sane semantics
       | for fixed-size integers, IMHO. Particularly comparisons.
       | Comparing signed and unsigned ints is not UB and cannot silently
       | give the wrong answer; integers are always compared on the same
       | number line. It takes only a maximum of one extra comparison to
       | achieve this. AFAICT this is what achieved by the std::cmp*
       | referenced in the article.
        
       | phoe-krk wrote:
       | Looking at horror stories like this along with their "solutions"
       | that are done by slapping more standard functions on top of the
       | already existing piles, I'm glad that I do my personal
       | programming in languages which 1) have meaningful numeric towers
       | and automatically do promotion/demotion as appropriate, 2) don't
       | implicitly convert signed integers into unsigned integers and
       | error out instead.
       | 
       | With each standard revision, C++ is becoming more and more
       | horrible as much as it strives to become a better and more useful
       | language.
        
         | GeorgeTirebiter wrote:
         | Pray tell, what are your personal programming languages?
        
           | phoe-krk wrote:
           | Lisp and Erlang. The latter doesn't have a numeric tower but
           | has automatic bigint conversion and handling. The former is
           | eager to error upon signed/unsigned type violations once you
           | specify them.
        
             | jcelerier wrote:
             | > has automatic bigint conversion
             | 
             | how can one implement this without a branch before
             | literally any arithmetic operation?
        
               | throwawaymaths wrote:
               | You're not using Erlang for speed. You're using Erlang
               | for reliability. (Note this is not the same thing as
               | correctness or "safety")
        
               | phoe-krk wrote:
               | In CL, you can declare that you need modulo arithmetic,
               | which will give you branchless arithmetic operations.
               | 
               | In Erlang, you don't; the language was never made for
               | that sort of speed.
        
             | AlexSW wrote:
             | Not sure Erlang is strong on the basic types front on the
             | whole though; I've never been a fan of strings in Erlang.
             | Greatly prefer C++ on this front.
        
       ___________________________________________________________________
       (page generated 2022-09-13 23:02 UTC)