【问题标题】:C Preprocessor generate macros by concatenation and stringification [duplicate]C预处理器通过连接和字符串化生成宏[重复]
【发布时间】:2015-03-13 14:10:32
【问题描述】:

我有一组目标宏,我想根据选择的宏为其生成别名,如下所示:

选择宏:

#define I2C_MODULE 1

别名宏(概念形式):

#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>

目标宏(来自我无法控制的外部文件):

#define INT_I2C0   24 
#define INT_I2C1   53
...
#define I2C0_BASE  0x40020000
#define I2C1_BASE  0x40021000
...   

我想让预处理器生成别名宏 I2C_MODULE_BASEI2C_MODULE_NVIC 选择宏 I2C_MODULE,但在大量阅读 Q1P1 和许多其他我忘记的参考资料之后,我最终硬编码了它们的值。下面我展示了我当前的工作定义,然后是我最后一次失败的生成宏的尝试:

什么有效:

#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1

什么不起作用:

#define I2C_MODULE 1
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

/* Attempt 1 */
#define I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)

/* Attempt 2 */
#define _I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define _I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)
#define I2C_MODULE_BASE _I2C_MODULE_BASE
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC

编辑:我扩展了accepted answer 以到达我想要的位置,如下所示:

#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c

#define _I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define _I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)

#define I2C_MODULE_BASE _I2C_MODULE_BASE(I2C_MODULE)
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC(I2C_MODULE)

【问题讨论】:

  • 您需要什么?宏还是字符串?
  • 我不明白,你为什么要处理字符串?普通的旧 ## 标识符连接在这里应该可以正常工作,但诚然,如果您开始嵌套这些构造,您可能会遇到评估顺序问题
  • 看看C preprocessor and token concatenation——它应该能解决你的问题。
  • 注:以下划线开头的名称保留用于“实现”(有关详细信息,请参阅 ISO/IEC 9899:2011 §7.1.3 保留标识符)。最好避免自己定义此类名称 - 尽管您经常(甚至通常)会侥幸成功,直到您迁移到新的操作系统或类似的东西。

标签: c concatenation c-preprocessor stringification


【解决方案1】:

这似乎有效:

#define I2C_MODULE 1

//Alias macros (conceptual form):
//#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
//#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>

//Target macros (from an external file out of my control):

#define INT_I2C0   24 
#define INT_I2C1   53

#define I2C0_BASE  0x40020000
#define I2C1_BASE  0x40021000

#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c

#define I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)

extern int i2c_module_base = I2C_MODULE_BASE(I2C_MODULE);
extern int i2c_module_nvic = I2C_MODULE_NVIC(I2C_MODULE);

extern int i2c_module_base_0 = I2C_MODULE_BASE(0);
extern int i2c_module_nvic_0 = I2C_MODULE_NVIC(0);

extern int i2c_module_base_1 = I2C_MODULE_BASE(1);
extern int i2c_module_nvic_1 = I2C_MODULE_NVIC(1);

示例输出(来自cpp):

# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
# 21 "xx.c"
extern int i2c_module_base = 0x40021000;
extern int i2c_module_nvic = 53;

extern int i2c_module_base_0 = 0x40020000;
extern int i2c_module_nvic_0 = 24;

extern int i2c_module_base_1 = 0x40021000;
extern int i2c_module_nvic_1 = 53;

这与我对C preprocessor and token concatenation 的回答密切相关。

I2C_MODULE_BASEI2C_MODULE_NVIC 宏当然还有其他的写法,但重点是:

  1. 使用## 标记粘贴运算符(而不是# 字符串化运算符)。
  2. 使用两级宏(例如,I2C_MODULE_BASEPASTE3)。

【讨论】:

  • 谢谢乔纳森!它按预期工作。请参阅我的 OP 编辑​​以了解最终用途是什么
【解决方案2】:

我怀疑您正在编写一个 I2C 驱动程序,该驱动程序通常可以在同一个微控制器中处理多个 I2C 硬件外围设备,而无需多次重写所有相同的代码。

在这种情况下,您真正​​要寻找的可能是这样的:

#define I2C1 ((volatile uint8_t*)0x12345678)  // address of first hw register for I2C1
#define I2C2 ((volatile uint8_t*)0x55555555)  // address of first hw register for I2C2

/* map all registers used for I2C, they will have same register layout for every 
   peripheral no matter which one:  */
#define I2C_CONTROL(base) (*(base + 0))
#define I2C_DATA(base)    (*(base + 1))
...


// create some dummy typedef to make your functions look nice:
typedef volatile uint8_t* I2C_t; 


// define whatever functions you need in the driver:
void i2c_init (IC2_t bus);
void i2c_send (I2C_t bus, const uint8_t* data, size_t n);
...

// implement functions in a bus-independent way:
void i2c_init (IC2_t bus)
{
  I2C_CONTROL(bus) = THIS | THAT; // setup registers
}


// caller code:

i2c_init(I2C1);
i2c_init(I2C2);
...
i2c_send(I2C1, "hello", 5);
i2c_send(I2C2, "world", 5);

【讨论】:

  • 你几乎是正确的!但问题是,驱动程序已经编写好了(它是德州仪器的 C 语言 TivaWare),但它的开箱即用抽象级别不够高,您可以按名称选择外围设备并让它执行所有必需的配置.相反,它需要你去寻找几组相关的宏,这是我试图通过使用选择宏来巩固的。
  • @LuisE。好的,以上是编写自己的驱动程序时有用的提示和技巧。 (由于 TI 与大多数半导体公司一样,因其源代码质量非常差而臭名昭著,所以您最终可能还是要自己编写一个......)
【解决方案3】:

只需使用#if / #else / #endif

#if (I2C_MODULE == 0)
#define I2C_MODULE_BASE I2C0_BASE
#define I2C_MODULE_NVIC INT_I2C0
#elif (I2C_MODULE == 1)
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
#else
#error Unknown configuration
#endif

【讨论】:

  • 这是一个简单的方法,但是当I2C_MODULE 可以达到 8 个时它变得很麻烦:)
猜你喜欢
  • 2012-07-21
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-26
  • 2022-06-14
  • 1970-01-01
相关资源
最近更新 更多