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
Description: PGP signature
