改运行时研究一个编译时间一
Change runtime research for a compile time one
我正在尝试用 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>);
现在我有一个组件存储类型,这在这里不是问题,所以我们称之为存储(每个组件的类型不同):
struct Storage {};
我想 link 每个组件或每个组件组,以及它们的 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>()
功能,而且在 运行 时不花钱? 好吧,我的意思不是只是 return 存储的引用。
编辑:到目前为止我认为的唯一方法很简单(听起来不错)。
伪代码
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)
);
这样我应该能够在编译时找到索引并在 运行 时访问正确的元素。但我是元编程的新手,所以我想有人可以做得更好。
首先,不用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));
}
其中 index_if
是函数 that would work on an arbitrary predicate instead of a specific element. This functionality will be added to Hana when I get some free time (see related ticket) 的简单变体。
您似乎正在尝试制作一个可以使用不同键查找单个实例的地图。这是我编写的旧实现的片段。我稍微修改了一下,但应该能表达意思。
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
中使用的键(这是指向它的所有键的元组)。它基本上只是一个映射到映射,但有了它,您可以使用任何一个键获得对单个实例的引用。
我正在尝试用 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>);
现在我有一个组件存储类型,这在这里不是问题,所以我们称之为存储(每个组件的类型不同):
struct Storage {};
我想 link 每个组件或每个组件组,以及它们的 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>()
功能,而且在 运行 时不花钱? 好吧,我的意思不是只是 return 存储的引用。
编辑:到目前为止我认为的唯一方法很简单(听起来不错)。
伪代码
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)
);
这样我应该能够在编译时找到索引并在 运行 时访问正确的元素。但我是元编程的新手,所以我想有人可以做得更好。
首先,不用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));
}
其中 index_if
是函数
您似乎正在尝试制作一个可以使用不同键查找单个实例的地图。这是我编写的旧实现的片段。我稍微修改了一下,但应该能表达意思。
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
中使用的键(这是指向它的所有键的元组)。它基本上只是一个映射到映射,但有了它,您可以使用任何一个键获得对单个实例的引用。