It's expensive and inefficient to go it alone. It makes it harder to hire and harder to both benefit from and contribute back to the open source community. So they make their pitch and see if anyone else wants to get on board and only if that doesn't pan out will they try to go it alone. The price of going it alone might actually be higher then the benefit.
> Support maintaining and evolving software written in C++ for decades
...
> This requirement should not imply compatibility, but instead some (likely tool-assisted) migratability.
I think the idea is you can gradually deprecate outdated features if there's a way of automatically refactoring them out.
So you might still be able to compile old code, you might just need to run it through a bunch of migration scripts first.
Incidentally, golang did not end up replacing C++ or Java in the slightest, especially at Google. It is in the same space as python.
I am not saying that is the case here one way or the other, though.
Having a PhD means you studied for years to push boundaries, nothing more, nothing less. Lets not forget that the bosses usually decides exactly what should be done with sprints and standup, especially in teams that are not research focused.
This was in contrast to "developers" who wrote application code in numerous languages COBOL, Pascal, Fortran, BASIC, etc. And they in general, did not manage the underlying system.
Over the years I think those lines have blurred and the roles are all smashed together sometimes called devops or other terms.
They did that already with Go (one can argue that Go has very different priorities than C++, but Go matches "simple, easy, customized-for-server apps" perfectly).
I recently worked on a large system that had three virtual baseclasses. But trying to do it any other way would have involved a lot of painful head stands.
I designed BFD using a vtable implemented manually in C. It was quite painful though there was no other choice. I’m glad I don’t have to deal with that any more.
I wrote a “serializer/deserializer” package that did t actually save anything but could represent the data and swizzle it so the in-memory structures could use raw pointers. This could be validated as correct in CI.
Then in a separate I/O library I wrote a module that could generate JSON and store it in a file, using specializations of these virtual baseclasses. When the underlying library changed representation client code didn’t have to written.
My JSON thing was too slow so someone wrote one that backed into a SQL database. A lot faster, though versioning went out the window. Anyway someone could write a fast binary format if that matters and they should all continue to work.
* I mostly gave up on MI in the mid 80s when it was clear it made encapsulation harder to understand (and this was in a language with explicit method combinators too). However I still find mixin classes quite valuable, though again not in widespread use.
Also this means changes to the underlying representation don’t always require changes to the dependent classes, which I suppose you could consider an ABI change.
Maybe an easier way to characterize it is to consider the virtual classes a way to implement plug ins.
Many people consider virtual inheritance a complexity of the language. Why is it possible for a class to inherit from a base class multiple times through multiple paths (the standard case) or to have the compiler coalesce those paths so that the child class only inherits once from the parent/grandparent? The thing is, once you have multiple inheritance, somebody is going to try to inherit from the same base class multiple times. The language can either support it or declare it an error. It’s not clear to me why it’s wrong to for the language to allow it, even though everywhere I’ve worked with a C++ coding standard has said “if your design requires virtual inheritance, discuss your design with other experienced programmers to see if that can be changed.”
It's a no-win choice, though I agree C++ chose the wrong path: trying as much as possible to avoid adding new keywords. It leads to the problem you describe, as well as some confusion in this very thread (which could be my fault, but honestly I don't know -- that's the problem!).
The committee seems to have moved away from this over the years which has lead to complaints of too many keywords and identifiers. Which is why I call it no win.
The function virtual specifier in the base clause is reasonable: if you store something into parent_class.instannce_var and there are two copies of parent_class in your structure which one is stored to and is the choice consistent in all compilation units? This specifier guarantees that the answer is "yes". I don't know why it isn't the default though.
More generally, back in the day I used to use a bunch of different method combinators with flavors (later CLOS). Apart from the useful :before and :after, they generally lead to undebuggable code.
I do think it's a useful feature, except as I think most people finally agree, MI is more often than not a cause for confusion.
It's funny that of all the different method combinators (another source of confusion) only that one was chosen.
BTW I don't think a unique_ptr is needed in the case I described.
For example in some piece of graph-based code I’ve been working on recently, all types returned through public interfaces are pure abstract interfaces. There are different reasons for this, one of them being difficulty to automatically wrap the implementation classes themselves to Python and Java with SWIG, because they use CRTP and other template constructs that are fundamentally incompatible with automatic script interface generation. There is a base interface class INode with an implementation class template Node<T>, but there are also derived interface classes that add additional interfaces to INode, such as ICompositeNode for example. Without virtual base classes it is impossible to have an implementation class CompositeNode<T> that derives from both ICompositeNode (for the interface) and Node<T> (for the implementation), because it is ambiguous. Virtual inheritance solves this.
I never considered this a very special or difficult use case, it seems pretty much any kind of component-bases programming using abstract interface classes would require this sooner or later?
No. Virtual base classes come up in multiple inheritance.
Consider you have a class Circle. It derives from both Shape and Savable. But Shape and Savable both derive from a parent base class Base. Let's say that Base has some member function, print. Also assume that neither Circle, Shape or Savable have an implementation of print. So when you have a Circle object, and you call print, does it go to Shape::print or Savable::print? If Base is a normal (non-virtual) base class, it's ambiguous. The compiler can't resolve it, as the Circle object literally has two Base instances inside of it: one from Shape and one from Drawable. The inheritance hierarchy has two roots, both of which are an instance of Base.
But if Base is a virtual base class, then Circle will only have one instance of Base. The inheritance hierarchy has only one root, which is Base.
This is generally called "the diamond problem": https://stackoverflow.com/questions/2659116/how-does-virtual...
I get that it's a bit of an inconvenient workaround, but it doesn't seem impossible to handle? (At least as far as I've thought it through. I might be missing something though.)
Maybe I'm not even fully grasping the problem myself, but in any case virtual inheritance on the interface classes resolved it ;-S
Confused, isn't that already the case normally? Base classes are already destroyed after derived classes, and owning pointers die before their targets.
It's super common to implement mix-ins, which is as based on templates as it's possible to be, with multiple inheritance, e.g.
struct featureA { int x; };
struct featureB { const char* foo{}; void print(); };
etcand then
template<typename... Args>
struct mixin: Args... {
template<typename... CtorArgs>
mixin(CtorArgs... args)
: Args{args}...
{ }
};
using foobar = mixin<featureA, featureB>;
void f() { foobar{123, "foo"}.print(); }Curiously recurring, even! [0]
Although what you're showing seems to be a variation on the curiously recurring template pattern, as the classes being mixed are listed 'symmetrically' without much regard for which will inherit from which. I found a blog post on 'mixin classes', which are like the CRTP but invert the inheritance relation. [1] What you've shown seems to be a third way.
[0] https://en.wikipedia.org/wiki/Curiously_recurring_template_p...
[1] https://www.fluentcpp.com/2017/12/12/mixin-classes-yang-crtp...
It is unfortunate that there is no explicit way to distinguish OOP is-a from code reuse, other than the presence of virtual functions in base classes, but in my 15 years of professional use of c++ I can't remember it ever being an issue.
And sometimes the same feature is one, sometimes the other. There is no shame in it. Language features are there to be used.
The reason C++ is the federal tax code of programming languages is that it is the accumulation of decades of ideas that seemed good at the time--some even were--where each better way to program ended up as the lifeblood of some special interest group that can never be removed, only piled on top of.
By subsetting C++, you can customize it into almost anything you want with just a few exceptions. You can't meaningfully simplify it in a backwards compatible way without breaking most subsets.
So these people are saying it should be simplified in a non-BC way, the only real way to do it, that preserves their own favored subset.
That's what most of us call a new programming language. Trying to turn C++ into !C++ guarantees they'll be fighting over this for years. With the resources Google has, I don't understand why they don't just throw off all C++-based constraints and use those years to create exactly what they need. With half the PL PhDs on the planet in their employ, you'd think somebody would be available. (Maybe even enough for multiple, parallel teams req'd to share ideas with each other.) It would take a few years to mature enough to rely on, but they'd be spending those years in the C++ cmte debates anyway. A new language called "non-BC C++" will need new tools, new libs, etc., anyway, and they'll have the current C++ to keep using until their alternative is ready.
Why not just create that simple, easy, customized-for-server apps language from scratch instead of making it by breaking C++?