Hi Cristopher,

Christopher Bazley <[email protected]> writes:

> OK. If that's what libgomp needs, then that's what makes sense. If
> what libgomp needed were ad hoc polymorphism, then _Generic would be
> perfectly adequate.

Scarce few programs have no need for those.  I can't think of many
programs that don't need lists but do need overloading.

> Callbacks and event handlers commonly have a parameter of type 'void
> *'. Requiring arguments of that type to be explicitly cast to some
> other pointer type makes source code harder to read and write, and
> actually detracts from type safety.

I see no difference between

  T *userptr = userptr_voidstar;

... and:

  auto userptr = static_cast<T *> (userptr_voidstar);

... in this regard.

In addition, unlike in C, it is possible to hide away the lossy type
games: https://godbolt.org/z/z5dzr93Eb

(the above is incomplete in a few obvious ways, but it's a quick sketch)

> Casting discards type information. Preventing implicit conversion of
> type 'void *' to other pointer types on assignment or initialisation
> prevents the compiler from checking that qualifiers weren't discarded,
> or even that the expression whose type is being cast actually has a
> pointer type.
>
> For example:
>
> void callback(const void *p)
> {
>   int *q = (int *)p; // whoops!
>   *q = 1;
> }
>
> void callback(long int p)
> {
>   int *q = (int *)p; // whoops!
>   *q = 1;
> }

Luckily neither of those are legal when using specific casts:

  ~$ g++ -S -o - -x c++ - <<< 'void f(const void *p) { auto x = static_cast<int 
*>(p); }'
        .file   "<stdin>"
  <stdin>: In function ‘void f(const void*)’:
  <stdin>:1:34: error: ‘static_cast’ from type ‘const void*’ to type ‘int*’ 
casts away qualifiers
  ~ 1 $ g++ -S -o - -x c++ - <<< 'void f(long int p) { auto x = static_cast<int 
*>(p); }'
        .file   "<stdin>"
  <stdin>: In function ‘void f(long int)’:
  <stdin>:1:31: error: invalid ‘static_cast’ from type ‘long int’ to type ‘int*’
  ~ 1 $ g++ -S -o - -x c++ - <<< 'void f(__UINTPTR_TYPE__ p) { auto x = 
static_cast<int *>(p); }'
        .file   "<stdin>"
  <stdin>: In function ‘void f(long unsigned int)’:
  <stdin>:1:39: error: invalid ‘static_cast’ from type ‘long unsigned int’ to 
type ‘int*’
  ~ 1 $ 

> I believe that the concept of any-type (as expressed via void *) is orthogonal
> to the concept of qualified type or subtype. Python's treatment of the type
> annotation Any supports this point of view.
>
> This flaw in the design of C++ is also what necessitated the invention of
> nullptr, which would otherwise be unnecessary. Had the purpose of 'void *' not
> been misunderstood, nullptr could simply have been defined as a macro: ((void
> *)0).

OTOH, there's no benefit to omitting a 'nullptr' constant from the
language.

>>> - C++ constructors being unable to return a failure indication, and the
>>>    gymnastics required to work around that without using exceptions 
>>> (leading to
>>>   many identically-named construction helpers).
>> I've been writing C++ in places where exceptions would be inconvenient
>> for many years now, and this is a fairly infrequent requirement IME.
>> It will be fine.
>
> Sorry but I'm not sure what you mean here.
>
> Are you saying that wanting to return a failure indication is
> infrequent, that doing without exceptions is infrequent, or both? I
> guess it depends what kind of code you are writing and what kind of
> system it runs on.

The former.

> Nowadays a lot of programmers would claim that malloc cannot fail, or
> at least that programs have no need to recover gracefully if it does
> fail. That does not apply to interactive programs running on operating
> systems that do not overcommit memory, but it might apply to
> non-interactive programs running on such systems.

Sure.  And very often, types that hold pointers (e.g. for smart
pointers) can encode such a fail state.  In cases where they cannot,
factory functions usually don't result in combinatorial explosion.

>>> - Erasure of designated initialisers that were vital for keeping the code
>>>    readable and maintainable (by preventing definitions, for example array
>>>   initialisers and enumerations, from diverging).
>> Designated initializers exist in C++.
>
> Not usefully, as far as I can tell: https://godbolt.org/z/7j6fdqzrb

I'm surprised GCC emits a sorry here, I hadn't noticed that.

> I find it frustrating that C++ lags more than a quarter of a century
> behind C in this respect. (I am aware why C++ does not allow arbitrary
> order of initialisers, but I do not see that as a point in its favour:
> debugging a mess caused by misordered constructor and destructor
> invocations is not fun.)

Such a "mess" is no different than the "mess" of relying on the
evaluation order of function arguments, except that in this case it's
more specified.

>>> - Gratuitous loss of the ability to find method definitions and usages using
>>>    simple tools like 'grep'. (I do not mean gratuitous as a perjorative but 
>>> in
>>>    the specific sense that some useful patterns require subtype 
>>> polymorphism, in
>>>    which case the equivalent C code would have the same issue, but most code
>>>   does not.)
>> There's no C pattern that is impossible in C++.  There's certainly
>> nothing that requires dynamic dispatch that wouldn't require a
>> (hand-rolled) vtable in C.
>
> The issue is not impossibility but reduced maintainability of code
> that has fewer unique identifiers. It's possible to similarly reduce
> the maintainability of C programs by using structs to implement
> namespaces, and invoking functions via members. Most C programmers
> consider that the downsides outweigh any advantages.

I find memorizing many redundant identifiers isn't much better.

A name should name equivalent behavior in all contexts.  A size is a
size.  A push_back is a push_back.  A pop_back is a pop_back.

>> Given that gcc is already in C++, it seems that that bridge was burned.
>
> It isn't as black and white as that. I know of at least one programmer who
> contributes to GCC because GCC is still largely written in the style of a C
> program, but does not feel inclined or able to contribute to Clang. He is
> probably unusual in that he is outspoken in his opinions.

Anecdotally, I've heard the opposite at least twice.

I have no doubt the phenomenon exists, but I am certain that the more
expressively powerful language allows for easier to write and
understand, and ergo higher quality, code long-term, with few or no
downsides.
-- 
Arsen Arsenović

Attachment: signature.asc
Description: PGP signature

Reply via email to