简体   繁体   中英

How to create compile-time class fields using templates?

Little background and rationale

I am designing a compile-time ECS. The design has its specifics - for instance, there will be a great variety of entities. This means that the overall number of components is likely to be fairly high whilst number of components per entity will be fairly low (having some obvious present-almost-everywhere-commons such as Sprite or BoundingBox )

My approach

I would implement the following interface:

/* Tell the compiler which fields we would use in this entity */
Entity<GraphicsComponent, PhysicsComponent> entity;

/* Populate the handles with the appropriate data (integer, pointer, doesn't matter) */
// via explicit creation
Entity.add<GraphicsComponent>(RenderSystem::create<GraphicsComponent>());
// or in some other way?

The inception of the Entity class I wrote:

template <typename ...>
class Entity final {};

I have, however, no idea how would I create the handle data fields for the respective Component types , and if it is even possible at all. Can anyone help me or explain why it wouldn't work (or possibly suggest a different solution)?

Just use std::tuple . Nb the get requires that each of Components... is a distinct type

template <typename ... Components>
class Entity final { 
    std::tuple<Components...> components;  
    template<typename Component>
    void add(Component component) // possibly cv / ref qualified
    {
        std::get<std::decay_t<Component>>(components) = component;
    }
};

Use std::variant for a mixture of flexibility and simplicity, at the cost of possibly sacrificing some memory.

template<typename... Components>
class Entity final {
    using variant = std::variant<Components...>;
    std::vector<variant> components;

public:
    template<typename T>
    void add(T&& t) {
        components.push_back(std::forward<T>(t));
    }
};

The memory problem arises when your component have largely varying sizes. Say one is 1 byte while another is 200 bytes, all the entries in the std::vector will be at least 200 bytes large.

Another solution is std::any .

template<typename... Components>
class Entity final {
    std::vector<std::any> components;

public:
    template<typename T>
    void add(T&& t) {
        static_assert((std::is_same_v<Components, std::decay_t<T>> || ...));
        components.push_back(std::forward<T>(t));
    }
};

std::any most certainly have everything on the heap except very small objects, but doesn't suffer from the aforementioned problem with std::variant .

In the case with std::any , we use the type system only to enable checks, not to govern the layout of data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM