2011-11-29 15 views
5

Ho un numero elevato di classi che vengono utilizzate per decorare alcuni metodi specifici.Riduzione del boilerplate durante i metodi di decorazione

Esiste un modo pulito per ridurre la quantità di codice boilerplate (principalmente tutti i parametri del costruttore e dei membri per mantenerli) che deve essere aggiunto a ciascuna di queste classi? O, ancora meglio, c'è un modo migliore per farlo?

Non riesco a utilizzare metodi virtuali e posso solo utilizzare il sottoinsieme di funzioni C++ 11 supportate da gcc 4.6 e vs2010.

Credo che i costruttori ereditari di C++ 11 possano essere d'aiuto, ma nessuno dei compilatori li supporta e non sono a conoscenza di una soluzione alternativa.

Ecco un esempio di ciò che queste classi attualmente assomigliano:

template<class Underlying, class T1> 
struct A : Base<A<Underlying, T1> > 
{ 
    typedef AImpl<decltype(declval<Underlying>().foo()), T1> impl; 
    A(Underlying u, T1 t1) : 
     u_(u), 
     t1_(t1) 
    {} 

    impl foo() const { return impl(u_.foo(), t1_); } 
    impl bar() const { return impl(u_.bar(), t1_); } 

    const Underlying u_; 
    T1 t1_; 
}; 


template<class Underlying, class T1, class T2> 
struct B : Base<B<Underlying, T1, T2> > 
{ 
    typedef BImpl<decltype(declval<Underlying>().bar()), T1, T2> impl; 
    B(Underlying u, T1 t1, T2 t2) : 
     u_(u), 
     t1_(t1), 
     t2_(t2) 
    {} 

    impl foo() const { return impl(u_.bar(), 999, t2_); } 
    impl bar() const { return impl(u_.foo(), t1_, t2_); } 

    const Underlying u_; 
    T1 t1_; 
    T2 t2; 
}; 

risposta

1

Si potrebbe utilizzare i modelli variadic in GCC 4.6.

template<class Underlying, class... T> 
struct B : Base<B<Underlying, T...>> 
{ 
    typedef BImpl<decltype(declval<Underlying>().bar()), T...> impl; 

    template<typename V...> 
    B(Underlying u, V... v) : 
     u_(u), 
     t_(std::forward<V>(v)...) 
    {} 

    impl foo() const { return impl(u_.bar(), 999, std::get<1>(t_)); } // Not sure what the 999 is? 
    impl bar() const { return impl(u_.foo(), std::get<0>(t_), std::get<1>(t_)); } 

    const Underlying u_; 
    std::tuple<T...> t_; 
}; 
+0

Ma non in VC++ 2010 ... – ildjarn

+0

Penso che l'uso di 'forward' potrebbe essere leggermente errato (inefficiente almeno) qui. Per impostazione predefinita, qualsiasi uso di 'forward' dovrebbe essere in una chiamata di metodo con tipi di parametri dedotti. Ciò significa 'modello B (sotto u, V && ... t): u_ (u), t_ (std :: forward (t) ...) {}'. Tre cose: 1) La funzione stessa dovrebbe essere un modello. 2) Gli argomenti dovrebbero essere presi come 'U && ...', e 'forward' deve essere usato. Il codice corrente * copierà * i parametri in 'B' e quindi potrebbe * spostarli * nella classe base. Idealmente, vuoi zero copie e invece muovi completamente, quando possibile. –

+1

Due cose, get non è un membro di tuple nell'implementazione standard (è in Boost) e l'uso di forward non è corretto nel costruttore. Dovrebbe essere 'template B (Sotto u, V && ... v): u_ (u), t_ (std :: forward (v) ...) {}'. – DRayX

1

Se ha bisogno di lavorare in VS2010, allora la cosa migliore è Boost.Preprocessor. Non è robusto come i modelli variadici in quanto ha una profondità di ricorsione massima di 3 e un conteggio massimo di 256 iterazioni, ma funziona in ogni compilatore con o senza supporto C++ 11. Non è del tutto chiaro cosa stai cercando di fare con questi esempi, ma se stai solo cercando di decorare le funzioni, io uso il seguente (utilizza C++ 11 per l'inoltro perfetto con Boost.Preprocessor invece dei modelli variadic):

#define FORWARD(z, n, data) \ 
    ::std::forward<BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 0, data), n)> \ 
    (BOOST_PP_CAT(BOST_PP_TUPLE_ELEM(2, 1, data), n)) \ 
    /**/ 

//------------------------------------------------------------------------ 
// template<class R, class... A> 
// ::std::function<R(A...)> decorate(::std::function<R(A...)> const& func) 
// { 
//  return [func](A&&... a) -> R 
//  { 
//   /* DECORATOR STUFF */ 
//   return func(::std::forward<A>(a)...); 
//  }; 
// } 
//------------------------------------------------------------------------ 
#define MACRO(z, n, _) \ 
    template<class R BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, class A)> \ 
    ::std::function<R(BOOST_PP_ENUM_PARAMS_Z(z, n, a)> \ 
     decorate(::std::function<R(BOOST_PP_ENUM_PARAMS_Z(z, n, A))> const& func) \ 
    { \ 
     return [func](BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, &&a)) -> R \ 
     { \ 
      /* DECORATOR STUFF */ \ 
      return func(BOOST_PP_ENUM_ ## z(n, FORWARD, (A, a))); \ 
     }; \ 
    } \ 
    /**/ 

BOOST_PP_REPEAT(10, MACRO, ~) 

#undef MACRO 
#undef FORWARD 

Se si sta solo tentando di associare gli argomenti delle funzioni, quindi std::bind è quello che si sta cercando. Nota che puoi fare tutto questo con solo lambdas, ma le funzioni di decoratore possono essere usate per decorare più funzioni (dove come lambdas sono ovviamente una off) e il binding può essere un po 'più conciso per gli argomenti di binding (comunque, l'equivalente lambda a volte è più leggibile).

Problemi correlati