2013-01-17 19 views
5

Ho a che fare con un unione in C++ e vorrei avere un modello di funzione che accede al membro del sindacato attivo in base a un parametro del modello.selezionare un membro del sindacato in base a un parametro del modello

Codice è qualcosa di simile (doSomething è solo un esempio):

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 

enum class ActiveMember { 
    I16 
    , I32 
} 

template <ActiveMember M> 
void doSomething(Union a, const Union b) { 
    selectMemeber(a, M) = selectMember(b, M); 
    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 

per raggiungere questo obiettivo ho trovato solo cattive hack come la specializzazione, o un modo non omogeneo per accedere e assegnare.

Mi manca qualcosa e cose del genere dovrebbero farlo con un altro approccio?

+3

perché pensi che la specializzazione sia un "brutto attacco"? non esiste una cosa come "static if" in C++ (ancora), quindi non vedo nessun altro modo per farlo se non definire modelli separati. potrei sbagliarmi però –

+0

Immagino tu intenda 'selectMemeber (a, M) = selectMember (b, M);'? – Philipp

+0

@Philipp, sì, grazie !. Modificato. – user1476999

risposta

6

Possibilità 1

invece di utilizzare un enum, è possibile utilizzare le strutture semplici di scegliere il membro:

typedef short int16_t; 
typedef long int32_t; 

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 

struct ActiveMemberI16 {}; 
struct ActiveMemberI32 {}; 

template <typename M> 
void doSomething(Union& a, Union b) { 
    selectMember(a, M()) = selectMember(b, M()); 

    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 

int16_t& selectMember(Union& u, ActiveMemberI16) 
{ 
    return u.i16; 
} 

int32_t& selectMember(Union& u, ActiveMemberI32) 
{ 
    return u.i32; 
} 

int main(int argc, char* argv[]) 
{ 
    Union a,b; 
    a.i16 = 0; 
    b.i16 = 1; 
    doSomething<ActiveMemberI16>(a,b); 
    std::cout << a.i16 << std::endl; 

    b.i32 = 3; 
    doSomething<ActiveMemberI32>(a,b); 
    std::cout << a.i32 << std::endl; 
    return 0; 
} 

Ciò richiede di definire una struttura e un metodo selectMember per ogni membro nel sindacato, ma almeno puoi usare selectMember attraverso molte altre funzioni.

Nota che ho trasformato gli argomenti in riferimenti, è possibile modificare questo se non appropriato.

Possibilità 2

Lanciando il puntatore del sindacato al puntatore tipo desiderato, si può andare con una singola funzione selectMember.

typedef short int16_t; 
typedef long int32_t; 

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 
template <typename T> 
T& selectMember(Union& u) 
{ 
    return *((T*)&u); 
} 

template <typename M> 
void doSomething(Union& a, Union b) { 
    selectMember<M>(a) = selectMember<M>(b); 

    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 



int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Union a,b; 
    a.i16 = 0; 
    b.i16 = 1; 

    doSomething<int16_t>(a,b); 
    std::cout << a.i16 << std::endl; 

    b.i32 = 100000; 
    doSomething<int32_t>(a,b); 
    std::cout << a.i32 << std::endl; 
    return 0; 
} 
+1

+1 per la possibilità 2. non mi è venuto in mente, ben fatto –

+0

Ho pensato alla possibilità 2, ma mi sembrava illegale. Sto leggendo e sembra che mi sbagliavo. Grazie – user1476999

+0

Potatoswatter ha avuto la stessa idea nella mia domanda; è davvero la soluzione più semplice. – MSalters

3

Non sono sicuro del perché consideri la "specializzazione dei modelli" una "cattiva modifica", ma non esiste una cosa come "statica se" in C++, quindi se vuoi che il compilatore distingua il codice prodotto in base al risultato di un'espressione valutata allo in fase di compilazione, è necessario definire diverse versioni specializzate del modello.

Ecco come si potrebbe definire:

#include <iostream> 

using namespace std; 

union Union { 
    int16_t int16; 
    int32_t int32; 
}; 

enum class ActiveMember { 
    INT16 
    , INT32 
}; 

// Declare primary template 
template <ActiveMember M> 
void doSomething(Union a, const Union b); 

// First specialization 
template <> 
void doSomething<ActiveMember::INT16>(Union a, const Union b) 
{ 
    a.int16 = b.int16; 

    // Do what you want here... 
    cout << "int16" << endl; 
} 

// Second specialization 
template <> 
void doSomething<ActiveMember::INT32>(Union a, const Union b) 
{ 
    a.int32 = b.int32; 

    // Do what you want here... 
    cout << "int32" << endl; 
} 

e questo è come lo si dovrebbe utilizzare.

int main() 
{ 
    Union u1, u2; 
    u1.int32 = 0; 
    u2.int32 = 0; 

    doSomething<ActiveMember::INT16>(u1, u2); 
    doSomething<ActiveMember::INT32>(u1, u2); 

    return 0; 
} 
+1

Perché la specializzazione non è una soluzione estensibile, in questo caso implica la riscrittura del codice. Ovviamente per questo esempio è probabilmente l'opzione migliore (il più semplice). Ma perché ci sono solo una funzione e due membri, ma cosa succede se hai bisogno di scrivere diverse funzioni e ci sono dieci membri? – user1476999

+0

@ user1476999: puoi sempre calcolare il codice in modo da avere solo un modello che deve essere specializzato, come 'get (u)'. Quindi puoi usarlo genericamente in tutte le altre funzioni. –

+0

@ user1476999: due considerazioni. primo: quale sarebbe il tipo di ritorno della funzione 'selectMember'? dal momento che stai lavorando su un sindacato, il tipo di ritorno dipende dal valore di 'M'. secondo: se C++ supportava qualcosa come "static_if", che permettesse di condizionali valutati in fase di compilazione (in modo che il ramo falso non venga nemmeno compilato), ciò renderebbe le cose più semplici e non richiedesse alcuna specializzazione. sfortunatamente, al momento non abbiamo la funzione. ci sono proposte per aggiungerlo allo standard successivo anche se –

0

L'unica soluzione che posso pensare è quello di aggiungere operatori al sindacato:

union Union 
{ 
    char c; 
    short s; 
    int i; 
    float f; 
    double d; 

    operator char() const { return c; } 
    operator short() const { return s; } 
    operator int() const { return i; } 
    operator float() const { return f; } 
    operator double() const { return d; } 
    template <typename T> operator T() const { /* invalid conversion */ T t; return t; } 

    Union &operator =(char ac) { c = ac; return *this; } 
    Union &operator =(short as) { s = as; return *this; } 
    Union &operator =(int ai) { i = ai; return *this; } 
    Union &operator =(float af) { f = af; return *this; } 
    Union &operator =(double ad) { d = ad; return *this; } 
    template <typename T> Union &operator =(T at) { /* invalid asignement */ return *this; } 
}; 

Esso consente di controllare il comportamento del sindacato quando funziona come un some type:

template <typename T> 
void doSomething(Union a, const Union b) 
{ 
    // call the 'b' conversion operator and the 'a' asignment operator. 
    a = static_cast<T>(b); 
} 

int main(int argc, char **argv) 
{ 
    Union a, b; 

    doSomething<int>(a, b); // calls a.i = b.i 
    doSomething<char>(a, b); // calls a.c = b.c 

    return 0; 
} 

La versione modello degli operatori corrisponde alle conversioni non valide.

Problemi correlati