BlackMATov / meta.hpp

C++20 Dynamic Reflection Library
https://github.com/BlackMATov/meta.hpp
MIT License
134 stars 8 forks source link

Getting the value from a member without knowing the type #91

Closed AaronFrans closed 4 months ago

AaronFrans commented 4 months ago

Is there a way to get the value from a member without knowing the type? I'm currently iterating through a list of members via the get_memberts() function. I've tried a few different things, but can't seem to make it work.

This is the code:

auto metaData = transform->GetMetaData();
for (const auto& member : metaData.get_members()) {

    const auto value = member.get(transform);
    spdlog::info("name: {}, value: {}",
        member.get_name());
}
BlackMATov commented 4 months ago

member::get, function::call and other dynamic methods return "uvalue", which is essentially "std::any". To do anything with this value in the "static world", you must either have a static type, or something like switching by type or map (type -> function with the desired functionality). Here you can find a little example how to work with uvalues: https://github.com/BlackMATov/meta.hpp/blob/main/develop/manuals/meta_examples/uvalue_example.cpp

AaronFrans commented 4 months ago

So there's no way to get the value without castiung it with the as function. Would you recommend usining getter and setter functions instead then? I'm trying to get to something like the component in unity, where i can gget all of my gameobjects andedit their components in 1 ui element.

BlackMATov commented 4 months ago

To make an analog of a Unity-like inspector, you could write something like property drawers for each type you want to show (which is what Unity does under the hood). Little pseudocode of this:

class member_drawer {
public:
    virtual void draw(const meta::uvalue& instance, const meta::member& member) = 0;
};

class int_member_drawer : public member_drawer {
public:
    void draw(const meta::uvalue& instance, const meta::member& member) override
    {
        meta::uvalue value = member.get(instance);

        int typed_value = value.as<int>();

        if ( ImGui::InputInt(member.get_name().c_str(), &typed_value) ) {
            member.set(instance, typed_value);
        }
    }
};

class float_member_drawer : public member_drawer {
public:
    void draw(const meta::uvalue& instance, const meta::member& member) override
    {
        meta::uvalue value = member.get(instance);

        float typed_value = value.as<float>();

        if ( ImGui::InputFloat(member.get_name().c_str(), &typed_value) ) {
            member.set(instance, typed_value);
        }
    }
};

using member_drawer_ptr = std::shared_ptr<member_drawer>;
using member_drawer_map = std::map<meta::any_type, member_drawer_ptr>;

member_drawer_map member_drawers = {
    { meta::resolve_type<int>(), std::make_shared<int_member_drawer>() },
    { meta::resolve_type<float>(), std::make_shared<float_member_drawer>() }
};

And using:

struct component {
    component() = default;
    component(const component&) = default;
    virtual ~component() = default;
    META_HPP_ENABLE_POLY_INFO()
};

struct position_component : component {
    META_HPP_ENABLE_POLY_INFO(component)
public:
    explicit position_component(int nx, int ny) : x{nx}, y{ny} {}

    int x{};
    int y{};
};

struct rotation_component : component {
    META_HPP_ENABLE_POLY_INFO(component)
public:
    explicit rotation_component(float nr) : r{nr} {}

    float r{};
};

void draw_component_inspector(component* component) {
    // meta_poly_ptr returns a pointer of the most derived type
    // (position_component* or rotation_component* in this case)
    meta::uvalue instance_ptr = component->meta_poly_ptr();
    meta::pointer_type instance_ptr_type = derived_instance_ptr.get_type().as_pointer();

    // to get all members of the component we should extract a class type from the pointer
    meta::class_type instance_type = derived_instance_ptr_type.get_data_type().as_class();

    for (const meta::member& member : instance_type.get_members()) {
        member_drawers.at(member.get_type())->draw(instance_ptr, member);
    }
}

And of course after this simple inspector you can add custom drawers support, additional metadata to draw sliders, ranges and so on.

Enjoy! :-)

AaronFrans commented 4 months ago

I see, I'll test this on but this seems like it will solve my issues. Thank You!

BlackMATov commented 4 months ago

Can I close the issue? :-)

AaronFrans commented 4 months ago

Sorry forgot, its working :)