2015-02-15 9 views
11

Ho valori interi che vengono utilizzati per accedere ai dati in archivi dati non collegati, ad esempio, handle. Ho scelto di avvolgere gli interi in una struttura per avere oggetti fortemente tipizzati in modo che i diversi numeri interi non possano essere mescolati. Sono, e devono essere, POD. Questo è quello che sto usando:L'accesso alla matrice della struttura POD come matrice del suo singolo membro viola l'aliasing rigoroso?

struct Mesh { 
    int handle; 
}; 
struct Texture { 
    int handle; 
}; 

ho array di queste maniglie, come ad esempio: Texture* textureHandles;.

A volte ho bisogno di passare una serie di maniglie come int* a più parti generiche del codice. In questo momento sto utilizzando:

int* handles = &textureHandles->handle; 

che richiede essenzialmente un puntatore al primo elemento della struct e lo interpreta come un array.

La mia domanda è fondamentalmente se questo è legale, o se viola il rigoroso aliasing per manipolare int* handles e Texture* textureHandles puntando alla stessa memoria. Penso che questo dovrebbe essere consentito poiché il tipo sottostante (int) è accessibile allo stesso modo in entrambi i casi. La prenotazione che ho è legata al fatto che accedo a più strutture prendendo l'indirizzo di un membro all'interno di una struttura.

Come un'estensione alla mia prima domanda, sarebbe il seguente essere ok?

int* handles = reinterpret_cast<int*>(textureHandles); 
+0

Si desidera utilizzare le strutture per ottenere tipi forti e quindi si desidera castare il tipo per ottenere int. Hai il peggio di entrambi i mondi. –

+0

@NeilKirk Solo le funzioni molto specifiche useranno array int * grezzi. Il resto utilizzerà le strutture digitate. Sono semplicemente lì per evitare errori quando si usano le maniglie nel caso generale. – rasmus

+1

Penso che dovresti dirci di più sul tuo progetto attuale in quanto il tuo design è molto strano. –

risposta

10

reinterpret_cast<int*>(textureHandles) è sicuramente buono come &textureHandles->handle. Esiste un'eccezione speciale nello standard, ereditata anche da C, che dice che un puntatore a una struttura di layout standard, opportunamente convertito, punta al membro iniziale di quella struttura e viceversa.

Utilizzare anche questo per modificare la maniglia. Non viola le regole di aliasing, perché stai utilizzando un lvalue di tipo int per modificare un sottooggetto di tipo int.

L'incremento del puntatore risultante e il suo utilizzo per accedere ad altri elementi in una matrice di oggetti Texture, tuttavia, è un po 'incerto. Jerry Coffin ha già sottolineato che è possibile che sia sizeof(Texture) > sizeof(int). Anche se sizeof(Texture) == sizeof(int), tuttavia, l'aritmetica del puntatore viene definita solo per i puntatori negli array (dove un oggetto arbitrario può essere considerato come una matrice di lunghezza 1). Non disponi di un array di int ovunque, quindi l'aggiunta è semplicemente indefinita.

+0

Sei sicuro di non violare le regole di aliasing? Cosa succede se ho scritto al membro utilizzando un puntatore int e quindi leggere il membro tramite un puntatore Texture? –

+1

@NeilKirk Va ancora bene. Hai un lvalue di tipo 'Texture' per un oggetto di tipo' Texture', e un lvalue di tipo 'int' per un sub-oggetto di tipo' int'. Non ci sono problemi di aliasing lì. – hvd

+0

http://stackoverflow.com/a/28529680/2068573 –

5

No, non è garantito il funzionamento. In particolare, al compilatore è permesso inserire padding dopo ogni elemento di una struct, ma non è permesso inserire padding tra gli elementi di una matrice.

Detto questo, con una struttura di un solo elemento (di tipo int, o qualcosa di almeno altrettanto grande, come ad esempio long), le probabilità sono piuttosto buone che la maggior parte dei compilatori non inserire alcun imbottitura, in modo che l'uso corrente è probabilmente discretamente sicuro come regola generale.

+0

Non parli di aliasing severo che penso sarebbe un problema qui, ma non sono un esperto. –

+0

Per questa domanda mi interessa solo il caso specifico in cui una struct contiene un singolo membro. In questo caso, il compilatore ha davvero il permesso di inserire il padding? – rasmus

+0

@rasmus: Sì, lo è. Non può inserire padding prima dell'elemento, ma può dopo di esso. –

1

Viola certamente rigorosa aliasing, e se la funzione può accedere la matrice sia attraverso il int* e Mesh* o un Texture*, si può molto ben gestito in problemi (anche se probabilmente solo se modifica la matrice in qualche modo).

Dalla tua descrizione del problema, non penso che le regole dello rigoroso aliasing siano davvero ciò che ti interessa. Il vero problema è se il compilatore può aggiungere padding alle strutture che non sono presenti nello int, in modo che sizeof(Mesh) > sizeof(int). E mentre la risposta è formalmente sì, non riesco a immaginare un compilatore che farebbe , almeno oggi, e almeno con i tipi int o più grandi nello struct. (Una parola indirizzata macchina sarebbe probabilmente aggiungere imbottitura per un struct che conteneva solo char.)

La vera domanda è probabilmente più dal fatto che il codice generico è eredità, e non può essere modificato, oppure no. In caso contrario, la soluzione più ovvia è quella di creare un tipo di maniglia generico:

struct Handle 
{ 
    int handle; 
}; 

e poi o derivano i tipi di da esso, o utilizzare il reinterpret_cast come lei propone. Esiste (o almeno era) una garanzia che consentiva di accedere a un membro di un struct tramite un puntatore a una diversa struttura , a condizione che il membro e tutti i membri precedenti fossero identici. Questo è il modo per simulare l'ereditarietà in C. E anche se la garanzia è stato rimosso — e l'unica ragione per cui era sempre presente in C++ era per motivi di compatibilità C — nessun compilatore oserebbe violare essa, data la quantità di software esistente che dipende da questo. (L'implementazione di Python, per esempio. E praticamente tutti i plugin Python , compresi quelli scritti in C++.)

+0

Non viola il rigoroso aliasing per accedere a oggetti o sottooggetti definiti come 'int' tramite lvalues ​​di tipo' int', e davvero non vedo come potresti anche fare un argomento legittimo altrimenti. Potresti elaborare? (La garanzia che fai notare non garantisce effettivamente ciò che pensi che faccia, anche in C. Funziona solo nei sindacati. C'è una diversa garanzia che aiuta qui, che un puntatore a una struttura di layout standard punta alla sua iniziale membro.) – hvd

+0

Nota: sarei pienamente d'accordo con la tua risposta se fosse il contrario. Dato un oggetto 'int' arbitrario, tentare di accedervi come se fosse un' Handle' non è una buona idea, indipendentemente dal fatto che si trovi in ​​un array. Ma non è quello che l'OP sta chiedendo. – hvd

+0

Nessuna parte del codice è legacy. Il codice in questione viene utilizzato per mappare gli handle ai dati e avere la possibilità di inserire più handle contemporaneamente nella mappa (da cui gli array 'int'). Ma con le informazioni di queste fantastiche risposte probabilmente ripenserò a questa parte del design. Il problema con la sottoclasse di un handle sarebbe che le maniglie digitate non sarebbero più il POD. Quale è un requisito per me. – rasmus

Problemi correlati