如何在所需的偏移量处设置结构元素 [英] How to set structure element at desired offset

查看:27
本文介绍了如何在所需的偏移量处设置结构元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在嵌入式编程中,当描述硬件时,通常需要将结构元素放置在硬件工程师设计它们时已知的预定义位置.例如,让我们定义一个结构 FPGA,它有大约 100 个寄存器/区域,如下面的简化示例所示:

In embedded programming when describing the hardware one often needs to place struct elements at known predefined positions as the HW engineer designed them. For example, let's define a structure FPGA, which has some 100 registers/areas 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 frustrated and angry of manual calculations and possible errors in the case of a change in the structure. Is there any way to do this more robust/convenient? I tried to write it this way:

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

但这不会编译抱怨不完整的结构......请注意,我的示例已简化.实际上,必须定义大约 20-30 个这样的备用字段 - 结构非常大.

but this does not compile complaining about incomplete struct... Note, that my example is simplified. In reality there are some 20-30 such spare fields that must be defined - the structure is very big.

推荐答案

好吧,这不会赢得环球小姐奖,但我认为它可以满足您的需求:

Well, this 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));
}

假设您想要 N=20 个填充字段.您必须在结构的开头添加这些 PAD_FIELDS(i, 中的 N 个,其中 i 运行例如从 1 到 20(如我的示例)或从 0到 19 或任何让你快乐的东西.然后,当您需要填充时,您可以添加例如 , 0x80) 这意味着下一个字段将位于距结构开头的偏移 0x80 处.

Let's say you want N=20 padding fields. You have to add N of those PAD_FIELDS(i, in the beginning of your structure, where i runs for example from 1 to 20 (as in my example) or from 0 to 19 or whatever makes you happy. Then, when you need the padding you add for example , 0x80) which means that the next field will be positioned at offset 0x80 from the beginning of the structure.

运行此代码后,它输出以下文本:

Upon running this code, it outputs the following text:

CPLD_Version offset a0
NID_Version offset 10a2

这个宏的工作方式是用你的字段定义一个结构,然后合并你的字段,并添加根据结构计算的填充.

The way this macro works is it defines a structure with your fields, it then incorporates your fields, and adds the padding computed according to the structure.

如果你不介意一些 boost::preprocessor 魔法,这里有一种方法可以让你自动化整个 PAD_FIELDS(1,PAD_FIELDS(2,PAD_FIELDS(3,PAD_FIELDS(4,...)一开始:

If you don't mind some boost::preprocessor magic, here's a way you can automate the whole PAD_FIELDS(1,PAD_FIELDS(2,PAD_FIELDS(3,PAD_FIELDS(4,... in 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));

注意用法的变化:

  1. 在结构的开头编写 CONTAINS_PADDING(n),其中 n 是所需的填充元素的数量.
  2. 就在结构末尾之前,您必须添加一个)".
  3. 代替 ,0x0A) 来指定填充,您必须编写 SET_OFFSET(0x0A);(; 是可选的).
  1. In the beginning of the structure you write CONTAINS_PADDING(n) where n is the number of padding elements desired.
  2. Right before the end of the structure you have to add a ")".
  3. Instead of ,0x0A) to specify the padding you have to write SET_OFFSET(0x0A); (the ; is optional).

这篇关于如何在所需的偏移量处设置结构元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