【问题标题】:Change runtime research for a compile time one为编译时更改运行时研究
【发布时间】:2017-03-29 09:29:56
【问题描述】:

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

假设我有一个 constexpr hana::tuplehana::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>)
);

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

所以问题是:我怎样才能拥有一些 getStorage&lt;Component&gt;() 函数,它在运行时不会花费任何成本? 我的意思是什么都没有,我的意思是只返回存储的引用。

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

伪代码

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)
);

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

【问题讨论】:

    标签: c++ metaprogramming boost-hana


    【解决方案1】:

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

    // "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&lt;Component&gt;() 函数:

    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_ifin this answer 提供的函数的一个简单变体,它适用于任意谓词而不是特定元素。当我有空闲时间时,此功能将添加到 Hana(请参阅 related ticket)。

    【讨论】:

    • 非常感谢您的帮助,但这是否意味着 getStorage 将减少为 return hana::second(index) ?您甚至不需要带有 found 变量的 constexpr 关键字?看来我必须学习很多元编程的东西。
    • found 变量是一个integral_constant,它包含索引值的类型。所以不,不需要constexpr
    • 不应该是return hana::second(hana::at(map, found.value()));吗?发现是hana::optional
    【解决方案2】:

    看起来您正在尝试制作一个可以使用不同键查找单个实例的地图。这是我编写的旧实现的 sn-p。我稍微修改了一下,但它应该传达了这个想法。

    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 中使用的键(这是指向它的所有键的元组)。它基本上只是一个映射到映射的映射,但通过它,您可以使用任何一个键获取对单个实例的引用。

    【讨论】:

    • 看来我必须先获得一些经验才能理解这段代码抱歉。我会记住的,稍后再回来,谢谢:)
    • 我添加了一点描述。 Lambda 对可读性有很大帮助,但在 constexpr(c++1z 之前)或 decltype 中不允许使用。
    • 抱歉,我在任何地方都找不到一件事:^hana::on^。顺便说一句,我没有任何 c++ 限制(目前使用 c++1z 和概念 TS)。
    • 想想[](auto ...args) { return make_tuple(extract_keys(args)...); }。如果你可以使用 constexpr lamdas 那就这样做。 ^ 是 hana 的中缀运算符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-01
    相关资源
    最近更新 更多