2010-10-29 10 views
18

In generale, vorrei utilizzare boost::mpl::for_each<>() per attraversare un boost::mpl::vector, ma questo richiede un funtore con una funzione template dichiarata come la seguente:È possibile iterare un vettore mpl :: in fase di esecuzione senza creare un'istanza dei tipi nel vettore?

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

Il mio problema con questo è che non voglio che l'oggetto T da istanziare da for_each<>. Non ho assolutamente bisogno del parametro T nello operator(). C'è un modo per realizzare questo, o un'alternativa a for_each<> che non passa un oggetto di tipo T alla funzione template?

In modo ottimale, vorrei la definizione dell'operatore() per assomigliare a questo:

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

E, naturalmente, non voglio T da un'istanza a tutti prima della chiamata. Anche altri suggerimenti/suggerimenti sono ben accetti.

risposta

13

Interessante domanda! Per quanto posso dire, Boost.MPL non sembra fornire un tale algoritmo. Tuttavia, scrivere i propri non dovrebbe essere troppo difficile con gli iteratori.

Ecco una possibile soluzione:

#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 denominazione non può essere molto ben scelto, ma ben ...]

Ecco come si usa questo 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

Guida molto utile qui. Grazie. – Marcin

1

Beh, prima di tutto, la chiamata statica nel codice significa che il tuo oggetto esisterà. Prima/dopo non ha senso in questo senso. L'unica volta, "Non voglio che venga creata un'istanza di T prima della chiamata", ha senso quando T è un modello. Non lo è, perché non può essere. È vero che è quella linea che fa sì che l'oggetto esista, ma sono abbastanza sicuro che non esisterà solo lì una volta che il prodotto è stato compilato.

In secondo luogo, non credo che esista un metodo corrente per utilizzare for_each senza creare un'istanza. IMHO questo è un bug in MPL causato da una decisione discutibile di usare operator(). Non dirò che è sbagliato dal momento che conosco lo sviluppatore e lui è molto più intelligente di me, ma sembra così da qui ora che lo porti avanti.

Quindi, penso che tu sia bloccato a dover rifare un for_each che chiama una funzione basata su modelli che non richiede il parametro. Sono quasi certo che sia possibile, ma anche ugualmente certo che non è prontamente disponibile come componente premade in MPL.

1

Marcin, hai ragione. Ci ho pensato e non vedo una soluzione facile per questo. Anche se non è possibile scrivere il operator() vuoto, sarebbe almeno possibile utilizzare un puntatore, che non ha bisogno di un oggetto reale per esistere. Devi implementare la tua implementazione, a quanto pare.

+0

Penso che non ci sia una soluzione "facile" perché la soluzione _easy_ è già lì: usando 'mpl :: begin' e' mpl :: end'. Fondamentalmente devi attraversarlo da solo. Proprio come devi scrivere molto a lungo 'for (std :: vector :: iterator it = v.begin() ...' cose per gli iteratori Puoi scrivere una funzione di ordine superiore per semplificarla ma alla fine sei ancora è necessario attraversarlo come suggerito da @Luc Touraille – kizzx2

14

Ho appena incontrato la stessa situazione e fornito una soluzione diversa al problema che vorrei condividere. Non è così ovvio, ma utilizza un algoritmo esistente. L'idea è di usare invece i puntatori.

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

così qui otteniamo un puntatore nullo, ma non ci interessa perché non li useremo.

Come ho detto prima che non è evidente nel codice e richiederebbe probabilmente alcuni ulteriori commenti, ma si risolve ancora la domanda senza scrivere alcun codice aggiuntivo.

aggiunto

Penso Diego Sevilla suggerito qualcosa di simile.

+4

Questo è abbastanza intelligente.Puoi usare 'mpl :: transform' per definire la versione del" puntatore "al volo:' boost :: mpl :: for_each > :: tipo> (Functor()); ' – kizzx2

+4

@ kizzx2 È anche potuto non fornire puntatore, ma un tipo di vuoto che contiene il tipo di – Klaim

1

Ecco una soluzione alternativa altamente ispirato Luc Touraille's answer.

Questa versione viene fatto utilizzando Metafunction classes invece di funzioni che consente al static_for_each di essere chiamato anche al di fuori di ambiti di funzione (utile se il lavoro deve essere totalmente fatto a compiletime modo da non avere funzioni inutili chiamate a runtime).

Inoltre dà una maggiore interazione grazie ai first e last typedef, che consente di ottenere informazioni fuori dal giro, se necessario, un po 'come il modo in cui una return lavori per una funzione.

È anche possibile accedere al risultato di iterazione precedente all'interno di ciascuna iterazione grazie al secondo parametro di modello Previous passato alla classe di metafunzione F.

Infine è possibile fornire dati al processo di loop utilizzando il parametro di modello Initial, verrà assegnato come valore del parametro Previous della prima iterazione.

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

Ecco un semplice esempio che dà e recupera i dati:

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

Queste caratteristiche rende l'uso di static_for_each più simile all'uso di anelli di runtime comune (while, for, BOOST_FOREACH .. .) come puoi interagire più direttamente con il ciclo.

+0

Come. Per quanto posso dire, hai appena re-implementato [mpl :: fold] (http://www.boost.org/doc/libs/release/libs/mpl/doc/refmanual/fold.html). di 'mpl :: for_each' è che opera in fase di esecuzione (beh, l'iterazione viene eseguita in fase di compilazione, ma richiama le operazioni di runtime) e ho pensato che l'OP volesse questo comportamento. sed uno dei numerosi algoritmi di compilazione forniti da MPL. –

+0

@LucTouraille Sono d'accordo con te.Questa risposta era più intesa come informazione bonus che come risposta diretta. – Drax

0

mi è piaciuto (up-votato) la soluzione con puntatore e propria definito * _for_each funzione. Ecco un'alternativa usando type wrapper per T se l'obiettivo è evitare la creazione dell'oggetto fino a quando non è necessario.

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