Search code examples
c++boostiteratormetaprogrammingboost-mpl

unexpected result using boost mpl inserter iterator


I had expected the following to give the same result:

namespace mpl = boost::mpl;

template<int from, int to>
struct
make_vector1
 : mpl::copy<
     mpl::range_c<int,from,to>, 
     mpl::inserter< 
       mpl::vector<>,
       mpl::push_back<mpl::placeholders::_1,
              mpl::placeholders::_2 // <- Copy int_ types
             >
       > 
     >  
{};

template<int from, int to>
struct
make_vector2 
 : mpl::copy<
     mpl::range_c<int,from,to>, 
     mpl::inserter< 
       mpl::vector<>,
       mpl::push_back<mpl::placeholders::_1,
              mpl::int_<mpl::placeholders::_2::value> // <- Alternative?
              >
       > 
     >
{};

But they do not.

int
main  (int ac, char **av)
{
  typedef make_vector1<0,3>::type v1;
  typedef make_vector2<0,3>::type v2;

  //returns 0, as I would expect
  std::cout<<"I1 = "<<mpl::at<v1,mpl::int_<0> >::type::value <<std::endl;

  //returns 2, which has me stumpted.
  std::cout<<"I2 = "<<mpl::at<v2,mpl::int_<0> >::type::value <<std::endl;
}

Any idea what is going on here?

I want to use the second method to construct a mpl::vector of Example types, where:

template<int i>
struct Example : mpl::int_<i>
{};

but I cannot get it working.

Many thanks


Solution

  • You get a 2 because ::value on _2 is defined to be 2 (the placeholder index). MPL don't define :: on placeholders for obvious reason so you can not directly do this.

    Now, accessing an element in a mpl::range_c already gives you an mpl::int_ as you noticed so there is no need to try to extract the numerical value to put it back. The abstraction of the iteration over mpl sequence do it for you.

    For your actual use, case you can use a meta-function taking a mpl::int_ and returning your Example. You have to grasp that no proper generic meta-programming nor meta-lambda function can be done with integral type, thus the mpl::int_ abstraction :

    #include <boost/mpl/at.hpp>
    #include <boost/mpl/copy.hpp>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/range_c.hpp>
    #include <boost/mpl/push_back.hpp>
    
    namespace mpl = boost::mpl;
    
    template<int I> struct Example : boost::mpl::int_<I> 
    {
      static void foo() { std::cout << "**" << I << "**\n";}
    };
    
    template<class T> struct make_example
    {
      typedef Example<T::value> type;
    };
    
    template<int from, int to>
    struct
    make_vector2 
     : mpl::copy<
         mpl::range_c<int,from,to>, 
         mpl::inserter< 
           mpl::vector<>,
           mpl::push_back<mpl::placeholders::_1,
                  make_example<mpl::placeholders::_2> // <- Alternative?
                  >
           > 
         >
    {};
    
    int main(int ac, char **av)
    {
      typedef make_vector2<0,3>::type v2;
    
      mpl::at<v2,mpl::int_<0> >::type::foo();
    }
    

    I added foo() just to assess we go into the proper class type after calling at.

    So let's recap :

    • integral template parameters are dodgy and that's why MPL use int_. Every integral constant sequence actually return int_ already to keep the level of abstraction.
    • placeholders have a ::value for internal purpose, hence your initial result
    • Any meta-function an be turned into a lambda by instantiating it with a placeholders.