Date: Mon, 12 Mar 2018 17:22:12 -0700

On 3/12/18, Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> wrote:

> On Mar 12, 2018, Lawrence Crowl <Lawrence_at_[hidden]> wrote:

>> So the application intended modular arithmetic? I was concerned about

>> the normal case where 'unsigned' is used to constrain the value range,

>> not to get modular arithmetic.

>

> IMNSHO, if anyone is using unsigned types "to constrain the value range,"

> they are doing computers wrong. That is *not* what signed vs. unsigned

> types are for.

I note that size_t is unsigned to constrain the value range, not to obtain

modular arithmetic.

> As Lawrence himself wrote earlier in this thread:

>> If integer overflow is undefined behavior, then it is wrong. Tools can

>> detect wrong programs and report them.

>

> The contrapositive is: "If the programmer is using a type where integer

> overflow is well-defined to wrap, then we can assume that the program

> relies on that wrapping behavior (because there would otherwise be a

> strong incentive for the programmer to use a type that detects and

> reports unintended overflow)."

Yes. For the most part, programmers should be explicit when they want

anything other than mathematical integers.

> The original design for the STL contained the "unsigned for value range"

> antipattern. Consequently, they ran into trouble immediately: for

> example, `std::string::find

> <https://en.cppreference.com/w/cpp/string/basic_string/find>` returns an

> index into the string, naturally of type `std::string::size_type`. But

> size_type is unsigned! So instead of returning "negative 1" to indicate

> the "not found" case, they had to make it return `size_type(-1)`, a.k.a.

> `std::string::npos` -- which is a positive value! This means that

> callers have to write cumbersome things such as

>

> if (s.find('k') != std::string::npos)

>

> where it would be more natural to write

>

> if (s.find('k') >= 0)

I won't disagree with the easier syntax of the latter, but I note that

the function itself is returning two things: the search status, and iff

successful the index of the found object.

> This is sort of parallel to my quotation of Lawrence above: If every

> possible value in the domain of a given type is a valid output (e.g.

> from `find`), then there is no value left over with which the function

> can signal failure at runtime. And if every possible value in the

> domain is a valid *input* (e.g. to `malloc`), then there is no way for

> the function to detect incorrect input at runtime.

This problem is why I proposed the status_value class.

> If it weren't for the STL's `size_type` snafu continually muddying the

> waters for new learners, I doubt people would be falling into the

> "unsigned for value range" antipattern anymore.

Well, I would, but I tend to use it to avoid ill-formed negative inputs.

> On Mar 12, 2018, Lawrence Crowl <Lawrence_at_[hidden]> wrote:

>> So the application intended modular arithmetic? I was concerned about

>> the normal case where 'unsigned' is used to constrain the value range,

>> not to get modular arithmetic.

>

> IMNSHO, if anyone is using unsigned types "to constrain the value range,"

> they are doing computers wrong. That is *not* what signed vs. unsigned

> types are for.

I note that size_t is unsigned to constrain the value range, not to obtain

modular arithmetic.

> As Lawrence himself wrote earlier in this thread:

>> If integer overflow is undefined behavior, then it is wrong. Tools can

>> detect wrong programs and report them.

>

> The contrapositive is: "If the programmer is using a type where integer

> overflow is well-defined to wrap, then we can assume that the program

> relies on that wrapping behavior (because there would otherwise be a

> strong incentive for the programmer to use a type that detects and

> reports unintended overflow)."

Yes. For the most part, programmers should be explicit when they want

anything other than mathematical integers.

> The original design for the STL contained the "unsigned for value range"

> antipattern. Consequently, they ran into trouble immediately: for

> example, `std::string::find

> <https://en.cppreference.com/w/cpp/string/basic_string/find>` returns an

> index into the string, naturally of type `std::string::size_type`. But

> size_type is unsigned! So instead of returning "negative 1" to indicate

> the "not found" case, they had to make it return `size_type(-1)`, a.k.a.

> `std::string::npos` -- which is a positive value! This means that

> callers have to write cumbersome things such as

>

> if (s.find('k') != std::string::npos)

>

> where it would be more natural to write

>

> if (s.find('k') >= 0)

I won't disagree with the easier syntax of the latter, but I note that

the function itself is returning two things: the search status, and iff

successful the index of the found object.

> This is sort of parallel to my quotation of Lawrence above: If every

> possible value in the domain of a given type is a valid output (e.g.

> from `find`), then there is no value left over with which the function

> can signal failure at runtime. And if every possible value in the

> domain is a valid *input* (e.g. to `malloc`), then there is no way for

> the function to detect incorrect input at runtime.

This problem is why I proposed the status_value class.

> If it weren't for the STL's `size_type` snafu continually muddying the

> waters for new learners, I doubt people would be falling into the

> "unsigned for value range" antipattern anymore.

Well, I would, but I tend to use it to avoid ill-formed negative inputs.

-- Lawrence Crowl

Received on 2018-03-13 01:22:14