Is it possible to iterate an mpl::vector at run ti

2019-02-01 21:49发布

问题:

Generally, I would use boost::mpl::for_each<>() to traverse a boost::mpl::vector, but this requires a functor with a template function declared like the following:

template<typename T> void operator()(T&){T::staticCall();}

My problem with this is that I don't want the object T to be instantiated by for_each<>. I don't need the T parameter in the operator() at all. Is there a way to accomplish this, or an alternative to for_each<> that doesn't pass an object of type T to the template function?

Optimally, I would like the operator() definition to look like this:

template<typename T> void operator()(){T::staticCall();}

And of course, I don't want T to be instantiated at all prior to the call. Any other tips/suggestions are also welcome.

回答1:

Interesting question! As far as I can tell, Boost.MPL does not seem to provide such an algorithm. However, writing your own should not be too difficult using iterators.

Here is a possible solution:

#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/vector.hpp>

using namespace boost::mpl;


namespace detail {

template < typename Begin, typename End, typename F >
struct static_for_each
{
    static void call( )
    {
        typedef typename Begin::type currentType;

        F::template call< currentType >();
        static_for_each< typename next< Begin >::type, End, F >::call();
    }
};


template < typename End, typename F >
struct static_for_each< End, End, F >
{
    static void call( )
    {
    }
};

} // namespace detail


template < typename Sequence, typename F >
void static_for_each( )
{
    typedef typename begin< Sequence >::type begin;
    typedef typename end< Sequence >::type   end;

    detail::static_for_each< begin, end, F >::call();
}

[The naming may not be very well chosen, but well...]

Here is how you would use this algorithm:

struct Foo
{
    static void staticMemberFunction( )
    {
        std::cout << "Foo";
    }
};


struct Bar
{
    static void staticMemberFunction( )
    {
        std::cout << "Bar";
    }
};


struct CallStaticMemberFunction
{
    template < typename T >
    static void call()
    {
        T::staticMemberFunction();
    }
};


int main()
{
    typedef vector< Foo, Bar > sequence;

    static_for_each< sequence, CallStaticMemberFunction >(); // prints "FooBar"
}


回答2:

Just encountered the same situation and provided different solution to the problem which I would like to share. It's not as that obvious, but it uses an existing algorithm. The idea is to use pointers instead.

typedef boost::mpl::vector<type1*, type2*> container;

struct functor
{
    template<typename T> void operator()(T*)
    {
        std::cout << "created " << typeid(T).name() << std::endl;
    }
};

int main()
{
    boost::mpl::for_each<container>(functor());
}

so here we get a null pointers, but we don't care as we are not going to use them.

As I stated before that is not obvious in the code and would probably require some additional comments, but it still solves the question without writing any additional code.

added

I think Diego Sevilla suggested something similar.



回答3:

Well, first of all, the static call in your code means that your object will exist. Before/after is meaningless in that regard. The only time, "I don't want T to be instantiated at all prior to the call," make sense is when T is a template. It's not, because it can't be. It is true that it is that line that causes the object to exist, but I am pretty sure it won't just exist there once the product is compiled.

Second of all, I don't believe that there's a current method to use for_each without instantiating. IMHO this is a bug in MPL caused by a questionable decision to use operator(). Won't say it's wrong since I know the developer and he's a lot smarter than I am, but it seems so from here now that you bring this up.

So, I think you're stuck having to remake a for_each that calls a templated function that doesn't require the parameter. I'm almost certain it is possible, but also equally certain it's not readily available as a premade component in MPL.



回答4:

Marcin, you're very right. I've been thinking this along and I don't see an easy solution for this. Even if you cannot write the empty operator(), it would be at least possible to use a pointer, that doesn't need an actual object to exist. You have to roll your own implementation, it seems.



回答5:

Here is an alternative solution highly inspired from Luc Touraille's answer.

