7
segfault69
332d

"Reflective" programming...

In almost every other language:
1. obj.GetType().GetProperties()
or
for k, v in pairs(obj) do something end
or
fieldnames(typeof(obj))
or
Object.entries(obj)

2. Enjoy.

In C++: 💀
1. Use the extern keyword to trick compilers into believing some fake objects of your chosen type actually exist.

2. Use the famous C++ type loophole or structured binding to extract fields from your fake objects.

3. Figure out a way to suppress those annoying compiler warnings that were generated because of your how much of a bad practice your code is.

4. Extract type and field names from strings generated by compiler magic (__PRETTY_FUNCTION__, __FUNCSIG__) or from the extremely new feature std::source_location (people hate you because their Windows XP compilers can't handle your code)

5. Realize your code still does not work for classes that have private or protected fields.

6. Decide it's time to become a language lawyer and make OOPers angry by breaking encapsulation and stealing private fields from their classes using explicit template instantiation

7. Realize your code will never work outside of MSVC, GCC or CLANG and will always be reliant on undefined behaviors.

8. Live forever in doubt and fear that new changes to the compiler magic you abused will one day break your code.

9. SUFFER IN HELL as you start getting 5000 lines worth of template errors after switching to a new compiler.

Comments
  • 6
    You know, it might be difficult to do in c++ because reflection isn’t supported in c++.

    It’s usually not supported in systems languages at large. Only bytecode compiled languages like c# and Java. Because they put more metadata about the types in their compiled output. Probably because the VM needs such information to run.
  • 2
    @AlgoRythm Swift is a nice exception of a somewhat in-between language wich supports reflection.
    It does even have a kind of compile time reflection for macros (similar to Rust)
  • 2
    Adding to what @AlgoRythm said, if you truly need reflection (most likely not), you can build it yourself in a much better way than fancy macros.

    Do grab clang's AST lib and use it to parse your sources. Enable a build step that runs a program made with this lib that generates additional source files containing metadata about your classes. You can actually collect (and call!, although that requires explicit layouting for max ABI compatibility) private fields that way.

    That's actually what C#, java and the like actually do.
  • 2
    There are a bunch of libraries but it won't be as good as C# because it just wasn't a priority, ever. It's essentially impossible to apply compatibility guarantees to a reflection API so the committee is rightfully cautious.

    Herb Sutter is rolling this boulder alongside like 15 others related to metaprogramming plus his utopia of a safe language, it may make it into C++26 but I wouldn't get my hopes up.
  • 3
    @lorentz There are really efforts and hopes to make C++ safe? 😂
    Oh boy, that’s like trying to make JS good.
  • 3
    @Lensflare Well, Herb Sutter has a successor language that promises total compatibility like Typescript. If you haven't heard of cpp2 and are interested in language design I recommend checking it out.

    Also Ranges are now standard and they're the perfect solution (IMO the only lasting solution) to bounds errors. More range stuff is already scheduled for 26, some of it just barely missed the deadline for 23.
  • 1
    @lorentz thanks for the hint, I’ll look it up.
  • 2
    @Lensflare There are some issues like toctou and deadlocks that can never be solved with language design. Static thread safety requires very stern memory model constraints that C++ will never enact. Lifetimes are already managed by compilers as a concept and there are warnings for them, but ultimately I doubt they will be standardized because there are multiple conflicting models and none of them can represent every C++ memory management strategy.

    The two main problems you can solve are really bad defaults and memory safety, and these are the ones herb targets.

    (Edit: on topic, I left this comment in the textbox for 10 minutes so I got a data race.)
  • 1
    @lorentz I skimmed over the design goals and was disappointed but not surprised to not find anything about algebraic data types (sum types).
    I would argue that this is related to safety but this is just my opinion.
  • 1
    @Lensflare I often find myself refactoring my algebraic datatypes to have a single field that is defined externally as a struct, because I find that many of my ADTs end up having many derivatives that differ only subtly in the set of possible options, and so being able to reference the same variant data from multiple ADTs decreases duplication. Given this, I'm starting to have some doubts about the benefits of ADTs over an alternative that always includes this refactoring step from the get go, which is supported in C++ through std::variant.
  • 0
    @Lensflare There is structural pattern matching support in cpp2 (and I think Circle too actually) for this variant-based pattern:

    edit: wrong link removed, still searching
  • 1
    @Lensflare Okay I actually can't find a test case for this so it might not yet be implemented, but std::variant should be matchable using the inspect expression according to the paper linked in the cppfront readme:

    https://open-std.org/jtc1/sc22/...

    I only just now realized that they used operator overloading to deifne this so if that proposal gets merged C++ will have custom pattern matchers, beating Rust's pattern matching capabilities by a mile by my personal standards.

    Whadya know, I may actually switch if cppfront matures a bit.
  • 1
    @Lensflare I also recommend looking into Circle, its a C++ successor language that adds feature flags to essentially transform the language into an awesome Rust-like thing feature by feature. They have a really good explanation why std::variant sucks and of course the reason is that exceptions combined with the way std::variant is defined force implementations to define an "empty" variant state.

    I swear, "exceptions combined with #feature force implementations to define #case_everyone_agrees_shouldn't_exist" fits like 20% of C++'s major problems.
Add Comment