Hi Arsen,

On 08/05/2026 19:06, Arsen Arsenović wrote:
Christopher Bazley <[email protected]> writes:

I haven't worked on libgomp, but I agree with Martin's points.

A lot of compilers can plausibly support modern C that will never
support modern C++.

libgomp is a GCC runtime library.  The only compiler of concern is GCC.

Modern C offers features for convenience (e.g. auto) and macro-based
polymorphism (e.g. _Generic). Subtype polymorphism is more limited
than in C++ but still achievable with a reasonable degree of type
safety.

Yet, it offers no destructors, and no move semantics, which are arguably
the most important features of C++11 and onwards.  I'd trade _Generic
for those two, and make it out of the deal like a bandit.

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.

The issue with malloc seems to me like a canary in the coal mine. I
don't know whether libgomp makes significant use of callback functions
and event handlers, but they are likely to suffer from similar
issues. If the return type of malloc is perceived to be a problem,
then it is trivial to write a wrapper macro, e.g.

I don't see what issue that is.  Previously, allocation and
initialization were separate.  They aren't in C++.  I don't see how that
could apply to either callbacks or event handlers.

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.

(Note that every single instance of an allocation in libgomp falls under
  the case of trivial initialization by merit of being a C codebase, and
  ergo, even the initially-proposed mechanical conversion of allocation
  sites is not incorrect, just presumably inconsistent with newer code)

C projects 'upgraded' to C++ can end up worse than reasonable code
written from scratch according to good idioms of either language, due
to:

Yes, this isn't surprising.  That's exactly why I was arguing not to do
mechanical explicit void* casts in the cases where memcpy is immediately
followed by initialization.

The instances where allocating uninitialized memory is genuinely useful
are in the tiny minority, but they do exist in libgomp.  These need to
still call malloc explicitly with a computed size, as in C.

There are also a number of useful flexible array members, but for these,
each such type can provide an allocation-and-construction helper.

- C++'s overly strict type rules concerning void *. Without care, C++'s type
   system has the ironic effect of making code *less* type-safe. (For the same
   reason that it would be less type-safe for MyPy to require explicit
  conversion of 'Any' to some other type.)

?  Those rules aren't overly strict.  I don't follow how this analogy
with 'Any' demonstrates a lowering of type safety.

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;
}

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).

- 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.

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.

- 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 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.)

- 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.

Do note, however, that such a table exists in libgomp already:
https://gcc.gnu.org/cgit/gcc/tree/libgomp/libgomp.h#n1391

As I said, I'm not familiar with libgomp, so I have no idea how much
it might benefit in areas where C++ truly does excel, such as object
lifetime management. In my experience, few C projects have complex
object lifetimes (by design), but maybe libgomp is one of them.

Trivial objects still benefit from destructors.  unique_ptr is a
distilled example.

Lastly, some C programmers are put off contributing to C++
codebases. I don't know how common the converse is.

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.

Have a lovely day.

You too.
--
Christopher Bazley
Staff Software Engineer, GNU Tools Team.
Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
http://www.arm.com/

Reply via email to