2012-05-10 5 views
5
template <int I> struct int_ {}; 

template < typename ... Pack > 
struct thingy 
{ 
    void call() 
    { 
     f(???); 
    } 
}; 

Quando un'istanza che dovrebbe finire per essere:Interweave un VT decomprimere con una meta-sequenza

struct thingy<int,char,double> 
{ 
    void call() 
    { 
     f(int, int_<1>(), char, int_<2>(), double, int_<3>()); 
    } 
} 

Cosa ne pensi, si può fare? Come?

L'unica cosa che mi viene in mente è quello di avere sovraccarichi per thingy con N diversi parametri in questo modo:

template < typename T0 > struct thingy<T0> { ... }; 
template < typename T0, typename T1 > struct thingy<T0,T1> { ... }; 

etc... 

Con un'implementazione chiamata in ciascuno.

risposta

4

Può essere fatto

Sì, certo.

Come?

In diversi passaggi.

  • Devi essere in grado di creare una serie di numeri interi
  • È necessario essere in grado di intercalare due sequenze

Cominciamo con la gamma di numeri interi.

template <size_t...> 
struct IntegralPack {}; 

template <size_t A, size_t... N> 
IntegralPack<N..., A> push_back(IntegralPack<N...>); 

template <size_t A, size_t... N> 
IntegralPack<A, N...> push_front(IntegralPack<N...>); 

template <size_t L, size_t H> 
struct IntegralRangeImpl { 
    typedef typename IntegralRangeImpl<L+1, H>::type Base; 
    typedef decltype(push_front<L>((Base()))) type; 
}; 

template <size_t X> 
struct IntegralRangeImpl<X, X> { 
    typedef IntegralPack<> type; 
}; 

template <size_t L, size_t H> 
struct IntegralRange { 
    static_assert(L <= H, "Incorrect range"); 
    typedef typename IntegralRangeImpl<L, H>::type type; 
}; 

Il passo di conversione è abbastanza facile (per fortuna):

template <typename...> 
struct TypePack {}; 

template <size_t... N> 
TypePack<int_<N>...> to_int(IntegralPack<N...>); 

Così la prossima difficoltà è l'unione.

template <typename... As, typename... Bs> 
TypePack<As..., Bs...> cat(TypePack<As...>, TypePack<Bs...>); 

template <typename, typename> struct Interleaver; 

template <> 
struct Interleaver<TypePack<>, TypePack<>> { 
    typedef TypePack<> type; 
}; 

template <typename A0, typename B0, typename... As, typename... Bs> 
struct Interleaver<TypePack<A0, As...>, TypePack<B0, Bs...>> { 
    typedef typename Interleaver<TypePack<As...>, TypePack<Bs...>>::type Base; 
    typedef decltype(cat(TypePack<A0, B0>{}, Base{})) type; 
}; 

Mettere del tutto:

template <typename... Pack> 
struct thingy { 
    typedef typename IntegralRange<1, sizeof...(Pack) + 1>::type Indexes; 
    typedef decltype(to_int(Indexes{})) Ints; 
    typedef typename Interleaver<TypePack<Pack...>, Ints>::type Types; 

    void call() { this->callImpl(Types{}); } 

    template <typename... Ts> 
    void callImpl(TypePack<Ts...>) { 
     f(Ts{}...); 
    } 
}; 

Tadaam!

+0

Bella risposta: mi ci vorrebbe una notte intera per inventare qualcosa del genere da solo! +1 –

+0

Dolce. Dovrò dare un'occhiata. Pubblicherò la mia risposta che lo fa senza farlo ... ottiene la chiamata senza rispondere alla domanda che è. Sfortunatamente, mentre ci lavoravo, mi sono imbattuto in altri problemi con l'interfaccia del mio tipo sottostante, ho dato di matto per un po ', ho fatto un tentativo e sono andato a letto. –

+0

@CrazyEddie: Sarei felice se potessi pingare (scrivi un commento qui) una volta che hai risposto :) –

0

Quindi il modo in cui sono andato in giro è un po 'più specifico di ciò che stavo effettivamente facendo. Risulta che alcune informazioni che pensavo fossero a parte il punto mi hanno aiutato. Penso comunque che una tecnica simile potrebbe essere usata per qualsiasi caso.

Per una cosa ... nel mio caso "thingy <>" contiene effettivamente dei valori e viene passato alla funzione di invoker. Questo in realtà aiuta molto.

Inoltre, poiché l'oggetto era convertire le cose in thingy per servire come induce per un altro thingy strano, e gli inti che venivano passati a indicizzare la prima cosa ... gli ints finiscono per essere tutti 1 quando faccio la mia ricorsione. Così, mentre ciò che cercavo era qualcosa di simile (semplificato per rimuovere il secondo tupla):

f(get(my_thingy, idx<1>), get(my_thingy, idx<2>), ...) 

Si scopre la ricorsione si libera di IDX < 2> ...IDX:

template < typename Fun, typename H, typename ... T, typename ... Args > 
auto call(Fun f, thingy<H,T...> const& t, Args&& ... args) 
    -> decltype(call(f,static_cast<thingy<T...>const&>(t), get(t,idx<1>), std::forward<Args>(args)...) 
{ 
    return call(f, static_cast<thingy<T...>const&>(t), get(t, idx<1>), args...); 
} 

template < typename Fun, typename ... Args > 
auto call(Fun f, thingy<> const&, Args&& ... args) 
    -> decltype(f(std::forward<Args>(args)...)) 
{ 
    return f(std::forward<Args>(args)...); 
} 

non sono stato in grado di completamente prova cosa, perché la funzione non riesce get su di me per qualche ragione quando si utilizza const & ... un pò mi incazzare. Sono abbastanza fiducioso che questo è il trucco però.

Se il parametro su idx non è sempre 1, penso che potrebbe essere inoltrato in un modo simile.

+0

'static_cast const &> (t)' questo sembra implicare che tu abbia una gerarchia di ereditarietà che va qui, che non corrisponde alla definizione di 'thingy' fornita nella domanda: la mente che modifica la nuova definizione in questa risposta? –