Hacker News new | past | comments | ask | show | jobs | submit login

...yes? The C++ is absolutely ABI stable, and is described in excruciating detail, as demonstrated by all those C++ libraries, that you don't have to recompile for on every OS update.

I suspect you're confusing when you make an ABI change, without changing the API, which is indeed an easy thing to do: You have to make sure that you only add members to the end of objects, however that exact constraint also applies to C.




> The C++ is absolutely ABI stable

C++ ABI doesn't even exist, there are several competing standards (much fewer now than there used to be) and nothing is guaranteed even between different compiler versions, let alone different compilers. Not that long ago we used to have two implementations of std::string in gcc you had to choose from and if the library you're linking with chose another one, you were out of luck.


A thing that does not exist, still is leveraged to build whole distros, and its too high stability resulted in the creation of Carbon.


This happens to work on Linux where the two major compilers (gcc and clang) are (mostly?) compatible. Windows is a different story: C++ code build with MSCV is generally not compatible with GCC/clang and vice versa, the most notable differences being vtable layout and default 32-bit calling conventions.


[edit: and I just realized I replied to you elsewhere. sigh. keeping it classy :-/]

Windows is a different platform, so comparing it to linux isn't relevant, any more than saying I can't run the code I compiled for sparc on a ppc Mac.


But "C++ on Linux" isn't "C++". It's a subset of C++ usage. The lack of competing compilers on some OS might be a feature of those OS, or in the eyes of some mabye even a failure of those OS, but it's certainly no feature of the language. ABI compatibility isn't about using the same binary on different OS, it's about running binaries from different compilers/compiler versions/compiler configurations in the same process.


The point is: C++ libraries compiled for Windows with MSVC are not ABI compatible with C++ libraries compiled for Windows by GCC or clang.


Fragile base class problem is solved, then? When did that happen?


No, but that's also not relevant as C++ is ABI stable. I can take any C++ compiler, and I can compile any C++ library, and then I can take some other compiler and compile some other piece of C++ that calls my library, and it will Just Work.

I can then go back to my library, and make a bunch of ABI safe changes, just as I would have to do in C, and compile my library again, with yet another compiler. At that point the program that was using the older version of my library, would continue to work with the new build of my library without needing to be recompiled.

This is because C++ is ABI stable.

If I am in C++, and I make an ABI breaking change to something that is used as a base class elsewhere, then I have broken the ABI.

But this problem also exists in C, for exactly the same reason, for example lets make some silly example C

If I am a C library, and my header declares a struct

    struct AwesomeThing {
      intptr_t foo;
    };

    void doSomething(struct AwesomeThing* thing) {
      thing->foo = 0;
    }
And then someone uses my library in their code:

    struct ImAwesomeToo {
      struct AwesomeThing theAwesomest;
      int* thisFieldIsGreat;
    };

    int doSomethingElse() {
      struct ImAwesomeToo thing;
      thing.thisFieldIsGreat = malloc(sizeof(int));
      doSomething(&thing.theAwesomest);
      free(thing.thisFieldIsGreat);
    }
Obviously this is a somewhat silly example, but now say I make a change to my library:

    struct AwesomeThing {
      intptr_t foo;
      double bar;
    }

    void doSomething(struct AwesomeThing* thing) {
      thing->bar = 42;
    }
By the standard rules I haven't "broken" ABI, but now that call to free() is going to cause problems.

That's the fragile base class problem.

If you are making an API that will have ABI stability requirements you have to expend quite a bit of effort designing the API so it's not only pleasant to use, but also can be evolved without breaking the ABI. As with C APIs, the people who make C++ APIs, know how to make them robust as well.

That said you could have a C or C++ ABI that is ABI stable, it just hurts performance, and obviously adopting that would break ABI :D

Anyway, the problem with what rust and co are saying, is that the same source can result in different ABIs from one compiler version to the next, or for one API to compile to a different ABI depending on what the rest of the code in the project is doing.

That means the OS can't use the safe language as an actual OS API, which I just think is wasting an opportunity.

