Boost logo

Boost :

From: degski (degski_at_[hidden])
Date: 2020-03-02 14:52:57


On Mon, 2 Mar 2020 at 03:16, Alexander Grund via Boost <
boost_at_[hidden]> wrote:

> Nothing stops you from converting to signed, do what you want including
> your "check for negative"-based overflow detection and finally
> converting back to unsigned when passing it to an API expecting an
> unsigned number (number not type, although the type is unsigned too, see
> above)
>

Could you please try and explain, why you think that signed is not a good
type for a size (other than stating that "size cannot be negative"), what I
am saying is that "valid-size cannot be negative"? You could also make the
size a complex number, that would be an analogue. The imaginary-componant
will have to be zero, but otherwise it would work just fine. The fact that
that set is larger than the problem domain is IMO orthogonal to that.

> Didn't you argue in the mail before that there will never be anything of
> size 2^32 and hence even not anything like 2^64? How could you overflow
> that then?
>

If you are manipulating (subtracting, adding differences) pointers, I
thought I wrote that. The pointers might be pointers into Virtual Memory
and can have any value [1, 2^56].

> Finding bugs related to this is hard, using int's you'll know right away.
> How? Only if you underflow. On unsigned you'll get a very large number
> if you go below zero, on signed you get a negative number. Both can be
> detected. But you talked about overflow. For unsigned you'll get a small
> number (that is wrong obviously but you COULD check) but for signed you
> get UB and hence can't even check for that.
> >> If you get an unsigned
> >> value there is no need to check for below zero, if you get a signed
> >> value you might. It is the same there is `not_null<T>` in GSL(?).
> >>
> > But you would need to check if it wrapped
> No. If you call `obj.size()` you get an unsigned value that is a valid
> unsigned value. It cannot wrap when returning (conditions apply).
>

 Tautology: "... an unsigned value that is a valid unsigned value", they
always are, whether it's the right number is another question.

If obj.size() returns a signed value you'll got to check for <0 before
> using the value unless the API somehow promises to not return negative
> values. Encoding this in the type is the natural thing to do.
>

No, it's not, unsigned types are good for bit-manipulation only, nothing
else. Unsigned types don't follow the rules of mathematics, they are
fundamentally flawed by nature. The fact that int's are limited in range is
not flaw, but an implementation detail. The mathematically correct way of
doing things (on a Turing-machine) is to use signed big-ints.

> and if you start adding and subtracting these things
> See above. For a representation (and hence API) unsigned makes sense.
> For using arbitrary operations it may not. Use the types that fir your
> use case. Adding is save for unsigned, as you argued: The type is wide
> enough for all uses as a size of something. Subtraction might not but
> you can check first (`if(a <= size) return size - a; else throw `)
>

You've now just precluded the use of noexcept (noexcept move f.e.,
super-important in modern C++) and added a branch (cannot be removed by
something clever, exactly because it is unsigned, the compiler can make no
assumptions and the code has to go through the math) to your code, all that
because it upsets you that something that should not occur in the first
place in correct code can occur iff one is writing code that now (as one
observed it got negative) is known to have a bug.

What is much better is to use signed int's combined with assert's.

The use of unsigned is false security (actually no security) and serves
nothing. In the end, you still need to write correct code (so the signed
int's WON'T BE negative, there where they shouldn't be), but this practice
makes you're code less flexible, more verbose (the unavoidable casts add to
that) and probably slower than using signed. All that because of this
'natural' way of looking at sizes.

> > I don't understand.
>
> Maybe after the above? You want a guarantee to have a non-negative
> number. "unsigned" is that but it suffers from underflow going
> undetected. A `not_null<T>` like "wrapper" which otherwise behaves as T
> but guarantees the non-negativity would make the type suitable for
> representing an unsigned number in a signed type suitable for
> operations. Obviously if you subtract something from a
> `not_negative<int>` it will become a plain "int". Once you pass it to an
> API expecting a `not_negative<int>` the precondition will be checked.
>

Got it now, yeah that would be great, but for now that would be run-time,
no? And I guess, due to the halting problem, it can never be compile-time,
unless it's a limited problem.

degski

-- 
@systemdeg
"We value your privacy, click here!" Sod off! - degski
"Anyone who believes that exponential growth can go on forever in a finite
world is either a madman or an economist" - Kenneth E. Boulding
"Growth for the sake of growth is the ideology of the cancer cell" - Edward
P. Abbey

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk