Live data from Hacker News

Dropping support for old C++ standards

lists.boost.org

61–70 of 100 posts

Re: Dropping support for old C++ standards

#61
post #56

Earlier quoted context omitted.

C++ doesn't have the same kind of head as you are thinking. The standard gets pretty well-tested before being standardized as the most recent ("head") version. C++'s head gets more testing than most libraries ever get for any version.

Anyone with proper experience on C++ ecosystem knows this isn't the case. Not only is ISO full of DR and things that probably shouldn't have been standardized in first place (thankfully some of them were latter removed), there is plethora of compilers to chose from.

Most people looking for modern C++ are choosing one of three compilers. Most code mostly works.

Re: Dropping support for old C++ standards

#62
post #2

Titus Winters leading the google abseil library eventually came to the conclusion that the only sane way to manage a large scale C++ system is to "live at head" [1] -- that is, libraries should live at the head production version of their dependencies. This is patchworked around in more easygoing languages with dependency management systems, docker containers, etc. etc. but if you can enforce living at head from the…

Google concludes the way they’ve always done things is the only way that scales, news at 11. Staying at HEAD can be quite nice in some respects, but the conclusion there only applies to Google, and only then the Google that was actively created to be amenable to the results you’re seeing.

Re: Dropping support for old C++ standards

#63
post #28

Earlier quoted context omitted.

I've never worked in a big C++ code base or had that issue but could you library B in a namespace?

Do you mean have library B built with its own separate copy of the new version? e.g. You have Library A using LibDependency-1.0.0 and Library B using a separately compiled LibDependency-2.0.0? Then have MyAwesomeApp linking LibA and LibB and just accept the binary+memory overhead of two copies (albeit different versions) of LibDependency?

Probably, unless you need to share LibDependency data structures between 1.0.0 and 2.0.0, in which case, it depends on the implementation of LibDependency.

Rather than trying to do this at the language level with namespaces or whatever, it's probably easier to compile and link each version of the problem dependency into a separate library (static or dynamic), then to make sure each of your own libraries and executables only directly links to one version of the problem library.

This way, you don't have to rename or namespace anything, because conflicting names will only be exposed externally if you're linking to the problem library dynamically, in which case you should be able to arrange for the names to be qualified by the identity of the correct version of the problem library at dynamic load time (how to ensure this is platform-specific).

Re: Dropping support for old C++ standards

#64
post #56

Earlier quoted context omitted.

Anyone with proper experience on C++ ecosystem knows this isn't the case. Not only is ISO full of DR and things that probably shouldn't have been standardized in first place (thankfully some of them were latter removed), there is plethora of compilers to chose from.

Most people looking for modern C++ are choosing one of three compilers. Most code mostly works.

Those three compilers don't work everywhere, aren't allowed everywhere, and mostly works isn't "The standard gets pretty well-tested before being standardized as the most recent".

How were GC API, auto_ptr, modules, concepts and co-routines pretty well tested?

In those three compilers they surely weren't, so in which ones?

Re: Dropping support for old C++ standards

#65
post #21
post #13

Earlier quoted context omitted.

That's fine as long as HEAD doesn't break things. I can no longer count the number of times we had an issue with a "supposedly" minor release that ended up breaking major things in our stack. Most of them were things that could have been detected using unit tests or some kind of basic regression testing. If you have a 1000 dependency packages, and at any point in time 0.1% of them are broken, then odds are you will a…

We should be clear... in these large organizations... HEAD is always broken. But it has the advantage of being broken for everyone, tested by everyone, fixed by everyone, and thus fixed for everyone. And this usually makes it far better than the alternatives. Having 1000 dependencies with versions pinned means you are living alone and will run into fewer issues, but when they do come, they will be absolute nightmares…

> it has the advantage of being broken for everyone, tested by everyone, fixed by everyone

The correct word here is not advantage but risk.

While making a product you rely on something, some other product/package. Thousand of them if you are unlucky. If you have 1000 packages to collaborate in the development/testing/QA of them you will do nothing else! Also try building a house on a concrete that is still maturing, with tools not ready yet, I dare you! Careful developers rely on reliable things. It is a shame in this industry this is not available. Either released(!) things are not ready yet, or are in demise already, the sweet spot is tiny.

Re: Dropping support for old C++ standards

#66
post #2

Titus Winters leading the google abseil library eventually came to the conclusion that the only sane way to manage a large scale C++ system is to "live at head" [1] -- that is, libraries should live at the head production version of their dependencies. This is patchworked around in more easygoing languages with dependency management systems, docker containers, etc. etc. but if you can enforce living at head from the…

I'm not sure to understand: What "living at the head" means?

Re: Dropping support for old C++ standards

#67
post #38

Earlier quoted context omitted.

Does that include the head of GCC/LLVM?

It does. Google mirrors every commit to LLVM in their monorepo, builds and tests the whole monorepo with a fresh Clang nightly, and (ideally) one of those Clang nightlies is released as the new stable compiler for all users of the monorepo every week. This helps keep Google at HEAD and helps keeps LLVM upstream stable.

I thought llvm lived in /third_party, and internal projects usually target specific LLVM versions, not the current open source trunk.

Re: Dropping support for old C++ standards

#68
post #28

Earlier quoted context omitted.

Do you mean have library B built with its own separate copy of the new version? e.g. You have Library A using LibDependency-1.0.0 and Library B using a separately compiled LibDependency-2.0.0? Then have MyAwesomeApp linking LibA and LibB and just accept the binary+memory overhead of two copies (albeit different versions) of LibDependency?