Swift manages, and ostensibly performs the same kind of optimizations within a module, which is most of what you want. That said because it's the system API all objects are refcounted, and the recount is atomic - I was writing a raytracer in it (this is how I learn programming languages) and the refcount overhead was annoying to deal with.

Obviously there are trade offs in all the choices, but I still feel like rust, etc could do more. Rust has rich support for annotations, so an "abi_stable" annotation seems like it would be perfectly reasonable - it would be in keeping with rust's general theme of the default behaviour not having any implicit performance costs, but would be easy and very clear when you were making something into an API.


> As with C APIs, the people who make C++ APIs, know how to make them robust as well.

It seems too optimistic for me. I use Gentoo, and I believe that the only reason I have no such troubles is Gentoo with all its maintainers who do a great work of testing different combinations of libraries. And of course `ebuild` that can rebuild libc.so without breaking the system. I tried it once when I just started with linux, and I failed. Never tried to do it again myself, I let `ebuild` do it. It is not so easy, you know, to change version of a library which is a dependency of every binary in the system. And it is a C library. I wouldn't even try to do it, if it was a C++ library. Though who knows, two decades ago when I started with linux I probably was dumb enough to try.


The C++ standard does not define an ABI.

> I can take any C++ compiler, and I can compile any C++ library, and then I can take some other compiler and compile some other piece of C++ that calls my library, and it will Just Work.

This is most definitely not true. It just happens that clang and gcc are mostly compatible, but MSVC and clang/gcc is not! For example, vtables are implemented differently and therefore virtual method calls can crash (unless you follow certain rules, e.g. no virtual destructors, no overloaded virtual methods, etc.)

With 32-bit code, compilers won't even use the same calling convention by default... (look up 'thiscall')

EDIT: strange to see that someone downvoted...


