2009-10-08 12 views
39

Dla niektórych kompilatorów, istnieje specifier pakowania dla kodowanym, na przykład ::Visual C++ odpowiednik __attribute__ 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)" 

muszę coś, co można umieścić w struct definicji .

Jakieś informacje/hack/sugestia? TIA ...

+1

@Caspin: Chcę spakować całą strukturę. @Inne: chcę mieć #define (w rzeczywistości 2), aby to osiągnąć dla kompilatorów, których użyję. Na niektórych z nich sztuczka #pragma nie działa. – Malkocoglu

+0

Należy pamiętać, że gcc obsługuje '#pragma pack' od co najmniej wersji 4.0; nie wiesz o RealView ARM. – Tom

Odpowiedz

22

nie wiem zgrabny sposób to zrobić, ale można ewentualnie zrobić coś strasznego tak:

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

Wtedy dla MSVC, packed.h:

#define PACKED 
#pragma pack(push,1) 

endpacked. h

#pragma pack(pop) 
#undef PACKED 

Dla gcc, packed.h:

#define PACKED __attribute__ ((__packed__)) 

endpacked.h:

#undef PACKED 

Zasadniczo pakowania jest zbyt zależne od platformy. Załóżmy, że twoja spakowana struktura ma w sobie 8-bitowe pola i rozważmy jakiś system z 16-bitowym bajtem. Nie może mieć struktury reprezentującej twoje dane po prostu przez pakowanie - musisz wiedzieć, w jaki sposób 8-bitowe bajty są konwertowane na 16-bitowe bajty po przesłaniu między tymi dwoma systemami. Struktura na maszynie 16-bitowej może wymagać bitfieldów, w takim przypadku musisz wiedzieć, w jaki sposób implementacja je określa.

Jeśli więc kod ma być ogólnie przenośny, konieczne może być zdefiniowanie dowolnych spakowanych struktur w sekcji nagłówkowej specyficznej dla platformy. Albo raczej skonstruuj swój kod, aby przyszły port mógł to zrobić, jeśli musi.

+2

Technicznie, możesz zrobić to wszystko w jednym nagłówku: '#ifndef PACKED' /' #define PACKED'/'#else/*! PACKED * /'/'#undef PACKED' /' #endif/* PACKED */', a następnie dwa razy ten sam nagłówek, jeden raz, aby włączyć i raz wyłączyć. Użyteczność/czystość/zdrowy rozsądek takiej praktyki jest dyskusyjna, ale tak samo praktyka sugerujesz. –

+7

Myślę, że wezmę "początek/koniec musi pasować", powyżej "musi być zrobione nawet kilka razy", dziękuję ;-). Jak już powiedziałem w odpowiedzi, w zasadzie potrzebna jest specyficzna dla platformy definicja struktury. W praktyce jednak obejmuje to trzy kompilatory, o które pyta obecnie dana osoba, pozwala na większą jednolitość niż zwykłe kopiowanie/wklejanie i nie jest spektakularnie okropne w porównaniu z rój '# ifWIN32 #element __GNUC__' itd. wokół każdej definicji struktury. '# include' to poprawna/jedyna metoda w C do abstrakcyjnego wykorzystania lub ponownego wykorzystania fragmentu kodu zawierającego dyrektywy preprocesora. –

2

Dlaczego potrzebujesz czegoś w strukturze?

Myślę, że #pragma pack(1) jest taki sam, czy też czegoś brakuje?

Można to zrobić:

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

Ale wygląda brzydko.

+10

To właściwie #pragma pack (push, 1) i #pragma pack (pop). – Timbo

58

Można zdefiniować Pack to dla GNU gcc

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

i tak dla Visual C++:

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

i używać go tak:

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

Początkowo myślałem, że to nie działa dla typedef'd struktur, ale może, jeśli ty typedef PACK (struct {int a;}) foo ;. Myślę, że zależałoby to od składni #pragma/__ pragma danego kompilatora. Jednak nie będzie on działał dla wyliczeń, ponieważ są one wewnętrznie oddzielone przecinkami (tak, wyliczenia mogą być spakowane i jest to czasami najczystszy sposób na rozwiązanie problemu). –

+1

To podejście zostało użyte przez Symbiana dla wszystkich takich atrybutów, ponieważ dotyczyło ono wielu kompilatorów, a niektóre chciały bitu kompilatora napisanego przed identyfikatorem, inne po itp. – Will

+2

Podoba mi się to rozwiązanie, jednak zajęło mi dużo czasu dowiedzieć się, że MSVC++ obsługuje bitfields trochę inaczej niż GCC. Używałem bitfów "int" o różnych rozmiarach (1, 7, 8, 16) i odkryłem, że MSVC++ nadal chciał spakować wszystko w 4-bajtowe granice. Przekształcając się w char na 1,7 i 8 i krótko na 16, otrzymałem pakowanie, którego oczekiwałem. – Compholio

