How to set an element of the structure at the desired offset

In embedded programming, when describing hardware, it is often necessary to place structural elements in known predetermined positions, as designed by the HW engineer. For example, let's define an FPGA structure that has about 100 registers / regions, as in the following simplified example:

struct __attribute__ ((__packed__)) sFPGA { uchar Spare1[0x24]; ushort DiscreteInput; uchar Spare2[0x7A]; //CPLD_Version is required to be at offset 0xA0, so 0xA0-0x24-2=0x7A ushort CPLD_Version; }; 

Now I am upset and angry at manual calculations and possible errors in case of a change in structure. Is there any way to make this more reliable / convenient? I tried to write it like this:

 uchar Spare2[0xA0 - offsetof(sFPGA, Spare2)]; 

but this does not compile complaints about the incomplete structure ... Please note that my example is simplified. In fact, there are 20-30 such reserve fields that need to be defined - the structure is very large.

+6
source share
3 answers

Well, it won’t win the Miss Universe award, but I think it does what you want:

 #include <boost/preprocessor/cat.hpp> typedef unsigned char uchar; typedef unsigned short ushort; #define PAD_FIELDS(i_,f_, n_) \ typedef struct __attribute__((packed)) {f_} ftype##i_; \ typedef struct __attribute__((packed)) {f_ uchar t_;} ttype##i_; \ f_ uchar BOOST_PP_CAT(padding,i_)[n_ - sizeof (BOOST_PP_CAT(ftype,i_)) * (sizeof (BOOST_PP_CAT(ftype,i_)) != sizeof (BOOST_PP_CAT(ttype,i_)))]; struct sFPGA { PAD_FIELDS(1, PAD_FIELDS(2, uchar Spare1[0x24]; ushort DiscreteInput; //CPLD_Version is required to be at offset 0xA0 , 0xA0) // First padding ushort CPLD_Version; uchar more_stuff[0x50]; ushort even_more[4]; //NID_Version is required to be at offset 0x10A2 , 0x10A2) // Second padding ushort NID_Version; } __attribute__((packed)); int main() { printf("CPLD_Version offset %x\n", offsetof(sFPGA,CPLD_Version)); printf("NID_Version offset %x\n", offsetof(sFPGA,NID_Version)); } 

Say you want to fill in the fields N = 20. You must add N from these PAD_FIELDS(i, at the beginning of your structure, where i works, for example, from 1 to 20 (as in my example) or from 0 to 19, or from which makes you happy.Then when you need to fill in, add, for example , 0x80) , which means that the next field will be located at offset 0x80 from the beginning of the structure.

After running this code, it displays the following text:

 CPLD_Version offset a0 NID_Version offset 10a2 

How this macro works, it defines a structure with your fields, then it includes your fields and adds an add-on calculated according to the structure.

If you don't mind boost :: preprocessor magic magic, here you can automate the whole PAD_FIELDS(1,PAD_FIELDS(2,PAD_FIELDS(3,PAD_FIELDS(4,... at the beginning:

 #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/comma.hpp> #include <boost/preprocessor/repetition/repeat.hpp> #include <boost/preprocessor/punctuation/paren.hpp> typedef unsigned char uchar; typedef unsigned short ushort; #define PAD_FIELDS(i_,f_, n_) \ typedef struct __attribute__((packed)) {f_} BOOST_PP_CAT(ftype,i_); \ typedef struct __attribute__((packed)) {f_ uchar t_;} BOOST_PP_CAT(ttype,i_); \ f_ uchar BOOST_PP_CAT(padding,i_)[n_ - sizeof (BOOST_PP_CAT(ftype,i_)) * (sizeof (BOOST_PP_CAT(ftype,i_)) != sizeof (BOOST_PP_CAT(ttype,i_)))]; #define PADMAC(z,n,s) PAD_FIELDS BOOST_PP_LPAREN() n BOOST_PP_COMMA() #define PADREP(n) BOOST_PP_REPEAT(n, PADMAC, junk) #define FORCE_EVAL(...) __VA_ARGS__ #define CONTAINS_PADDING(n) FORCE_EVAL(PADREP(n) #define SET_OFFSET(o) BOOST_PP_COMMA() o BOOST_PP_RPAREN() struct sFPGA { CONTAINS_PADDING(2); uchar Spare1[0x24]; ushort DiscreteInput; //CPLD_Version is required to be at offset 0xA0 SET_OFFSET(0xA0); ushort CPLD_Version; uchar more_stuff[0x50]; ushort even_more[4]; //NID_Version is required to be at offset 0x10A2 SET_OFFSET(0x10A2); ushort NID_Version; ) } __attribute__((packed)); 

Note what changed when using:

  • At the beginning of the structure, you write CONTAINS_PADDING(n) , where n is the number of desired fill elements.
  • Right before the end of the structure you must add a ")".
  • Instead ,0x0A) to indicate padding, you should write SET_OFFSET(0x0A); ( ; optional).
+1
source

The language simply does not allow you to force a particular add-on. Even if you add your own add-on, the compiler does not know what you are trying to do. He can easily add his own extra padding to align the participants as he wants.

Of course, for a particular processor, OS, and compiler, you might just be lucky that the addition you add manually may just work to be the addition you want - you will have to write a test program to make sure member offsets are what you think.

If you absolutely must access data with certain offsets, you can try the non-standard extension __attribute__(packed) gcc (but see this ); or write custom I / O routes to de / serialize data from a form to / from a struct to simplify access at the C programming level.

+1
source

How about this:

 struct sFPGA { struct Internal_S { uchar Spare1[0x24]; ushort DiscreteInput; } s; uchar Spare2[0xA0 - sizeof (struct Internal_S)]; ushort CPLD_Version; }; 
0
source

All Articles