Most active commenters
  • maccard(7)
  • meindnoch(4)
  • gpderetta(4)

←back to thread

196 points svlasov | 14 comments | | HN request time: 0s | source | bottom
Show context
lallysingh ◴[] No.40851756[source]
Wow this got really long. I was one of the coauthors for a reflection proposal (N3340) over a dozen years ago. Implementing compile-time reflection is honestly trivial - you basically transfer data from the symbol table on-demand into template specializations. It was roughly 1500 LOC to modify g++ to do it.

Looking at the examples (https://isocpp.org/files/papers/P2996R4.html#examples) what really stands out is the direct integration of type-syntax into the language. It fits in with a certain token-substitution way that connects back to templates. It also replaces some of the uglier operators (typeof?).

I hope it goes int! During the language's stagnation I left for a while, perhaps it'll be competitive again soon.

replies(2): >>40851990 #>>40855315 #
stiglitz ◴[] No.40851990[source]
By ”stagnation” do you mean “not getting new features”?
replies(3): >>40852374 #>>40852457 #>>40852476 #
jacoblambda ◴[] No.40852476[source]
C++ has gotten a ton of quality of life features with each update. The issue is less that new features aren't coming and more that new features bake through countless iterations of proposals for close to or often over a decade until everyone in WG21 is happy.

So it's not that we aren't getting features. They are coming quite fast and people regularly complain that new C++ has too many things for them to learn and keep up with. The issue is that those are the same features everyone has been asking for for over a decade so the people that really care found workarounds and eventually move over to the new std way of doing things when they can while everyone else continues waiting for that one feature they really care about.

replies(7): >>40853490 #>>40854139 #>>40854365 #>>40854369 #>>40854878 #>>40855193 #>>40856663 #
maccard ◴[] No.40854878[source]
Also that the features c++ is getting are bolt on additions that we already have solutions for. I think fmt is a great example - fmt is a header only library that can be dropped in. Meanwhile std format was standardised without printing to stout. That took 3 years to standardise. Meanwhile we’re working on things like ranges, and instead of implementing them in the language it’s shoe horned in as a library feature - we now pay massive compile time hits for these features that are being shoved in alongside the kitchen sink. Meanwhile the solution (modules) has been talked about longer than I’ve been writing c++, it’s still unusable, and it hasn’t shown one of the key things people have been begging for for a decade - faster compile times.

I think the committee is focused on the wrong things

replies(3): >>40855086 #>>40856885 #>>40857248 #
1. meindnoch ◴[] No.40855086[source]
>instead of implementing them in the language it’s shoe horned in as a library feature

Quite the opposite. Proliferating the language itself with ad-hoc constructs would be shoe-horning.

replies(1): >>40858602 #
2. maccard ◴[] No.40858602[source]
I disagree completely. Libraries like ranges are dumped into algorithm, and are de-facto considered parts of the language. Reflection has gone back to have range support added, for example. Another one is that span has a performance overhead due to it being implemented as a normal type. If it was part of the language rather than a library type, the compiler could make assumptions about it, but instead it’s treated equivalent to me writing it myself. I would much rather gcc saw me passing a span around and could treat it as a special built in type.
replies(1): >>40859078 #
3. meindnoch ◴[] No.40859078[source]
>Another one is that span has a performance overhead due to it being implemented as a normal type. If it was part of the language rather than a library type, the compiler could make assumptions about it, but instead it’s treated equivalent to me writing it myself.

False. Nothing prevents compilers from giving their own stdlib types special treatment under the hood.

replies(1): >>40859244 #
4. maccard ◴[] No.40859244{3}[source]
That would be an ABI break which is just not happening, and you know it. As it is we’ve decided it’s more important to be able to use std span from libc++ on clang than it is to have an optimised version for people on their tool chain.
replies(1): >>40860319 #
5. meindnoch ◴[] No.40860319{4}[source]
So you're saying that turning std::span from a standard library class into a language feature wouldn't break the ABI? How so? How would such a language construct fit into the existing ABI?

(for context: parent is referring to the fact that x64 calling conventions mandate structs larger than 64 bits to be passed in memory, which means that passing a 128bit std::span is going to be less efficient than passing a separate 64bit index and 64bit length, as those can go into registers)

replies(2): >>40867269 #>>40875837 #
6. maccard ◴[] No.40867269{5}[source]
No, the cat’s out of the bag with span (and unique pointer) now, we can’t go back. We knew this was a problem from unique_ptr and had an opportunity to not make the same mistake again, but instead we chose back compat for a new feature over something novel and performant
7. gpderetta ◴[] No.40875837{5}[source]
What is the issue exactly? Span is trivially copyable and destructible and, at least with the Itanium ABI, it can be passed (and returned) via registers: https://gcc.godbolt.org/z/4rbcshve4 .

Other ABIs might have different constraints but there is no reason why they couldn't special case std::span. In fact if span was a built-in type there is nothing preventing a compiler form picking a suboptimal ABI and being stuck with it. In any case it is not a standardization issue, but purely a QoI.

replies(2): >>40877397 #>>40881412 #
8. meindnoch ◴[] No.40877397{6}[source]
Yes, GCC can pass it in two registers. On the other hand Microsoft's x64 ABI doesn't:

>Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller.

https://learn.microsoft.com/en-us/cpp/build/x64-calling-conv...

9. maccard ◴[] No.40881412{6}[source]
The parent commenter handled the ABI question for me, but I want to respond to :

> In any case it is not a standardization issue, but purely a QoI.

This is an enormous problem in the C++ ecosystem - playing hot-potato with whose fault it is, instead of trying to actually fix it. Span is a decent example, the standards committee get to say it's a vendor issue, and the vendors get to say that their hands are tied by the ABI. The result is that people spend time arguing about who should fix it rather than actually fixing it.

replies(1): >>40881647 #
10. gpderetta ◴[] No.40881647{7}[source]
That's all well and good, but what would you do exaclty? The standard comitee cannot impose an ABI as it would be ignored by implementors. Implementors either own the ABI (MS for example) and are responsible for their owns screwups or there are other bodies that are responsible (the informal forum for the inter-vendor Itanium ABI for another example).

In any case this has nothing to do with std::span being an technically a library type or a built in. There is really no fundamental difference between the two.

For example std::complex and std::initializer_list have special handling on many compilers, just to mention two types.

replies(1): >>40881974 #
11. maccard ◴[] No.40881974{8}[source]
> That's all well and good, but what would you do exaclty?

Start with having the standards committee accept that they are in fact where the buck stops, and that the language includes, whether they like it or not, the toolchain. They don't have to decide upon the toolchain, but their current MO of "toolchain/ABI issue == not our problem (except for when we decide we're not willing to make any backwards incompatible ABI changes, but only sometimes)." The vendors are already jumping through hoops to support what is being standardised (modules being the perfect example here).

