Re: Transitioning libgomp from C to C++ implementation
Christopher Bazley via Gcc <[email protected]>
| Newsgroups | gmane.comp.gcc.devel |
|---|---|
| Organization | Arm |
| Message-ID | <[email protected]> |
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/