Можно ли выполнить итерацию mpl :: vector во время выполнения без создания экземпляров типов в векторе? - PullRequest
18 голосов
/ 29 октября 2010

Как правило, я бы использовал boost::mpl::for_each<>() для обхода boost::mpl::vector, но для этого требуется функтор с функцией шаблона, объявленной так:

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

Моя проблема в том, что я не хочу, чтобы объект T создавался с помощью for_each<>. Мне вообще не нужен параметр T в operator(). Есть ли способ сделать это или альтернатива for_each<>, которая не передает объект типа T в функцию-шаблон?

Оптимально, я бы хотел, чтобы определение оператора () выглядело так:

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

И, конечно, я не хочу, чтобы T вообще создавался до вызова. Любые другие советы / предложения также приветствуются.

Ответы [ 6 ]

14 голосов
/ 07 ноября 2010

Просто столкнулся с той же ситуацией и предоставил другое решение проблемы, которой я хотел бы поделиться. Это не так очевидно, но он использует существующий алгоритм. Идея состоит в том, чтобы вместо этого использовать указатели.

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());
}

поэтому здесь мы получаем нулевые указатели, но нам все равно, потому что мы не собираемся их использовать.

Как я уже говорил ранее, это неочевидно в коде и, вероятно, потребует некоторых дополнительных комментариев, но все же решает вопрос без написания дополнительного кода.

добавил

Я думаю Диего Севилья предложил нечто подобное.

13 голосов
/ 29 октября 2010

Интересный вопрос!Насколько я могу судить, Boost.MPL, похоже, не обеспечивает такой алгоритм.Тем не менее, написание собственного не должно быть слишком сложным с использованием итераторов.

Вот возможное решение:

#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();
}

[Именование может быть не очень хорошо выбрано, но хорошо ...]

Вот как бы вы использовали этот алгоритм:

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"
}
1 голос
/ 30 октября 2012

Вот альтернативное решение, вдохновленное ответом Люка Турэля .

Эта версия выполняется с использованием классов метафункций вместо функций, которые позволяют вызывать static_for_each даже вне областей функций (полезно, если работа должна быть полностью выполнена во время компиляции, поэтому у вас нет ненужных функции, вызываемые во время выполнения).

Более того, он дает больше взаимодействия благодаря типам first и last, позволяющим при необходимости извлекать информацию из цикла, немного похоже на то, как return работает для функции.

Вы также можете получить доступ к предыдущему результату итерации в каждой итерации благодаря второму параметру шаблона Previous, переданному в класс метафункций F.

Наконец, вы можете предоставить данные процессу цикла, используя параметр шаблона Initial, он будет задан как значение параметра Previous первой итерации.

# 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;
};

Вот простой пример, который дает и получает данные:

# 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);                                                                                                                                                                                                        
}

Эти функции делают использование static_for_each более похожим на использование обычных циклов выполнения (while, for, BOOST_FOREACH ...), поскольку вы можете более напрямую взаимодействовать с циклом .

1 голос
/ 29 октября 2010

Марцин, ты очень прав. Я думал об этом вместе, и я не вижу простого решения для этого. Даже если вы не можете написать пустой operator(), по крайней мере можно использовать указатель, которому не нужен фактический объект для существования. Похоже, вам нужно накатить собственную реализацию.

1 голос
/ 29 октября 2010

Ну, во-первых, статический вызов в вашем коде означает, что ваш объект будет существовать. До / после не имеет смысла в этом отношении. Единственный момент, когда «я не хочу, чтобы T вообще создавался до вызова», имеет смысл, когда T является шаблоном. Это не так, потому что этого не может быть. Это правда, что именно эта линия заставляет объект существовать, но я уверен, что он не будет существовать там после компиляции продукта.

Во-вторых, я не верю, что существует текущий метод для использования for_each без создания экземпляров. ИМХО, это ошибка в MPL, вызванная сомнительным решением использовать operator (). Не скажу, что это неправильно, так как я знаю разработчика, и он намного умнее меня, но теперь кажется, что вы подняли этот вопрос.

Итак, я думаю, что вы застряли в необходимости переделать for_each, который вызывает шаблонную функцию, которая не требует параметра. Я почти уверен, что это возможно, но в то же время уверен, что он недоступен в качестве готового компонента в MPL.

0 голосов
/ 07 июня 2017

Мне понравилось решение с указателем и собственной функцией * _for_each. Вот альтернативный вариант использования обёртки типа для T, если цель состоит в том, чтобы избежать создания объекта до тех пор, пока он не понадобится.

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());
}
...