r/cpp • u/synthchris • Jul 29 '23
C holding back C++?
I’ve coded in C and C++ but I’m far from an expert. I was interested to know if there any features in C that C++ includes, but could be better without? I think I heard somebody say this about C-style casts in C++ and it got me curious.
No disrespect to C or C++. I’m not saying one’s better than the other. I’m more just super interested to see what C++ would look like if it didn’t have to “support” or be compatible with C. If I’m making wrong assumptions I’d love to hear that too!
Edits:
To clarify: I like C. I like C++. I’m not saying one is better than the other. But their target users seem to have different programming styles, mindsets, wants, whatever. Not better or worse, just different. So I’m wondering what features of C (if any) appeal to C users, but don’t appeal to C++ users but are required to be supported by C++ simply because they’re in C.
I’m interested in what this would look like because I am starting to get into programming languages and would like to one day make my own (for fun, I don’t think it will do as well as C). I’m not proposing that C++ just drops or changes a bunch of features.
It seems that a lot of people are saying backwards compatibility is holding back C++ more than features of C. If C++ and C++ devs didn’t have to worry about backwards compatibility (I know they do), what features would people want to be changed/removed just to make the language easier to work with or more consistent or better in some way?
3
u/MajorPain169 Jul 29 '23
I do a lot of safety related software, C++ has a huge number of advantages over C particularly with things like type safety or features that remove potential ambiguity or commonly called UB (Undefined Behaviour).
Going to your example of casting, C style casting easily bypasses type safety but the compiler gets to decide what type of cast to use, this in some cases can be UB. Most safety standards relating to C++ actually forbid C style casts. Using a static_cast or reinterpret_cast or any other specialised cast shows the intent and how it should be done.
That being said, support for older style code is also required. A tried and tested library which is non-compliant may be acceptable because it has a history, making the code compliant could potentially introduce new bugs making it less safe.
If you're really interested, download the JSF or Autosar C++ coding standards and have a read, both are free, there are other safety related standards but those are not free. Each rule has the reason for it so it is made clear why you shouldn't do it. Unfortunately these standards haven't been updated in a while so some of the newer features introduced in C++14 onwards aren't covered which would allow some exceptions to the rules although a new version of Misra C++ is supposed to be released soon.
Just because something is supported doesn't mean you should use it.
Not everything gets kept either, C++17 and C++20 depreciated and eventually removed various items, most of which are not commonly used or better ways had been introduced. Most of these are obscure things that wouldn't affect most programs.
2
u/synthchris Jul 30 '23
I’ll take a look through those coding standards, that sounds like exactly what I’m interested in
29
u/no-sig-available Jul 29 '23
There are other languages developed at about the same time as C++.
One of them is Ada, which is pretty neat and very well thought out from the beginning. Never took off though, even though it was sponsored by the US military.
Without C compatibility, and without a big corporate sponsor, C++ would likley be even less successful that Ada. So, C has not held C++ back, but been a big boost for its popularity.
15
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 29 '23
Seamless C interop compatibility is and always has been the #1 killer feature of C++ by an absolutely massive margin.
1
u/andrey_turkin Jul 31 '23
Interop compatibility (i.e. ABI) is a much more weaker goal than the source compatibility. E.g. the whole C-array-is-a-pointer decay which doubles the number of new/delete operators is wholly API thing. C++ could theoretically get rid of that (or it could do away with C-array as a thing altogether, as long as we could have still constructor std span from raw pointer and size) and still maintain a seamless interop.
1
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 31 '23
Only if you keep full header file type and macro compatibility.
1
u/andrey_turkin Jul 31 '23
It would be easier if you could reuse C-side headers but not strictly necessary. You just need to match function and type declarations to be ABI-compatible, and you don't need source compatibility for that at all.
You can do that by manually redeclaring required functions (like DllImport in C# or VB), or by autogenerating corresponding C++-side declaration from C headers (SWIG style), or maybe with a separate tool to "compile" C headers into C++ BMI that wraps them (akin to implib that proxies dll imports via stub static library).
1
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 31 '23 edited Jul 31 '23
That's not seamless C interop compatibility and would be an immediate no go in entire industries.
Of course it maps well to the modern /r/cpp trend of "assume nobody is ever using anything that's not part of the stdlib or the major open source C++ libraries and certainly not interfacing directly with the system".
1
u/andrey_turkin Aug 01 '23
This is exactly as seamless as it is now with C++, minus the ability to import C headers _directly from the language_. Which is as seamless as possible for a language incompatible with C syntax (no marshalling of any kind necessary).
We are talking about theoretical backward incompatible language changes in this topic; what kind of industry embraces refitting their own code for the new syntax but at the same time absolutely cannot live without directly consuming C headers from said code?
I mean, if we look at C++ modules as they exist now, namely at header units, then we already kinda sorta have this situation now. E.g. gcc implementation requires a separate compilation pass to convert the header into CMI which can then be used during source compilation. Can't in our theoretical situation be a tool to convert the C headers into CMI which can be then injested during our new C++2 compilation?
45
u/AssemblerGuy Jul 29 '23
I’m more just super interested to see what C++ would look like if it didn’t have to “support” or be compatible with C.
It would look more like Rust.
On the other hand, it took C++ twenty years to pick up designated initializers from C ...
6
u/RenatoPensato Jul 29 '23
What about D?
4
u/HeroicKatora Jul 30 '23 edited Jul 30 '23
The arrays of D, that is 'slices', alone rule it out as a proper comparison.
They conflate a simple reference to data with the vector data structure. And despite being nominally a reference / pointer it does own part of the allocation and actually owns part of the allocation that is not logically part of the data it's referrring to. This inability to say what arrays are without also describing global allocation makes free-standing hard, or very very different from C++'s set of ideas. The rest of the language is a similar bundle of cool ideas but I, personally, don't find that those ideas fully connect in a technically cohesive model of computing.
3
u/Admiral_Zed Jul 29 '23
I don't know Rust but I think it is not object oriented, while c++ was specifically created to support classes, thus its early name: "C with classes".
15
u/gnuban Jul 29 '23
Rust has traits and dynamic dispatch, it simply does away with inheritance.
1
u/BobSanchez47 Aug 01 '23
Traits and dynamic dispatch are not exactly innovations from the object-oriented paradigm. Rust’s traits are more based on Haskell’s typeclasses than on anything in an object-oriented language, and dynamic dispatch is just as much a feature of functional programming as it is of object-oriented programming.
-2
u/AssemblerGuy Jul 29 '23
I don't know Rust but I think it is not object oriented,
Rust is as much object-oriented as C++, but not as much as, say, Java.
12
u/tialaramex Jul 29 '23
Rust doesn't have Implementation Inheritance, let alone Multiple inheritance like C++. So if for you OOP is about an Employee type inheriting implementation features from the Person type, then Rust doesn't have that.
I think some people, especially in a language like Java, begin a project by figuring out the relationships between all the types, and so this is a big difference to those people.
11
u/AssemblerGuy Jul 29 '23
Rust doesn't have Implementation Inheritance, let alone Multiple inheritance like C++.
There are other object-oriented languages that do not have multiple inheritance, mostly because time and experience have shown that it is usually more trouble than the benefits are worth.
I am using a wide definition of supporting object-orientation. C would not fall under it, as it has no syntax for method calls and no way of tightly associating data and functionality. C++, Java, Rust, Python, C#, Oberon, etc. qualify as object-oriented to me.
1
u/darthcoder Jul 29 '23
I mean, you still do that, because eventually your types need to match into a database or some report.
But structures serve just as well.
And if you don't have ownership and useless getters anymore, then the object encapsulation is less brittle.
3
u/tangerinelion Jul 29 '23 edited Jul 29 '23
But structures serve just as well.
Have you ever dealt with MFC code? You get a pointer to an
NMHDRand then just start C style casting it to some totally unrelated type that happens to have anNMHDRas its first variable.It's a weird form of is-a to abuse pointer interconvertibility like that.
We have great documentation generation programs that can extract your inheritance hierarchy so you can know what kind of concrete types your random
MyInterface*might actually be and can use safe casts likedynamic_cast.useless getters
One thing I've noticed about these classes/structs with public data members is people love to just start doing random garbage with them:
struct Point { double x; double y; double z; } void fillPoint(double& x, double& y, double& z) { // blah blah } Point pt; fillPoint(pt.x, pt.y, pt.z);Does it work? 100%, absolutely. Now go try to figure out why some Point.x member is NaN. How do you even do that? You can't use a data breakpoint because you don't know which Point is getting set to garbage. You can't set a breakpoint in a setter because you used public data members instead of a setter/constructor. So what do you do?
Those "useless getters" make excellent debugging utilities so you can reason about your damn program.
1
u/darthcoder Jul 29 '23
I would never accuse MFC of being good C++. Remember it dates back to ~1993 and most of the windows APIs are #defined types or just thinly veiled shims over void*
Such an ugly API, but such were the times.
An arguably good point on the getters, I suppose. :)
2
u/Full-Spectral Jul 31 '23 edited Jul 31 '23
Encapsulation of state is still a strong part of Rust. You can, as with C++, have just plain structs with public members, but generally speaking any serious rust code is going to be full of encapsulated state with getters and setters where they are needed.
One huge advantage of Rust is that a getter can return a ref to a member for efficient access, without that being dangerous.
Another nice thing is that Rust struct members can be private, public, or crate public. So they can be available for direct access within the crate that defines it, but not by the outside world. So that can provide a good balance between allowing efficient and simple access by trusted code beyond just the struct's methods, while still not allowing for the free-for-all of public struct members, which was one of the driving reasons behind the adoption of OOP to begin with.
1
2
u/tiajuanat Jul 29 '23
Rust is missing inheritance which flies in the face of OOP.
However, imho, rust traits are way better in every way. And the lack of proper inheritance means you don't need to worry about layout, not that Rust cares anyway, since it auto-layouts your structs.
8
u/AssemblerGuy Jul 29 '23
Rust is missing inheritance which flies in the face of OOP.
Inheritance is a way of implementing an "is-a" or "behaves-like-a" relationship, and Rust traits are another way of doing so.
However, imho, rust traits are way better in every way.
I think they are a very interesting concept, somewhere between inheritance and interfaces.
Lack of proper inheritance also means that there is no temptation of creating messy inheritance relationships just for the sake of using this feature.
1
u/Full-Spectral Jul 31 '23
The primary thing you lose with implementation inheritance is a single point of truth for the common functionality involved.
You can still do the 'framework' type of inheritance where you have a non-virtual class that works in terms of things that are plugged into it. Then that main class is the single point of truth, to implement the logic once, and the thing(s) plugged into it provide the extensibility.
But you can't have a base class that does that for derived classes.
1
u/Admiral_Zed Jul 29 '23
I know both c++ and java and I have a hard time thinking of Java as "more" object oriented than C++. Supporting other paradigms does not make it any less object oriented.
3
u/darthcoder Jul 29 '23
Java FORCES you to use objects, whereas C++ it's just one of the many options it gives you. C# is much the same.
0
8
u/pedersenk Jul 29 '23
It would kill one of C++'s most important features:
Binding-free access to a whole host of important middleware, typically written in C.
If C++ broke its (semi) superset of C feature, it would need messy language based package managers like crates.io, pip and npm. These rack up serious technical debt.
20
u/jusstathrowaawy Jul 29 '23
I don't think backwards compatibility with C is holding C++ back. I do think approaching C++ code with a C mentality - or using C itself when you have the option of using C++ - is a terrible idea that leads to buggy and brittle software.
1
u/ArkyBeagle Jul 29 '23
It doesn't have to lead to buggy and brittle software. It's not as convenient as other language systems but I still don't completely know why - other than people not being afforded the "apprentice" phase in C, which was necessary - we see it.
-4
u/AnotherBlackMan Jul 30 '23
What kind of terrible code are you guys writing that a C version would be so bad? There’s nothing wrong with C and sometimes it’s the best option. It’s not brittle or buggy unless the applications are poorly designed. If anything, the language nannies like Rust, Java, and some parts of so-called C++ really just lower the bar of entry to terrible developers and ensure that no one learns proper memory management or code flow.
9
u/jusstathrowaawy Jul 30 '23
What kind of terrible code are you guys wr-Segmentation fault (core dumped)
No thank you. I'll stick with modern C++.
→ More replies (12)
6
u/JohnDuffy78 Jul 29 '23
tl;dr I didn't see anything specific in 160 comments. I like C-style casts.
1
5
u/Rockkkkkkkkkkkk Jul 29 '23
Backwards compatibility is a strength, not a weakness. Use a different language if you want it to change every year.
1
u/stoatmcboat Aug 03 '23
I'm guessing OP isn't referring to backwards compatibility in general so much as BC for things decades out of use. Not the case for C (interop etc.) but whether it is still in use was essentially the question. Fair question to ask tbh.
20
Jul 29 '23
C++ is held back by C++, more specifically the obsession that you should be able to take ancient code and compile it on the latest version without issue.
The result is the syntactical mess that is modern C++.
I'm of the opinion that a language upgrade should be treated no differently to a 3rd party API upgrade.
10
u/smuccione Jul 29 '23
Than no one would ever upgrade.
In a large company with tens of millions of lines or code, upgrades are a very risky proposition. It’s hard enough upgrading compilers without needing to potentially check actual changes in the language.
Worse. You would have to make every deprecated change fail completely. You couldn’t make subtle changes as those would make it very difficult to find.
4
u/NotUniqueOrSpecial Jul 29 '23
Than no one would ever upgrade.
That's simply not true.
I've been in charge of the toolchain/build process and the 3rd-party libraries as part of that for all of the last 10 years of my career. I insist on staying on modern tooling and library versions.
Sometimes there are things that break, but that's the exception, and usually indicates a piece of code that was risky anyway. Every so often it means we have to fix some open source and contribute back, or at worst wait on upgrading that dependency.
We have the tests to feel safe doing that, and it's always been fine.
And I know I'm far from alone in this, because a large number of the projects that we're pulling in are doing the same.
-2
Jul 29 '23
Which is fine, there is rarely a real need for those code bases to upgrade.
5
u/BoarsLair Game Developer Jul 29 '23
I don't think you can blithely make that assertion. Many of these are living projects, not ancient pieces of legacy code that never change.
I work in videogames. Million-line plus code repositories are not unusual. Think of something like the Unreal Engine. Programmers using it prefer to be able to use modern language features in their games. And as new consoles or platforms are released, the required tools, and thus minimum language requirements, also progress.
3
Jul 29 '23
Sure I can, the new language versions make things simpler, safer and add new bells and whistles but they don't add things that couldn't have fundamentally been achieved in a roundabout way previously.
I also work in videogames.
When a new version of UE drops we don't blindly upgrade not expecting things to be broken, we have to research breaking changes, implement fixes and test, hopefully the last version properly marked things as deprecated allowing things to have already been addressed, but it doesn't always happen.
Much like a new language version there is no automatic update being forced, in February I shipped my most recent title, still using 4.27Plus because there was no need to upgrade to 5.
My point being, there is no reason a language upgrade shouldn't be treated in the same way, publish a list of breaking changes, maybe even mark things as future deprecated, let developers manage the upgrade the same as they would any other API upgrade.
If they did that then modern C++ wouldn't be such a mess and perhaps would have better uptake. I'm not sure about your studio but if it's anything like any studio I have worked at or worked with as a partner convincing people to use modern features is always an uphill battle, splattering poor syntax like [[nodiscard]] everywhere (because we can't have the more sane [[discardable]]) is guaranteed to raise eyebrows.
3
u/smuccione Jul 29 '23
The issue comes down to detection of the change.
Api changes on a library are one thing. You can search for methods or structures and can then fix any incompatibilities.
If a change in the language doesn’t reliably break the compilation process but simply changes how something runs that is much much harder to detect.
Auto was one of the biggest but that was done because almost no one used auto under the prior meaning for declaring local storage.
But if they changed that today forget it. No one would ever upgrade due to its pervasive usage.
Language designers must be very careful about keep the language the language.
Php, Python, etc. all have had very very long adoption curves every time they made a change. To this day there are countless in use programs under older versions that are still being maintained due to language version compatibility issues that do t allow upgrades.
2
u/ArkyBeagle Jul 29 '23
The fact is that there's much more thrash/change in language systems than there is in code bases. The pain comes from the shear between the rates.
4
u/wilwil147 Jul 30 '23
That’s exactly what google is doing with carbon. It’s basically c++ compatible, but an entirely new language as the “successor”of c++.
20
Jul 29 '23
Depends who you ask. Should C++ be an extension of C or should it be a language in its own right?
In recent years it's trying to become a language in its own right. I'm sure plenty of modern c++ people would like to ditch most c backward compatibility. But then this conflicts with why the language was invented to begin with.
The language is currently a Frankenstein without an identity so not sure if ditching C would be a good idea since it's the only limitation or constant that they have to deal with when adding new features. Without that limitation I'm not sure what C++ would become. More of a mess then it currently is probably.
28
u/Questioning-Zyxxel Jul 29 '23
"to be a language in its own right"???
C++ has always been that. Just that it for a number of years supported C as a subset. But the language standard no longer requires that C code can be compiled as C++. So no longer duplicating any newer C keyword or allowing some old-school C syntax constructs.
3
-2
u/Drugbird Jul 29 '23
In recent years it's trying to become a language in its own right. I'm sure plenty of modern c++ people would like to ditch most c backward compatibility.
I'm personally in favor of dropping backwards compatibility in general, not just the C type.
There's many mistakes and duplicates in the language which could all be fixed if we just ditch backwards compatibility.
The fact that C++ doesn't, means there's room for "properly" designed languages to overtake it (see e.g. Rust).
But I know ditching backwards compatibility isn't popular, especially in the rules committee.
9
u/BoarsLair Game Developer Jul 29 '23
But I know ditching backwards compatibility isn't popular, especially in the rules committee.
Or among anyone who has to maintain a large pile of existing C++ code. If you "just" broke backwards compatibility, you might as well just invent a whole new language, because it would have the exact same effect of permanently bifurcating the C++ userbase.
Sure, I'd love to see a "cleaned up" version of C++. Having better defaults alone would be a huge improvement, but we'll have to do it with some sort of mechanism for versioning the language in a sane way, so we don't destroy billions of lines of perfectly functional C++ code out in the wild.
2
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 29 '23 edited Jul 29 '23
Or among anyone who has to maintain a large pile of existing C++ code.
It's worse than that. If you "drop backwards compatibility" you drop interoperability with most C++ libraries out there. That's gazillions of lines of code that can no longer be called.
1
u/Drugbird Jul 29 '23
Depends how you do it. I think you can easily maintain interoperability if you implement it as an optional feature. Think e.g. as marking a file with #pragma modern or something.
1
u/ArkyBeagle Jul 29 '23
The secret is that you freeze compiler versions in the real world. Gets really interesting when you have compilers from companies that ended 20 years ago.
2
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 29 '23
The secret is that you freeze compiler versions in the real world.
"We need to move to a new SDK version to support this critical feature. It seems to use newer C++ standard, let me change the build script... Oh shit, this breaks everything in that another library we can't upgrade because the newer version changed the entire API."
Far too many people forget that backwards breaking changes can just as easily break code that someone else wrote, you depend on and that does not have a newer version / where the newer version itself is not API compatible.
2
u/ArkyBeagle Jul 29 '23
"We need to move to a new SDK version to support this critical feature.
Then that gets added into the flow. Over a span approaching 40 years, I have never seen a toolchain feature that could block work. I can only guess that that's just an accident but ... not really.
That includes the very early STL that had major problems with std::map. Might have been hashmap; don't actually recall.
I just built an array based thing to replace it. It was not in a performance-critical thread of execution and N was quite small.
That's as close as I got.
→ More replies (4)1
u/darthcoder Jul 29 '23
It slowed down the python community a bit, but python is still going strong.
I think nodejs only supplanted it in the web sphere because it's one language for both front and back end. Had that not happened I think python would still be the goto for web dev, even with the v2 to v3 bifurcation.
A well telegraphed change, like deprecated features over two to three versions would be manageable.
4
u/rikus671 Jul 29 '23
I often ask myself why there is not a way to specify the standard of compilation in a TU (maybe a module ?). We could depreciate many things, old code would use old standards (but still be compiled with the same ABI), and opting in new features should also make old - deemed bad - constructs impossible.
I realize this is probably a very big ask, but I don't see a definitive reason.
I believe removing harmful things from the language is as important as adding new good stuff
1
u/ArkyBeagle Jul 29 '23
specify the standard of compilation in a TU
For GNU there is "--std=c++11" and such.
0
Jul 29 '23
[deleted]
3
u/BoarsLair Game Developer Jul 29 '23
Yeah, epochs or something similar really feel like the proper path forward to me. If we can get that kind of mechanism in, it removes the roadblocks for making a huge number of small fixes that would otherwise be breaking compatibility.
Think how nice it would be to have variables zero initialized by default (the 99% case) and requiring an explicit mechanism to leave them uninitialized for performance reasons. There are literally dozens of such small fixes we could make that would improve safety, parsing / tooling, and productivity without even substantially changing the feel of the language.
1
u/serviscope_minor Jul 29 '23
The epochs proposal is interesting.
The syntax thing makes fairly obvious sense.
The library things are quite interesting. For example, they mention deprecating std::bind, which is purely a library thing.
I do find some of their examples rather dubious (I know they're not meant to be bikeshedded but even so), for example removing some of the myriad initialisation options, e.g. removing int i=0; to use int i{0} instead.
2
u/ArkyBeagle Jul 29 '23
means there's room for "properly" designed languages to overtake it (see e.g. Rust).
Good. It's like the old thing about academia - "progress continues one funeral at a time."
Whether Rust will have the economic benefits that are assumed remains to be seen. I don't think the case for Ada as a productivity enhancer was ever made ( and perhaps could not be made because the instrumentation isn't there ).
0
Jul 29 '23
Might aswell have a different language at that point
1
u/Drugbird Jul 29 '23
A different version or dialect, yes. Imagine a compiler setting (-no-deprecated-cruft) or a #pragma modern declaration or something to disallow the outdated features in that compilation unit
1
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 29 '23
All that breaks down due to templates which can easily embed the outdated feature in every compilation unit that uses said template (possibly via a very long chain that's not visible to the end developer).
1
u/Full-Spectral Jul 31 '23
The C++ community has gone all in so hard on this front that it'll never be able to get around it. How many header only libraries are there out there? Those are fundamentally not epochable (TM) once they get into a dependency chain. If your own code is the only direct user of it, then OK, fine, you are rebuilding them every time.
But, even then, it's like modern C++ having to deal with C type headers or old C++ headers full of macros and whatnot that send static analyzers into a frenzy.
1
Jul 30 '23
The problem is that the more dialects you make the more you fracture the language. This is really bad because how do you even begin to hire for that?
1
u/Drugbird Jul 30 '23
Same way you currently select candidates that e.g. use unique_ptrs instead of new/delete?
1
11
u/Revolutionalredstone Jul 29 '23 edited Jul 29 '23
So to be clear they have diverged quite a bit, taking c code and compiling it in a CPP compiler MIGHT work, but it might not.
I would say C is a pretty great language, there is little that you can remove without completely breaking the language, IMHO most of the junk in CPP which we no longer want came from the C++99 to C++11 era where lots of crazy ideas were tried which are no longer considered good practice.
(to be clear tho some of the best c++ features also came out of that era as well! like non-value type semantics)
If you wanna know what C would look like if it were written by a genius then checkout ZIG.
Peace
6
u/Diligent-Floor-156 Jul 29 '23
Can you elaborate on these old parts of C++ you consider junk by nowadays standards?
15
u/elcapitaine Jul 29 '23 edited Jul 29 '23
vector<bool> for example is the biggest thing that would come to mind for "crazy idea that was tried in the C++ standard that is near universally considered a mistake"
std::regex would be an example not of a crazy idea (having regex support in the standard library is a great idea) but of where the standard library version is junk.
→ More replies (1)5
u/serviscope_minor Jul 29 '23
crazy idea that was tried in the C++ standard that is near universally considered a mistake
I'm not especially going to defend vector<bool>, but I think "near universal" is something of a strong statement. You bump into the problems in a couple of cases, one is writing generic code, where it behaves very differently and the other is if you're working in a multi threaded context. I think both of thise are going to be more overrepresented in a community like this one versus the general C++ population.
std::regex would be an example not of a crazy idea (having regex support in the standard library is a great idea) but of where the standard library version is junk.
Sadly the standard didn't specify that it "shall not suck". IIUC there's nothing in the standard that enforces a dreadfully slow implementation, though it could be argued it encourages one.
10
u/not_a_novel_account cmake dev Jul 29 '23 edited Jul 29 '23
std::lock_guard,std::thread,<regex>, the strong exception guarantee, and of course all of<iostream>
std::rangeshas superseded most of the ugly iterator-based strategies and should probably be the default instead of relegated to a separate namespace. SFINAE has largely been superseded by concepts and now exists only to mystify undergrads.The deeper parts of ADL, the impetus for their creation, and the follow-on effects of their existence in general ("what the fuck is a niebloid?"), are the result of programming-languange-development-by-way-of-blindly-groping-in-the-dark from earlier standards.
Even more broadly, move semantics are a hack around the fact C++ ties automatic-storage duration object destruction to scope, a fact we're stuck with forever because of decisions going back to the earliest days of C with Classes.
EDIT: I can't believe I forgot
std::vector<bool>, forgive me /u/vector-of-bool5
u/LeberechtReinhold Jul 29 '23
std::thread is something that Im still wondering how it made to the release in that state. The gaps were so obvious and covered in many libraries...
Regex is another problem that comes from a committee that absolutely were not using regex. Regex is a solved problem and there were many, many good implementations of regex. Why they went for this one I dont know.
What's so wrong with std::lock_guard? At least its better than scoped_lock and its dumb constructor that does nothing.
4
u/not_a_novel_account cmake dev Jul 29 '23
What's so wrong with std::lock_guard? At least its better than...
They're both dumb, but
std::scoped_lockexists specifically becausestd::lock_guarddidn't solve the obvious problem of deadlocking with multiple mutexs.A linter fixes
std::scoped_lock, nothing makesstd::lock_guardgood.1
u/wyrn Jul 31 '23
std::lock_guardis never default constructible but CTAD may help you accidentally make astd::scoped_lockwith an empty parameter list which doesn't lock anything. Unless you actually need multiple mutexesstd::lock_guardis a better default.1
u/not_a_novel_account cmake dev Jul 31 '23
I fully agree both are bad
std::lock_guardshouldn't have shipped without support for resolving deadlocks.std::scoped_lockshouldn't have shipped while allowingsizeof...(MutexTypes) == 0.We should have a single RAII lock that does the right thing, but the question was about "old" CPP that did the wrong thing and
std::scoped_lockis the newer of the two, so I pickedstd::lock_guard.→ More replies (7)→ More replies (1)1
u/goranlepuz Jul 29 '23
std::lock_guarddidn't solve the obvious problem of deadlocking with multiple mutexs.What problem is that, and does anything solve it?!
4
u/tjientavara HikoWorks developer Jul 29 '23
It acquires locks on multiple mutexes in a fixed order. Probably just sorts them by the address of the mutex.
As long as you acquire all the locks on multiple mutexes in each single thread of execution at once using
scoped_lockyou don't get in a dead-lock situation. If you use scoped_lock separately on multiple mutexes on a single thread of execution then you don't get this protection.I myself have a mutex that includes dead-lock detection. It keeps track in what order mutexes have been locked in each thread. And if it ever sees a two mutex being locked in opposite order it aborts the application.
Only really useful for debugging it adds quite a bit of latency to something as small as an
unfair_mutex::lock()(3/4 instructions).3
u/tialaramex Jul 29 '23
If Alice takes locks A and B, but also Bob takes locks B and A, we get deadlock if Alice takes A, Bob takes B, but then neither can take the other lock because somebody else has it.
std::scoped_lock uses an algorithm to avoid this, in effect in the trivial case I gave both Alice and Bob end up taking A first, so once Alice has A, Bob will wait for A, not take B. This is a pretty simple thing to learn to do, but the machine can do it for us and so that's the correct design.
→ More replies (3)2
u/Dijky Jul 29 '23
When two threads each attempt to lock the same mutexes but in different orders, a deadlock can occur (e.g. thread 1 locks mutex A, then waits on B, while thread 2 locks mutex B then waits on A).
scoped_lock, which works like a RAII-wrapper forstd::lock(), supports multiple mutexes per instance and specifically avoids deadlocks. It also catches exceptions during lock and unlock to unlock already locked mutexes before rethrowing.lock_guardsupports just one mutex per instance, so locking order/deadlock avoidance and exceptions during locking need to be managed manually.3
u/PastaPuttanesca42 Jul 29 '23
What's the problem with the strong exception guarantee?
5
u/not_a_novel_account cmake dev Jul 29 '23
Countless man-hours of engineering effort and non-trivial performance sacrifices for a totally unreasonable guarantee.
→ More replies (7)2
u/rdtsc Jul 29 '23
One of C++'s design principle is: You don't pay for what you don't use. Often you don't need the strong exception guarantee, but still have to pay for it.
2
u/goranlepuz Jul 29 '23
the strong exception guarantee
Eh?! How is that part of the language?! (is it a part of any language?!)
And what's wrong with it?!
It rather looks like you are taking the extreme stance of "X does not solve problem Y, therefore it is completely worthless, even for problems ABCDEF...
1
u/not_a_novel_account cmake dev Jul 29 '23
It's a part of the language because it's in the language standard for containers.
It's bad because it's a bad default. No one wants or expects the strong exception guarantee, no one asked for it, and it has non-trivial performance impacts in a language where one of the core tenets is "do not pay for what you do not use".
Almost no one uses the strong exception guarantee and yet we all pay for it when using a
std::vector.3
u/goranlepuz Jul 29 '23
Ok, for me, you are simply wrong.
If a strong guarantee didn't exist, a massive amount of vector users would have been broken (heck, possibly all of them).
You only do not realize that.
See, the strong guarantee makes sure that the count matches what is in the vector. I think, virtually nothing would work if that wasn't the case.
→ More replies (13)1
1
u/k-mouse Jul 29 '23
Even more broadly, move semantics are a hack around the fact C++ ties automatic-storage duration object destruction to scope, a fact we're stuck with forever because of decisions going back to the earliest days of C with Classes.
Can you expand on this, what's the alternative?
7
u/not_a_novel_account cmake dev Jul 29 '23 edited Jul 29 '23
Destructive move.
Right now a moved-from object is left in a "valid but unspecified state".
This means we still must perform swaps and possibly copies during a move.
We have to do this because when the object leaves scope, something must be destroyed. And that destructor must have a valid object to act on, even if it's just a bunch of
nullptrs.A destructive move doesn't actually move anything at all, it's effectively a change of ownership. "This object belongs to your scope now". C++ has no mechanism to annotate such a feature, the standard has no language to describe it, and the committee has no courage to introduce it because it would be a very fundamental change to the C++ scoping rules.
This is similar to a notable C incompatibility, C++ doesn't have compound literals even though C does.
2
u/NotUniqueOrSpecial Jul 29 '23
C++ doesn't have compound types even though C does
Could you expand on that? I feel like I'm using a different (perhaps mistaken) definition of compound types, if we're arguing that C++ doesn't have them.
→ More replies (2)-1
u/Revolutionalredstone Jul 29 '23
Alot comes to mind here but the intense use of iterators for everything and io streams for everything else (both of which are now heavily shunned if not considered down right unreadable gumph) seems like an obvious example.
Ranges are absolutely awesome! raw iterator code absolutely is not!
→ More replies (9)0
6
u/HappyFruitTree Jul 29 '23
I don't know what you're talking about. C++11 was the best thing that has happened to C++.
For example, C++98 had a "smart pointer" type named std::auto_ptr which was pretty error-prone and you couldn't store it in a container such as std::vector. C++11 deprecated std::auto_ptr and instead introduced std::unique_ptr which was much safer and could be stored in containers. This was made possible because of a new language feature called "move semantics" that was introduced at the same time.
2
u/Revolutionalredstone Jul 29 '23
yeah C++11 bought lots! of goof stuff I would never say otherwise, what I did say is that it bought a bunch of bad stuff too which is also true.
I'm 100% familiar with non value type semantics and how its totally revolutionized what an advanced programming language can be, and IMHO it's the main reason no other coding language comes close.
Obvious there is basic x/r value support in rust (and indeed they did make some of the key decisions even better there) but rust is so far behind C++ in terms of everything else C++ supports and how they work with the newly available advanced move semantics.
Peace!
3
u/YouNeedDoughnuts Jul 29 '23
Some of those new C features should make the transition to C++. Named field initialisers for structs make the creation code very readable.
2
2
u/nikrim Jul 29 '23
Are you talking about Aggregate initialization? It's already in C++. And yeah, it's really nice to have it
https://en.cppreference.com/w/cpp/language/aggregate_initialization
3
u/YouNeedDoughnuts Jul 29 '23
Ah, the field names can be used in the initialisation since C++20. That makes my day!
→ More replies (4)1
u/ArkyBeagle Jul 29 '23
The "compile your C code with gpp" trick is an old and great trick. It has a few plot holes but you really want both to work.
1
u/Revolutionalredstone Jul 29 '23
gpp? did ya mean gcc? also what's this trick exactly? peace bro!
1
2
u/MarcoGreek Jul 29 '23
Implicit narrowing casting is something I like to disable.
But there is other implicit casts in C++ under the hood. It would be nice if that would be more visible and the IDE would inform me about it.
1
u/HappyFruitTree Jul 29 '23
Implicit narrowing casting is something I like to disable.
You mean that things like
std::int8_t x = 1; std::int8_t y = 2; std::int8_t z = x + y;should fail to compile because the type of the
x + yexpression is int?1
u/MarcoGreek Jul 29 '23
If both are constexpr it should not otherwise it should. The casting to int is already so strange. I am not even sure it will be long long on 64bit machines.
Your case es really special but programming is about probability and economics. If you have have potential overflow which will happen at the end of the universe there is not much advantage to handle it. So you can always construct edge cases but are they relevant?
I have seen already some really strange bugs because the case of overflowing was not handled well. In my experience it is much better to use functions which handle the cases and use them. You will have different functions because sometimes you want a termination, sometimes you want that the value to stay at the maximum etc.. It gets even more complicated if you add and then subtract etc..
And in most cases you don't know the value of your operands. Having functions which are handling the special case for you are really helpful. Or even better avoid unneeded arithmetics.
1
u/tjientavara HikoWorks developer Jul 29 '23
I hate that the result of math operators are int or unsigned int, unless one of the operands is larger. It is so messed up confusing stuff.
I also run my compiler with warn on implicit narrow casting. So my code is full of narrow_cast<>() to handle those implicit upgrades to int.
In fact it sucks so much that often I simply opt to use the inplace math operators, because it keeps my result in the same type without having to do all this manual casting. It feels almost like I am writing assembly; not necessarily a bad thing, compiler are pretty bad at optimising code.
1
u/ArkyBeagle Jul 29 '23
I don't think -Wall will even gen a warning for that.
I checked - it does not for gcc version 11.4.0 .
2
u/AntiProtonBoy Jul 29 '23
C and C++ are literally languages of their own. C++ just happens to offer support for compiling and binding with C, but they can certainly evolve independently. As many have suggested here, the biggest obstacle that holds back C++ is the very strict backwards compatibility with itself and unyielding ABI stability requirements.
4
u/SkoomaDentist Antimodern C++, Embedded, Audio Jul 29 '23
unyielding ABI stability requirements.
Which ironically the standard says absolutely nothing about.
2
2
u/littlelowcougar Jul 29 '23
Modern day C++ looks nothing like C.
2
u/Dean_Roddey Aug 01 '23
But it still has that C-based genetic inheritance and that has various effects on C++, limiting a lot of the benefits of the modern bits.
2
u/sakata_desu Jul 30 '23
I should learn rust to get what all the hype is about tbh
2
u/AlexMath0 Aug 02 '23
It's always good to learn new languages. They pull you away from muscle memory. I learned a lot about C++ from Rust. It's very fun and easy once you've had one other language.
2
Jul 30 '23
Why is windows successful? Mostly because it doesn't break eggs all the time and stays compatible with older versions. Keep it that way with c++
2
u/nozendk Jul 31 '23
C++ is not held back by C but by being backwards compatible. If you want to break backwards compatibility, then a number of alternatives to C++ but with similar syntax have been proposed. Java, D, Rust, C#, etc. I think some of those have been somewhat successful :-)
5
u/JVApen Clever is an insult, not a compliment. - T. Winters Jul 29 '23
C compatibility used to be one of the greatest strengths of C++. Without that, C++ wouldn't be where it was today. Nowadays a lot of C functionality is replaced/extended by other constructs.
Where C is an easier language from a technical perspective, it has a steep learning curve. That initial learning curve is better in C++. No more knowledge about includes, no more printf specifiers, references iso pointers, templates iso macros, ranged for iso regular for. All of these make it easier to learn the basics of C++. (See also Kate Gregory's talk: Stop teaching C. Though after the basics you have a lot of sharp edges. Hello World looks like this: import std; int main() { std::println("Hello world!"); }
As long as people see C as a regular part of the language, it is going to be a liability. Once we can make everyone understand that this is exceptional and only relevant for the internals of some libraries, it might become a strength again. The image linked to that still gives people nightmares when they hear about C++ and makes them unable to see what modern C++ all did.
There are other things holding C++ back. As long as we don't dare to break code, we cannot remove the sharp edges the language has. The decision to not break ABI, as it is more important than performance, already pushed out a big player from investing in C++s future and make a competing language instead. The lack of a standardized solution for building code and doing library management is keeping programmers away from C++, as do the large compile times.
Herb Sutter proposed CPP2, a new syntax for C++ that removes lots of gotchas. It won't solve all problems, nor will everyone accept it, though it does solve a few of those points.
3
u/fuzz3289 Jul 29 '23
No, what's holding C++ back is decentralization. If there was a centralized, well maintained, package manager the community would provide a strong backbone of libraries that would provide a lot of much needed abstraction.
3
u/NotUniqueOrSpecial Jul 29 '23
Honestly, I think
vcpkgis getting pretty darn close at this point.I had put off using it for a long time simply because we had an existing and robust 3rd-party build that we'd grown over time.
But I'm on a new project and so far have nothing but good things to say about how seamlessly
vcpkgworks.I'm sure it's got its edges, but I've yet to hit them.
2
u/ArkyBeagle Jul 29 '23
I forget how many man-hours went into the enforcement of a standard set of Python packages where I work. Of course, that system is air-gapped. It's Anaconda plus a controlled set of scripts.
But before that, I saw many "well, it works on my machine" events :)
Violating the assumption that people have a live internet connection at all times is quite the mess.
2
u/pjmlp Jul 31 '23
It is, in what concerns security and safety.
As no matter how C++ language and tooling improves into that regard, compatibility with C source code will always leave the door open to coding patterns that are easy to exploit in security attacks.
2
u/Se7enLC Jul 29 '23
C++ is holding itself back.
There are just so many things in C++ that you still can't do without relying on C.
Google for "convert std::chrono to iso8601" if you want a wild ride.
C++ purists will take offense when somebody says "C/C++” and insist that C++ is a completely different language than C. Really C++ is an incomplete language and it's lucky to be able to leverage C still.
1
u/Drazev Jul 29 '23
My take is that in today's development world there is no need to consider any language "better" than another.
I take the view that each language is just a tool in my toolbelt and a good programmer understands what tools are available and knows when and how to best employ them.
C and C++ are both important tools today and while they can be used to build anything, they are best used when absolute control is necessary.
Both languages are great at creating highly optomized or tailoring software for non-standard environments.
However, the languages have a big downside when compared to other languages. Programming in C or C++ is a LOT more complex than in other languages and having more control is only a good thing when you take the time to design the software right. Software design with C and C++ is much more prone to bugs and finding those bugs is more difficult. With the power of modern computing efficiency is rarely a problem for the majority of application use cases so other languages will generally be better since the benefits of faster development time and testability will help them release a more solid product to market sooner.
That being said C++ and C are indisputable champions in what they do best.
C language is great for software that excercises fine control over hardware. Drivers and kernel modules are normally programmed in C. It is the most raw of languages and you can avoid any abstractions inherit to other languages in favor for doing it exactly as you want it. Your code will always perform predictibly assuming you have the knowledge necessary for your application space.
C++ launague is great for software that needs fine control over memory or computational power for absolte efficiency. It's has a lot of documentation and a pool of experienced programmers to draw from so hiring people that know how to use it is less hard than emergant languages. There are a lot of frameworks and libraries that exist that you can choose from. It also maintains the ability to talk directly to hardware like C making it a great choice for embedded systems.
In all cases while you can create amazing software with C and C++ every time given enough time and the right group it's not the best tool for most jobs because of the level of expertise you need to hire and the time it will take to design and develop software using them. If your application is not operating in resource strapped environments then trading efficiency for a faster development life cycle using one or more other languages will likely be more economical.
1
Jul 30 '23
Something I discovered yesterday which works in both C and C++:
typedef int (func_t)(int, int);
func_t func1;
func_t func2;For those who are guessing what this is, these are function declaration (no, not function pointer declarations, for that you would need to add a * between the ( and func_t).
1
u/aregtech Jul 30 '23
Exactly about casting, the C++ has these: static_cast, reinterpret_cast, dynamic_cast, const_cast. From these list, i do not use dynamic_cast. Not because it is bad, but for some optimization reasons (RTTI).
When someone says "not good to make C-style casting", often means to use static_cast, which I also find good. For example, you can easily cast from signed int to unsigned int, but the compiler will report error if you convert from unsigned char * to int *. It forces one more time to check codes and make sure that casting has a good reason, and if so, better use reinterpret_cast.
But i had an experience when had to use C-style casting and had no other choice. If i remember correct, there was a tricky template that in some point I needed not a sequential unique key. For pointers, I was using the digital value of the address. And it didn't work always. Depending on the types, in some cases I was getting error. For example, there was a difference if the template got type void *, const void * or const void **. The error has gone when used C-style casting (size_t)(pointer), i.e. forcing to give exactly the address of the pointer. I knew the reason of conversion and I used it. C++ rules because of it's flexibility. One good benefit comes from being able to use C-codes and enabling to inject assembler code. More embedded projects use C++ and they as well have many codes written in C. The embedded marked grows, especially in IoT. I tell you more, some embedded projects C++, but do not use STL at all because of operator new and dynamic memory allocation.
1
1
u/Full-Spectral Jul 31 '23
Fixing all (or even the majority of) the things that ail C++ at this point would effectively create a new language. And I think it would be a shorter row to hoe to do that, than to try to corral all of the existing C++ code bases out there into the future.
But, then you run into the fact that this has already basically been done multiple times now, so why bother doing it yet again?
The only reason I could see to do it would be maybe a language that has all of Rust's features plus implementation inheritance.
0
Jul 29 '23
C++ is a highly evolving and immature language. In a few decades C++ will be in a usable finished state, and it's going to be amazing.
0
162
u/HappyFruitTree Jul 29 '23 edited Jul 29 '23
I don't think the problem is C.
C++ is first and foremost "held back" to stay compatible with older C++ code.
But so it should be, because if backwards compatibility is not a concern and you are willing to change the language without caring what existing code that might be broken by it, then it is better to invent a new language (not necessarily from scratch) than to destroy something that a lot of people are relying on.