Передача двух наборов аргументов в метафункцию
Есть задачка:
Напишите метафункцию Zip (аналог std::transform), которая принимает два списка целых чисел одинаковой длины, а так же бинарную метафункцию, и возвращает список, получившийся в результате поэлементного применения метафункции к соответствующим элементам исходных списков. Пример:
template<int a, int b>
struct Plus
{
static int const value = a + b;
};
// два списка одной длины
using L1 = IntList<1,2,3,4,5>;
using L2 = IntList<1,3,7,7,2>;
// результат применения — список с поэлементными суммами
using L3 = Zip<L1, L2, Plus>::type; // IntList<2, 5, 10, 11, 7>
Даны IntList и InCons (IntCons позволяет увеличить список на один элемент — он добавляется в начало списка):
struct IntList;
template <int h, int... I>
struct IntList<h, I...>
{
static const int Head = h;
using Tail = IntList<I...>;
};
template<>
struct IntList<> {};
template <int value, class IL>
struct IntCons {};
template <int value, int... primes>
struct IntCons<value, IntList<primes...>>
{
using type = IntList<value, primes...>;
};
Моё приблизительное решение:
template<int... L1, int... L2, template <int, int> class Op>
struct Zip;
template<int... val, int... val2, template <int, int> class Op >
struct Zip<IntList<val...>, IntList<val2...>, Op>
{
using value = typename Op<IntList<val...>::Head, IntList<val2...>::Head>::value;
using type = typename IntCons<value, Zip<IntList<val...>::Tail, IntList<val2...>::Tail, Op>>::type;
};
Естественно, не рабочее. Соответственно вопрос - каким образом можно передать два IntList'а в функцию Zip? Два набора аргументов передать невозможно. Что-то в духе template<template<int... Values> class F1, template<int... Vals2> class F2, template <int, int> class Op> struct Zip; тоже не работает. Помогите пожалуйста понять синтаксис, который день к задаче возвращаюсь.
UPD: Задачка конечно же решалась крайне просто:
template<typename L1, typename L2, template<int, int> class Op>
struct Zip {
using Next1 = typename L1::Tail;
using Next2 = typename L2::Tail;
using type = typename IntCons< Func<L1::Head, L2::Head>::value, typename Zip<Next1, Next2, Op>::type >::type;
};
template<template<int, int> class Op>
struct Zip<IntList<>, IntList<>, Op>{
using type = IntList<>;
};
Ответы (2 шт):
Не претендую на единственно-верное решение (или даже хорошее), но я бы сделал как-то так:
// main.cpp
#include <iostream>
struct EmptyNode {
const static size_t size = 0;
};
template <int Val, typename Tail>
struct MetaListImp {
const static size_t size = Tail::size + 1;
template <int Num>
static constexpr int get() {
static_assert(Num >= 0);
if constexpr (Num == 0) {
return Val;
} else {
return Tail::template get<Num - 1>();
}
}
};
template <int Val, int... Args>
auto makeList() {
if constexpr (sizeof...(Args) != 0) {
auto tail = makeList<Args...>();
return MetaListImp<Val, decltype(tail)>{};
} else {
return MetaListImp<Val, EmptyNode>();
}
}
template <int... Args>
struct MetaList {
using type = decltype(makeList<Args...>());
};
template <int First, int Second>
struct Plus {
const static int result = First + Second;
};
template <int Num,
typename List1,
typename List2,
template <int, int>
typename Op>
auto calc() {
if constexpr (Num == List1::size - 1) {
return MetaListImp<
Op<List1::template get<Num>(), List2::template get<Num>()>::result,
EmptyNode>{};
} else {
auto tail = calc<Num + 1, List1, List2, Op>();
return MetaListImp<
Op<List1::template get<Num>(), List2::template get<Num>()>::result,
decltype(tail)>{};
}
}
template <typename List1, typename List2, template <int, int> typename Op>
struct Zip {
static_assert(List1::size == List2::size);
using type = decltype(calc<0, List1, List2, Op>());
};
int main() {
using first = MetaList<0, 1, 2, 3, 4>::type;
using second = MetaList<1, 2, 3, 4, 5>::type;
using output = Zip<first, second, Plus>::type;
std::cout << "size of output: " << output::size << std::endl;
std::cout << output::get<0>() << std::endl;
std::cout << output::get<1>() << std::endl;
std::cout << output::get<2>() << std::endl;
std::cout << output::get<3>() << std::endl;
std::cout << output::get<4>() << std::endl;
return EXIT_SUCCESS;
}
А вообще я бы посоветовал вам посмотреть boost::mpl - библиотека как раз заточена на dimension anlyzis
UPD: усовершенствовал код:
// main.cpp
#include <iostream>
struct EmptyNode {
const static size_t size = 0;
};
template <int Val, typename Tail>
struct MetaListImp {
using tail = Tail;
static constexpr size_t size = Tail::size + 1;
static constexpr int val = Val;
};
template <int Val, int... Args>
struct MetaListConstructor {
static constexpr size_t tailLen = sizeof...(Args);
using type = MetaListImp<Val, typename MetaListConstructor<Args...>::type>;
};
template <int Val>
struct MetaListConstructor<Val> {
static constexpr size_t tailLen = 0;
using type = MetaListImp<Val, EmptyNode>;
};
template <int... Args>
struct MetaList {
using type = typename MetaListConstructor<Args...>::type;
};
template <int First, int Second>
struct Plus {
static constexpr int result = First + Second;
};
template <template <int, int> typename Op, typename List1, typename List2>
struct ZipConstructor {
using type = MetaListImp<Op<List1::val, List2::val>::result,
typename ZipConstructor<Op,
typename List1::tail,
typename List2::tail>::type>;
};
template <template <int, int> typename Op>
struct ZipConstructor<Op, EmptyNode, EmptyNode> {
using type = EmptyNode;
};
template <typename List1, typename List2, template <int, int> typename Op>
struct Zip {
static_assert(List1::size == List2::size);
using type = typename ZipConstructor<Op, List1, List2>::type;
};
template <typename List>
void printMetaList() {
if constexpr (List::size > 0) {
std::cout << List::val << std::endl;
printMetaList<typename List::tail>();
}
}
int main() {
using first = MetaList<0, 1, 2, 3, 4>::type;
using second = MetaList<1, 2, 3, 4, 5>::type;
using output = Zip<first, second, Plus>::type;
printMetaList<output>();
return EXIT_SUCCESS;
}
Вы как-то сильно переусложнили. Это делается вот так:
template <typename A, typename B, template <int, int> class Op>
struct Zip;
template <int ...A, int ...B, template <int, int> class Op>
struct Zip<IntList<A...>, IntList<B...>, Op>
{
using type = IntList<Op<A, B>::value...>;
};