Я написал следующий базовый шаблон Tuple:
template <typename... T>
class Tuple;
template <uintptr_t N, typename... T>
struct TupleIndexer;
template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : public Tuple<Tail...> {
private:
Head element;
public:
template <uintptr_t N>
typename TupleIndexer<N, Head, Tail...>::Type& Get() {
return TupleIndexer<N, Head, Tail...>::Get(*this);
}
uintptr_t GetCount() const {
return sizeof...(Tail) + 1;
}
private:
friend struct TupleIndexer<0, Head, Tail...>;
};
template <>
class Tuple<> {
public:
uintptr_t GetCount() const {
return 0;
}
};
template <typename Head, typename... Tail>
struct TupleIndexer<0, Head, Tail...> {
typedef Head& Type;
static Type Get(Tuple<Head, Tail...>& tuple) {
return tuple.element;
}
};
template <uintptr_t N, typename Head, typename... Tail>
struct TupleIndexer<N, Head, Tail...> {
typedef typename TupleIndexer<N - 1, Tail...>::Type Type;
static Type Get(Tuple<Head, Tail...>& tuple) {
return TupleIndexer<N - 1, Tail...>::Get(*(Tuple<Tail...>*) &tuple);
}
};
Он работает просто отлично, и я могу получить доступ к элементам в виде массива, используя tuple.Get<<em>Index</em>>()
- но я могу сделать это только если язнать индекс во время компиляции.Однако мне нужно обращаться к элементам в кортеже по индексу во время выполнения, и я не буду знать во время компиляции, какой индекс должен быть доступен.Пример:
int chosenIndex = getUserInput();
void* chosenElement = tuple.Get(chosenIndex);
cout << "The option you chose was: " << ((MyAbstractBaseClass*) chosenElement)->getInfo() << endl;
Каков наилучший способ сделать это?
РЕДАКТИРОВАТЬ:
Хакерское решение ниже:
Хорошо, у меня есть идея.Я уже придумал один способ сделать это еще до того, как опубликовал этот вопрос, но он был хакерским и вызывал предупреждения.Так как другого решения не ожидается сразу, возможно, вы, ребята, могли бы помочь мне улучшить мой хакерский.: -)
К кортежу обычно нельзя получить доступ как к массиву, потому что элементы не обязательно имеют одинаковый размер.(Следовательно, умножение в стиле массива для получения правильного смещения в структуре класса не поможет.) Однако мне удалось обойти это, создав статическую таблицу, которая содержит список смещений для кортежа.Вот полный набор кортежей и связанных с ними шаблонов:
#include <cstddef>
template <typename... T>
class Tuple;
template <uintptr_t N, typename... T>
struct TupleIndexer;
template <typename... T>
struct TupleOffsets;
template <typename Head, typename... Tail>
struct TupleOffsets<Head, Tail...> {
TupleOffsets() { Init(offsets); }
static void Init(uintptr_t* offsets);
uintptr_t const& operator[] (uintptr_t i) const { return offsets[i]; }
private:
uintptr_t offsets[sizeof...(Tail) + 1];
};
template <typename Head, typename... Tail>
void TupleOffsets<Head, Tail...>::Init(uintptr_t* offsets) {
typedef Tuple<Head, Tail...> Type;
*offsets = offsetof(Type, element);
TupleOffsets<Tail...>::Init(++offsets);
}
template <>
struct TupleOffsets<> {
TupleOffsets() {}
static void Init(uintptr_t* offsets) {}
};
template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : public Tuple<Tail...> {
private:
Head element;
public:
void* Get(uintptr_t i) {
return (uint8_t*) this + offsets[i];
}
template <uintptr_t N>
typename TupleIndexer<N, Head, Tail...>::Type& Get() {
return TupleIndexer<N, Head, Tail...>::Get(*this);
}
uintptr_t GetCount() const {
return sizeof...(Tail) + 1;
}
private:
static const TupleOffsets<Head, Tail...> offsets;
friend struct TupleOffsets<Head, Tail...>;
friend struct TupleIndexer<0, Head, Tail...>;
};
template <typename Head, typename... Tail>
const TupleOffsets<Head, Tail...> Tuple<Head, Tail...>::offsets;
template <>
class Tuple<> {
public:
uintptr_t GetCount() const {
return 0;
}
};
template <typename Head, typename... Tail>
struct TupleIndexer<0, Head, Tail...> {
typedef Head& Type;
static Type Get(Tuple<Head, Tail...>& tuple) {
return tuple.element;
}
};
template <uintptr_t N, typename Head, typename... Tail>
struct TupleIndexer<N, Head, Tail...> {
typedef typename TupleIndexer<N - 1, Tail...>::Type Type;
static Type Get(Tuple<Head, Tail...>& tuple) {
return TupleIndexer<N - 1, Tail...>::Get(*(Tuple<Tail...>*) &tuple);
}
};
На практике это работает.Тем не менее, компилятор выдает предупреждение за использование offsetof для типов данных, отличных от POD, и я не уверен, насколько это решение переносимо.Кто-нибудь знает, как я могу улучшить это решение?