frengels / matter

C++20 highly parallel ECS implementation
19 stars 0 forks source link

Component proxy objects #86

Open frengels opened 5 years ago

frengels commented 5 years ago

Proxies can be used to provide securities for requested read/write behavior. For example there can be proxies for read and write. The write proxy could implement the function writable() which returns a non-const reference to the contained component, also when that function is executed it will trigger a component change event when this function was called. Other behavior such as sorted could be implemented.

So this behaviour must be able to define its own components that it has access to, for example for the creation of change_event<Component>. Optionally collect and mutate the entire list of components with sorted. Sorted is vastly more complex so those concerns can be left for later. For now defining extra components to access should be enough.

frengels commented 5 years ago

a few possible classes:

// provides read-only access to a component
template<typename C>
struct read {
private:
    const C* val_;

public:
    constexpr read(const C& val) noexcept
        : val_{std::addressof(val)}
    {}

    constexpr const C& readable() const noexcept {
        return *val_;
    }
};
// provides mutable access to  a component
template<typename C>
struct readwrite {
private:
    C* val_;

public:
    constexpr readwrite(C& val) noexcept
        : val_{std::addressof(val)}
    {}

    constexpr C& writable() noexcept {
        return *val_;
    }
};
frengels commented 5 years ago

How to support optional. Optional works on a per group basis, so there must be support for generating metadata for a group. This metadata will then be passed together with the group_view and index and it's our responsibility to return the appropriate type.

template<typename C>
struct optional_meta {
    // id_type is unknown upon construction so can't store the id in constructor
    constexpr optional_meta() noexcept = default

    // process functions will be called once per region and their result will be forwarded to the next region
    // other processing functions are `process_registry_view`, 
    // `process_group_vector`, `process_group` and `process_entity`.
    // if one of the calls is not found then the result will be passed to the next available function.
    // registry -> registry_view -> group_vector -> group (passes group_view) -> entity
    template<typename Id, typename... Cs>
    constexpr const registry<Id, Cs...>& process_registry(const registry<Id, Cs...>& reg) noexcept {
        return reg;
    }

    // generate information from group_view
    template<typename Id, typename... Cs, typename... GroupComps>
    constexpr component_storage_t<C>* process_group(
            const group_view<GroupComps...>& grp_view,
            // this registry is the return value from process_registry
            const registry<Id, Cs...>& reg) {
        registry<Id, Cs...>::id_type id = reg.id<C>();

        auto grp = grp_view.underlying_group();

        if (auto er_storage = grp.find_id(id); er_storage != grp.end()) {
            return std::addressof(er_storage->get<C>());
        }
        return nullptr;
    }

    template<typename... Cs>
    constexpr optional<C> process_entity(
            const group_view<Cs...>& grp_view, 
            std::size_t idx, 
            // this is the result from the process_group function
            component_storage_t<C>* storage) {
        if (storage) {
            auto& component = storage->operator[](idx);
            return optional<C>{std::addressof(component)};
        }
        return optional<C>{};
    }
};

template<typename C>
struct optional {
    using meta_type = optional_meta<C>;
private:
    C* val_{nullptr};
public:
    template<typename UnaryFunction>
    UnaryFunction then(UnaryFunction f) {
        if (val_) {
            f(*val_);
        }
    }

    template<typename... Cs>
    static constexpr generate_metadata(const matter::group_view<Cs...>& grp_view) noexcept
    {
        auto any_grp = grp_view.underlying_group();
        if (
};