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/