I've never tried it but I read that a use for namespaces was talking a library and wrapping all the #include statements in namespace library {} or whatever to avoid one stepping on another. Depending on how the library is written (and if it's all in source form rather than .lib files) I guess it should work?

The problem comes from what happens when you're trying to use LibDependency from your own code, say for example you're have something like:

    LibDependency.h:
      typedef struct SomeOpaqueLibDependencyType * SomeOpaqueLibDependencyTypeRef;
      SomeOpaqueLibDependencyTypeRef MakeTheThing();
      void UseTheThing(SomeOpaqueLibDependencyTypeRef);

   
    LibraryA:
      ...
      SomeOpaqueLibDependencyTypeRef getSomething();

    LibraryB:
      ...
      void doSomething(SomeOpaqueLibDependencyTypeRef);

    MyAwesomeApp:
      LibraryB.doSomething(LibraryA.getSomething()) // pseudocode
The problem is the because LibraryA and LibraryB have distinct copies of LibDependency, the source/api compatible type you're using may have an incompatible internal structure.

As a library author there are things you can do for ABI compatibility, but they all basically boil down to providing a bunch of non-opaque API types that have some kind of versioning (it's either an explicit version number, or it's a slightly implicit size field which IIRC is the MS standard). You also have opaque types where the exposure of details is more more restricted, generally either just an opaque pointer, or maybe a pointer to a type that has a vtable (either an automatic one or a manually constructed one). In general use of the non-opaque portions of the API are fairly restricted because they have ABI implications, so a user of a library will communicate by providing those non opaque data to the library, but the library will provide largely opaque results with an ABI stable API that can be used to ask questions of an otherwise opaque type.

This works in general, and it means you don't have to rebuild everything from scratch any time you update anything. It breaks down however when you have different versions of the same library in the same process. The problem is that while you see a single opaque type, it's not opaque to the library itself so while an opaque type from two different versions of a library may look the same to you, the implementation may differ between the two versions. Take a hypothetical:

    LibDependency.h:

      typedef struct OpaqueArray *ArrayRef;
      struct ArrayCallbacks {
        int version;
        size_t elementSize;
        void (*destroyElement)(void *);
        void (*copyElement)(void *, const void*);
      };
      ArrayRef ArrayCreate(const ArrayCallbacks*, size_t);
      void ArraySet(ArrayRef, size_t, void*);
      void *ArrayGet(ArrayRef, size_t);
which is a kind of generic vaguely ABI stable looking thing (I'm in a comment box, assume real code would have more thought/have fewer errors), but lets imagine the a "plausible" v1.0

    LibDependency-1.0.c:
      struct InternalArrayCallbacks {
        void (*destroyElement)(void *);
        void (*copyElement)(void *, const void*);
      };
      struct OpaqueArray {
        InternalArrayCallbacks callbacks;
        size_t elementSize;
        char buffer[];
      }
      ArrayRef ArrayCreate(const ArrayCallbacks* callbacks, size_t size) {
        size_t allocationSize = sizeof(OpaqueArray) + size * callbacks->elementSize;
        OpaqueArray *result = (OpaqueArray *)malloc(allocationSize);
        /* initialize result, copy appropriate callbacks, etc */
        return result;
      }
      void ArraySet(ArrayRef array, size_t idx, void* value) {
        array->callbacks.copyElement(array->buffer + idx * array->elementSize, value);
      }
etc.

Now v1.1 says "oh maybe we should bounds check":

    LibDependency-1.1.c:
      ...
      struct OpaqueArray {
        InternalArrayCallbacks callbacks;
        size_t elementSize;
        size_t maxSize;
        char buffer[];
      }
      ...
      void ArraySet(ArrayRef array, size_t idx, void* value) {
        if (idx >= array->maxSize) abort();
        array->callbacks.copyElement(array->buffer + idx * array->elementSize, value);
      }
There's been no source change, no feature change, and from a library/OS implementors PoV no ABI change, but if I had an ArrayRef from the 1.0 implementation and passed it somewhere that would be using the 1.1 implementation, or vice versa, the result would be sadness.

As a library implementor there's a lot you have to do and/or think about to ensure ABI stability, and it is manageable, but more or less all of the techniques break down when the scenario is "multiple versions of the same library inside a single process".

Re: Dropping support for old C++ standards

#69
post #42

Earlier quoted context omitted.

Exactly! People point out RedHat provide newer compilers (and newer patched C++ standard libraries depending on the default one!) and he says "It's not us, it's our customers. And no, they won't use any compiler which isn't the system default". Either you only use the system defaults, and that includes the old Boost included with RHEL, or you don't. I guess he includes a private Boost copy as part of his project. Wel…

Customer is king, when one needs them, it is the one trying to make money that needs to accommodate. When not, others will.

Customer is king indeed but why they expect/want to use latest boost if their infra is old? Use the latest version your infra supports, simple!

Re: Dropping support for old C++ standards

#70
I really like what Herb Sutter is doing with cpp2/cppfront: it's a new language that translates to C++, by defaulting to C++ good practices and "avoiding 95% of C++ pitfalls". Please watch its presentation on it. It's designed to interact with C++.

Backward compatibility is good to have, but C++ needs for alternatives that allow it to drop support for old things, because the language needs to evolve and backward compatibility is preventing it, and it turns into very very long compile time.

And even if backward compatibility is out in C++, it would break in the language, not in the ABI, so old C++ and new C++ could still easily cohabit together, quite like C has always lived with C++ for a long time now.

I like C++, but nobody denies that C++ carries a lot of weight for being disliked.

Post reply on HN