Как установить элемент структуры на желаемое смещение

Во встроенном программировании при описании аппаратного обеспечения часто необходимо размещать элементы структуры в известных предопределенных позициях, как их разработал инженер HW. Например, давайте определим структуру FPGA, которая имеет около 100 регистров / областей, как в следующем упрощенном примере:

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;
};

Теперь я расстроен и зол на ручные расчеты и возможные ошибки в случае изменения структуры. Есть ли способ сделать это более надежным / удобным?
Я попытался написать это так:

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

но это не компилирует жалобы на неполную структуру …
Обратите внимание, что мой пример упрощен. В действительности существует около 20-30 таких запасных полей, которые необходимо определить — структура очень большая.

6

Решение

Ну, это не получит награду Мисс Вселенная, но я думаю, что она делает то, что вы хотите:

#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));
}

Допустим, вы хотите N = 20 полей заполнения. Вы должны добавить N из этих PAD_FIELDS(i, в начале вашей структуры, где i например, от 1 до 20 (как в моем примере) или от 0 до 19, или что угодно, что делает вас счастливым.
Затем, когда вам нужно заполнение, вы добавляете, например, , 0x80) Это означает, что следующее поле будет расположено со смещением 0x80 от начала структуры.

После запуска этого кода он выводит следующий текст:

CPLD_Version offset a0
NID_Version offset 10a2

Этот макрос работает так, как он определяет структуру с вашими полями, затем включает ваши поля и добавляет заполнение, вычисленное в соответствии со структурой.

Если вы не возражаете против магии boost :: preprocessor, вот способ, которым вы можете автоматизировать весь PAD_FIELDS(1,PAD_FIELDS(2,PAD_FIELDS(3,PAD_FIELDS(4,... в начале:

#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));

Обратите внимание, что изменилось в использовании:

  1. В начале структуры вы пишете CONTAINS_PADDING(n) где n количество требуемых элементов заполнения.
  2. Прямо перед концом структуры вы должны добавить «)».
  3. Вместо ,0x0A) чтобы указать отступ вы должны написать SET_OFFSET(0x0A); ( ; необязательно).
2

Другие решения

Язык просто не позволяет вам форсировать определенные отступы. Даже если вы добавите свой собственный отступ, компилятор понятия не имеет, что вы пытаетесь сделать. Это может легко добавить свой собственный дополнительный отступы для выравнивания членов пути Это хочет.

Конечно, для конкретный Процессор, ОС и компилятор, вы может быть просто повезло в том, что отступы вы добавляете вручную может быть просто работайте так, как вам нужно — вы должны написать тестовую программу, чтобы убедиться, что смещения членов соответствуют вашим ожиданиям.

Если вам абсолютно необходим доступ к данным с определенными смещениями, вы можете попробовать нестандартный __attribute__(packed) расширение gcc (но смотрите этот); или написать собственные маршруты ввода / вывода для де / сериализации данных из формы, в которой они находятся в / из struct для более легкого доступа на уровне программирования C.

1

Как насчет этого:

struct sFPGA {
struct Internal_S {
uchar   Spare1[0x24];
ushort  DiscreteInput;
} s;
uchar   Spare2[0xA0 - sizeof (struct Internal_S)];
ushort  CPLD_Version;
};
0
По вопросам рекламы [email protected]