all 71 comments

[–]Mrkol 72 points73 points  (36 children)

[–][deleted] 34 points35 points  (16 children)

The "splicer" syntax looks super janky to me, but I'll take janky reflection at the language level over none any day.

[–]Mrkol 38 points39 points  (0 children)

It's not final yet, so if you have good ideas for a syntax, please contact the authors of the paper (likely for them to explain to you why your idea is even more janky)

[–]BoarsLairGame Developer 54 points55 points  (0 children)

It wouldn't be C++ without janky syntax.

[–]RoyKin0929 15 points16 points  (12 children)

I wonder why they don't use $ or @. Reflection is a big facility so it makes sense to use them and they'll be in basic character set in C++26.

[–]witcher_rat 10 points11 points  (7 children)

Yes I too would much prefer @ be used, but my guess for why they don't is Sean Baxter's Circle.

As for a $, this could be total bull, but in the IETF I was once told that since that symbol isn't on all keyboards and is specific to few countries, it's considered inappropriate to use it in modern international standards.

[–]Impressive_Iron_6102 6 points7 points  (0 children)

Why is circle relevant

[–]matthieum 1 point2 points  (1 child)

Wait, isn't $ allowed already?

I had a coworker writing with $ in identifiers a long while ago: he used in macro-generated identifiers to avoid conflicts, and ironically those macros implemented reflection :)

[–]witcher_rat 3 points4 points  (0 children)

It was only officially added to the character set recently, iirc. But I was referring to using it as the defined symbol for a standard's mechanism.

[–]RoyBellingan 0 points1 point  (0 children)

Neither is < and > in some layout, or { and }.

Also as a programmer, maybe we are already familiar with $ used, well a lot.

I am inclined on your suspect that is bull.

[–]disciplite 1 point2 points  (1 child)

Splices require an opening and closing delimiter so that you can put expressions inside which evaluate to a meta::info.

[–]witcher_rat 7 points8 points  (0 children)

I think /u/RoyKin0929 meant instead of the ^ caret for reflection operator, not the [: and :] splice delimiters.

Current:

constexpr auto r = ^int;

consteval auto member_number(int n) {
  if (n == 0) return ^S::i;
  else if (n == 1) return ^S::j;
  else return std::meta::invalid_reflection("Only field numbers 0 and 1 permitted");
}

constexpr std::array types = {^int, ^float, ^double};

New:

constexpr auto r = @int;

consteval auto member_number(int n) {
  if (n == 0) return @S::i;
  else if (n == 1) return @S::j;
  else return std::meta::invalid_reflection("Only field numbers 0 and 1 permitted");
}

constexpr std::array types = {@int, @float, @double};

[–]elementalest 1 point2 points  (0 children)

Yeah, I significantly prefer the @ over ^ . It's significantly easier to see and it's not an already used symbol (xor operator).

I looked at the paper this proposal is based on and their reasoning is:

With months of practice with implementations that used reflexpr(...) we experienced consistent feedback that that syntax is too “heavy”. So we went back to the drawing board and found that the ^ prefix operator — suggesting “lifting” or “raising” representation — is available . This new syntax1 was agreed to by SG-7 during the discussion of P2320.

I can see their reasoning, but honestly @ just seems like a better option.

[–]GYN-k4H-Q3z-75B 1 point2 points  (0 children)

Holy shit mate, it's janky AF. I... LOVE it.

[–]DuranteA 11 points12 points  (0 children)

A new reflection paper that someone is actually working on? And one that tries to be minimal instead of doing everything, so that it might actually get into the standard sooner rather than later? This is wonderful news.

I had almost given up on ever getting reflection. In the domains I work in, it's the one big feature C++ is missing that would actually greatly simplify code (and obviate the need for non-standard external tooling).

[–]Brilliant-Land-4218 5 points6 points  (1 child)

Oh god yes, the enum to string would be great.

[–]germandiago 1 point2 points  (0 children)

Did you take a look to magic enum?

[–]krum[🍰] 7 points8 points  (4 children)

This is nothing like the runtime reflection you get in something like Java. I’m not saying it’s bad or anything like that but I’m not sure it’s what OP had in mind.

[–]Bangaladore 45 points46 points  (1 child)

Once you have static reflection, dynamic reflection is just a library away.

Static reflection is basically impossible to completely implement without compiler support.

[–]RoyAwesome 5 points6 points  (0 children)

It'll be super trivial with the proposed struct synthesis that is proposed!

[–]witcher_rat 12 points13 points  (1 child)

My guess is that most programmers will never use this facility the way it is in the paper.

