本文介绍了实际使用的X宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚学会的。的X宏什么现实世界使用你见过?当他们是这个职位的合适的工具?

I just learned of X-Macros. What real-world uses of X-Macros have you seen? When are they the right tool for the job?

推荐答案

我在几年前发现了X宏,当我开始利用函数指针在我的code。我是一个嵌入式程序员,我用状态机频繁。我经常会写code是这样的:

I discovered X-macros a couple of years ago when I started making use of function pointers in my code. I am an embedded programmer and I use state machines frequently. Often I would write code like this:

/* declare an enumeration of state codes */
enum{ STATE0, STATE1, STATE2, ... , STATEX, NUM_STATES};

/* declare a table of function pointers */
p_func_t jumptable[NUM_STATES] = {func0, func1, func2, ... , funcX};

问题是,我认为这是非常容易出错不得不保持我的函数指针表的顺序,使其符合我的状态枚举的排序。

The problem was that I considered it very error prone to have to maintain the ordering of my function pointer table such that it matched the ordering of my enumeration of states.

我的一个朋友给我介绍的X宏和它像一个光球在我的头上去了。说真的,你去哪儿了我所有的生活的X宏!

A friend of mine introduced me to X-macros and it was like a light-bulb went off in my head. Seriously, where have you been all my life x-macros!

所以现在我在下表中定义:

So now I define the following table:

#define STATE_TABLE \
        ENTRY(STATE0, func0) \
        ENTRY(STATE1, func1) \
        ENTRY(STATE2, func2) \
        ...
        ENTRY(STATEX, funcX) \

我可以按如下方式使用它:

And I can use it as follows:

enum
{
#define ENTRY(a,b) a,
    STATE_TABLE
#undef ENTRY
    NUM_STATES
};

p_func_t jumptable[NUM_STATES] =
{
#define ENTRY(a,b) b,
    STATE_TABLE
#undef ENTRY
};

作为奖励,我也可以有pre-处理器建立我的函数原型如下:

as a bonus, I can also have the pre-processor build my function prototypes as follows:

#define ENTRY(a,b) static void b(void);
    STATE_TABLE
#undef ENTRY

另一个用途是声明和初始化寄存器

Another usage is to declare and initialize registers

#define IO_ADDRESS_OFFSET (0x8000)
#define REGISTER_TABLE\
    ENTRY(reg0, IO_ADDRESS_OFFSET + 0, 0x11)\
    ENTRY(reg1, IO_ADDRESS_OFFSET + 1, 0x55)\
    ENTRY(reg2, IO_ADDRESS_OFFSET + 2, 0x1b)\
    ...
    ENTRY(regX, IO_ADDRESS_OFFSET + X, 0x33)\

/* declare the registers (where _at_ is a compiler specific directive) */
#define ENTRY(a, b, c) volatile uint8_t a _at_ b:
    REGISTER_TABLE
#undef ENTRY

/* initialize registers */
#define ENTRY(a, b, c) a = c;
    REGISTER_TABLE
#undef ENTRY

然而

我最喜欢的用法是当它涉及到通信处理器

My favourite usage however is when it comes to communication handlers

首先,我创建出一个通讯表,其中包含每个命令名称和code:

First I create a comms table, containing each command name and code:

#define COMMAND_TABLE \
    ENTRY(RESERVED,    reserved,    0x00) \
    ENTRY(COMMAND1,    command1,    0x01) \
    ENTRY(COMMAND2,    command2,    0x02) \
    ...
    ENTRY(COMMANDX,    commandX,    0x0X) \

我都在表中的大写和小写的名字,因为上壳体将被用于枚举和函数名称小写

I have both the uppercase and lowercase names in the table, because the upper case will be used for enums and the lowercase for function names.

然后我还定义结构为每个命令定义每个命令的样子:

Then I also define structs for each command to define what each command looks like:

typedef struct {...}command1_cmd_t;
typedef struct {...}command2_cmd_t;

etc.

同样我定义结构为每个命令响应:

Likewise I define structs for each command response:

typedef struct {...}command1_resp_t;
typedef struct {...}command2_resp_t;

etc.

然后我可以定义我的命令,code枚举:

Then I can define my command code enumeration:

enum
{
#define ENTRY(a,b,c) a##_CMD = c,
    COMMAND_TABLE
#undef ENTRY
};

我可以定义我的命令长度枚举:

I can define my command length enumeration:

enum
{
#define ENTRY(a,b,c) a##_CMD_LENGTH = sizeof(b##_cmd_t);
    COMMAND_TABLE
#undef ENTRY
};

