c++ boost vector foreach boost-mpl

c++ - ¿Es posible iterar un mpl:: vector en tiempo de ejecución sin crear una instancia de los tipos en el vector?



boost foreach (6)

En general, usaría boost::mpl::for_each<>() para atravesar un boost::mpl::vector , pero esto requiere un functor con una función de plantilla declarada como la siguiente:

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

Mi problema con esto es que no quiero que el objeto T sea instanciado por for_each<> . No necesito el parámetro T en el operator() en absoluto. ¿Hay alguna forma de lograr esto, o una alternativa a for_each<> que no pase un objeto de tipo T a la función de plantilla?

De manera óptima, me gustaría que la definición de operador () se vea así:

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

Y, por supuesto, no quiero que se cree una instancia de T antes de la llamada. Cualquier otro consejo / sugerencia también son bienvenidos.


¡Interesante pregunta! Por lo que puedo decir, Boost.MPL no parece proporcionar tal algoritmo. Sin embargo, escribir los tuyos no debería ser demasiado difícil con los iteradores.

Aquí hay una posible solución:

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

[La denominación puede no estar muy bien elegida, pero bien ...]

Aquí es cómo utilizarías este algoritmo:

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" }


Aquí hay una solución alternativa altamente inspirada en la respuesta de Luc Touraille .

Esta versión se realiza utilizando clases de Metafunciones en lugar de funciones que permiten static_for_each a static_for_each incluso fuera de los ámbitos de funciones (útil si el trabajo debe realizarse totalmente en tiempo de compilación, por lo que no tiene funciones innecesarias en tiempo de ejecución).

Además, proporciona más interacción gracias a las first y last definiciones de tipo, lo que permite obtener información fuera del bucle si es necesario, un poco como la forma en que funciona un return para una función.

También puede acceder al resultado de la iteración anterior dentro de cada iteración gracias al segundo parámetro de plantilla Pasado Previous a la metafunción de clase F

Finalmente, puede proporcionar datos al proceso de bucle utilizando el parámetro Plantilla Initial , que se dará como el valor del parámetro Previous de la primera iteración.

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

Aquí hay un ejemplo simple que da y recupera datos:

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

Estas características hacen que el uso de static_for_each más similar al uso de bucles de tiempo de ejecución comunes ( while , for BOOST_FOREACH ...), ya que puede interactuar más directamente con el bucle.


Bueno, en primer lugar, la llamada estática en su código significa que su objeto existirá. Antes / después no tiene sentido en ese sentido. La única vez que "no quiero que T se cree una instancia antes de la llamada" tiene sentido cuando T es una plantilla. No lo es, porque no puede ser. Es cierto que es esa línea la que hace que el objeto exista, pero estoy bastante seguro de que no existirá allí una vez que se compile el producto.

En segundo lugar, no creo que haya un método actual para usar for_each sin crear instancias. En mi humilde opinión, esto es un error en MPL causado por una decisión cuestionable de usar operator () No diré que está mal, ya que conozco al desarrollador y él es mucho más inteligente que yo, pero parece que desde aquí ahora mencionas esto.

Entonces, creo que estás atascado al tener que rehacer un for_each que llama a una función de plantilla que no requiere el parámetro. Estoy casi seguro de que es posible, pero también estoy seguro de que no está fácilmente disponible como componente prefabricado en MPL.


Marcin, tienes mucha razón. He estado pensando esto y no veo una solución fácil para esto. Incluso si no puede escribir el operator() vacío operator() , sería al menos posible usar un puntero, que no necesita un objeto real para existir. Tienes que rodar tu propia implementación, parece.


Me acabo de encontrar la misma situación y proporcioné una solución diferente al problema que me gustaría compartir. No es tan obvio, pero utiliza un algoritmo existente. La idea es utilizar punteros en su lugar.

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

así que aquí tenemos un puntero nulo, pero no nos importa ya que no los vamos a utilizar.

Como dije antes, eso no es obvio en el código y probablemente requeriría algunos comentarios adicionales, pero aún así resuelve la pregunta sin escribir ningún código adicional.

adicional

Creo que Diego Sevilla sugirió algo similar.


Me gustó (votó al alza) la solución con puntero y propia función definida * _for_each. Aquí hay una alternativa que usa el tipo de envoltura para T si el objetivo es evitar la creación de objetos hasta que sea necesario.

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