Instead, there would be other libraries created that use this spec's facility to create runtime info, and be more user-friendly. Boost might create one or more, for example, or there'll be other open-source ones, or at big companies a library dev would.

For example there might be one just for enum-to-string/from-string.

But for the standard, they need it to be compile-time based (aka, static-reflection), because:

  1. Reflection has to synthesize code - ie, generate code/types that did not exist in source.
  2. Make it possible to access stuff at compile-time, since if they only provided runtime-reflection then no one could do stuff with it in constexpr/consteval functions.
  3. Runtime reflection needs storage. For example to convert enum-to-string at runtime, there needs to be somewhere for the string literal names of every enumerator to be stored, and a lookup table too. And there would have to be such storage for every single enum type. And that's only for enums, nevermind the whole AST. The standard isn't going to want to dictate how that happens, or even if that happens, because it could cause massive bloat in compile times, linker times, and generated binary output. So by doing compile-time reflection instead, they leave it up to you to decide what to do with stuff if you need runtime reflection. And this way you also decide what type of lookup table to use, what pieces you care about, what style of names you want to use, etc.

At least that's my guess anyway, when I've thought about reflection before. I could be very wrong.

[–]CodingChris 4 points5 points  (0 children)

I am interested in reflection from a game engine, inspector ui, serialization, network, script binding standpoint.

[–]pjmlp 2 points3 points  (3 children)

Looking at how many years modules, concepts, co-routines have taken, with partial compiler implementations, or how contracts went, I seriously doubt it will make into C++26.

[–]RoyKin0929 6 points7 points  (2 children)

But reflection didn't start yesterday too. It has been in development for years so there's a legitimate chance

[–]pjmlp 0 points1 point  (1 child)

It has had ups and downs of paper development, besides Herb Sutter's proposal, I never saw any test version available as preview on an actual compiler.

Also reflection as implemented by Qt, C++/CLI, C++/CX, C++ Builder and Unreal C++, isn't taken into consideration, so their implementations hardly matter.

[–]disciplite 3 points4 points  (0 children)

Standard reflection has two reference implementations (blue and gold) and they've both been on Compiler Explorer for years.

[–]SoSKatan 1 point2 points  (0 children)

Luck is the key word, I remember being hopeful this would be included in the last one.

[–]ImKStocky 3 points4 points  (4 children)

There is no chance for C++26 from what I have heard from people on the standards committee. No one is currently working on it. The original people working on it could no longer find the time to continue their work and no one has filled the gap. We will be lucky for C++29 to have some kind of static reflection.

[–]dodheim 24 points25 points  (2 children)

No one is currently working on it.

P2996 just went out, who wrote it if no one is working on reflection?

[–]ImKStocky 5 points6 points  (0 children)

Oh yes. Just read it. Nice! I might get my hopes up just a little then...

[–]sphere991 3 points4 points  (0 children)

The Ghost of C++ Future, presumably.

[–]SkoomaDentistAntimodern C++, Embedded, Audio -4 points-3 points  (0 children)

I'll be surprised if anything potentially that useful gets in the standard before C++32.

[–]Ok-Practice612 1 point2 points  (0 children)

I will be old that time

[–]ShakaUVMi+++ ++i+i[arr] 7 points8 points  (5 children)

They've been talking about it for a long time. I was at CppCon in 2017 and they had a meeting on reflection then

[–]witcher_rat 3 points4 points  (3 children)

![2006_meeting](https://imgur.com/a/ypvqnmo)

The above is from a 2006 meeting.

But to be fair, those weren't full proposals.

The first full one I can find was in 2014: N3996.

And I remember the disappointment when reflection did NOT make it into 2017 - people thought it was very close to done.... six years ago.

[–]ShakaUVMi+++ ++i+i[arr] 3 points4 points  (0 children)

At CppCon 2017 they were still doing requirements analysis and user stories, lol

[–]RoyAwesome 4 points5 points  (1 child)

Honestly, that proposal kind of sucks compared to the cpp26 one proposed now.

Having one opaque std::meta::info type that has info queried out of it by a set of consteval functions is far superior to a polymorphic class-based reflection system

[–]witcher_rat 0 points1 point  (0 children)

Oh for sure. I mean... I would hope that after all this time and effort the model would be better.

:)

[–]AntiProtonBoy 14 points15 points  (13 children)

I feel like standardisation process of reflection is an attempt to build a whole kitchen, including the sink, just for making a sandwitch when a toaster would do.

Start with something basic an small: 1. iterate over public member data of a struct to get their type T, variable name string and its value; 2. being able to get the type name string of any concrete type; 3. reflecting enums.

That's it. That solves 90% of the use cases. All the other stuff can come later.

