2009-10-08 4 views
39

Per alcuni compilatori Visual C, v'è un identificatore di imballaggio per le strutture, per esempio ::++ equivalente di __attribute__ del GCC ((__packed__))

 
RealView ARM compiler has "__packed" 
Gnu C Compiler has "__attribute__ ((__packed__))" 
Visual C++ has no equivalent, it only has the "#pragma pack(1)" 

Ho bisogno di qualcosa che posso mettere in struct definizione .

Qualche informazione/suggerimento/suggerimento? TIA ...

+1

@Caspin: Voglio imballare l'intera struttura. @Others: Voglio avere un #define (in effetti 2) per ottenere questo risultato per i compilatori che userò. Il trucco #pragma non funziona su alcuni di essi. – Malkocoglu

+0

Nota che gcc ha supportato '#pragma pack' almeno dalla versione 4.0; non sono sicuro di RealView ARM. – Tom

risposta

22

non so un modo chiazza di petrolio di farlo, ma si potrebbe fare qualcosa di orribile come questo:

#include "packed.h" 
struct Foo { /* members go here */ } PACKED; 
#include "endpacked.h" 

Poi per MSVC, packed.h:

#define PACKED 
#pragma pack(push,1) 

endpacked. h

#pragma pack(pop) 
#undef PACKED 

per gcc, packed.h:

#define PACKED __attribute__ ((__packed__)) 

endpacked.h:

#undef PACKED 

Fondamentalmente, l'imballaggio è troppo dipendente dalla piattaforma. Supponiamo che la struttura compressa contenga al suo interno campi a 8 bit e consideri alcuni sistemi con un byte a 16 bit. Non può avere una struttura che rappresenti i tuoi dati solo con il packing: dovresti sapere come i byte da 8 bit vengono convertiti in byte da 16 bit quando vengono trasferiti tra i due sistemi. La struttura sulla macchina a 16 bit potrebbe aver bisogno di bitfield, nel qual caso dovresti sapere come li mette in atto l'implementazione.

Quindi, se il codice è destinato a essere generalmente portatile, potrebbe essere sufficiente definire qualsiasi struttura impacchettata necessaria in una sezione specifica della piattaforma del file di intestazione. O meglio, strutturare il codice in modo che una porta futura possa farlo se necessario.

+2

Tecnicamente, è possibile eseguire tutto ciò in un'unica intestazione: '#ifndef PACKED' /' #define PACKED'/'#else/*! PACKED * /'/'#undef PACKED' /' #endif/* PACKED */'e quindi includere la stessa intestazione due volte, una volta per abilitare e una volta per disabilitare. L'utilità/pulizia/sanità di una tale pratica è discutibile, ma lo è anche la pratica che suggerisci. –

+7

Penso che prenderò "begin/end deve match", oltre "deve essere fatto un numero pari di volte", grazie ;-). Come ho detto nella risposta, in linea di principio è necessaria una definizione di struttura specifica della piattaforma. In pratica, però, questo copre i tre compilatori di cui si occupa attualmente l'interrogante, consente una maggiore comunanza di un sacco di copia/incolla, e non è terribilmente spettacolare rispetto a uno sciame di '#if _WIN32 #elif __GNUC__' e così via attorno a ogni definizione di struttura. '# include' è l'unico modo corretto in C per astrarre o riutilizzare un frammento di codice che contiene le direttive del preprocessore. –

2

Perché hai bisogno di qualcosa nella struttura?

Penso che lo #pragma pack(1) sia lo stesso o mi manchi qualcosa?

Si può fare questo:

struct Foo 
{ 
#pragma pack(push, 1) 
int Bar; 
#pragma pack(pop) 
}; 

Ma sembra brutto.

+10

In realtà è #pragma pack (push, 1) e #pragma pack (pop). – Timbo

58

È possibile definire PACK come questo per GNU GCC

#define PACK(__Declaration__) __Declaration__ __attribute__((__packed__)) 

e come questo per Visual C++:

#define PACK(__Declaration__) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop)) 

e usarlo in questo modo:

PACK(
struct myStruct 
{ 
    int a; 
    int b; 
}); 
+1

Inizialmente pensavo che questo non funzionasse per le strutture typedef, ma potrebbe accadere se si digita PACK (struct {int a;}) foo ;. Penso che dipenderebbe dalla sintassi #pragma/__ pragma del compilatore in questione. Tuttavia, non funzionerà per enumerazione poiché sono separate internamente con virgole (sì, le enumerazioni possono essere compresse ed è talvolta il modo più pulito per risolvere il tuo problema). –

+1

Questo approccio è stato usato da Symbian per tutti questi attributi, poiché miravano a molti compilatori e alcuni volevano il bit specifico del compilatore scritto prima di un identificatore, altri dopo ecc. – Will

+2

Mi piace questa soluzione, tuttavia, mi ci è voluto molto tempo per capire che MSVC++ gestisce i bitfield in modo leggermente diverso da GCC. Stavo usando bitfield "int" di varie dimensioni (1, 7, 8, 16) e ho scoperto che MSVC++ voleva ancora impacchettare tutto nei limiti a 4 byte. Convertendo su char per 1,7 e 8 e short per 16 I, ho ottenuto l'imballaggio che mi aspettavo. – Compholio

