- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我刚刚了解到 X-Macros .您见过 X-Macros 的哪些实际用途?他们什么时候是完成这项工作的正确工具?
最佳答案
几年前,当我开始在我的代码中使用函数指针时,我发现了 X-macros。我是一名嵌入式程序员,经常使用状态机。通常我会写这样的代码:
/* 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};
问题是我认为必须维护我的函数指针表的顺序以匹配我的状态枚举的顺序很容易出错。
我的一个 friend 向我介绍了 X-macros,它就像一个灯泡在我脑海中熄灭了。说真的,我这辈子你都去哪儿了 x-macros!
所以现在我定义下表:
#define STATE_TABLE \
ENTRY(STATE0, func0) \
ENTRY(STATE1, func1) \
ENTRY(STATE2, func2) \
...
ENTRY(STATEX, funcX) \
我可以按如下方式使用它:
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
};
作为奖励,我还可以让预处理器按如下方式构建我的函数原型(prototype):
#define ENTRY(a,b) static void b(void);
STATE_TABLE
#undef ENTRY
另一种用法是声明和初始化寄存器
#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
然而,我最喜欢的用法是在通信处理程序方面
首先我创建一个通讯表,包含每个命令名称和代码:
#define COMMAND_TABLE \
ENTRY(RESERVED, reserved, 0x00) \
ENTRY(COMMAND1, command1, 0x01) \
ENTRY(COMMAND2, command2, 0x02) \
...
ENTRY(COMMANDX, commandX, 0x0X) \
我在表中有大写和小写名称,因为大写将用于枚举,小写将用于函数名称。
然后我还为每个命令定义了结构体来定义每个命令的样子:
typedef struct {...}command1_cmd_t;
typedef struct {...}command2_cmd_t;
etc.
同样,我为每个命令响应定义结构:
typedef struct {...}command1_resp_t;
typedef struct {...}command2_resp_t;
etc.
然后我可以定义我的命令代码枚举:
enum
{
#define ENTRY(a,b,c) a##_CMD = c,
COMMAND_TABLE
#undef ENTRY
};
我可以定义我的命令长度枚举:
enum
{
#define ENTRY(a,b,c) a##_CMD_LENGTH = sizeof(b##_cmd_t);
COMMAND_TABLE
#undef ENTRY
};
我可以定义我的响应长度枚举:
enum
{
#define ENTRY(a,b,c) a##_RESP_LENGTH = sizeof(b##_resp_t);
COMMAND_TABLE
#undef ENTRY
};
我可以确定有多少命令如下:
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,我只是将其用作编译器为我生成我的命令定义数量的一种方式。
注意然后我可以生成我的函数指针表如下:
p_func_t jump_table[NUMBER_OF_COMMANDS] =
{
#define ENTRY(a,b,c) process_##b,
COMMAND_TABLE
#undef ENTRY
}
还有我的函数原型(prototype):
#define ENTRY(a,b,c) void process_##b(void);
COMMAND_TABLE
#undef ENTRY
最后,为了有史以来最酷的用途,我可以让编译器计算我的传输缓冲区应该有多大。
/* 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
同样,这个 union 就像我的偏移量结构,它没有被实例化,相反我可以使用 sizeof 运算符来声明我的传输缓冲区大小。
uint8_t tx_buf[sizeof(tx_buf_t)];
现在我的传输缓冲区 tx_buf 是最佳大小,当我向这个通信处理程序添加命令时,我的缓冲区将始终是最佳大小。酷!
另一个用途是创建偏移量表:由于内存通常是嵌入式系统的约束,因此当它是一个稀疏数组时,我不想为我的跳转表使用 512 字节(每个指针 2 字节 X 256 个可能的命令)。相反,我将为每个可能的命令提供一个 8 位偏移量表。然后使用此偏移量索引到我的实际跳转表中,该表现在只需要是 NUM_COMMANDS * sizeof(pointer)。在我的例子中,定义了 10 个命令。我的跳转表是 20 字节长,我有一个 256 字节长的偏移表,总共 276 字节而不是 512 字节。然后我这样调用我的函数:
jump_table[offset_table[command]]();
代替
jump_table[command]();
我可以像这样创建一个偏移表:
/* 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”中定义的标准库宏
附带的好处是,有一种非常简单的方法可以确定是否支持命令代码:
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()”的函数,如果使用任何无效的命令字节索引到我的偏移表中,将调用该函数。
关于c - X-Macros 的实际使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63669550/
问题故障解决记录 -- Java RMI Connection refused to host: x.x.x.x .... 在学习JavaRMI时,我遇到了以下情况 问题原因:可
我正在玩 Rank-N-type 并尝试输入 x x .但我发现这两个函数可以以相同的方式输入,这很不直观。 f :: (forall a b. a -> b) -> c f x = x x g ::
这个问题已经有答案了: How do you compare two version Strings in Java? (31 个回答) 已关闭 8 年前。 有谁知道如何在Java中比较两个版本字符串
这个问题已经有答案了: How do the post increment (i++) and pre increment (++i) operators work in Java? (14 个回答)
下面是带有 -n 和 -r 选项的 netstat 命令的输出,其中目标字段显示压缩地址 (127.1/16)。我想知道 netstat 命令是否有任何方法或选项可以显示整个目标 IP (127.1.
我知道要证明 : (¬ ∀ x, p x) → (∃ x, ¬ p x) 证明是: theorem : (¬ ∀ x, p x) → (∃ x, ¬ p x) := begin intro n
x * x 如何通过将其存储在“auto 变量”中来更改?我认为它应该仍然是相同的,并且我的测试表明类型、大小和值显然都是相同的。 但即使 x * x == (xx = x * x) 也是错误的。什么
假设,我们这样表达: someIQueryable.Where(x => x.SomeBoolProperty) someIQueryable.Where(x => !x.SomeBoolProper
我有一个字符串 1234X5678 我使用这个正则表达式来匹配模式 .X|..X|X. 我得到了 34X 问题是为什么我没有得到 4X 或 X5? 为什么正则表达式选择执行第二种模式? 最佳答案 这里
我的一个 friend 在面试时遇到了这个问题 找到使该函数返回真值的 x 值 function f(x) { return (x++ !== x) && (x++ === x); } 面试官
这个问题在这里已经有了答案: 10年前关闭。 Possible Duplicate: Isn't it easier to work with foo when it is represented b
我是 android 的新手,我一直在练习开发一个针对 2.2 版本的应用程序,我需要帮助了解如何将我的应用程序扩展到其他版本,即 1.x、2.3.x、3 .x 和 4.x.x,以及一些针对屏幕分辨率
为什么案例 1 给我们 :error: TypeError: x is undefined on line... //case 1 var x; x.push(x); console.log(x);
代码优先: # CASE 01 def test1(x): x += x print x l = [100] test1(l) print l CASE01 输出: [100, 100
我正在努力温习我的大计算。如果我有将所有项目移至 'i' 2 个空格右侧的函数,我有一个如下所示的公式: (n -1) + (n - 2) + (n - 3) ... (n - n) 第一次迭代我必须
给定 IP 字符串(如 x.x.x.x/x),我如何或将如何计算 IP 的范围最常见的情况可能是 198.162.1.1/24但可以是任何东西,因为法律允许的任何东西。 我要带198.162.1.1/
在我作为初学者努力编写干净的 Javascript 代码时,我最近阅读了 this article当我偶然发现这一段时,关于 JavaScript 中的命名空间: The code at the ve
我正在编写一个脚本,我希望避免污染 DOM 的其余部分,它将是一个用于收集一些基本访问者分析数据的第 3 方脚本。 我通常使用以下内容创建一个伪“命名空间”: var x = x || {}; 我正在
我尝试运行我的test_container_services.py套件,但遇到了以下问题: docker.errors.APIError:500服务器错误:内部服务器错误(“ b'{” message
是否存在这两个 if 语句会产生不同结果的情况? if(x as X != null) { // Do something } if(x is X) { // Do something } 编
我是一名优秀的程序员,十分优秀!