我可以定义我的响应长度枚举:

I can define my response length enumeration:

enum
{
#define ENTRY(a,b,c) a##_RESP_LENGTH = sizeof(b##_resp_t);
    COMMAND_TABLE
#undef ENTRY
};

我能确定有多少命令有如下:

I can determine how many commands there are as follows:

typedef struct
{
#define ENTRY(a,b,c) uint8_t b;
    COMMAND_TABLE
#undef ENTRY
} offset_struct_t;

#define NUMBER_OF_COMMANDS sizeof(offset_struct_t)

请注意:我从来没有真正实例化offset_struct_t,我只是把它作为一种方式,编译器生成对我来说我的命令定义的数

NOTE: I never actually instantiate the offset_struct_t, I just use it as a way for the compiler to generate for me my number of commands definition.

请注意,然后我可以生成我的函数指针表如下:

Note then I can generate my table of function pointers as follows:

p_func_t jump_table[NUMBER_OF_COMMANDS] =
{
#define ENTRY(a,b,c) process_##b,
    COMMAND_TABLE
#undef ENTRY
}

和我的函数原型:

#define ENTRY(a,b,c) void process_##b(void);
    COMMAND_TABLE
#undef ENTRY

现在最后为有史以来最酷的使用,我可以让编译器计算我的发送缓冲区应为多大。

Now lastly for the coolest use ever, I can have the compiler calculate how big my transmit buffer should be.

/* reminder the sizeof a union is the size of its largest member */
typedef union
{
#define ENTRY(a,b,c) uint8_t b##_buf[sizeof(b##_cmd_t)];
    COMMAND_TABLE
#undef ENTRY
}tx_buf_t

此外,这工会就像我的偏移结构,它没有实例化,而不是我可以用sizeof操作符来声明我的发送缓冲区的大小。

Again this union is like my offset struct, it is not instantiated, instead I can use the sizeof operator to declare my transmit buffer size.

uint8_t tx_buf[sizeof(tx_buf_t)];

现在我的发送缓冲区tx_buf是最佳尺寸,当我命令添加到该通讯科处理,我的缓冲永远是最佳尺寸。太酷了!

Now my transmit buffer tx_buf is the optimal size and as I add commands to this comms handler, my buffer will always be the optimal size. Cool!

另外一个用途是创建偏置表:
由于内存往往是嵌入式系统约束,我不想用我的跳表512字节(每指针×256可能的命令2个字节),当它是一个稀疏数组。相反,我将有8位偏移表为每个可能的命令。此偏移随后被用于索引到我的实际跳转表而现在只需要NUM_COMMANDS *的sizeof(指针)。在我的情况有10条命令定义。我的跳转表是20字节长,我有一个偏移表是256个字节长,这是一个总的276bytes代替512字节的。然后,我打电话给我的功能,像这样:

One other use is to create offset tables:Since memory is often a constraint on embedded systems, I don't want to use 512 bytes for my jump table (2 bytes per pointer X 256 possible commands) when it is a sparse array. Instead I will have a table of 8bit offsets for each possible command. This offset is then used to index into my actual jump table which now only needs to be NUM_COMMANDS * sizeof(pointer). In my case with 10 commands defined. My jump table is 20bytes long and I have an offset table that is 256 bytes long, which is a total of 276bytes instead of 512bytes. I then call my functions like so:

jump_table[offset_table[command]]();

而不是

jump_table[command]();

我可以创造一个偏移表像这样:

I can create an offset table like so:

/* initialize every offset to 0 */
static uint8_t offset_table[256] = {0};

/* for each valid command, initialize the corresponding offset */
#define ENTRY(a,b,c) offset_table[c] = offsetof(offset_struct_t, b);
    COMMAND_TABLE
#undef ENTRY

其中offsetof处于STDDEF.H

where offsetof is a standard library macro defined in "stddef.h"

作为一个附带的好处,有一个很简单的方法来确定是否命令code为支持或不:

As a side benefit, there is a very easy way to determine if a command code is supported or not:

bool command_is_valid(uint8_t command)
{
    /* return false if not valid, or true (non 0) if valid */
    return offset_table[command];
}

这也是为什么我在我COMMAND_TABLE保留命令字节0。我可以创建一个名为一个函数process_reserved()如果有无效的命令字节用来索引到我的偏移表将被调用。

This is also why in my COMMAND_TABLE I reserved command byte 0. I can create one function called "process_reserved()" which will be called if any invalid command byte is used to index into my offset table.

这篇关于实际使用的X宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-03 22:06