Измените исследование времени выполнения на время компиляции

Я пытаюсь реализовать универсальную библиотеку ECS на C++ для обучения. Я думал о многих способах реализации, но всегда сталкивался с проблемой. Так что, если бы вы могли помочь мне с этим :

Допустим, у меня есть 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>)
);

Но теперь проблема во времени выполнения. Если я инициализирую этот кортеж, но с реальным хранилищем, а не с 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)
);

Таким образом, я должен иметь возможность найти индекс во время компиляции и просто получить доступ к нужному элементу во время выполнения. Но я новичок в метапрограммировании, так что думаю, кто-то мог бы сделать что-то намного лучше.


person Mathieu Van Nevel    schedule 29.03.2017    source источник


Ответы (2)


Во-первых, нет необходимости использовать 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 — это тривиальный вариант функции, представленной в этом ответе, который будет работать с произвольным предикатом вместо определенного элемента. Эта функция будет добавлена ​​в Hana, когда у меня будет свободное время (см. соответствующий запрос) .

person Louis Dionne    schedule 29.03.2017
comment
Большое спасибо за вашу помощь, но означает ли это, что getStorage будет уменьшено до return hana::second(index) ? Вам даже не нужно ключевое слово constexpr с найденной переменной? Похоже, мне нужно многому научиться метапрограммированию. - person Mathieu Van Nevel; 29.03.2017
comment
Переменная found представляет собой переменную integral_constant, которая содержит значение индекса в своем типе. Так что нет, не нужно constexpr. - person Louis Dionne; 29.03.2017
comment
не должно ли быть return hana::second(hana::at(map, found.value()));? найдено hana::optional - person Reza; 20.05.2020

Похоже, вы пытаетесь создать карту, которая может искать один экземпляр с использованием разных ключей. Вот фрагмент из старой реализации, которую я написал. Я немного изменил его, но он должен передать идею.

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 (который является кортежем всех ключей, которые на него указывают). По сути, это просто карта для сопоставления, но с ее помощью вы можете получить ссылку на один экземпляр, используя любой из ключей.

person Jason Rice    schedule 29.03.2017
comment
Похоже, мне нужно получить некоторый опыт, прежде чем я смогу понять этот код, извините. Буду иметь в виду и вернусь позже, спасибо :) - person Mathieu Van Nevel; 29.03.2017
comment
Добавил немного описания. Лямбда-выражения значительно улучшили бы читабельность, но они не разрешены в constexpr (до C++1z) или внутри decltype. - person Jason Rice; 29.03.2017
comment
Извините, я нигде не смог найти одну вещь: ^hana::on^. Кстати, у меня нет никаких ограничений С++ (сейчас использую С++ 1z и концепцию TS). - person Mathieu Van Nevel; 30.03.2017
comment
Подумайте [](auto ...args) { return make_tuple(extract_keys(args)...); }. Если вы можете использовать лямбды constexpr, сделайте это. ^ — это инфиксный оператор ханы. - person Jason Rice; 30.03.2017