However this document is a bit raw, lots of todos, so I'm not sure how close are we to such flag.
[0] https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
With C++ modules putting an end to the naive #include, translation units could specify the C++ specification they are compliant with, and hence gracefully remove or add new features on a translation-unit level.
I am compiling my C++ codebase with gcc, clang, emscripten to wasm, visual studio. Impossible if all dependencies are not present in a "third-party" folder.
Google's Abseil library is intended to be used this way.
Otherwise, use the C ABI between dependencies.
All major OS have a standard, very stable C++ ABI. Some of them also guarantee a stable ABI for the library components.
Of course you cannot use a library built for, say, Windows on Linux but that's true for all languages.
And then there are other flags - for instance when gcc/libstdc++ broke std::string in order to switch from a recounted string to a non-refcounted with small string optimisation in order to be C+11 compatible ...
Sure, there are system defaults, but things vary and often recompiling everything and statically linking avoids those issues.
Further without buyin from the actual standard there’s no gurantee a breaking change won’t be forced by the standard at some point.
That's only because they changed the standard library. The ABI itself didn't change much if at all, I believe you can still get the latest compiler to link with the far more compatible MSVCRT.DLL that's been there since the first Win32 OS (Win95).
Starting with VS 2015 Microsoft promised to make these changes much less frequent but AFAIK didn't promise to never make changes here. MSVCRT.DLL has kept stability but Microsoft makes no official promises about it. It's considered undocumented.
Source: https://docs.microsoft.com/en-us/cpp/build/reference/decorat...
Undecorated name Decorated name
int a(char){int i=3;return i;}; ?a@@YAHD@Z
void __stdcall b::c(float){}; ?c@b@@AAGXM@Z
OK, Microsoft.That's because it doesn't need to be said. Countless numbers of existing applications would break if it changed, and MS still cares enough about backward compatibility and knows that many huge and important customers depend on them to not do that.
Microsoft promises backward compatibility but not forward compatibility. There is no promise you can link new code with old DLLs. Just that older binaries will still execute unmodified. Very recently with VS2015 they promised "some" forward compatibility and to make fewer changes.
In practice there were things you could do to increase the chances of this working. But to call this "stable" would render the term meaningless.
> The decorated naming conventions have changed in various versions of Visual Studio
Although one might argue that is only a C++ subset.
COM is based on the way Visual C++ lays out the VMT, and only masochists use it from bare bones C.
COM is a C ABI in a sense that its spec defines everything in these terms. Yes, in practice it translates nicely to C++ vtables, but the stability guarantees are defined on the lower level.
On the other hand, the other group values stability and not feature proliferation. They don't want to have a constantly changing foundation, but something stable they can build on. They want a small language which they can more easily master and then use to solve problems with. They think code should not ever need to change again as long as the problem it solves is the same. Their attitude can be summed up as "do what you can with what you have."
The C++ and a lot of newer programming languages (especially web stuff) seem to mainly be composed of people from the former group, which is much larger than the latter, but I think we really need more of the latter group in a lot of the tech industry. My go-to language is still C89 and I don't need anything more, because it gets the job done and it's simple enough to be very portable.
Conflict builds when developers exist in the same ecosystem with different opinions on what this art means to them. It feels like everyone is accomplishing different goals because we really are.
Some people want to continually iterate their code until it matches a perfect vision in their head; It's less (if even at all) about the functionality and more about how we got there. That obviously frustrates people who care about the functionality far more.
Thinking of programming as an art is entirely the wrong direction. Pattern matching, especially exhaustive polymorphic type checking will categorically reduce the amount of logic errors possible with the program. This is logic over art at work.
The main issue for me with c++ is that its overall design has largely been artistic over logical. So tacking on pattern matching makes it an even bigger mess than it already is...
Another person had a similar notion to you and my response to him is more in depth: https://news.ycombinator.com/item?id=21952737
In this case the language we designed is assembly. The parameter for success is: less possibility for bugs. Exhaustive type matching is a definitive and logical improvement because it shrinks the codomain of possible erroneous states, thus less bugs.
My favorite example is the Requests library for Python. It dubs itself "HTTP for Humans" -- I find that quite nice from a human perspective, but it may not be the most logical networking choice in all cases. It's an artistic choice by the developer to make it more human-friendly.
At any rate I think the original discussion is, unfortunately, not the greatest example to make this comment on. Comparing pattern matching to switch-cases is like comparing a high-quality brush to a low-quality one.
Whenever something is "user defined" that means you make it up because you're actually exiting the world of the simple program. These things are akin to business requirements or UX/UI.
A popular word that's often used to figure out these things is the word "design" as opposed to "calculate." When we "calculate" something we are performing a mechanical operation designed to solve a problem with the most "optimal" solution. When we "design" something we are pulling a solution out of our imagination with no way to verify that it is the most "optimal" solution. Note that the word "optimal" is something we make up and define ourselves.
For example: What is the optimal way to get from point A to point B? One definition of "optimal" in mathematics (a small universe similar to programming) is the shortest distance. For this we have a calculation: a line. Another definition of "optimal" in the reality we occupy as humans (a large universe much bigger than math or the simulation in our computer systems) is the shortest time it takes for a human to move from A to B. The solution to this is "designed" we have several options to choose from but to fulfill the requirement of shortest time... we currently tend to choose a plane as it is the fastest vehicle available. However, we have no way of knowing whether the plane we took is the best possible solution humanity has ever come up with. The amount of possibilities here is so large we can't calculate a solution.
Programming within the bounds of "design" requirements and user specified definitions of "optimal" lives in a small universe similar to a mathematical universe of axioms and theorems meaning that we can very well calculate the most optimal programs rather then design a sub optimal one. There is much research on this topic, I'd look into Prolog, category theory, dependent types, formal methods and that kind of thing to learn more. It's very deep and is basically a whole different topic.
So given a portion of the definition of "optimal" that most people can agree on: "less bugs at zero performance cost," pattern matching is a calculated improvement over if statements and switch cases. Thanks to exhaustive matching you must handle every possible instantiation of a type or the program cannot compile. This is a definitive and logical improvement over all other case handling methodologies.
There is no need for an artistic analogy to illustrate a point, the improvement is definitive and logical.
Whenever a human turns to "art" to solve a problem it literally means they don't have the knowledge on how to find the most "optimal" solution. Also very likely they don't even have a clear definition of what they want as "optimal."
For how many generations?
C++ is a good demonstration of both the costs and benefits of adding new features. The costs are obvious: the language and standard library are absolutely full of duplicate features, where the old thing is deprecated in favor of the new thing, but the old thing still has to be supported for compatibility's sake.
But so are the benefits. C++ hasn't always moved so quickly, after all. For 13 years, from its initial standardization in 1998 to its first major revision in 2011, the language was effectively frozen. It was never a small language, but it was stable, not a constantly changing foundation.
It was also absolutely horrid.
This is how you iterate over a std::vector in C++98:
for (std::vector<int>::iterator it = vec.begin();
it != vec.end();
++it) {
int number = *it;
// use `number`
}
One of the features added in C++11 is the range-based for loop, which simplifies that to just: for (int number : vec) {
// use `number`
}
The old style was perfectly working, and still works. The problem it solves has not changed. It's just that it is, and has always been, a terrible solution! The noisiness hurts not only ease of use (obviously), but also robustness and understandability: it tends to obscure the actual logic the programmer intended, making code harder to read.Adding the range-based for loop did burden everyone with additional complexity – but it was clearly worth it. The new style is not just "more 'modern'", it's straight-out better. Sure, code that uses the old style does not need to change. Often it's best to leave it alone. But if so, that doesn't mean the existing code is perfect, just that the benefits of refactoring it don't outweigh the costs (such as the risk of introducing bugs). When writing new code, on the other hand, there's no reason not to use the new style.
Not all features are such a slam dunk. Usually the benefits are not so clear, and often the amount of complexity added is higher. The result is a difficult tradeoff, a competition between different people with different interests, just as you say. People are justified in thinking the benefits are not worth the cost.
But there are benefits, almost always. This pattern matching proposal, for example, adds a ton of syntax and duplicates a feature (the switch statement), and overall it seems more complex than it needs to be. I'm not a fan: I'm not sure whether C++ needs pattern matching at all, and if it does get it then I'd prefer a different design. But if this design were accepted, I'd still enjoy using it in my code! After all, pattern matching itself has decades of history in functional programming languages, and has proven to be an elegant and expressive way to write certain kinds of algorithms and use certain kinds of data structures – things that in today's C++ are rather awkward.
for(int i = 0; i < vec.size(); i++)
// do something with vec[i]
That's even simpler and more in line with what vectors are usually used for, and when it comes time to debug, you don't have to deal with the template-hell that is the standard library iterator-framework. The for : may look simpler at first glance, but quick turns into a nightmare when you're trying to do some deep debugging (and if you haven't had to debug by reading the Asm, I think you haven't done enough C++...) because it obscures a significant amount of complexity which really shouldn't be necessary anyway.You would have to write
for(std::size_t i = 0, N = vec.size(); i < N; i++)
// do something with vec[i]
instead if you still insist in raw loops.But if you look at https://gcc.godbolt.org/z/8JwJbw, you will notice that the range-based loop is actually the one which will produce the smaller code which may translate into more efficiency, and is also more readable.
We haven't even brought const- and reference-correctness into the picture here. That makes the number of potential errors in the it-example even worse. There are also iterators where the size is not known up front, streaming input for example.
The point is, some features in a language can reduce complexity and improve readability (like foreach) while others can increase it (move assignment constructors?). This is only looking at it from a developers point of view though, compiler authors might disagree.
Exactly. Admittedly with enough modern C++ experience and having used pattern matching I find the syntax pretty readable and clear. But even if it weren't I'd probably still be +1 pattern matching.