Re: Transitioning libgomp from C to C++ implementation

Arsen Arsenović via Gcc <[email protected]>
Newsgroups gmane.comp.gcc.devel
Message-ID <[email protected]>
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ć
signature.asc (application/pgp-signature, 288 B)
-----BEGIN PGP SIGNATURE-----

iKIEARYKAEoWIQT+4rPRE/wAoxYtYGFSwpQwHqLEkwUCaf8O+hsUgAAAAAAEAA5t
YW51MiwyLjUrMS4xMiwyLDIQHGFyc2VuQGFhcnNlbi5tZQAKCRBSwpQwHqLEk59o
AP0cU9ZDdOb6E58RDD97nYA7yBpP8+B5gCMqbnt1ZEoQhgEA/zhRKmWCVVEjpAbb
Jr/if2wIenR/iNFl5WDXHZE9TAY=
=w7It
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.