2013-11-01 15 views
6

Dato che voglio eseguire il filtraggio su alcuni dati, come evitare di generare questi dati in fase di runtime, ma mantenere la flessibilità di modificare le dimensioni e la distribuzione dei dati di questi filtri, mantenendo anche un codice pulito e riutilizzabile. So che posso utilizzare i modelli per fare qualcosa di simile al seguente:Come si generano staticamente dati float al momento della compilazione?

template <int x> class Filter 
{ 
    static const float f; 
    static const Filter<x-1> next; 
    inline float* begin(const Filter<x>& el){ return &f;  } 
    inline float* end(const Filter<x>& el) { return (&f)+x+1; } 
}; 
template <> class Filter<0> 
{ 
    static const float f; 
    inline float* begin(const Filter<0>& el){ return &f; } 
    inline float* end(const Filter<0>& el) { return (&f)+1; } 
}; 

template <int x> const float Filter<x>::f = someDistribution(x); 
template <>  const float Filter<0>::f = someDistribution(0); 

Questo sarà davvero generare i dati in mio filtro secondo l'indice x nell'oggetto filtro secondo someDistribution (...). Tuttavia ci sono alcuni aspetti negativi per il mio utilizzo ...

1) Penso di aver ragione nel dire che mentre questi dati non vengono generati sulla costruzione dell'oggetto, vengono generati una volta all'avvio del programma. - questo potrei tollerare anche se preferirei che il filtro sia calcolato in tempo reale e cotto in quel momento (questo è possibile anche per i dati float?)

2) Il filtro non istanzia il membro "successivo" a meno che non ci sia una funzione di membro (che si chiama da qualche parte!), che attraversa la lunghezza della struttura, vale a dire

// inside template<int x> class Filter 
inline void instantiate() const { next.instantiate(); }; 
// then inside template<> class Filter<0> 
inline void instantiate() const { }; 

che devo fare male da richiedere la funzione di un'istanza precipitare e questo non riesce la clausola di facile manutenzione.

modifica: la ragione per cui mi interessa qui è che vorrei fare in modo che i membri next siano istanziati in modo da poter attraversare l''array' statico utilizzando le funzioni di inizio e fine.

Quindi in primo luogo come posso risolvere il problema 2 e farla finita con la funzione instantiate, e in secondo luogo è possibile risolvere il problema 1 in modo che questi dati è generato dinamicamente in fase di compilazione e sostenuto in.

(NB I su problemi simili ho usato gli script di pre-compilazione Python per generare file sorgente contenenti i dati del filtro, ma non voglio usarli qui dato che è il proprio bollitore di pesce!)

+2

Non penso che la tua funzione generica 'end' sia legale. Aggiungere più di 1 all'indirizzo di una variabile non matrice deve essere illegale. –

+0

quindi questo dovrebbe essere ok dato che il 'float f' dovrebbe essere tutto contiguo in memoria permettendoti di prendere un puntatore al primo e iterare fino alla fine. È importante sottolineare che l'iteratore finale deve essere dereferenziabile ed è legale tenere un puntatore alla memoria non inizializzata. Concordo sul fatto che avrei potuto essere più chiaro scrivendo '((float *) (& f)) + x + 1', tuttavia ritengo che il significato del resto dello snippet di codice sia più chiaro con la forma breve usata sopra. Grazie per il controllo però. – geoff3jones

risposta

2

Dal momento che non è possibile utilizzare contexpr ... Per quanto riguarda le vostre domande:

  1. A meno che non si sta cercando per il prossimo più grande numero primo, non si dovrebbe essere disturbato quei semplici calcoli avvengono una volta all'avvio. Provare a misurarlo ed è probabile che si scopre che l'inizializzazione viene eseguita in meno di un millisecondo.

    Detto questo, i valori calcolati all'avvio si comportano come variabili (devono essere asked per il loro valore ogni volta che vengono utilizzati), mentre il valore della costante in fase di compilazione è sempre noto. Quindi il primo potrebbe essere un po 'più lento, ma probabilmente senza alcun significato.

    Misurare sempre prima di introdurre un inconveniente.

  2. Ancora, perché ti importa?Se il tipo Filter per il particolare x non viene utilizzato da nessuna parte nel codice, perché il valore dovrebbe essere lì intorno da qualche parte?

    Le statiche dei modelli sono problematiche se dipendono l'una dall'altra: nel caso in cui non lo fanno, ogni f è autonomo.

Detto tutto questo, un ottimo strumento per armeggiare intorno è http://gcc.godbolt.org/ - si vede il montaggio durante la digitazione. NON supporta i compilatori MS, ma ti dà una buona idea su come i compilatori ottimizzano le cose.

Se il disrtibution è abbastanza semplice da essere una macro che sarà una costante in fase di compilazione:

#define someDistribution(x) x * x 

template <int x> struct Filter 
{ 
    static const float f; 
}; 

template <int x> const float Filter<x>::f = someDistribution(x); 

int main() 
{ 
    return Filter<200>::f + Filter<100>::f; 
} 

L'assemblea (Clang):

main:         # @main 
    movl $50000, %eax   # imm = 0xC350 
    ret 

Se si cambia someDistribution essere un funzione, anche in linea uno, vedrai che il calcolo dovrà aver luogo.

MODIFICA: ricorda che puoi fare tutto con i macachi, incluso "specializzarli" per determinati valori. La distribuzione semplice dovrebbe essere compatibile con il preprocessore.

+0

Ho aggiornato la mia risposta. – gwiazdorrr

+0

Grazie, questo risponde alla mia domanda e mi indirizza anche nella giusta direzione. Anche per quanto riguarda "perché?" nella seconda parte penso di essere ambiguo (e correggerò sopra), ma mi piacerebbe essere in grado di usare begin() end() per accedere ai dati, ma se tutti i membri 'next' non sono stati istanziati questo ovviamente crea spazzatura. OH e il tuo collegamento allo strumento - fantastico! :) – geoff3jones

-1

È possibile ottenere una parte di il puzzle con modelli variadici. Una volta che il supporto di integer_sequence viene aggiunto alla libreria standard, è possibile utilizzare quello invece di seq/gen_seq.

#include <array> 
#include <iostream> 

using namespace std; 

template<size_t... Is> struct seq {}; 
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {}; 
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{}; 

template<typename Func, size_t... Is> 
const array<float, sizeof...(Is)>& make_coeffs(Func f, seq<Is...>) { 
    static const auto coeffs = array<float, sizeof...(Is)>{ f(Is)... }; 
    return coeffs; 
} 

float square(float x) { return x * x; } 

int main() { 
    const auto coeffs = make_coeffs(square, gen_seq<10>{}); 
    for (float x : coeffs) { 
     cout << x << " "; 
    } 
    cout << endl; 
} 

per rendere questo momento della compilazione, piuttosto che inizializzato all'avvio se si vuole veramente il supporto constexpr che VS 2013 non ha. Ecco il constexpr version:

#include <array> 
#include <iostream> 

using namespace std; 

template<size_t... Is> struct seq {}; 
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {}; 
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{}; 

template<typename Func, size_t... Is> 
constexpr array<float, sizeof...(Is)> make_coeffs(Func f, seq<Is...>) { 
    return array<float, sizeof...(Is)>{ f(Is)... }; 
} 

constexpr float square(float x) { return x * x; } 

int main() { 
    constexpr auto coeffs = make_coeffs(square, gen_seq<10>{}); 
    static_assert(coeffs[3] == 9, ""); 
    for (float x : coeffs) { 
     cout << x << " "; 
    } 
    cout << endl; 
}