I can't speak for std.complex as I've never had to use it, but I think initializer list would be a great example of "how much better would this be if it was special cased into the compiler". The benefit we would get from initialisation being consistent with the compiler far outweighs the benefit of being able to use libc++'s initialiser list with clang.

> There is really no fundamental difference between the two.

Except there is. If I write an implementation of the standard library, and provide an implementation of std span as (abbreviated) - https://gcc.godbolt.org/z/c1sz4neKG it's got to respect the various conventions instead of being treated as an opaque type (like a slice in go). If it's a `_Span`, the compiler is free to go "ok you're using this thing that I know all the internals of, and can reason about. I can elide bounds checks that don't pass muster, I can specify that I will generate code for this specific type that puts extent and data as registers in the following cases". But instead, on x64 (where I work 99% of the time so it's where my effort/knowledge is, sorry), we're bound by >64 == memory.

Now, you might call that a QOI issue, but I'd call it a design flaw that could have avoided an implementation issue, that we see on many features.

replies(1): >>40882071 #
12. gpderetta ◴[] No.40882071{9}[source]
> except for when we decide we're not willing to make any backwards incompatible ABI changes

That's not an exception. The committee is not willing because the implementors explicitly said it is not going to happen, no matter how much Google cries.

> Except there is. If I write an implementation of the standard library, and provide an implementation of std span

if you write it as an user you are constrained by the ABI. But implementors are not: they can bless their own span with superpowers if they want to (in practice they would use special attributes). And there is no reason the compiler can't have builtin knowledge of the semantics of std::span (the same way it has knowledge of printf, malloc and the various math functions for example).

> But instead, on x64 (where I work 99% of the time so it's where my effort/knowledge is, sorry), we're bound by >64 == memory.

[Note this is an MSVC-specific ABI issue not a general x64 one. GCC uses the Itanium ABI on x64]

But the MSVC issue is really a red herring: there is a-priori no reason to expect they would have picked a better ABI for a built-in _Span. The committee cannot force compilers to be optimal (it can't even force conformance).

(Note I'm not singling out MSVC, GCC also has multiple less than ideal ABI decisions).

replies(1): >>40882291 #
13. maccard ◴[] No.40882291{10}[source]
> The committee is not willing because the implementors explicitly said it is not going to happen,

Yeah, and I think this is the problem at the root of my gripe. If the committee was willing to reach this point earlier I think we’d be better off!

> But implementors are not: they can bless their own span with superpowers if they want to

Except that they don’t. And we can go around in circles here - I maintain this is a design issue, and it should be fixed at the design stage, rather than passed on to the compiler vendor who are stuck behind the theoretical design that pretends an ABI doesn’t exist, and their customers who will not upgrade if they break the ABI.

Lastly, I agree that the committee cannot force conformance or optimality, nor should they. But their unwillingness to accept that unless it’s technically impossible, the vendors will move mountains for conformance. This leaves us fighting with each other over who is to blame (see this thread), and in my opinion the end result is a half baked outcome that solves the paper problem but doesn’t solve the actual users wants.

replies(1): >>40882663 #
14. gpderetta ◴[] No.40882663{11}[source]
>> But implementors are not: they can bless their own span with superpowers if they want to > Except that they don’t. And we can go around in circles here

Except they do. Look at GCC code for std::complex compared to the equivalent hand rolled class: https://gcc.godbolt.org/z/nqcvhPWex . edit: note that in this case GCC is just silly with the hand rolled one, but it does show that they are treated differently.

GCC does similar things with span where the class has special annotations for reference tracking to improve warning messages.

Library vs builtin is an implementation issue, not a standardization one. But yes, we are going in circle.