Возможно ли итерация mpl:: vector во время выполнения без создания экземпляров в векторе?
Как правило, я использовал бы boost::mpl::for_each<>()
для перемещения по boost::mpl::vector
, но для этого требуется функтор с функцией шаблона, объявленной следующим образом:
template<typename T> void operator()(T&){T::staticCall();}
Моя проблема заключается в том, что я не хочу, чтобы объект T был создан for_each<>
. Мне вообще не нужен параметр T в operator()
. Есть ли способ выполнить это или альтернативу for_each<>
, которая не передает объект типа T функции шаблона?
Оптимально, я хотел бы, чтобы определение operator() выглядело так:
template<typename T> void operator()(){T::staticCall();}
И, конечно же, я не хочу, чтобы T был создан вообще до вызова. Любые другие советы/предложения также приветствуются.
Ответы
Ответ 1
Интересный вопрос! Насколько я могу судить, 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"
}
Ответ 2
Просто столкнулся с той же ситуацией и дал другое решение проблемы, которую я хотел бы поделиться. Это не так очевидно, но использует существующий алгоритм. Идея состоит в том, чтобы использовать указатели вместо этого.
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());
}
поэтому здесь мы получаем нулевые указатели, но нам все равно, поскольку мы не будем их использовать.
Как я уже говорил, это не очевидно в коде и, вероятно, потребует дополнительных комментариев, но он все еще решает вопрос, не набирая никакого дополнительного кода.
добавлен
Я думаю, что Диего Севилья предложила нечто подобное.
Ответ 3
Ну, во-первых, статический вызов в вашем коде означает, что ваш объект будет существовать. До и после в этом смысле бессмысленно. Единственный раз: "Я не хочу, чтобы T был создан вообще до вызова", имеет смысл, когда T является шаблоном. Это не так, потому что этого не может быть. Верно, что именно эта строка заставляет объект существовать, но я уверен, что он будет существовать не только после компиляции продукта.
Во-вторых, я не считаю, что существует текущий метод для использования for_each без создания экземпляра. IMHO это ошибка в MPL, вызванная сомнительным решением использовать operator(). Не скажу, что это неправильно, так как я знаю разработчика, и он намного умнее, чем я, но теперь кажется, что теперь вы это делаете.
Итак, я думаю, вы застряли, чтобы переделать for_each, который вызывает шаблонную функцию, которая не требует параметра. Я почти уверен, что это возможно, но также в равной степени уверенно, что он не доступен в качестве готового компонента в MPL.
Ответ 4
Марк, ты прав. Я думал об этом, и я не вижу в этом легкого решения. Даже если вы не можете написать пустой operator()
, было бы, по крайней мере, возможно использовать указатель, для которого не нужен фактический объект. Кажется, вам нужно выполнить собственную реализацию.
Ответ 5
Вот альтернативное решение, которое было вдохновлено Люком Торайлом.
Эта версия выполняется с помощью Классы метафайлов вместо функций, которые позволяют вызывать static_for_each
даже вне областей функций (полезно, если работа должна быть полностью выполнена в compiletime, поэтому у вас нет ненужных функций, называемых во время выполнения).
Кроме того, это дает больше взаимодействия благодаря first
и last
typedefs, позволяющим получать информацию из цикла, если это необходимо, немного похоже на способ работы return
для функции.
Вы также можете получить доступ к предыдущему результату итерации в каждой итерации, благодаря второму параметру шаблона Previous
, переданному классу metafunction 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...), поскольку вы можете напрямую взаимодействовать с циклом.
Ответ 6
Мне понравилось (опрошено) решение с указателем и собственной функцией * _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());
}