4 ms·
Serialization is the canonical example. Being able to turn struct MyStruct { int val = 42; string name = "my name"; }; into {
by maccard 5mo ago
Serialization is the canonical example. Being able to turn
struct MyStruct {
int val = 42;
string name = "my name";
};
into
{
"val": 42, // if JSON had integers, and comments of course
"name": "my name",
}
is incredibly powerfuly. If reflection supported attributes (i can't believe it shipped without, honestly), then you could also mark members as [[ignore]] and skip them.
- bluGill 5mo agoIt is powerful, but I'm not sure it is a good idea. Other languages have it, and there is lots of experience in all the ways things go wrong in the real world. I'm inclined to say you should hand write this code because eventually you will discover something weird anyway.
- SuperV1234 5mo agoI think this is a very bad take -- once you write it by hand you have to manually keep it in sync with the actual struct and ensure you made no mistakes. Reflection guarantees 1-1 future-proof mapping with the actual C++ struct, avoids boilerplate, and ensures that the serialization logic is correct.
- bluGill 5mo agoThe protocol is important though, not the internal structure. When you only have exactly one version of a program talking to the same version of itself you don't care. However when you are mixing versions or worse programming language (and thus can't mix structs which are implementation details of your language) the protocol is what matters. That is if you are worried about doing this by hand reflection is not the answer, something like protobuf where your data structures are generated is the answer.
- gpderetta 5mo agoI completely understand your point. Then again you might be able to use reflection to verify that your manually rolled implementation actually serializes all fields.
- electroly 5mo agoCan you give an example of a language ecosystem that went with reflection-based JSON serialization/deserialization and then went on to regret it? I can't think of any, and don't agree with your conclusion. It works great, and manually writing serialization and matching deserialization code is terrible, annoying, error-prone work.
- maccard 5mo agoI disagree. Rust's defacto default is serde, golang comes with batteries included, dotnet/java have had it for _years_, and all the dynamic languages do it.
- SuperV1234 5mo agoYou can achieve that since C++20 (or C++17 if you don't care about the member names). E.g. https://www.linkedin.com/posts/vittorioromeo_cpp-gamedev-reflection-share-7374128575727271936-cX4j https://www.linkedin.com/posts/vittorioromeo_cpp-gamedev-ref... (The link above shows ImGui generation, but the same exact logic can be applied for serialiation to JSON/YAML/whatever.)
- maccard 5mo agoSure, but > The magic sauce? Boost.PFR! An incredibly clever library that enables reflections on aggregates, even in C++17. That's not vanilla C++!
- SuperV1234 5mo ago...so what? It's just a header you have to #include.
- maccard 5mo agoBy that logic why would anything have to be standardised?
- gpderetta 5mo agoThe question is whether something belong in the language or in a library (possibly the standard library). A guiding principle of C++ is that if something can be implemented cleanly and efficiently in a library, the language should not be extended to support the use case. Now boost.pfr is exceedingly clever, but relying on speculative pack expansions or using stateful metaprogramming hacks is not something I would call clean and efficient, so proper reflection is warranted. I do worry about the compile time impact though.
- SuperV1234 5mo agoBy your logic we shouldn't ever use external libraries. PFR has given us reflection since C++14. I also don't think the Standard Library is particularly well-defined nor well-implemented, as demonstrated by the atrocious compilation times.