Как реализовать boost MPL FOLD в С++ 11 только с использованием пакета параметров?

У boost mpl более распространенный алгоритм — fold . Этот алгоритм является базовым для многих других алгоритмов.

template< typename Seq, typename State, typename Op> struct fold { ... }
//Here Seq is <T0,T1,...,Tn> any sequence.
// result of fold is  op<  op < ...< op<State,T0>::type, T1>::type > ... >, Tn>::type

для получения дополнительной информации прочитайте ссылку.

Fold в С++ 11 с вариативными шаблонами можно переопределить следующее:

 template< typename state, typename op, typename ...elements> struct fold;

Как это реализовать - не проблема, но КАК ЭТО РЕАЛИЗОВАТЬ, используя только упаковку параметров, сложно или может быть неразрешимой проблемой.

В: Можно ли это реализовать только с помощью упаковки параметров??

я хочу что-то вроде

template< typename OP, typename State, typename ...T>
struct fold
{
     // only for illustration
     typedef  apply< apply<....<apply<Op,State,T>>...>::type type; 
};

person Khurshid    schedule 18.11.2013    source источник
comment
Как сделать X, используя технику Y, не является практической проблемой. Как это сделать без линейной глубины инстанцирования шаблона — практическая проблема, ответ на которую вы можете получить. Это то, что вы хотите, чтобы ответили? Или есть реальная практическая необходимость в реализации на основе пакета параметров, которую мне не хватает?   -  person Yakk - Adam Nevraumont    schedule 18.11.2013
comment
Это практически актуально, потому что реализация на основе пакетов параметров компилируется намного быстрее, чем линейная реализация. Я работаю над проектом, в котором использовалась библиотека boost msm, и проект компилировался около часа. Иногда может помочь ccache, но я потерял много времени на компиляцию.   -  person Khurshid    schedule 19.11.2013
comment
скорость компиляции - практическая проблема: спасибо, что упомянули об этом.   -  person Yakk - Adam Nevraumont    schedule 19.11.2013
comment
Для компиляции проекта потребуется около часа времени и более 6 Гб оперативной памяти. Для вашего ума это не практическая задача!!! Мне придется компилировать 4-5 раз в день. Мое 30-40% рабочего времени будет потрачено только на компиляцию.   -  person Khurshid    schedule 20.11.2013
comment
Я сказал, что это была практическая проблема в моем предыдущем комментарии? Я сейчас в замешательстве.   -  person Yakk - Adam Nevraumont    schedule 20.11.2013
comment
Ой, извини. Я думал, ты сказал образное выражение. Потому что много раз я это слышал. Многие программисты, которых я знаю, думают, что время компиляции не является практической проблемой, главное - скорость выполнения. и извините за мой плохой английский.   -  person Khurshid    schedule 20.11.2013


Ответы (2)


Вот пример реализации логарифмической глубины рекурсии шаблона fold.

#include <cstddef>

template<typename... Ts> struct types {};
template<typename T, typename U>
struct concat;
template<typename... Ts, typename... Us>
struct concat< types<Ts...>, types<Us...> > {
  typedef types<Ts..., Us...> result;
};
template<typename Ts, typename Us>
using Concat = typename concat<Ts, Us>::result;

template<std::size_t n, typename Ts>
struct split;
template<std::size_t n, typename... Ts>
struct split<n, types<Ts...>> {
private:
  typedef split<n/2, types<Ts...>> one;
  typedef split<n-n/2, typename one::right> two;
public:
  typedef Concat< typename one::left, typename two::left > left;
  typedef typename two::right right;
};
template<typename... Ts>
struct split<0, types<Ts...>> {
  typedef types<> left;
  typedef types<Ts...> right;
};
template<typename T, typename... Ts>
struct split<1, types<T, Ts...>> {
  typedef types<T> left;
  typedef types<Ts...> right;
};
template<template<typename, typename>class OP, typename State, typename Ts>
struct fold_helper;
template<template<typename, typename>class OP, typename State, typename... Ts>
struct fold_helper<OP, State, types<Ts...>> {
private:
  typedef split<sizeof...(Ts)/2, types<Ts...>> parts;
  typedef typename parts::left left;
  typedef typename parts::right right;
  typedef typename fold_helper<OP, State, left>::result left_result;
public:
  typedef typename fold_helper<OP, left_result, right>::result result;
};
template<template<typename, typename>class OP, typename State>
struct fold_helper<OP, State, types<>> {
  typedef State result;
};
template<template<typename, typename>class OP, typename State, typename T>
struct fold_helper<OP, State, types<T>> {
  typedef typename OP<State,T>::type result;
};
template<template<typename, typename>class OP, typename State, typename... Ts>
struct fold {
  typedef typename fold_helper<OP, State, types<Ts...>>::result type;
};
template<template<typename, typename>class OP, typename State, typename... Ts>
using Fold = typename fold<OP, State, Ts...>::type;

template<typename left, typename right>
struct op_test {
  typedef int type;
};

int main() {
  Fold< op_test, double, int, char, char*, int* > foo = 8;
}

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

Когда у меня есть две половины, я fold по первой половине, затем беру результат этого и fold по второй половине.

В то время как работа O(N) выполняется, вы в любой момент находитесь в глубине только O(lg(N)) .

Я не вижу теоретической причины, по которой мы не можем достичь глубины O(lg(lg(N)) , но я не вижу и смысла: при максимальной глубине ~ 1000 вам нужно будет иметь десятки вложенных fold на списки типов длиной в 100 секунд, чтобы исчерпать пространство стека шаблона: по моему опыту, компилятор взрывается задолго до того, как будет достигнут предел логарифмической глубины.

И теперь он компилируется: http://ideone.com/CdKAAT

split — это обычно полезный способ работы с длинными списками без рекурсии один раз для каждого элемента. fold и fold_helper довольно очевидны, если у вас есть логарифмическое split.

person Yakk - Adam Nevraumont    schedule 18.11.2013
comment
Я проверил это с 1000 аргументами с clang 3.3. Собирается за 3,5 с. Это не требует глубокой рекурсии, но компилируется примерно в 3 раза медленнее, чем линейная реализация fold. - person Khurshid; 19.11.2013
comment
@khurshid попробуйте 10000 - если у компилятора есть ограничение на рекурсию шаблона, это должно работать медленнее, чем линейное. Как вы заметили, для компиляции наиболее практичных размеров потребуется больше времени. - person Yakk - Adam Nevraumont; 19.11.2013

Почему ограничение только на упаковку параметров?

Как и во многих решениях для пакетов параметров, вам, вероятно, придется углубиться либо в какую-то перегрузку, либо в частичную специализацию. Учитывая, что вы пишете метафункцию шаблона, я бы выбрал частичную специализацию.

В приведенном ниже примере обрабатывается случай с 0 элементами путем возврата состояния, и мы используем эту позицию в шаблоне для накопления результата. Специализация обрабатывает случай 1+, комбинируя первый элемент с состоянием и рекурсивно вызывая метафункцию шаблона.

template< typename OP, typename State, typename ...T>
struct fold
{
    typedef State type;
};

template<typename OP, typename State, typename Head, typename... Tail>
struct fold< OP, State, Head, Tail...>
{
   typedef typename fold<OP, typename OP::template apply<State,Head>::type, Tail...>::type type;
};

http://ideone.com/DDCCbB

person Dave S    schedule 18.11.2013