Преобразование непрозрачных массивов в аргументы функций с использованием шаблонов и списков переменных

трансляторЯ хотел бы отобразить некоторые массивы из контейнера шаблонов в аргументы функции в соответствии с определенным порядком, определенным индексами, хранящимися в списке шаблонов с переменными параметрами (я не могу придумать более простой способ определения проблемы).

Массивы хранятся с использованием void* но безопасность типов между массивами и параметрами функции гарантируется вспомогательным классом. Этот же вспомогательный класс должен расширять заданные пакеты параметров, извлекать соответствующие массивы, связывать их с указателем функции и вызывать функцию. Вот где я застрял.

подробно: Я заранее прошу прощения за длинный вопрос и размещение кода, который не компилируется, но я старался быть как можно более кратким.

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

Для простоты мы предполагаем вспомогательные объекты typelist TLAlg::length<TL> а также TLAlg::TypeAt определены и позволяют пользователям получить доступ к длине списка типов и N-го типа соответственно.

Контейнерный класс выделяет массив для каждого типа в списке типов (называемом полем) и сохраняет непрозрачный указатель на эти буферы. Безопасный геттер реализован для доступа к определенному индексу поля. Его реализация заключается в следующем:

// container class, stores an array for each type in the typelist
template<class TL>
class Volume {
public:
// list of opaque buffers
void *opaque_buffers[TLAlg::length<TL>::value];

template<int size>
Volume(const size_t (&dim)[size]){
// each opaque_buffers is initialized here
...
}

// getters are using the field index for type-safety
template <int index> typename
TLAlg::TypeAt<TL, index>::Result &
get(const std::initializer_list<size_t> &position);
};

Мы хотим реализовать Functor объект, который будет применять данную функцию к объему с использованием определенного подмножества списка типов. Вместо того, чтобы манипулировать массивами напрямую, пользователь дает список индексов полей, к которым он хочет получить доступ, и указатель на функцию, которую нужно применить. Объект functor отвечает за установку правильных аргументов.

Чтобы повысить безопасность типов, мы разделяем их в два списка: только чтение и чтение / запись (чтение const и не const). Данный прототип функции должен соответствовать определению объекта функтора: код компилируется только в том случае, если указатель на данную функцию точно соответствует определению аргумента, поэтому нам не нужно беспокоиться о несовпадении типов. Реализация функтора:

template<typename TL, class T1, class T2> struct Functor{};
template< typename TL,
template<size_t...> class T1, size_t... A1, // read only arguments
template<size_t...> class T2, size_t... A2  // read-write arguments
>
struct Functor< TL, T1<A1...>, T2<A2...> >{
// type of the function pointer
typedef void (*Type)(const typename TLAlg::TypeAt<TL, A1>::Result* ... ,
typename TLAlg::TypeAt<TL, A2>::Result* ...);

Functor(Volume<TL> &v, Type f): f(f){
// At this point we have everything we need: the volume, the function
// and the list of arguments, but how to combine them all?

// maybe some magic here to map the opaque pointers to the arguments?
}

void operator()(){
// or maybe here?
}
}

Как видите, в настоящий момент функтор ничего не делает, потому что я не знаю, как сопоставить два пакета параметров с массивами контейнеров и связать их с указателем функции …

Для ясности, вот пример использования для класса functor:

// main Typelist
typedef Typelist<float, Typelist<double, Typelist<int, NullType>>> Pixel;

// function we want to apply: reads first and last field of the list and updates second
void foo(const float  *f1,
const int    *f3,
double *f2){}

// helper class to store the parameter packs
template<size_t ... T> struct Pack {};

int main(){
// volume declaration
Volume<Pixel> volume((size[]){1024,1024});

// delare typesafe functor
Functor<Pixel,     // typelist
Pack<0,2>, // list of read-only fields
Pack<1>    // list of read-write fields
> apply_foo(volume, foo);

apply_foo(); // <- this does nothing at the moment
}

Я пытался играть с std::forward а также std::bind долгое время, но пока не могу найти правильное решение. Подставляя список типов для std::tuple может быть рассмотрено, но предпочтительно сохранить текущее определение.

