←back to thread

196 points svlasov | 9 comments | | HN request time: 0.002s | source | bottom
Show context
stefanos82 ◴[] No.40851614[source]
Can 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?
replies(8): >>40851661 #>>40851675 #>>40851709 #>>40851795 #>>40851942 #>>40852552 #>>40853159 #>>40853615 #
greenavocado ◴[] No.40851942[source]
Any sort of reflection brings C++ one step closer to Python.

Implementing serialization for complex types often requires manual code writing or external tools. With static reflection you could automate this process

  template<typename T>
  void serialize(const T& obj, std::ostream& os) {
      for_each(reflect(T), [&](auto member) {
          os << member.name() << ": " << member.get(obj) << "\n";
      });
  }
Simplified property systems

    class Person {
    public:
        Person(const std::string& name, int age)
            : name(name), age(age) {}

        std::string getName() const { return name; }
        void setName(const std::string& name) { this->name = name; }

        int getAge() const { return age; }
        void setAge(int age) { this->age = age; }

    private:
        std::string name;
        int age;

        REFLECT_PROPERTIES(
            (name, "Name of the person"),
            (age, "Age of the person")
        )
    };

    int main() {
        Person person("Alice", 30);

        auto properties = reflect::getProperties<Person>();

        for (const auto& prop : properties) {
            std::cout << "Property: " << prop.name 
                    << " (" << prop.description << ")" << std::endl;
            
            auto value = reflect::get(person, prop.name);
            std::cout << "Value: " << value << std::endl;

            if (prop.name == "age") {
                reflect::set(person, prop.name, 31);
            }
        }

        std::cout << "Updated age: " << person.getAge() << std::endl;

        return 0;
    }

Simplified template metaprogramming

    template<typename T>
    void printTypeInfo() {
        constexpr auto info = reflect(T);
        std::cout << "Type name: " << info.name() << "\n";
        std::cout << "Member count: " << info.members().size() << "\n";
    }
Easier to write generic algorithms that work with arbitrary types

    template<typename T>
    void printAllMembers(const T& obj) {
        for_each(reflect(T), [&](auto member) {
            std::cout << member.name() << ": " << member.get(obj) << "\n";
        });
    }
replies(1): >>40854231 #
1. tsimionescu ◴[] No.40854231[source]
Note that you should really be using std:print rather than std::cout if using modern C++.
replies(2): >>40854358 #>>40856006 #
2. npoc ◴[] No.40854358[source]
Just because it's newer doesn't make it better. There are good reasons for avoiding iostream
replies(2): >>40854750 #>>40854788 #
3. tsimionescu ◴[] No.40854750[source]
That's my point - iostream is a really bad piece of code, and if you're anyway going to use modern C++, it's really recommended to stop using it.
replies(2): >>40860669 #>>40864401 #
4. spacechild1 ◴[] No.40854788[source]
> There are good reasons for avoiding iostream

I guess that's not what you wanted to say, but I fully agree :)

5. greenavocado ◴[] No.40856006[source]
Fair enough.

Serialization

    #include <print>

    template<typename T>
    void serialize(const T& obj, std::ostream& os) {
        for_each(reflect(T), [&](auto member) {
            std::print("{}: {}\n", member.name(), member.get(obj));
        });
    }
Simplified property systems

    class Person {
    public:
        Person(const std::string& name, int age)
            : name(name), age(age) {}

        std::string getName() const { return name; }
        void setName(const std::string& name) { this->name = name; }

        int getAge() const { return age; }
        void setAge(int age) { this->age = age; }

    private:
        std::string name;
        int age;

        REFLECT_PROPERTIES(
            (name, "Name of the person"),
            (age, "Age of the person")
        )
    };

    int main() {
        Person person("Alice", 30);

        auto properties = reflect::getProperties<Person>();

        for (const auto& prop : properties) {
            std::print("Property: {} ({})\n", prop.name, prop.description);
            
            auto value = reflect::get(person, prop.name);
            std::print("Value: {}\n", value);

            if (prop.name == "age") {
                reflect::set(person, prop.name, 31);
            }
        }

        std::print("Updated age: {}\n", person.getAge());

        return 0;
    }
    
Simplified template metaprogramming

    template<typename T>
    void printTypeInfo() {
        constexpr auto info = reflect(T);
        std::print("Type name: {}\n", info.name());
        std::print("Member count: {}\n", info.members().size());
    }
    
Generic algorithm for printing all members

    template<typename T>
    void printAllMembers(const T& obj) {
        for_each(reflect(T), [&](auto member) {
            std::print("{}: {}\n", member.name(), member.get(obj));
        });
    }
6. npoc ◴[] No.40860669{3}[source]
Sorry, I got your comment completely backwards
7. pjmlp ◴[] No.40864401{3}[source]
iostream is good enough for most jobs, unless one is writing high performace IO code battling for each ms.
replies(1): >>40866381 #
8. tsimionescu ◴[] No.40866381{4}[source]
Or unless one wants to write formatted output, or unless one wants to handle IO errors with RAII...
replies(1): >>40866441 #
9. pjmlp ◴[] No.40866441{5}[source]
Perfectly fine with existing operators and handle classes.

Happily using iostreams since Turbo C++ 1.0 for MS-DOS in 1993, and will keep doing so, unless chasing ms optimizations.