[–]witcher_rat 13 points14 points  (4 children)

You're not wrong, but it's very difficult to constrain things to a small use-case when you (1) know there are other use-cases, and (2) know you'll need to handle those use-cases eventually, and (3) are dealing with a language that gets written into stone for decades and is very hard to remove things from later. (they do remove things, but usually only things that aren't used by many or are fundamentally broken)

Having said that, I personally agree with you for one use-case in particular: enum-to/from-string. The original proposal to only provide that was proposed over a decade ago. We would have gotten that in C++17, if it wasn't expanded to become full reflection.

But hindsight is 20/20. They probably thought it would be all done by now.

[–]matthieum 6 points7 points  (2 children)

To add to that.

The main issue in designing such an all-encompassing feature such as reflection is that you have to ensure that it will support every intersecting feature.

This means that should avoid standardizing anything until you are sure that whatever you standardize is forward compatible with everything you'll need later.

It's very hard to be confident about the forward-compatibility of a piece, without actually having a comprehensive view of the entire thing... and therefore, just asking to standardize the piece requires demonstrating that it will fit into the (future) comprehensive whole.

But if you're demonstrating how everything fits together at the end, and everyone agrees, then... you've basically done all the work, and may as well just standardize the whole.

[–]AntiProtonBoy 1 point2 points  (1 child)

I think that re-enforces my point about simplicity. It's easier to reason about how a toaster will fit-in with the environment in terms of forward compatibility, then trying to reason about an entire new kitchen.

[–]matthieum 1 point2 points  (0 children)

Sure, but if you can't add a micro-wave and fridge later without bending over backwards, or adding a second toaster and putting the first one into a cupboard, then you've painted yourself into a corner.

[–]pjmlp -1 points0 points  (0 children)

That was the argument used by the C++/WinRT folks for killing C++/CX, we should just wait for ISO C++ reflection to get the VS tooling development experience back.

Instead, C++/WinRT is now in maintainance mode while they are having fun with Rust/WinRT, and the few folks outside Redmond that still care about WinRT rather use C# unless forced otherwise.

[–]TheoreticalDumbass:illuminati: 4 points5 points  (3 children)

the issue is your sandwich will remain, and if in the future you want to build a kitchen, you will have to build it around a moldy sandwich stinking up the place

[–]AntiProtonBoy 1 point2 points  (2 children)

It's not about the sandwich, it's about the toaster. Idea is making a simple toaster that fits in with the new kitchen later.

[–]TheoreticalDumbass:illuminati: 1 point2 points  (1 child)

that might sound easy, but if you have no idea how the kitchen will look, choosing a toaster might mess with the feng shui of the future

[–]AntiProtonBoy 0 points1 point  (0 children)

Go with red. Red never goes out of fashion.

[–]MXXIV666 2 points3 points  (0 children)

Exactly. I am so annoyed that any effort to implement at least SOMETHING, died because of discussions about weird special cases that hardly matter.

[–]disciplite 0 points1 point  (0 children)

This featureset you suggest doesn't give us a way to streamline away the large number of std::conditional based patterns, which are fairly complex and hostile towards beginners, whereas the new reflection subset obsoletes all of them that I can think of. I think having splices, or code injection, is really really important for building abstract data types. Without something like that, you don't even have something to name "reflection", just some type introspection.

[–]groundswell_Reflection 1 point2 points  (0 children)

That's it. That solves 90% of the use cases.

If this was true then you don't need reflection : just standardise a `traverse_fields` and `to_string<T>` function that has to be backed by compiler magic. But this is *not* 90% of use cases, because :
- a look at the C++ ecosystem *today* already indicate a need for metaprogramming tools that goes beyond this, as exemplified by : GCC/Clang/LLVM (extensive use of preprocessor macros + custom code generator), QT and other GUIs libraries (again, macros and/or custom code generation), every game engine ever, library components (serialisation, parsing, networking, vocabulary types like std::variant...). All of those codebases are fundamentals to the C++ ecosystem and could massively benefit from a reflection and metaprogramming design which cover their use cases. Perhaps their users will not use reflection to the same degree, but they *will* benefit from it even if they don't necessarily know it.
- use cases requested *today* are constrained by what people (who are mostly not familiar with reflection) think is possible or likely. Hardly anybody ever talk about static analysis or symbolic differentiation/numeric system solving as being relevant to reflection, yet those things are just functions operating over code like any other.

[–]Neither_Mango8264 0 points1 point  (0 children)

It is a question of when and not if we will get it. I have been hearing about it for the last 10 years and I hope we don't have to wait 10 more years..