Hacker News new | past | comments | ask | show | jobs | submit login

This 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.



I suggest you to take a look at Boost.Describe.

I have a scripting layer in a game that needs to set properties in a C++ Model. I used a single Boost.Describe macro per struct and a generic get/set property. It worked very well and made me get rid of a lot of boilerplate.

https://www.boost.org/doc/libs/develop/libs/describe/doc/htm...


Boost.PFR is really neat and doesn’t need manual mapping for aggregate types


What I needed is accessing thosr by name. So PFR won't do.


Should be able to build that on if it isnt there. But at runtime the type would need to be a sum type like variant.


Yeah, that looks like it would do what I need.


Example 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;
    }


That is cool. Would it be possibly implement something like Golangs %v and %T printf formatters?


Serialization is largely a Solved Problem in modern C++ thanks to template metaprogramming. Wrangling a kludge DSL instead of Serialize<T> betrays poor lang knowledge...


One could argue that template metaprogramming is a kludge DSL of its own.


It absolutely is. The root problem is that the language itself doesn't permit you to make statements about your constructs without learning an entirely different set of tools. Whereas other languages have these tools as a core component. Templates seem like they were added to resolve a bunch of problems with the macro system. You can see that in the earliest use of templates to eliminate a lot of boilerplate around containers that in C you might have used the preprocessor to do.

From there a lot of functionality for making statements about what something is has been bolted on to the side of what is really a very sophisticated type-aware preprocessor, and it shows. It's very painful to use it when you go from the core C++ language to the template, because the semantics are very different. Which the same can be said of C++->Preprocessor.

I think proper reflection should be a core part of the language that can be evaluated at compile-time with simple constructs. It's hard to articulate specifics but I think this proposal greatly simplifies working with type metadata to the degree that it's an approachable problem without using someone else's library. This proposal seems to do that in my opinion, and I think even some of the weaker programmers I've worked with could use this effectively.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: