16 ms·
Fun with C++26 reflection: Keyword Arguments
- quietbritishjim 2y agoThis is all, as the title suggests, good fun, but I wouldn't use it for any real code. Instantiating the parameter struct on a separate line adds only a small amount of extra boilerplate at the call site, in return for which it's a million times easier to read than any of these tricks. FooArgs fooArgs; fooArgs.y = 4; foo(fooArgs); // Didn't set .x so it has default value Three lines instead of one seems like a lot of overhead, but in practice you would only bother for a function that takes loads of arguments so the extra overhead is really much smaller. ---- Smaller points: Are the fields in FooArgs really initialised if they're not explicitly set? I can believe they are, after all it's brace initialisation. But IMHO that code isn't super obvious. I'd be more comfortable if they had default member initialisers, i.e., "int x = 0; int y = 0;" in FooArgs. In my version above, you really do need these (unless you remember to brace initialise). It took me a while to see why they bothered to have a string template parameter for TypedArg in the first usage. It prevents mixing up two arguments: if TypedArg didn't have that, then you could call foo(y=3, x=2) and it would compile but have the effect that the parameter x would be 3 and y would be 2.
- oilkillsbirds 2y agoPoor, poor C++... cries in C
- anitil 2y agoI've seen cases where people do things like my_func(/*arg1=*/val1,/*arg2=*/val2) And I suppose you could write a validator to make sure that this worked. Or using an anonymous structure in C99, or a named structure in C89. And of course a pointer if you care about register/stack usage etc. I'm not sure what the other options are.
- ranger_danger 2y agoat that point why not just pass in a struct as your argument
- anitil 2y agoI suppose it depends on your application. I worked in embedded devices where stack was limited and the compiler was .... less than reliable about how it would manage passing a large struct to your function, which is why I mentioned passing in a pointer instead
- ranger_danger 2y agoyes a pointer to struct would be acceptable as well
- RossBencina 2y agoIn 1998 perhaps. It wouldn't be acceptable to today's C++ value-semantics crowd. Are you sure that you want to take the indirection penalty? or that the compiler will optimise it away?
- BodkinsOdds 2y agoC++ is already so reliant on optimizing `const&` parameters that I can't imagine why you wouldn't use them if you happen to be stuck with a shitty embedded toolchain (or MSVC) that can't be relied on to pass struct parameters efficiently.
- gpderetta 2y agoIf stack space usage is an issue, then it means that the struct is already being passed by hidden pointer (as opposed to using a register passing calling convention). Making the pointer explicit won't have any effect on performance (but it will on semantics).
- jcelerier 2y agodepending on the platform this may cause things to be pushed / poped on the stack instead of being passed as registers
- tjalfi 2y agoC++20 added designated initializers, so they're also an option. my_func({.arg1 = val1, .arg2 = val2});
- anitil 2y agoOh I thought that was a C99 addition? It's been a while since I've used them. Edit: struggling to find a source, though this GCC doc suggests it's C99 (but maybe that's only GCC?) - https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html
- AlotOfReading 2y agoIt was C99. It took C++ 21 years to copy it.
- eqvinox 2y agoIt's been the primary and most annoying interop issue for us when we have to integrate C++ code into our primarily C codebase (= build some pieces of our code in C++, which necessarily has to interact with at least our header files). (Second place: differences in available GCC/clang compiler extensions between C and C++ [our software does not support Windows/MSVC], third place: differences in what casts are permitted/how they are done)
- uecker 2y agoMy number one issue are VLA parameters int foo(int N, char buf[N]); which I like to use because they improve warning messages, but they are not accepted in C++.
- Cyph0n 2y agoWell, C is not the same as C++. It even says in that doc that “this extension is not implemented in C++”.
- 2y ago
- brandmeyer 2y ago> And I suppose you could write a validator to make sure that this worked. Like this one! https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argu...
- anitil 2y agoWow I had no idea, that's great!
- wffurr 2y ago>> I suppose you could write a validator to make sure that this worked No need to write it: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argu... Efb
- anitil 2y agoThat's great, I wish I'd known about this ages ago
- mgaunard 2y agowhy not just synthetise a matching aggregate as well as a function that takes that aggregate and forwards it to the normal function?
- anon-3988 2y agoI just don't understand why some people are so fascinated by this. Can you all admit that this is not at all practical? I swear C++ folks like it for the sake of it. No other engineer do this. Only antiques people or whatever. Can you imagine an engineer that is adamant on using his mystifying bespoke tool instead of just using a ruler. "But what if I have to measure it in the 4th dimension!?". I was expecting something simple but good Lord, its kwargs. Not some magical asynchronous runtime. inb4 there are still corner cases so you can't just say "users don't have to know the implementation details so only one person has to suffer". I bet money this abstraction is leaky. Why can't you just do this at the language level like any sane person?
- forrestthewoods 2y agoReflection is spectacularly useful and sorely missed in C++ But because this is C++ the committee designs the most insane and terrible version of reflection possible
- SideQuark 2y agoReflection is such an insanely useful tool that pretty much all modern languages have it. As usual C++ is 20 years behind language design and brings terrible syntax. But it is tremendously useful. https://en.wikipedia.org/wiki/List_of_reflective_programming_languages_and_platforms https://en.wikipedia.org/wiki/List_of_reflective_programming...
- sky2224 2y agoIsn't the reason why so many other languages have reflection due to the fact that they essentially have the information there from the get-go? For example, C# and Java have their intermediate language that has a bunch of meta information attached to it already or the info can be added easily if need be. Is C++ not more raw in its compilation process from code to object files? I mean sure, I guess meta-data could be added to the object files themselves for runtime inspection, but if it were really that simple, why wouldn't they have just added it if reflection is indeed that much of a sore spot for the language's purpose? Now, I'd like to add, my understanding of compiler internals is quite limited, so if I'm way off base here, please correct me. Additionally, while I too find reflection useful, I've seen a lot of cases where it ends up being a bandaid for poor design and gives people an all too enticing shovel to dig themselves deeper into a hole of technical debt. When used correctly, reflection can be quite elegant, but I think those cases are seldom advantageous in comparison to a different and likely better design approach.
- nialv7 2y agoI am scared by what C++ people think is fun.
- badmintonbaseba 2y agoIt's mostly C++. You are right to be scared.
- bluGill 2y agoWhat do you do with your Friday nights? I rewrote the boot sector on a floppy drive in raw machine code (not assembly, I wrote the bytes by hand) before trying to see how I could abuse C++. I consider the above normal engineering/hacking and wonder why someone who wouldn't think the above is fun is doing reading hackernews.
- feverzsj 2y agoThere are tons of more insightful examples in the proposal[0]. C++26 reflection could replace most meta programming tricks, though the syntax isn't the most pleasant. [0]: https://isocpp.org/files/papers/P2996R9.html https://isocpp.org/files/papers/P2996R9.html
- p0w3n3d 2y agofoo({.x=2, .y=2}) I remember using this syntax in C in my 2017 project. This is very clear to call methods like that, I used it with minor #defines, i found the inspiration in the book "21st century C."
- sagarm 2y agoThe post mentions this obvious solution before delving into madness.
- harrison_clarke 2y agoi believe this is standard since c99 you do have to cast it, though: `foo((vec2){.x=2, .y=2})` the oldest MSVC on godbolt also accepts it, so it should be very portable as long as you're not using some embedded compiler from the mid 90s a related fun thing is that you can pass it by pointer, and the lifetime will extend until the function is done: `foo(&(vec2){ .x=2, .y=2 })`
- p0w3n3d 2y agoSmells like UB. Is this defined in the standard?
- harrison_clarke 2y agoC99 spec, section 6.5.2.5: https://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf https://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf > 6. The value of the compound literal is that of an unnamed object initialized by the initializer list. If the compound literal occurs outside the body of a function, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block. it's valid until the end of the block. so, if the function returns the pointer, you can even use that until the end of the block there's actually an example in the spec, on that page: > drawline(&(struct point){.x=1, .y=1}, &(struct point){.x=3, .y=4});
- prabhu-yu 2y agoI liked the syntax of key word arguments in Python. Then asked myself on how come this is not popular in C/C++ world. After seaching internet, found the way and documented here. https://prabhuullagaddi.substack.com/p/simulation-of-keyword-named-arguments https://prabhuullagaddi.substack.com/p/simulation-of-keyword...
- BodkinsOdds 2y agoNeeding that MakeArguments macro makes this substantially worse than just defining an aggregate struct for your arguments and using designated initializers. I've never wanted to reorder named arguments anyway, I've only ever wanted to elide some of them.
- LeicaLatte 2y agoMight be a fun exercise to write a serialuzation/deserialization library using these features.
- thom 2y agoC++ reflection is now good enough that hopefully we’ll start to see more game engines using it to work out components and properties instead of weird macros. jcelerier’s work on things like Avendish really does feel quite fresh and modern, which is not my usual reaction to C++ frameworks. Obviously it’s lagging a good 20 years behind C# et al but we’ve come a long way since IUnknown.
- the_hoffa 2y agoI'm not sure if you mean to have a /s in there, but personally I never really liked reflection. C# had it but it was also in part because it interop'd with .NET which had C++.NET, VB.NET, F#.NET, VBScript.NET, ASP.NET, Core.NET, Web.NET, Net.NET and so much more .. reflection was an "easy" way to have other dev's interact with each others code as a type of "contract". I really like C# and what it can do, but having to check if a particular method exists within an external dependency is in part what lead to "dll-hell" .. it's the antithesis to an API and a "software contract" .. honestly it feels like C++26's "reflection" is more an answer to the ABI issue that has plagued C++ since its inception. If C++ really wants to help "game-engines" or make actual strides, then it should add basic GUI support to the language itself. That'd kill off 90% of the other framework/libraries out there. As with other parts of the language, you don't -have- to use it .. and since it's trying to be the next Java in it's eternal update path, why not add GUI support at a language level ??? Hell the std::thread just calls pthread_create or CreateThread under the hood anyways, just with a 15+ high stack frame .. why not add GUI!?
- spacechild1 2y ago> If C++ really wants to help "game-engines" or make actual strides, then it should add basic GUI support to the language itself. This feels like a total non-sequitur. What does GUI have to do with component and property systems? > why not add GUI!? Because that is 100% out of scope for the C++ standard library. Also, I don't even want to imagine what a GUI library designed by committee would look like...
- the_hoffa 2y ago
- hoseja 2y agoI don't want "powerful reflection features" that are coincidentally another turing-complete system. I just want enums to be nice and Qt moc functionality ;_;
- gpderetta 2y agoIf you are willing to use macros anyway, you can make the following work in C++ today: foo($(bar)=10, $(baz)="hello"); In fact you could 10 years ago when I implemented it[1]; in fact it allows significantly more than just named arguments (named tuples!), but please, consider it as some sort of art and not really something that should be anywhere close to production. [1] https://github.com/gpderetta/libtask/blob/a5e6e16ddc4e00d9f75633823e1a5572fb06869d/tests/q_test.cpp#L178 https://github.com/gpderetta/libtask/blob/a5e6e16ddc4e00d9f7...
- injidup 2y agoVscode and visual studio now add parameter inlay hints so Foo(10, 20) In your code gets rendered in the IDE as Foo(X=10, Y=20) Which solves a subset of the problems named parameters solves, namely the readability of the code.
- lallysingh 2y agoI'm disappointed to see the responses here. Reflection in C++ has been a wiggly sack of cats for decades. Of course it has to be all conpile-time. If you want to reify it, this is the way you get to choose the runtime version you like. And, being conpile-time, it ain't gonna be pretty. The new operator is nice. They found something that'll work compatibly with existing code. This is not easy stuff.
- maleldil 2y agoIIUC, the very first option (designated initialisers with custom structs per function) is how Zig does it, and it seems to work well enough there. It's verbose, so I wouldn't use it for everything, but it doesn't seem all that unreasonable.
- knorker 2y agoI love C++. I've coded in C++ for like 30 years at this point. C++11 breathed new life into the language, to the point where it's a different and much better language now. But some of these new features… I feel like they're a bit desperate attempts at fitting in with the kids. Not to start a language war, but I don't see how any attempt at stapling modern features onto C++ make it a good choice in 2025. There are at least two viable plug in replacements, that have good interop. Like I said, I've coded C++ for 30 years (along with other languages, sure), so I'm not a fad follower. I don't say it lightly, but I do say that coding C++ in 2025 means creating technical debt. And these features won't change that.
- pipeline_peak 2y agoReflection always seems like a convenient way for programmers to write less code at the cost of performance overhead. Also the type of trivial code that could be easily generated by an LLM anyway. Things like serializers and MVC event bindings come to mind.