根据参数值调用不同宏的C宏
C Macros that invoke different Macros according to the argument values
我正在尝试在 C2000 微控制器上实现寄存器访问宏。遗憾的是,这个微控制器没有 8 位类型,所以当我需要访问一个字节时,我需要调用一个宏
__byte(temp,0) = 0xCA
我的一些寄存器是字节,另一些是 16 位字。但是,我想使用相同的宏来访问数据。
#define REG_WIDGET_ADDR 0x1000
#define REG_WIDGET_IS_BYTE 1
#define REG_GADGET_ADDR 0x1002
#define REG_GADGET_IS_BYTE 0
我想像这样访问寄存器。
REG_ACCESS(WIDGET) = 0x0A;
REG_ACCESS(GADGET) = 0xCAFE;
这将被转换为不同的宏,如
REG_ACCESS2(REG_WIDGET_ADDR, REG_WIDGET_IS_BYTE) = 0x0A
REG_ACCESS2(REG_GADGET_ADDR, REG_GADGET_IS_BYTE) = 0xCAFE
然后当寄存器是一个字节时,REG_ACCESS2宏应该被转换成类似__byte(*(0x1000),0) = 0xA
的东西。当寄存器为 16 位字时,宏应转换为 (*(0x1002) = 0xCAFE
我知道如何创建在参数数量改变时调用不同宏的宏,但不知道如何在参数值改变时调用不同宏。我该如何进行?
编辑:
C2000没有8位寄存器,只有16位寄存器。但我想使用 DMA 对外部设备进行编程。我想在C2000 Ram中设置8位和16位“寄存器”,然后通过DMA传输到外部设备。
此代码可以满足您的要求:
#define REG_WIDGET_ADDR 0x1000
#define REG_WIDGET_IS_BYTE 1
#define REG_GADGET_ADDR 0x1002
#define REG_GADGET_IS_BYTE 0
// These two macros provide the requested expansions for word and byte access.
#define REG_ACCESS_T0(a) *(a)
#define REG_ACCESS_T1(a) __byte(*(a), 0)
// This macro uses argument b to select between the two macros above.
#define REG_ACCESS3(a, b) REG_ACCESS_T##b(a)
// This macro is needed to let argument b be replaced.
#define REG_ACCESS2(a, b) REG_ACCESS3(a, b)
// This macro uses x to get the corresponding address and is-byte macros.
#define REG_ACCESS(x) REG_ACCESS2(REG_##x##_ADDR, REG_##x##_IS_BYTE)
REG_ACCESS(WIDGET) = 0x0A;
REG_ACCESS(GADGET) = 0xCAFE;
宏替换结果为:
__byte(*(0x1000), 0) = 0x0A;
*(0x1002) = 0xCAFE;
第 1 部分
解决此预处理问题的经典方法涉及从片段中粘贴标记,然后匹配某些定义的标记。令牌碎片化的问题在于它不利于您的基本开发环境工具,例如“查找使用此标识符的位置”。
相反,我们可以采用将寄存器定义为数据帧的方法,如下所示:
#define WIDGET 0x1000, BYTE
#define GADGET 0x1002, WORD
但是:让我们不要完全采用这种未封装的方法。让我们为这个“数据类型”创建一个“构造函数”并使用它。原因是:如果我们向我们的寄存器添加另一个 属性,我们可以只向 constructor-like 宏添加一个参数,然后编译器将找到所有需要参数的地方,所以我们不需要'忘记任何:
#define REG(ADDR, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
另一个优点是“属性”现在有了名称。在我们的编程编辑器或IDE中,我们可以跳转到REG
的定义,看看WIDGET
的定义中0x1000
是什么意思,可以看到它对应一个名为ADDR
.
所以现在,定义了这个“数据结构”,我们可以编写访问器:
#define REG(ADDR, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// retrieve addr
#define ADDR(ADDR, BYTE_OR_WORD) ADDR
// retrieve BYTE or WORD
#define BYTE_OR_WORD(ADDR, BYTE_OR_WORD) BYTE_OR_WORD
那么我们可以像这样编写访问宏:
#define REG_ACCESS(REG) BYTE_OR_WORD(REG)(ADDR(REG))
例如
REG_ACCESS(WIDGET)
扩展到
BYTE(0x1000)
现在我们只需要定义 BYTE()
和 WORD()
来进行访问。这是一个文件中的完整图片:
// REG type constructor
#define REG(ADDRESS, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// Retrieve address field of type by destructuring and selecting
#define ADDR(ADDR, BOW) ADDR
// Retrieve BYTE-or-WORD field likewise
#define BYTE_OR_WORD(ADDR, BOW) BOW
// Access macro: retrieves the fields and builds expression
#define REG_ACCESS(REG) BYTE_OR_WORD(REG)(ADDR(REG))
// construct instances of various registers
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
// Test access
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
使用 gcc -E
进行测试:
$ gcc -E regaccess.c
# 1 "regaccess.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "regaccess.c"
# 18 "regaccess.c"
BYTE(0x1000)
WORD(0x1002)
请注意在解决方案中,每个标识符在某处都有一个定义,并且对该标识符的所有引用都使用该标识符:没有标识符是从无法搜索的部分粘贴在一起的。
如果我们在代码中看到REG_ACCESS(WIDGET)
,我们可以跳转到REG_ACCESS
的定义或WIDGET
的定义。
此外,该代码仅使用 ISO C 90 预处理器功能,但与函数式编程有一定关系:元组的构造、解构。这是因为预处理器被(或可以)视为一个纯 term-rewriting 系统。
第 2 部分
如果我们做的事情很简单,我们就不需要带有访问器的抽象层。因为它们只在单个宏中使用,REG_ACCESS
,我们可以让它们消失:
// REG type constructor
#define REG(ADDRESS, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// Access macro's implementation
#define X_REG_ACCESS(ADDR, BOW) BOW(ADDR)
// Access macro's interface
#define REG_ACCESS(REG) X_REG_ACCESS(REG)
// construct instances of various registers
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
// Test access
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
X_REG_ACCESS
解构类型的两个部分,ADDR
和 BOW
,并且不使用任何抽象访问器,做它需要的:构造类型的应用程序 byte-or-word 函数名称到类型的地址。
它的核心是简单本身:BOW(ADDR)
。将 ADDR
传递给 BYTE
或 WORD
.
第 3 部分
但是,哲学问题:我们为什么不这样做:
#define WIDGET BYTE(0x1000)
#define GADGET WORD(0x1001)
// nothing to do: WIDGET and GADGET are already access expressions:
#define REG_ACCESS(REG) REG
我们如何为早先的废话辩护呢?一个原因是早期的方法允许我们 macro-time 访问有关寄存器的信息,而不仅仅是生成访问该寄存器的代码。如果我们只想要它的地址怎么办?或者是 BYTE
还是 WORD
.
我们的系统是可扩展的。假设我们也需要寄存器名称,并且布尔值 1
或 0
值谓词判断寄存器是否为字节数据类型。复杂示例:
// Machine type abstraction byte or word
#define DATA_TYPE(IS_BYTE, ACCESS_FN) IS_BYTE, ACCESS_FN
// Register abstraction
#define REG(ADDRESS, DATA_TYPE, NAME) ADDRESS, DATA_TYPE, NAME
#define X_REG_ACCESS(ADDR, IS_BYTE, ACCESS_FN, NAME) ACCESS_FN(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS(REG)
#define X_REG_ADDR(ADDR, IS_BYTE, ACCESS_FN, NAME) ADDR
#define REG_ADDR(REG) X_ADDR(REG)
#define X_REG_NAME(ADDR, IS_BYTE, ACCESS_FN, NAME) NAME
#define REG_NAME(REG) X_REG_NAME(REG)
#define X_REG_IS_BYTE(ADDR, IS_BYTE, ACCESS_FN, NAME) IS_BYTE
#define REG_IS_BYTE(REG) X_REG_IS_BYTE(REG)
// Database of machine types
#define BYTE DATA_TYPE(1, get_byte)
#define WORD DATA_TYPE(0, get_word)
#define WIDGET REG(0x1000, BYTE, "I2C Acme Widget")
#define GADGET REG(0x1002, WORD, "SPI Mikro Gadget")
// Debug print about register
#define PRINT_REG_INFO(REG) \
printf("register " X_REG_NAME(REG) ", @%x, is byte: %s", \
X_REG_ADDR(REG), X_REG_IS_BYTE(REG) ? "yes" : "no")
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
PRINT_REG_INFO(WIDGET);
PRINT_REG_INFO(GADGET);
输出:
get_byte(0x1000)
get_word(0x1002)
printf("register " "I2C Acme Widget" ", @%x, is byte: %s", 0x1000, 1 ? "yes" : "no");
printf("register " "SPI Mikro Gadget" ", @%x, is byte: %s", 0x1002, 0 ? "yes" : "no");
第 4 部分
注意这里的第 3 部分 DATA_TYPE
是如何拼接成 REG
类型的,它有四个元素。也就是说,REG
访问器处理展开数据类型字段的平面结构。这可能是不希望的。如果我们想向 DATA_TYPE
添加一个字段,我们必须编辑包含它的所有其他类型以向其函数添加参数。
解决方法是让我们的构造函数添加括号,如下所示:
#define REG(ADDRESS, DATA_TYPE, NAME) (ADDRESS, DATA_TYPE, NAME)
然后调用 X_
扩展宏而不带括号:
#define X_REG_ACCESS(ADDR, DATA_TYPE, NAME) ACCESS_FN(DATA_TYPE)(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS REG
注意X_REG_ACCESS
扩展如何从ACCESS_FN(ADDR)
变为ACCESS_FN(DATA_TYPE)(ADDR)
:DATA_TYPE
现在被封装了,我们必须使用它的访问器来获取访问函数名。
修改了整个第 3 部分的解决方案:REG
又是一个三元组,其中 two-element DATA_TYPE
是一个元素:
// Machine type abstraction byte or word
#define DATA_TYPE(IS_BYTE, ACCESS_FN) (IS_BYTE, ACCESS_FN)
#define X_IS_BYTE(IS_BYTE, ACCESS_FN) IS_BYTE
#define IS_BYTE(DATA_TYPE) X_IS_BYTE DATA_TYPE
#define X_ACCESS_FN(IS_BYTE, ACCESS_FN) ACCESS_FN
#define ACCESS_FN(DATA_TYPE) X_ACCESS_FN DATA_TYPE
// Register abstraction
#define REG(ADDRESS, DATA_TYPE, NAME) (ADDRESS, DATA_TYPE, NAME)
#define X_REG_ACCESS(ADDR, DATA_TYPE, NAME) ACCESS_FN(DATA_TYPE)(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS REG
#define X_REG_ADDR(ADDR, DATA_TYPE, NAME) ADDR
#define REG_ADDR(REG) X_ADDR REG
#define X_REG_NAME(ADDR, DATA_TYPE, NAME) NAME
#define REG_NAME(REG) X_REG_NAME REG
#define X_REG_IS_BYTE(ADDR, DATA_TYPE, NAME) IS_BYTE(DATA_TYPE)
#define REG_IS_BYTE(REG) X_REG_IS_BYTE REG
// Database of machine types
#define BYTE DATA_TYPE(1, get_byte)
#define WORD DATA_TYPE(0, get_word)
#define WIDGET REG(0x1000, BYTE, "I2C Acme Widget")
#define GADGET REG(0x1002, WORD, "SPI Mikro Gadget")
// Debug print about register
#define PRINT_REG_INFO(REG) \
printf("register " X_REG_NAME REG ", @%x, is byte: %s", \
X_REG_ADDR REG, X_REG_IS_BYTE REG ? "yes" : "no")
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
PRINT_REG_INFO(WIDGET);
PRINT_REG_INFO(GADGET);
我正在尝试在 C2000 微控制器上实现寄存器访问宏。遗憾的是,这个微控制器没有 8 位类型,所以当我需要访问一个字节时,我需要调用一个宏
__byte(temp,0) = 0xCA
我的一些寄存器是字节,另一些是 16 位字。但是,我想使用相同的宏来访问数据。
#define REG_WIDGET_ADDR 0x1000
#define REG_WIDGET_IS_BYTE 1
#define REG_GADGET_ADDR 0x1002
#define REG_GADGET_IS_BYTE 0
我想像这样访问寄存器。
REG_ACCESS(WIDGET) = 0x0A;
REG_ACCESS(GADGET) = 0xCAFE;
这将被转换为不同的宏,如
REG_ACCESS2(REG_WIDGET_ADDR, REG_WIDGET_IS_BYTE) = 0x0A
REG_ACCESS2(REG_GADGET_ADDR, REG_GADGET_IS_BYTE) = 0xCAFE
然后当寄存器是一个字节时,REG_ACCESS2宏应该被转换成类似__byte(*(0x1000),0) = 0xA
的东西。当寄存器为 16 位字时,宏应转换为 (*(0x1002) = 0xCAFE
我知道如何创建在参数数量改变时调用不同宏的宏,但不知道如何在参数值改变时调用不同宏。我该如何进行?
编辑:
C2000没有8位寄存器,只有16位寄存器。但我想使用 DMA 对外部设备进行编程。我想在C2000 Ram中设置8位和16位“寄存器”,然后通过DMA传输到外部设备。
此代码可以满足您的要求:
#define REG_WIDGET_ADDR 0x1000
#define REG_WIDGET_IS_BYTE 1
#define REG_GADGET_ADDR 0x1002
#define REG_GADGET_IS_BYTE 0
// These two macros provide the requested expansions for word and byte access.
#define REG_ACCESS_T0(a) *(a)
#define REG_ACCESS_T1(a) __byte(*(a), 0)
// This macro uses argument b to select between the two macros above.
#define REG_ACCESS3(a, b) REG_ACCESS_T##b(a)
// This macro is needed to let argument b be replaced.
#define REG_ACCESS2(a, b) REG_ACCESS3(a, b)
// This macro uses x to get the corresponding address and is-byte macros.
#define REG_ACCESS(x) REG_ACCESS2(REG_##x##_ADDR, REG_##x##_IS_BYTE)
REG_ACCESS(WIDGET) = 0x0A;
REG_ACCESS(GADGET) = 0xCAFE;
宏替换结果为:
__byte(*(0x1000), 0) = 0x0A;
*(0x1002) = 0xCAFE;
第 1 部分
解决此预处理问题的经典方法涉及从片段中粘贴标记,然后匹配某些定义的标记。令牌碎片化的问题在于它不利于您的基本开发环境工具,例如“查找使用此标识符的位置”。
相反,我们可以采用将寄存器定义为数据帧的方法,如下所示:
#define WIDGET 0x1000, BYTE
#define GADGET 0x1002, WORD
但是:让我们不要完全采用这种未封装的方法。让我们为这个“数据类型”创建一个“构造函数”并使用它。原因是:如果我们向我们的寄存器添加另一个 属性,我们可以只向 constructor-like 宏添加一个参数,然后编译器将找到所有需要参数的地方,所以我们不需要'忘记任何:
#define REG(ADDR, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
另一个优点是“属性”现在有了名称。在我们的编程编辑器或IDE中,我们可以跳转到REG
的定义,看看WIDGET
的定义中0x1000
是什么意思,可以看到它对应一个名为ADDR
.
所以现在,定义了这个“数据结构”,我们可以编写访问器:
#define REG(ADDR, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// retrieve addr
#define ADDR(ADDR, BYTE_OR_WORD) ADDR
// retrieve BYTE or WORD
#define BYTE_OR_WORD(ADDR, BYTE_OR_WORD) BYTE_OR_WORD
那么我们可以像这样编写访问宏:
#define REG_ACCESS(REG) BYTE_OR_WORD(REG)(ADDR(REG))
例如
REG_ACCESS(WIDGET)
扩展到
BYTE(0x1000)
现在我们只需要定义 BYTE()
和 WORD()
来进行访问。这是一个文件中的完整图片:
// REG type constructor
#define REG(ADDRESS, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// Retrieve address field of type by destructuring and selecting
#define ADDR(ADDR, BOW) ADDR
// Retrieve BYTE-or-WORD field likewise
#define BYTE_OR_WORD(ADDR, BOW) BOW
// Access macro: retrieves the fields and builds expression
#define REG_ACCESS(REG) BYTE_OR_WORD(REG)(ADDR(REG))
// construct instances of various registers
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
// Test access
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
使用 gcc -E
进行测试:
$ gcc -E regaccess.c
# 1 "regaccess.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "regaccess.c"
# 18 "regaccess.c"
BYTE(0x1000)
WORD(0x1002)
请注意在解决方案中,每个标识符在某处都有一个定义,并且对该标识符的所有引用都使用该标识符:没有标识符是从无法搜索的部分粘贴在一起的。
如果我们在代码中看到REG_ACCESS(WIDGET)
,我们可以跳转到REG_ACCESS
的定义或WIDGET
的定义。
此外,该代码仅使用 ISO C 90 预处理器功能,但与函数式编程有一定关系:元组的构造、解构。这是因为预处理器被(或可以)视为一个纯 term-rewriting 系统。
第 2 部分
如果我们做的事情很简单,我们就不需要带有访问器的抽象层。因为它们只在单个宏中使用,REG_ACCESS
,我们可以让它们消失:
// REG type constructor
#define REG(ADDRESS, BYTE_OR_WORD) ADDRESS, BYTE_OR_WORD
// Access macro's implementation
#define X_REG_ACCESS(ADDR, BOW) BOW(ADDR)
// Access macro's interface
#define REG_ACCESS(REG) X_REG_ACCESS(REG)
// construct instances of various registers
#define WIDGET REG(0x1000, BYTE)
#define GADGET REG(0x1002, WORD)
// Test access
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
X_REG_ACCESS
解构类型的两个部分,ADDR
和 BOW
,并且不使用任何抽象访问器,做它需要的:构造类型的应用程序 byte-or-word 函数名称到类型的地址。
它的核心是简单本身:BOW(ADDR)
。将 ADDR
传递给 BYTE
或 WORD
.
第 3 部分
但是,哲学问题:我们为什么不这样做:
#define WIDGET BYTE(0x1000)
#define GADGET WORD(0x1001)
// nothing to do: WIDGET and GADGET are already access expressions:
#define REG_ACCESS(REG) REG
我们如何为早先的废话辩护呢?一个原因是早期的方法允许我们 macro-time 访问有关寄存器的信息,而不仅仅是生成访问该寄存器的代码。如果我们只想要它的地址怎么办?或者是 BYTE
还是 WORD
.
我们的系统是可扩展的。假设我们也需要寄存器名称,并且布尔值 1
或 0
值谓词判断寄存器是否为字节数据类型。复杂示例:
// Machine type abstraction byte or word
#define DATA_TYPE(IS_BYTE, ACCESS_FN) IS_BYTE, ACCESS_FN
// Register abstraction
#define REG(ADDRESS, DATA_TYPE, NAME) ADDRESS, DATA_TYPE, NAME
#define X_REG_ACCESS(ADDR, IS_BYTE, ACCESS_FN, NAME) ACCESS_FN(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS(REG)
#define X_REG_ADDR(ADDR, IS_BYTE, ACCESS_FN, NAME) ADDR
#define REG_ADDR(REG) X_ADDR(REG)
#define X_REG_NAME(ADDR, IS_BYTE, ACCESS_FN, NAME) NAME
#define REG_NAME(REG) X_REG_NAME(REG)
#define X_REG_IS_BYTE(ADDR, IS_BYTE, ACCESS_FN, NAME) IS_BYTE
#define REG_IS_BYTE(REG) X_REG_IS_BYTE(REG)
// Database of machine types
#define BYTE DATA_TYPE(1, get_byte)
#define WORD DATA_TYPE(0, get_word)
#define WIDGET REG(0x1000, BYTE, "I2C Acme Widget")
#define GADGET REG(0x1002, WORD, "SPI Mikro Gadget")
// Debug print about register
#define PRINT_REG_INFO(REG) \
printf("register " X_REG_NAME(REG) ", @%x, is byte: %s", \
X_REG_ADDR(REG), X_REG_IS_BYTE(REG) ? "yes" : "no")
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
PRINT_REG_INFO(WIDGET);
PRINT_REG_INFO(GADGET);
输出:
get_byte(0x1000)
get_word(0x1002)
printf("register " "I2C Acme Widget" ", @%x, is byte: %s", 0x1000, 1 ? "yes" : "no");
printf("register " "SPI Mikro Gadget" ", @%x, is byte: %s", 0x1002, 0 ? "yes" : "no");
第 4 部分
注意这里的第 3 部分 DATA_TYPE
是如何拼接成 REG
类型的,它有四个元素。也就是说,REG
访问器处理展开数据类型字段的平面结构。这可能是不希望的。如果我们想向 DATA_TYPE
添加一个字段,我们必须编辑包含它的所有其他类型以向其函数添加参数。
解决方法是让我们的构造函数添加括号,如下所示:
#define REG(ADDRESS, DATA_TYPE, NAME) (ADDRESS, DATA_TYPE, NAME)
然后调用 X_
扩展宏而不带括号:
#define X_REG_ACCESS(ADDR, DATA_TYPE, NAME) ACCESS_FN(DATA_TYPE)(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS REG
注意X_REG_ACCESS
扩展如何从ACCESS_FN(ADDR)
变为ACCESS_FN(DATA_TYPE)(ADDR)
:DATA_TYPE
现在被封装了,我们必须使用它的访问器来获取访问函数名。
修改了整个第 3 部分的解决方案:REG
又是一个三元组,其中 two-element DATA_TYPE
是一个元素:
// Machine type abstraction byte or word
#define DATA_TYPE(IS_BYTE, ACCESS_FN) (IS_BYTE, ACCESS_FN)
#define X_IS_BYTE(IS_BYTE, ACCESS_FN) IS_BYTE
#define IS_BYTE(DATA_TYPE) X_IS_BYTE DATA_TYPE
#define X_ACCESS_FN(IS_BYTE, ACCESS_FN) ACCESS_FN
#define ACCESS_FN(DATA_TYPE) X_ACCESS_FN DATA_TYPE
// Register abstraction
#define REG(ADDRESS, DATA_TYPE, NAME) (ADDRESS, DATA_TYPE, NAME)
#define X_REG_ACCESS(ADDR, DATA_TYPE, NAME) ACCESS_FN(DATA_TYPE)(ADDR)
#define REG_ACCESS(REG) X_REG_ACCESS REG
#define X_REG_ADDR(ADDR, DATA_TYPE, NAME) ADDR
#define REG_ADDR(REG) X_ADDR REG
#define X_REG_NAME(ADDR, DATA_TYPE, NAME) NAME
#define REG_NAME(REG) X_REG_NAME REG
#define X_REG_IS_BYTE(ADDR, DATA_TYPE, NAME) IS_BYTE(DATA_TYPE)
#define REG_IS_BYTE(REG) X_REG_IS_BYTE REG
// Database of machine types
#define BYTE DATA_TYPE(1, get_byte)
#define WORD DATA_TYPE(0, get_word)
#define WIDGET REG(0x1000, BYTE, "I2C Acme Widget")
#define GADGET REG(0x1002, WORD, "SPI Mikro Gadget")
// Debug print about register
#define PRINT_REG_INFO(REG) \
printf("register " X_REG_NAME REG ", @%x, is byte: %s", \
X_REG_ADDR REG, X_REG_IS_BYTE REG ? "yes" : "no")
REG_ACCESS(WIDGET)
REG_ACCESS(GADGET)
PRINT_REG_INFO(WIDGET);
PRINT_REG_INFO(GADGET);