13

So che questo la domanda è vecchia ora, ma credo che ci sia una soluzione migliore di quelle pubblicate in precedenza. Dopo tutto, è possibile inserire il pragma nel caso MSVC nella riga della dichiarazione della struttura.Si consideri il seguente:

#ifdef _MSC_VER 
# define PACKED_STRUCT(name) \ 
    __pragma(pack(push, 1)) struct name __pragma(pack(pop)) 
#elif defined(__GNUC__) 
# define PACKED_STRUCT(name) struct __attribute__((packed)) name 
#endif 

allora questo può essere utilizzato in questo modo:

typedef PACKED_STRUCT() { short a; int b } my_struct_t; 
PACKED_SRUCT(my_other_struct) { short a; int b }; 

ecc

La chiave qui è che l'uso del __pragma ha solo bisogno di essere intorno alla linea di dichiarazione della struttura Questo deve includere il nome della struct se ne viene fornito uno, quindi il nome è un parametro della macro. Ovviamente, è facile estendere a enum/class, che lascerò come esercizio al lettore!

Il programma di test su pack documentation MSDN page è utile per verificarlo.

EDIT

Si scopre nella mia prova ho usato il processore Intel Compiler su Windows. Utilizzando icl.exe questo approccio funziona senza problemi, ma con il compilatore Microsoft (cl.exe) non lo fa (testato con il 2010 e il 2013).

+0

Mi piacerebbe molto se questo avesse funzionato, in quanto avrebbe mantenuto le macro strane limitate alla linea di apertura della definizione della struct, ma quando ho provato questo su MSVS 2010 con C++, ho ottenuto "errore C2143: errore di sintassi: mancante"; ' prima di '{' '. Ho dovuto tornare a qualcosa di simile al post di Steph, in cui l'intero corpo della definizione di struct è l'argomento macro, che tipo di schifo. Soprattutto dal momento che confonde la colorazione dei contesti vim sui file C (comunque vim lo gestisce bene con i file C++, non so perché). – phonetagger

+0

Non funziona nemmeno in MSVC 2015. –

+0

Tuttavia, la parola chiave 'PACKED_STRUCT' è interessante.È possibile combinare con la soluzione di Steph in questo modo: '#ifdef _MSC_VER' ' # definiscono PACKED_STRUCT (__Declaration__) __pragma (pacchetto (push, 1)) struct __Declaration__ __pragma (pack (pop)) '' #elif definito (__GNUC __) '' # definiscono PACKED_STRUCT (__Declaration__) struct __Declaration__ __attribute __ ((__ imballato __)) '' # endif' Modificato: blocchi di codice non rendano come previsto. Scusate. –

6

Un'altra soluzione, a seconda di quali compilatori è necessario supportare, è notare che GCC ha supportato il prgema di imballaggio in stile Microsoft almeno dalla versione 4.0.4 (la documentazione online è disponibile su gnu.org per le versioni 3.4.6 e 4.0.4 - i pragma non sono descritti nel primo e sono nel secondo). Ciò consente di utilizzare #pragma pack(push,1) prima di una definizione di struttura e #pragma pack(pop) dopo la definizione e verrà compilato in entrambi.

+0

Confermato di funzionare, ma la soluzione di IMHO @ Steph è molto più elegante che inserire coppie di '# pragma' prima e dopo ogni dichiarazione. –

5

È possibile farlo al contrario, poiché GCC supporta i pragmi relativi al pacchetto VC++. Guarda here per ulteriori informazioni.

estratto ...

Per la compatibilità con i compilatori Microsoft Windows, GCC supporta una serie di #pragma direttive che modificano l'allineamento massimo dei componenti del strutture (diversi campi di bit di lunghezza zero), i sindacati, e classi successivamente definite. Il valore n riportato sotto deve sempre essere un piccolo potere di due e specifica il nuovo allineamento in byte.

#pragma pack(n) imposta semplicemente il nuovo allineamento.

#pragma pack() imposta l'allineamento a quello che era in vigore quando compilazione avviato (vedi comando anche l'opzione linea -fpack-struct[=<n>] vedere Opzioni Codice Gen).

#pragma pack(push[,n]) spinge l'impostazione di allineamento corrente su uno stack interno e quindi imposta facoltativamente il nuovo allineamento.

#pragma pack(pop) ripristina l'impostazione di allineamento a quella salvata in la parte superiore dello stack interno (e rimuove quella voce dello stack).

Si noti che #pragma pack([n]) non influenza questo stack interno; quindi è possibile avere #pragma pack(push) seguito da più #pragma pack(n) istanze e finalizzato da un singolo #pragma pack(pop).

Alcuni obiettivi, ad es.i386 e powerpc supportano lo ms_struct#pragma che espone una struttura come il documento __attribute__((ms_struct)).

#pragma ms_struct on attiva il layout per le strutture dichiarate.

#pragma ms_struct off disattiva il layout per le strutture dichiarate.

#pragma ms_struct reset torna al layout predefinito.