[edit due to the above edit I just saw: wasn't me, I didn't see what you said as being inherently bad and so deserving of downvoting, otherwise I wouldn't have bothered replying :-/]

That sounds like what you're saying is that gcc or clang fail to conform to the platform ABI, which is a compiler bug. If a compiler wishes to ignore platform ABI, it doesn't make the ABI not a thing.

But if you are comparing ABI compatibility to MSVC, you are comparing the ABI used by the code generated by clang or gcc to what is by definition the windows platform ABI. If the code generated by a compiler targeting a given platform, does not match the ABI used by that platform, the problem is the compiler is generating broken code.

> vtables are implemented differently and therefore virtual method calls can crash

No, vtables are implemented incorrectly by the compiler. If the vtable for a type laid down by gcc or clang crashes when it is passed to a OS function, that means the compiler is broken, as the compiler has decided to ignore the platform ABI. Again, if I write a compiler that chooses to use the wrong ABI when interacting with the host OS, I don't get to claim that it is the language's fault, or the OS, or the ABI. Similarly I don't get compile code for linux that makes windows system calls, and then complain that there isn't a windows ABI.

Here is the thing: The ABI for vtables is specified for every non-trivial platform. The ABI for argument parameter ordering is specified for every platform. The ABI for struct layout and struct padding is specified for every platform.

"thiscall" is the ABI for member functions on windows, it's not some magical non-abi thing, is is by definition _the_ abi. There needs to be a name for it, because documentation at the very least has to be able to distinguish it from "cdecl". Importantly, claiming that it's a sign that C++ doesn't have an ABI, or that ABI isn't stable, is simply incorrect. The fact that it exists as a name we can reference is an indication that the ABI matters enough that it is specified. Claiming it's evidence of a lack of ABI is like claiming the C doesn't have an ABI because gcc has stdcall and cdecl - and thiscall is useful MS choosing it as the ABI for member functions on i386 is a reasonable performance win that gcc chose not to do, in favor of using cdecl everywhere, even if cdecl was a slow choice.

There is generally a pile of sadness when dealing with 32bit C++ as the various platform ABIs came into existence when C++ was new, and so the ABI for it on any given platform just happened to be whatever happened to be used by the first releases of the primary C++ compilers for those platforms. That's also why those ABIs - for windows, linux, macOS, etc - tended to not be super well designed or systematically created. Hence even when trying to match ABIs it was easy to hit edge cases where things went horribly wrong between completely different compilers. However even then, generation to generation of each of those compilers maintained ABI stability with themselves at least. Mercifully in the consumer 64-bit every compiler seems to have essentially gravitated to the itanium ABI, which is an actionably thought our and designed rather than evolved as the language is being invented.

So continuing to claim that there is not an ABI, or that that ABI is not stable, or whatever other claim you wish to make, does not make it become true just because you don't like C++. It also does not become true because you are a fan of a language that doesn't want to provide a stable ABI. The historical problems of gcc vs msvc were a product of how the respective ABIs were developed, but on architectures that are more modern it should not be a problem, and I'm sure that if you find places where the compilers differ from the host OS on a more modern platform the developers are much less likely to ignore the issue vs. i386 where they are stuck with whatever their exact ABI was in the early 90s.

In the end, to be very clear, C++ has an ABI, it is stable - a fact demonstrated by Windows, XNU, QT, etc all existing, and continuing to successfully exist. Technically the c++ standard library demonstrates this as well but given the various implementations of the standard library are now maintained largely by their respective compiler projects, that seems like cheating.


Surely if GCC's vtable layout isn't compatible with that used by MSVC then COM couldn't possibly work? Not much of the rest of the Win32 API would care though, other than DirectX and possibly a few more modern additions I'm not aware of.


Luckily, GCC's vtable layout is compatible with COM, but that's because COM puts certain restrictions on how you write C++ classes. Most notably, you must not define a virtual destructor and you must not use overloaded virtual methods. It just so happens that the main difference between MSVC and GCC/Clang concern the implementation of virtual destructors (the former uses 1 entry, the latter uses 2 entries) and the vtable ordering of overloaded virtual methods. This means that COM is not affected!


Thanks for your reply!

First off, I work mainly in C++ (Windows, Linux, macOS and some embedded) and I don't hate the language. I really wished that C++ had a well-specified ABI, but the sad reality is that it doesn't.

> if the code generated by a compiler targeting a given platform, does not match the ABI used by that platform, the problem is the compiler is generating broken code.

The compiler only has to correctly implement the C++ standard. The standard does not specify things like calling conventions, struct padding, implementation of virtual functions, so the compiler is free to do anything it wants. (Whether that is a good idea is another topic.)

> No, vtables are implemented incorrectly by the compiler.

The C++ standard does not even mandate that virtual functions are implemented with vtables.

> The ABI for vtables is specified for every non-trivial platform. The ABI for argument parameter ordering is specified for every platform. The ABI for struct layout and struct padding is specified for every platform.

AFAIK, Microsoft does not officially specify a C++ ABI at all (correct me if I'm wrong!) The only thing that comes close is COM - which can be implemented with a restricted subset of C++, but is really language-agnostic.

> Importantly, claiming that it's a sign that C++ doesn't have an ABI, or that ABI isn't stable, is simply incorrect. The fact that it exists as a name we can reference is an indication that the ABI matters enough that it is specified.

'thiscall' is not part of the C++ standard, it is a calling convention invented by Microsoft. The C++ standard does not talk about calling conventions at all.

> Mercifully in the consumer 64-bit every compiler seems to have essentially gravitated to the itanium ABI

This is true on Linux, but Microsoft specifies its own 64-bit calling convention (https://docs.microsoft.com/en-us/cpp/build/x64-calling-conve...) and leaves other parts of the C++ ABI unspecified.

Generally, you are right that some platforms, like Linux, have a de-facto C++ ABI, but that is not the same as saying that C++ itself has a well-defined ABI - which is simply not true.

Your initial claim was that binaries compiled with different compilers are always compatible (apart from possible library ABI mismatches). We both may wish this were true, but it is false in the general case (and specifically on Windows).


BTW, MSVC has repeatedly broken its ABI in the past. Only since 2015 they guarantee binary compatibility across compilers versions - with certain restrictions: https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: