Перебор типов кортежей в C

#c #templates #tuples #std #template-meta-programming

#c #шаблоны #кортежи #std #шаблон-мета-программирование

Вопрос:

Я хочу перебирать типы кортежей, а не их элементы.
Представьте, что у вас есть универсальный интерфейс базового класса Controller , и вы хотите иметь вектор указателей (я использую необработанный указатель вместо smart для удобства чтения): std::vector<Controller*> ctrls;

Теперь вы хотите добавить много реализаций Controller интерфейса к этому вектору, чтобы вы могли сделать это:

 ctrls.emplace_back(new MouseCtrl());
ctrls.emplace_back(new KeyboardCtrl());
ctrls.emplace_back(new ScreenCtrl());
(... and so on)
  

Но это некрасиво и не совсем расширяемо (как в принципе открытия-закрытия), поэтому было бы лучше иметь, например, кортеж: using Controllers = std::tuple<MouseCtrl, KeyboardCtrl, ScreenCtrl>; а затем, в некоторой функции инициализации, перебирать эти типы:

 for (T : Controllers> { 
   ctrls.emplace_back(new T());
}
  

Очевидно, что приведенный выше код не является допустимым синтаксисом C . Итак, вопрос в том, как это сделать?. Я просмотрел как std ::apply, так и std::visit / std::variant, но я понятия не имею, как это сделать (они перебирают элементы, а не типы).

Комментарии:

1. Типы в кортеже могут повторяться только во время компиляции.

2. std::tuple является типом значения. Если вы просто хотите по умолчанию сконструировать свои элементы, вы можете просто по умолчанию сконструировать std::tuple . Если вам нужна более сложная инициализация, возникает вопрос, как это должно быть сделано в вашем случае, на который, боюсь, мы не можем ответить без дополнительной информации. В общем случае вы «перебираете» типы с помощью переменных шаблонов

3. Повторяющийся вопрос на самом деле не отвечает на вопрос, если вы не можете создать кортеж. Вот решение, которое работает исключительно с типами: godbolt.org/z/rYfGoj (на который я не могу ответить на закрытый вопрос)

Ответ №1:

Вы можете использовать std::tuple_size и std::tuple_element , чтобы получить тип каждого элемента кортежа (это приведет к обратному порядку, но с небольшими изменениями вы можете изменить порядок):

 #include <iostream>
#include <variant>
#include <vector>

using namespace std;

using Controllers = tuple<int, char, float>;
using ControllersContainer = vector<variant<int, char, float>>;

template <size_t N>
void add(ControllersContaineramp; ctrls)
{
    ctrls.emplace_back(tuple_element_t<N-1, Controllers>{});
    add<N - 1>(ctrls);
}

template <>
void add<0>(ControllersContaineramp; ctrls)
{
    ctrls.emplace_back(tuple_element_t<0, Controllers>{});
}

int main()
{
    ControllersContainer ctrls;
    add<tuple_size_v<Controllers>>(ctrls);
}
  

Комментарии:

1. Важно отметить, что это будет работать только во время компиляции. Вероятно, это не то, чего хочет OP.

2. Вопрос был о переборе типов в кортеже. Кортеж является статическим типом — «итерация» возможна только во время компиляции. Хотя итерация здесь реализована с рекурсией.

3. @StPiere Это именно то, что я искал. Знаете ли вы, есть ли в стандартной библиотеке что-нибудь, что уже предоставляет такую функциональность, чтобы я мог пропустить добавление функции «добавить» и просто предоставить немного удовольствия от лямбды ctrls.emplace_back(tuple_element_t<0, Controllers>{}); ?

4. @zupazit: я не знаю о прямом методе. Еще одной возможностью было бы перейти к make_index_sequence и tuple_size с помощью оператора запятой.