2010-10-29 4 views
18

En general, me gustaría utilizar boost::mpl::for_each<>() para recorrer un boost::mpl::vector, pero esto requiere un funtor con una función de plantilla declarada como la siguiente:¿Es posible iterar un vector mpl :: en tiempo de ejecución sin instanciar los tipos en el vector?

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

Mi problema con esto es que no quiero que el objeto T para crear una instancia por for_each<>. No necesito el parámetro T en el operator() en absoluto. ¿Hay alguna manera de lograr esto o una alternativa al 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 del operador() para tener este aspecto:

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

Y, por supuesto, no quiero T para crear una instancia del todo antes de la llamada. Cualquier otro consejo/sugerencia también es bienvenido.

Respuesta

13

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

Aquí es una solución posible:

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

[El nombramiento no puede ser muy bien elegido, pero bien ...]

Aquí es como se puede utilizar 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" 
} 
+0

Muy útil orientación aquí. Gracias. – Marcin

1

Bueno, antes que nada, 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, "No quiero que T sea instanciado en absoluto 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 seguro de que no solo existirá allí una vez que se haya compilado 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 conozco al desarrollador y él es mucho más inteligente que yo, pero parece que sí de aquí ahora que sacas el tema.

Por lo tanto, creo que está atascado 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á disponible como componente prefabricado en MPL.

1

Marcin, tienes mucha razón. He estado pensando en esto y no veo una solución fácil para esto. Incluso si no puede escribir el operator() vacío, sería al menos posible utilizar un puntero, que no necesita que exista un objeto real. Usted tiene que implementar su propia implementación, parece.

+0

Creo que no hay una solución "fácil" porque la solución _easy_ ya está allí: usando 'mpl :: begin' y' mpl :: end'. Básicamente, necesitas atravesarlo tú mismo. Al igual que tiene que escribir 'mucho tiempo para (std :: vector :: iterator it = v.begin() ...' cosas para iteradores. Puede escribir una función de orden superior para simplificarlo, pero al final usted todavía necesita recorrerlo como @Luc Touraille sugirió. – kizzx2

14

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 usa un algoritmo existente. La idea es usar 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 una punteros nulos, pero no nos importa ya que no vamos a usarlos.

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

añade

Creo Diego Sevilla sugirió algo similar.

+4

Esto es bastante inteligente. Puede usar 'mpl :: transform' para definir la versión de" puntero "sobre la marcha:' boost :: mpl :: for_each > :: type> (Functor()); ' – kizzx2

+4

@ kizzx2 Incluso podría no proporcionar puntero sino un tipo vacío que contenga el tipo. – Klaim

1

Aquí hay una solución alternativa altamente inspirada en Luc Touraille's answer. Metafunction classes Esta función se realiza utilizando Metafunction classes en lugar de funciones que permiten llamar al static_for_each incluso fuera de los ámbitos funcionales (útil si el trabajo debe realizarse totalmente en tiempo de compilación para que no haya funciones innecesarias llamadas en tiempo de ejecución).

Además se da una mayor interacción gracias a los first y last typedefs, lo que permite obtener información fuera del circuito, si es necesario, un poco como la forma en que un return obras 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 Previous que se pasó a la clase de metafunción F.

Finalmente, puede proporcionar datos para el proceso de bucle mediante el parámetro de plantilla Initial, que se proporcionará 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í está un ejemplo sencillo que tanto 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 hace que el uso de static_for_each más similar a la utilización de los bucles de ejecución común (while, for, BOOST_FOREACH .. .) ya que puedes interactuar más directamente con el ciclo.

+0

As Hasta donde puedo decir, acaba de volver a implementar [mpl :: fold] (http://www.boost.org/doc/libs/release/libs/mpl/doc/refmanual/fold.html). La característica principal de 'mpl :: for_each' es que funciona en tiempo de ejecución (bueno, la iteración se realiza en tiempo de compilación, pero invoca operaciones en tiempo de ejecución), y asumí que el OP quería este comportamiento. De lo contrario, te tendría sed uno de los numerosos algoritmos de tiempo de compilación proporcionados por MPL. –

+0

@LucTouraille Estoy de acuerdo con usted.Esta respuesta fue más intencionada como información adicional que como respuesta directa. – Drax

0

Me gustó (votó) la solución con el puntero y la función * _for_each propia definida. Aquí hay una alternativa que usa wrapper tipo 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()); 
} 
Cuestiones relacionadas