Этот код может выглядеть странным и излишне сложным, но это очень упрощенная версия массивной среды, в которой использование этих классов имеет смысл.

Любая помощь будет высоко оценен.

Пояснения к ответу Якка:

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

typedef MakeTypeList((float,  p),
(float,  bnd),
(float,  gosa)) HimenoFields;

// I can now use aliases, and the declaration order does not matter in the code.
// here is an access to the second field:
volume.get<HimenoFields::bnd>({0,0,0});

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

Во-вторых, я понимаю, почему вы запутались в добытчике. Как я уже говорил, это очень упрощенная версия кода, несмотря на очень длинный вопрос. В реальной программе объемы являются многомерными и либо сплющиваются в одном массиве, либо распределяются в многомерном массиве, поэтому для геттера требуются полные координаты. Существует несколько реализаций этих геттеров с разными параметрами.

Наконец, Functor не нужно знать, к какому элементу применить функцию, потому что он сам контролирует пространство итерации и применяет предопределенный скелет (т. Е. Трафарет, волновой фронт …). Опять же, я пропустил это ради простоты.

2

Решение

Во-первых, я бы переписал ваш type_list:

template<typename... Ts>
struct type_list {};

с переменным типом вместо вашего хака с 18 аргументами. Пишу type_at<n>::type а также index_of<T>::value тогда не сложно Сопоставление между ними и вашей парной TypeList тоже не сложно

template<typename list>
struct make_TypeList;

template<typename T0, typename T1, typename...Ts>
struct make_TypeList<type_list<T0, T1, Ts...>> {
typedef typename make_Typelist< type_list<T1, Ts...> >::type tail;
typedef TypeList< T0, tail > type;
};
template<typename T0>
struct make_TypeList<type_list<T0>> {
typedef TypeList< T0, NullType > type;
};
template<>
struct make_TypeList<type_list<>> {
typedef NullType type;
};

если тебе это действительно нужно Есть причины работать со списками, не относящимися к типам, но вы не демонстрируете их.

Создать коллекцию индексов типов во время компиляции немного сложно, но если вы передадите верхнюю границу, вы сможете это сделать. Цель состоит в том, чтобы создать последовательность:

template<size_t... s>
struct seq {};

Если вы получаете эти индексы во время компиляции, это проще. Как только у вас есть эта последовательность, и у вас есть type_atВы можете написать вызывающую функцию вроде этого:

template<size_t... s, typename... Ts>
void evaluate( seq<s...> unused, void(*func)(Ts*... ts) ) {
func( &get_at<s>()... );
}

где мы распаковываем последовательность непосредственно в вызов функции. Как это часто бывает, рассматриваемая последовательность является просто 0,1,2,3,4,...,n-1, который может быть легко сгенерирован:

template<size_t max, size_t... s>
struct make_seq:make_seq<max-1, max-1, s...> {};
template<size_t... s>
struct make_seq<0, s...> {
typedef seq<s...> type;
};

Чтобы было ясно: operator() вызывает вспомогательную функцию после выполнения make_seq<sizeof...(Ts)>::type(), передавая это во вспомогательную функцию, которая затем вызывает func( &get_at<s>(/*n maybe?*/)... )и боб твой дядя

Меня смущает одна вещь:

// getters are using the field index for type-safety
template <int index> typename
TLAlg::TypeAt<TL, index>::Result &
get(const std::initializer_list<size_t> &position);

Я не уверен почему const std::initializer_list<size_t> &position нужно, или, по крайней мере, почему у вас нет:

template <int index> typename
TLAlg::TypeAt<TL, index>::Result &
get_at(size_t n);

что заставляет меня думать, что ваш operator() в вашем функторе отсутствует «к какому индексу применить этот функтор», если ваш Volume это массив нескольких типов.

Но я сильно подозреваю, что «создайте пакет индексов, вызовите вспомогательную функцию, чтобы вы могли разобраться, а затем разверните пакет в вызове функции» — это трюк, который вам не хватает.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]