17 ms·
Reflection for C++26
- pjmlp 2y agoNote that there are links pointing to examples on Compiler Explorer, using the EDG and clang preview implementations.
- a_e_k 2y agoYes, that's a clever way of demonstrating viability (and with more than one compiler implementation). I do like the examples that I see there. This seems like the kind of language feature that I might not make much use of directly in general application code, but would wrap up in utility functions or use via lower-level libraries that the application code builds on. E.g., they showed command-line parsing, but I could also see this for benchmarking and testing frameworks to automatically find the workloads and tests in a non-hacky way. I also wonder about how this feature interacts with translation units or modules and linkage, though. I'm reminded of the static initialization order fiasco; this seems like it might open up issues that make that look tame by comparison. (Not a complaint; I'm actually looking forward to this.)
- pjmlp 2y agoI am a big defender that the only way to fix many of the mistakes that ended up in the standard is to adopt the same policy as other languages, papers without preview implementations shouldn't be accepted. There are still a few gotchas being ironed out, there was a talk at ACCU about many corner cases.
- Dwedit 2y agoCompile time or runtime? Compile time reflection would be completely painless and bloat-free.
- armchairhacker 2y agoCompile-time (“static reflection”)
- shortrounddev2 2y agoRTTI is a super vital feature for deserializing without having to generate code or write a ton of tedious boilerplate. I'd be very happy with RTTI in C++ and my life would be instantly easier if there were RTTI in typescript so I didn't have to use any of the hacky solutions out there for deserializing JSON on backends without RTTI. I suppose C++'s template system might be able to generate JSON deserializers with static reflection as well
- chongli 2y agoYou don't need RTTI to deserialize data in a clean way. What you need is return-type polymorphism. Haskell has this and it makes writing serializers and deserializers symmetric and totally painless.
- fooker 2y agoReturn type polymorphism and inheritance doesn't mix very well. Swift got into this mess early in it's lifecycle and it's type checking is still more expensive than the rest of the compiler combined, and unpredictable on top of that.
- chongli 2y agoYeah if you ask me, inheritance is the one to go. Every time. Inheritance just makes things more complicated. It’s not a great tool of abstraction.
- Maxatar 2y agoC++ has both return type polymorphism and inheritance. What it doesn't have is the kind of type inference that Haskell has. Its type inference is very limited.
- fooker 2y agoYou can't have two functions that differ in just the return type in C++
- thechao 2y agoHaving implemented reflection in languages like C(++), before, it is most certainly not bloat-free. There are sorts of 'obvious' things programmers do (like enum-reflection) that end up injecting strings all over the place. The overhead is (worst case) proportional to the source-code size, in those cases. In other cases, you end up with bloat proportional to heavily-utilized template libraries. However, unless the reflection system is very clever, i.e., exposes enough information to the linker to strip duplicate-ish symbols, you end up with a bunch of reflection copies.
- rerdavies 2y agoextern templates address the issue of having multiple instances of a template being expanded inline, with only minimal mess and fuss. (A way to prevent inlining of templated code would have been nice too).
- zarzavat 2y agoI always thought it’s good practice in C/C++ to have only one translation unit in release builds e.g. SQLite amalgamations, instead of relying on LTO. It also speeds up compilation because it isn’t recompiling the same header files over and over again.
- kccqzy 2y agoIt's a cute idea but just not scalable. It doesn't speed up compilation because separate translation units can be compiled independently and cached.
- zarzavat 2y agoDo things that don’t scale :) Obviously it’s not suitable for dev builds, I don’t think anyone uses it for that. For release builds you would want to clear caches anyway.
- kccqzy 2y agoYou don't want to clear caches for release builds. That just makes release builds unnecessarily slow and impedes the flow of Continuous Deployment. You just need to have separate caches for different build flags, including a dedicated cache for release builds.
- bingo3131 2y agoFYI: this is the latest draft of the proposal and it has not been voted into C++26 yet, but it is getting close.
- steveklabnik 2y agohttps://github.com/cplusplus/papers/issues/1668#issuecomment-2192430067 https://github.com/cplusplus/papers/issues/1668#issuecomment... Looks like it did very well in St. Louis!
- TillE 2y agoFinally. I think there have been proposals since C++17 at least, and all I really wanted is for them to solve the common problem of basic static reflection for enums (without hacks like magic_enum uses).
- jjmarr 2y agomagic_enum is killing my build time with endless template instantiations. Is this going to be faster?
- leni536 2y agomagic_enum works by walking all possible enumeration values from one-by-one in a wide range at compile time, instantiating a function template for each one so it can extract the __PRETTY_FUNCTION__ name, which is very slow. The C++26 feature just directly returns the vector of the named enumerators in one go, so it should be way faster. They have a reference implementation on godbolt under clang, so you can play around with that. I did not try it yet.
- jjmarr 2y agoWow. I'm trying to make some of these template instantiations explicit on a large project I'm on as magic_enum is one of the largest contributors to our build-time. It's nice to know I can just transition to C++26 to fix this.
- mehrdadn 2y agoDo you think you could try my library [1] and let me know how it performs in comparison? I've been curious about its compile-time performance, but I've never tried to compare its performance against that of magic_enum. [1] https://news.ycombinator.com/item?id=32236447 https://news.ycombinator.com/item?id=32236447
- dakka1 2y agoIf you're using C++20 or above, you could try conjure_enum (https://github.com/fix8mt/conjure_enum https://github.com/fix8mt/conjure_enum) It's based on magic_enum but optimized for C++20. Not sure about compile times although in our testing and with our test users it hasn't been reported as an issue. Yes, there is magic_enum already - and we based this implementation on the core of magic_enum, but refocused for C++20, using some of the key features of this language version such constexpr algorithms, std::source_location and concepts; we also improved on and expanded the API.
- flykespice 2y agoHas finally the committee come to reflection after decades of standard revisions and footguns? /j
- stefanos82 2y agoCan I ask a naive question that consists of two parts and please don't flame me? lol * What type of problems static reflection could solve, in general? * Are there specific cases and / or situations where static reflection could resolve such case, even simplify an unnecessary complexity?
- adamnemecek 2y agoSerialization comes to mind.
- quotemstr 2y ago> What type of problems static reflection could solve, in general? Imagine making a plain struct Point { float x; float y; }; and wanting to serialize it to JSON without further ceremony
- throwway120385 2y agoThis is the thing that's driving me away from C++ very quickly. A big part of our code base is code that handles this, and it either has to be in a DSL and constantly recompiled or we have to make a bunch of boilerplate. It's a huge problem for the language not to be able to do this.
- greenavocado 2y agoExample of serializing a C++ object to JSON with reflection: template<typename T> std::string to_json(const T& obj) { std::ostringstream oss; constexpr auto type_info = reflect(T); if constexpr (type_info.is_fundamental()) { // Fundamental types (int, float, etc.) if constexpr (std::is_same_v<T, bool>) { oss << (obj ? "true" : "false"); } else if constexpr (std::is_arithmetic_v<T>) { oss << obj; } else if constexpr (std::is_same_v<T, std::string>) { oss << "\"" << obj << "\""; } } else if constexpr (type_info.is_enum()) { // Enums oss << "\"" << type_info.enum_name(obj) << "\""; } else if constexpr (type_info.is_array() || std::is_same_v<T, std::vector<typename T::value_type>>) { // Arrays and vectors oss << "["; bool first = true; for (const auto& elem : obj) { if (!first) oss << ","; oss << to_json(elem); first = false; } oss << "]"; } else if constexpr (std::is_same_v<T, std::map<typename T::key_type, typename T::mapped_type>>) { // Maps oss << "{"; bool first = true; for (const auto& [key, value] : obj) { if (!first) oss << ","; oss << "\"" << key << "\":" << to_json(value); first = false; } oss << "}"; } else if constexpr (type_info.is_class()) { // Classes and structs oss << "{"; bool first = true; for (const auto& member : type_info.members()) { if (!first) oss << ","; oss << "\"" << member.name() << "\":" << to_json(member.get(obj)); first = false; } oss << "}"; } return oss.str(); } enum class Color { Red, Green, Blue }; struct Address { std::string street; std::string city; int zip; }; struct Person { std::string name; int age; double height; Color favorite_color; Address address; std::vector<std::string> hobbies; std::map<std::string, int> scores; }; int main() { Person person { "John Doe", 30, 175.5, Color::Blue, {"123 Main St", "Anytown", 12345}, {"reading", "hiking", "coding"}, {{"math", 95}, {"history", 88}, {"science", 92}} }; std::cout << to_json(person) << std::endl; return 0; }
- ahartmetz 2y agoThis looks surprisingly fine! The opaque, extensible types remind me of Win32 with its extensibility through ever new message types. The syntax looks better than expected, too - well, it's better than templates...
- lallysingh 2y agoWow 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 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.
- stiglitz 2y agoBy ”stagnation” do you mean “not getting new features”?
- deleted 2y ago[deleted]
- vlovich123 2y agoN3340 is from 2011. Prior to c++11 they had failed to deliver major changes to the language. And arguably the disfunction is still there where big ideas get destroyed in committee (reflection) or take forever and come out half-baked (modules).
- rerdavies 2y agoSpeaking of half-baked, did continuations get fixed?
- CuriousCosmic 2y agoC++ 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.
- gpderetta 2y agoI have been waiting for static reflection for the last 20 years. The current proposal seems quite nice, but the real question is whether any non trivial usage will kill compilation performance.
- a1o 2y agoThe implementation that exists for clang is fast but we will see how it goes with MSVC and GCC.
- gpderetta 2y agoAt least is a proof existence that it can be done.
- flohofwoe 2y agoThat doesn't mean much unfortunately, Clang had a fully working C99 designated initialization in C++ for many years, but the designated initialization that ended up in C++20 is only a butchered version of the full feature set despite Clang clearly demonstrating that it's possible to integrate the full C99 designated init feature set into C++.
- kstrauser 2y agoI haven't touched C++ since undergrad. Neither have I written any Qt code. But from memory, doesn't Qt's moc implement some of this stuff because it wasn't available in C++? Could this replace moc?
- fluoridation 2y agoQt has straight-up dynamic reflection. You can get pointers to functions from strings, and such. This is just static reflections (which is still very useful!), so it's not a complete replacement. Even if it was, I would Qt would replace its build system.
- account42 2y agoStatic reflection makes it much easier to build dynamic reflection though, especially without meta-compilers and excessive macro use.
- shkurski_ 2y agomoc can be replaced without reflection: https://woboq.com/blog/verdigris-qt-without-moc.html https://woboq.com/blog/verdigris-qt-without-moc.html
- account42 2y agoUsing ugly macro hacks, yes.
- CuriousCosmic 2y agoQt's moc can already be replaced and it increasingly is being relied on less and less as time goes on but dropping moc requires dropping all lower C++ standards or maintaining separate moc and modern-c++ versions. And while it can currently be replaced with templates alone in fairly old versions of C++ (C++14 is the oldest I think), compile times are egregious unless you use very new, shiny features. And as much as I am pro "move to new shiny C++", one of the big commercial uses of Qt is in semi-embedded applications like car entertainment centers where you are stuck with whatever (often outdated) toolchain your SOC source relies on. So pushing for shiny new Qt risks either splitting Qt in half or abandoning a lot of very well paying users.
- jdeaton 2y ago[flagged]
- nox101 2y agoHaha https://www.youtube.com/watch?v=EqiLTgQcDPM https://www.youtube.com/watch?v=EqiLTgQcDPM But still, happy to have the new solutions
- syedhswhw 2y ago[flagged]
- qalmakka 2y agoWhile I love this paper and this proposal in general, as a C++ developer every time C++ adds a new major feature I get somewhat worried about two things: 1. how immense the language has become, and how hard it got to learn and implement 2. how "modernising" C++ gives developers less incentives to convince management to switch to safer languages While I like C++ and how crazy powerful it is, I also must admit decades of using it that teaching it to new developers has become immensely hard in the last few years, and the "easier" inevitably ends up being the unsafe one (what else can you do when the language itself tells you to refrain from using `new`?).
- fragmede 2y agoYeah that's because you're not supposed to be using "new" anymore since the introduction of smart pointers in C++11. Std::shared_ptr and std::unique_ptr are preferred. Shared pointers ref count and auto-delete, and unique pointers can't be copied.
- deleted 2y ago[deleted]
- pjmlp 2y agoWhile I share the feeling, I don't feel that my daily languages (Java, C#, TypeScript) are getting that far behind. Even Go is rediscovering that staying simple just doesn't happen for any language that gets industry adoption at scale.
- naertcxx 2y agoI agree with this. Python is now more complex than C++. Python looks as if it is simple, because the syntax looks clean. If you read the new "compilers" [1] in packages like PyTorch, which are unfortunately written in Python, you stare at a huge code base with walls of text, objects calling one another in a maze of ravioli code and generally no help at all to make sense of it all. Compare that to the gcc code bases, where it is always possible to find some entry point to understand the whole thing. [1] "compilers", because despite the huge code base (wasn't Python supposed to be terse?) they preprocess the function graph and then call g++ and Triton to do the actual work.
- forrestthewoods 2y agoI'm surprised at the positive response in this thread. I find the syntax of this beyond atrocious! My goodness C++ really does not know how to do anything simply does it?
- w4rh4wk5 2y agoDo I understand correctly that this proposal does not include annotations (i.e. attributes). More specifically, with this I can iterate over struct members and get their names and types, but I cannot attach additional information to these members, like whether they should be serialized or under which name. The referenced proposal P1887R1 covers this, but that's not included here, right? P1887R1: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1887r1.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p18...
- raymond_goo 2y agoCtrl-F "networking", cry, close page... See also: https://github.com/cplusplus/networking-ts https://github.com/cplusplus/networking-ts
- account42 2y agoWhat does this have to do with reflection. Also why do you need networking in the C++ standard library? Networking is neither something that is relevant as a vocabulary that needs to be common between libraries nor is it something that makes sense to be set in stone like basic algorithms. Just use OS interfaces or a third-party abstraction FFS.
- lpribis 2y agoWhat? This is only the proposal for reflection. Networking is completely separate.
- huhtenberg 2y agoOi vey. Poor C++. Look how they massacred my boy.
- guardian5x 2y agoWith every new C++ feature, I can't help to think "Oh yea, cause C++ isn't complicated enough"
- account42 2y agoI'm not convinced that wasting a simple clean syntax (prefix unary ^) is warranted for something that should be rare outside of a few libraries.
- SilverSlash 2y agoHow about: Deprecation for C++26?
- tempodox 2y agoHighly interesting, I'm looking forward to this. But the `member_number` functions in § 3.2 look disturbing to me. It's not discernible how invalid arguments are handled. Normally I'd look at generated assembly to answer a question like that, but this probably doesn't make sense with compile-time-fu (`constexpr`)…