13

wiem to pytanie jest już stare, ale uważam, że istnieje lepsze rozwiązanie niż te zamieszczane wcześniej. Możliwe jest umieszczenie pragmy w przypadku MSVC w linii deklaracji struct mimo wszystko.Rozważmy następujący:

#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 

Wtedy może to być wykorzystywane w taki sposób:

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

itp

Kluczem tutaj jest to, że korzystanie z __pragma tylko musi być wokół linii deklaracji struktury. Musi zawierać nazwę struktury, jeśli jest podana, stąd nazwa jest parametrem makra. Oczywiście można to łatwo rozszerzyć na enum/klasę, którą zostawię jako ćwiczenie dla czytelnika!

Program testowy na pack documentation MSDN page jest przydatny do weryfikacji tego.

EDIT

Okazuje się w moich testów używałem Intel kompilatora na Windows. Korzystanie z icl.exe to podejście działa bez problemu, ale z kompilatorem Microsoft (cl.exe), nie jest (testowane z 2010 i 2013).

+0

Byłbym zachwycony, gdyby to zadziałało, ponieważ sprawiłoby, że dziwność makr ograniczyłaby się do linii otwierającej definicję struktury, ale gdy próbowałem tego na MSVS 2010 z C++, otrzymałem komunikat o błędzie C2143: błąd składni: brakujący; " przed "{". Musiałem powrócić do czegoś podobnego do posta Stepha, gdzie cała treść definicji struct jest argumentem makro, który jest do dupy. Zwłaszcza, że ​​dezorientuje kontekst vim na plikach C (vim radzi sobie z plikami C++, ale nie wiem dlaczego). – phonetagger

+0

Czy _not_ działa również pod MSVC 2015. –

+0

Jednak słowo "PACKED_STRUCT" jako słowo kluczowe jest fajne.Można łączyć z roztworem Steph jest tak: '#ifdef _MSC_VER' ' #define PACKED_STRUCT (__Declaration__) __pragma (paczka (push 1)) struct __Declaration__ __pragma (paczka (pop)) '' #elif zdefiniowane (__GNUC __) '' #define PACKED_STRUCT (__Declaration__) struct __Declaration__ __attribute __ ((__ __ pakowane)) '' # endif' Zmieniano: bloki kodu nie czynią, jak oczekiwano. Przepraszam. –

6

Innym rozwiązaniem, w zależności od tego, jakie kompilatory potrzebujesz obsługiwać, jest zauważenie, że GCC obsługuje pragmy pakowania w stylu Microsoftu od wersji 4.0.4 (dokumentacja online jest dostępna na gnu.org dla wersji 3.4.6 i 4.0.4 - pragmy nie są opisane w pierwszym i są w drugim). To pozwala po prostu użyć #pragma pack(push,1) przed definicją struktury i #pragma pack(pop) po definicji i zostanie skompilowana w jednym z nich.

+0

Potwierdzony do pracy, ale rozwiązanie IMHO @ Steph jest dużo bardziej eleganckie niż stawianie par '# pragma' przed i po każdej deklaracji. –

5

Można to zrobić w drugą stronę, ponieważ GCC obsługuje pragmy związane z pakietami VC++. Aby uzyskać więcej informacji, patrz here.

Extract ...

Dla kompatybilności z Microsoft Windows kompilatorów GCC obsługuje zestaw z #pragma dyrektywach zmienić maksymalną wyrównanie członków struktur (inną niż zero szerokości bitfields), związki, i następnie zdefiniowane klasy . Wymagana wartość n zawsze musi być niewielka, o mocy dwóch i określa nowe wyrównanie w bajtach.

#pragma pack(n) po prostu ustawia nowe wyrównanie.

#pragma pack() ustawia wyrównanie do tego, który był w rzeczywistości, kiedy kompilacja zaczęło (zobacz też opcja wiersza polecenia -fpack-struct[=<n>] patrz Opcje Kod gen).

#pragma pack(push[,n]) przesuwa bieżące ustawienie wyrównania na wewnętrznym stosie , a następnie opcjonalnie ustawia nowe wyrównanie.

#pragma pack(pop) przywraca ustawienie wyrównania do ustawienia zapisanego na u góry stosu wewnętrznego (i usuwa ten wpis stosu).

Pamiętaj, że #pragma pack([n]) nie ma wpływu na ten wewnętrzny stos; w ten sposób możliwe jest posiadanie #pragma pack(push), po którym następuje wiele instancji #pragma pack(n) i sfinalizowanych przez pojedyncze #pragma pack(pop).

Niektóre cele, np.i386 i powerpc, obsługują architekturę, która określa strukturę jako udokumentowaną __attribute__((ms_struct)).

#pragma ms_struct on włącza układ deklarowanych struktur.

wyłącza układ zadeklarowanych struktur.

#pragma ms_struct reset powraca do domyślnego układu.