This version is done using Metafunction classes instead of functions which allows the static_for_each to be called even outside of function scopes (useful if the job has to be totally done at compiletime so you have no unnecessary functions called at runtime).

Furthermore it gives more interaction thanks to the first and last typedefs, allowing to get information out of the loop if necessary, a little like the way a return works for a function.

You can also access the previous iteration result within each iteration thanks to the second template parameter Previous passed to the metafunction class F.

Finally you can provide data to the loop process using the Initial template parameter, it will be given as the value of the Previous parameter of the first iteration.

# include <boost/mpl/begin_end.hpp>
# include <boost/mpl/next_prior.hpp>
# include <boost/mpl/apply.hpp>

namespace detail_static_for_each
{
  // Loop
  template<typename Begin, typename End, typename F, typename Previous>
  struct static_for_each
  {
  private:
    typedef typename Begin::type                                current_type;

  public:
    typedef typename boost::mpl::apply<F, current_type, Previous>::type             first;
    typedef typename static_for_each<typename boost::mpl::next<Begin>::type, End, F, first>::last   last;
  };

  // End of loop
  template<typename End, typename F, typename Last>
  struct static_for_each<End, End, F, Last>
  {
  public:
    typedef Last    first;
    typedef Last    last;
  };

} // namespace detail_static_for_each

// Public interface
template<typename Sequence, typename F, typename Initial = void>
struct  static_for_each
{
private:
  typedef typename boost::mpl::begin<Sequence>::type        begin;
  typedef typename boost::mpl::end<Sequence>::type          end;

  typedef typename detail_static_for_each::static_for_each<begin, end, F, Initial>  loop;

public:
  typedef typename  loop::first                 first;
  typedef typename  loop::last                  last;
};

Here is a simple example that both gives and retrieves data :

# include <iostream>

# include <boost/type_traits/is_same.hpp>

# include <boost/mpl/if.hpp>
# include <boost/mpl/vector.hpp>

# include "static_for_each.hpp"

struct is_there_a_float                                                                                                                                                                                              
{                                                                                                                                                                                                                    
    template<typename currentItem, typename PreviousIterationType>                                                                                                                                                     
    struct apply                                                                                                                                                                                                       
    {                                                                                                                                                                                                                  
        typedef typename boost::mpl::if_< PreviousIterationType,                                                                                                                                                         
                                          PreviousIterationType,                                                                                                                                                         
                                          boost::is_same<float, currentItem> >::type    type;                                                                                                                        
    };                                                                                                                                                                                                                 
};

struct  test                                                                                                                                                                                                         
{                                                                                                                                                                                                                    
    typedef boost::mpl::vector< char, long, long, double, float, int, char > sequence;                                                                                                                                 

    typedef static_for_each<sequence, is_there_a_float, boost::false_type>::last    found;                                                                                                               
};

int     main(void)                                                                                                                                                                                                   
{                                                                                                                                                                                                                    
    std::cout << std::boolalpha << test::found::value << std::endl;                                                                                                                                                    

    return (0);                                                                                                                                                                                                        
}

These features makes the use of static_for_each more similar to the use of common runtime loops (while, for, BOOST_FOREACH ...) as you can interact more directly with the loop.



回答6:

I liked (up-voted) the solution with pointer and own defined *_for_each function. Here is an alternative using type wrapper for T if the goal is to avoid object creation till it is needed.

template<typename T>
struct Wrapper
{
  typedef T type;
};

struct Functor
{
  template<typename T> void operator()(T t)
  {
    T::type obj(1);
    T::type::static_fuc();
  }
};

struct T1
{
  T1(int a) : m_a(a) { }
  int m_a;
  static inline void static_fuc() { }
};
struct T2
{
  T2(int a) : m_a(a) { }
  int m_a;
  static inline void static_fuc() { }
};

void fun()
{
  namespace mpl=boost::mpl;
  typedef mpl::vector<Wrapper<T1>,Wrapper<T2> > t_vec;
  mpl::for_each<t_vec>(Functor());
}