- 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/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!