I have the following mpl sequence
boost::mpl::vector_c<std::size_t, 0, 1, 2, 0, 1, 0>
and i need to transform it according to the following algorithm (runtime version):
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)
the result in my case should be:
boost::mpl::vector_c<std::size_t, 0, -1, -1, 1, -1, 2>
I can imagine at least two ways to achieve this at runtime using std::transform or std::accumulate, but have no clue of how to achieve the same result at compile time with mpl. The main problem for me is storing somehow the state 'i' (current number of zeroes found) as well as the output sequence.
Thank you very much!
I would like to share with al of you the adopted method, based on HighCommander4's answer. Indeed, it is possible to use boost::mpl::fold
with a custom metafunction. The only conceptual change, compared to HighCommander4 is the
packing of the current available free index as first element of the growing output sequence. It can be removed at the end
of the type calculation using 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: In this implementation a '1' in the input sequence is converted to an increasing index, while values larger than 1 are converted to std::size_t(-1). Zeroes are not allowed in the input sequence (checked elsewhere).
PS2: I have checked and indeed there are no facilities in boost::mpl
to convert an arbitrary mpl
sequence (as the one obtained as a result of the algorithm) back to an boost::mpl::vector
. Therefore it is
necessary to write code for this also... I ended up adapting the code in
<boost/fusion/container/vector/detail/as_vector.hpp>
to 'normalize' the type computation result.
Thank you again!
Template metaprogramming can indeed take some getting used to.
Notice how loops turn into recursion, if statement cases into template specializations, and "state" into additional template parameters:
#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>();
}
This can probably be written more nicely (perhaps with mpl::fold
).