1

我正在尝试在 C++ 中实现一个通用 ECS 库以用于学习目的。我想了很多方法来实现,但我总是遇到问题。所以,如果你能帮我解决这个问题:

假设我有一个constexpr hana::tuple组件hana::type_c,例如:

struct C1 {};
struct C2 {};
struct C3 {};

constexpr auto components = hana::to_tuple(hana::tuple_t<C1, C2, C3>);

而现在我有了一个组件存储类型,这里没有问题,所以我们称之为Storage(每个组件的类型不同):

struct Storage {};

我想将每个组件或每个组件组与其Storage类型相关联。所以最简单的方法是做这样的事情:

constexpr auto component_storage = hana::make_tuple(
hana::make_pair(hana::to_tuple(hana::tuple_t<C1, C2>), type_c<Storage>),
hana::make_pair(hana::to_tuple(hana::tuple_t<C3>), type_c<Storage>)
);

但现在的问题是运行时。如果我初始化该元组但使用真正的 Storage 而不再type_c<Storage>,我将不得不遍历元组以找到Storage我需要的。所有这些都在运行时没有?这真的很糟糕,我的上一个版本有类似的东西Component::getStorage()而且它是免费的(但更具限制性)。

所以问题是:我怎样才能拥有一些getStorage<Component>()在运行时不花钱的功能?好吧,我的意思是只返回存储的引用。

编辑:到目前为止我认为的唯一方法很简单(听起来不错)。

伪代码

struct LinkedStorage {

  hana::tuple<...>            storages;
  hana::tuple<hana::pair...>  index;
};

至少是这样的:

constexpr auto components = hana::to_tuple(hana::tuple_t<C1, C2, C3>);
constexpr auto storage = hana::to_tuple(hana::tuple_t<Storage, Storage>);
constexpr auto index = hana::make_tuple(
hana::make_pair(hana::to_tuple(hana::tuple_t<C1>, 0),
hana::make_pair(hana::to_tuple(hana::tuple_t<C2, C3>, 1)
);

像这样我应该能够在编译时找到索引并在运行时访问正确的元素。但我是元编程的新手,所以我想有人可以做得更好。

4

2 回答 2

3

首先,不需要使用to_tuple(tuple_t<...>); 你可以使用tuple_t<...>. 现在,我认为您真正想要做的(因为您似乎需要运行时存储,这是有道理的)是​​:

// "map" of a set of types to a storage of some type
using StorageMap = hana::tuple<
  hana::pair<hana::tuple<hana::type<C1>, hana::type<C2>>, StorageA>,
  hana::pair<hana::tuple<hana::type<C3>>, StorageB>
>;
// Actual object that contains the runtime storage (and the free mapping between types)
StorageMap map;

现在,您可以getStorage<Component>()像这样实现您的功能:

template <typename Component>
decltype(auto) getStorage() {
  auto found = index_if(map, [](auto const& pair) {
    return hana::contains(hana::first(pair), hana::type<Component>{});
  });
  return hana::second(hana::at(map, found));
}

where是此答案index_if中提供的函数的一个简单变体,它适用于任意谓词而不是特定元素。当我有空闲时间时,此功能将添加到 Hana(请参阅相关票证)。

于 2017-03-29T15:27:29.237 回答
1

看起来您正在尝试制作一个可以使用不同键查找单个实例的地图。这是我编写的旧实现的片段。我对其进行了一些修改,但它应该传达了这个想法。

namespace detail {
    // extractKeys - returns pairs of each element and itself
    struct extract_keys_fn
    {
        template<typename TypesType>
        constexpr auto operator()(TypesType s) const {
            return decltype(hana::unpack(typename TypesType::type{},
                hana::make_tuple
                ^hana::on^
                hana::reverse_partial(hana::make_pair, s)
            )){};
        }
    };
    constexpr extract_keys_fn extract_keys{};
}//detail

template<typename ...Pair>
struct multi_map
{
    // the keys must be `type<tuple<path...>>`
    using Storage = decltype(hana::make_map(std::declval<Pair>()...));

    // each key is a hana::tuple which contain the keys we
    // want to use to lookup an element
    using Lookup = decltype(hana::unpack(
        hana::flatten(hana::unpack(hana::keys(std::declval<Storage>()),
            hana::make_tuple ^hana::on^ detail::extract_keys)),
        hana::make_map
    ));

    constexpr multi_map()
        : storage()
    { }

    constexpr multi_map(Pair&&... p)
        : storage(hana::make_map(std::forward<Pair>(p)...))
    { }

    constexpr multi_map(Pair const&... p)
        : storage(hana::make_map(p...))
    { }

    constexpr multi_map(Pair&... p)
        : storage(hana::make_map(p...))
    { }

    template<typename T>
    constexpr decltype(auto) operator[](T t) const&
    {
        return hana::at_key(storage, hana::at_key(Lookup{}, t));
    }

    template<typename T>
    constexpr decltype(auto) operator[](T t) &
    {
        return hana::at_key(storage, hana::at_key(Lookup{}, t));
    }

    template<typename T>
    constexpr decltype(auto) operator[](T t) &&
    {
        return hana::at_key(storage, hana::at_key(Lookup{}, t));
    }

    Storage storage;
};

上面发生的事情的基础是storage包含hana::map您需要引用的实例。然后Lookup是将hana::map每个键指向使用的键storage(这是指向它的所有键的元组)。它基本上只是一个映射到映射的映射,但是通过它,您可以使用任何一个键来获取对单个实例的引用。

于 2017-03-29T17:52:13.687 回答