Boost.MPL преобразовать с состоянием?
У меня есть следующая последовательность mpl
boost::mpl::vector_c<std::size_t, 0, 1, 2, 0, 1, 0>
и мне нужно преобразовать его в соответствии со следующим алгоритмом (версия времени выполнения):
i=0
output_sequence=[]
for k in (0,...,len(input_sequence)-1):
if input_sequence[k] == 0:
output_sequence.append(i)
i=i+1
else:
output_sequence.append(-1)
результат в моем случае должен быть:
boost::mpl::vector_c<std::size_t, 0, -1, -1, 1, -1, 2>
Я могу представить по крайней мере два способа достичь этого во время выполнения, используя std::transform или std:: аккумулировать, но не имею ни малейшего представления о том, как добиться того же результата во время компиляции с помощью mpl. Основная проблема для меня - сохранить состояние 'i' (текущее число найденных нулей), а также последовательность вывода.
Большое спасибо!
ADDED
Я хотел бы поделиться со всеми вами принятым методом, основанным на ответе HighCommander4. Действительно, можно использовать boost::mpl::fold
с пользовательской метафункцией. Единственное концептуальное изменение по сравнению с HighCommander4 - это упаковка текущего доступного свободного индекса в качестве первого элемента растущей последовательности вывода. Его можно удалить в конце расчета типа, используя boost::mpl::pop_font
,
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/print.hpp>
namespace mpl=boost::mpl;
struct assign_index
{
enum { dim_dynamic, dim_static };
template<typename Output, typename Index,
typename Enable = typename mpl::if_<
mpl::equal_to<Index, mpl::size_t<1> >,
mpl::size_t<dim_dynamic>,
mpl::size_t<dim_static> >::type>
struct apply
{
typedef typename mpl::push_back<Output, mpl::size_t<-1> >::type type;
};
template<typename Output, typename Index>
struct apply <Output, Index, mpl::size_t<dim_dynamic> >
{
typedef typename mpl::front<Output>::type current;
typedef typename mpl::next<current>::type next;
typedef typename mpl::push_back<Output, current>::type append_type;
typedef typename mpl::push_front<
typename mpl::pop_front<append_type>::type,next
>::type type;
};
};
template<class input_sequence>
struct map_indices
{
// The first element of state0 keeps track of the current index count.
typedef mpl::vector_c<std::size_t, 0> state0;
typedef typename mpl::fold<input_sequence, state0, assign_index>::type output_sequence;
// Remove the first element from the final output sequence
typedef typename mpl::pop_front<output_sequence>::type type;
};
int main (int argc, const char** argv)
{
typedef mpl::vector_c<std::size_t, 1, 2, 3, 1, 2, 1> input_sequence;
typedef map_indices<input_sequence>::type output_sequence;
int i;
i=mpl::print<mpl::at_c<output_sequence, 0>::type>();
i=mpl::print<mpl::at_c<output_sequence, 1>::type>();
i=mpl::print<mpl::at_c<output_sequence, 2>::type>();
i=mpl::print<mpl::at_c<output_sequence, 3>::type>();
i=mpl::print<mpl::at_c<output_sequence, 4>::type>();
i=mpl::print<mpl::at_c<output_sequence, 5>::type>();
return 0;
}
PS: в этой реализации "1" во входной последовательности преобразуется в растущий индекс, а значения, большие 1, преобразуются в std::size_t(-1). Нули не допускаются во входной последовательности (проверено в другом месте).
PS2: я проверил и действительно нет никаких средств в boost::mpl
конвертировать произвольный mpl
последовательность (как полученная в результате алгоритма) обратно к boost::mpl::vector
, Поэтому необходимо написать код для этого также... Я в конечном итоге адаптировать код в
<boost/fusion/container/vector/detail/as_vector.hpp>
"нормализовать" результат вычисления типа.
Еще раз спасибо!
1 ответ
Шаблонное метапрограммирование действительно может потребовать некоторого привыкания.
Обратите внимание, как циклы превращаются в рекурсию, если операторы преобразуются в специализации шаблона, а "состояние" - в дополнительные параметры шаблона:
#include <cstddef>
#include <boost/mpl/print.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/size.hpp>
using boost::mpl::vector_c;
using boost::mpl::push_back;
using boost::mpl::pop_front;
using boost::mpl::front;
using boost::mpl::int_;
using boost::mpl::size;
typedef vector_c<std::size_t, 0, 1, 2, 0, 1, 0> input_sequence;
template <int i, int current>
struct next_i
{
static const int value = i;
};
template <int i>
struct next_i<i, 0>
{
static const int value = i + 1;
};
template <int i, int current>
struct next_output
{
static const int value = -1;
};
template <int i>
struct next_output<i, 0>
{
static const int value = i;
};
template <typename rest_of_input, int rest_of_input_size, int i, typename output_so_far>
struct loop
{
typedef typename front<rest_of_input>::type current_t;
static const int current = current_t::value;
static const int next_i_ = next_i<i, current>::value;
static const int next_output_ = next_output<i, current>::value;
typedef typename loop<
typename pop_front<rest_of_input>::type,
rest_of_input_size - 1,
next_i_,
typename push_back<output_so_far, int_<next_output_> >::type
>::type type;
};
template <typename rest_of_input, int i, typename output_so_far>
struct loop<rest_of_input, 0, i, output_so_far>
{
typedef output_so_far type;
};
template <typename input>
struct algorithm
{
typedef typename size<input>::type size_;
typedef typename loop<input, size_::value, 0, vector_c<std::size_t> >::type type;
};
typedef algorithm<input_sequence>::type output_sequence;
int main()
{
boost::mpl::print<output_sequence>();
}
Это может быть написано более красиво (возможно, с mpl::fold
).