如何在编译期间检查带有括号和类型转换的宏的值?

3

我正在嵌入式系统上运行C语言,并且我想确保某个 #define 的值等于特定的值。如果不是,我希望编译器失败并停止继续执行。

我的代码目前看起来像这样:

typedef uint32_t TickType_t; //I can NOT change/modify this typedef
#define configTICK_RATE_HZ  ( ( TickType_t ) 1000 ) //I can NOT change/modify this macro, it's autogenerated

我正在尝试在我的代码中执行以下操作:
#if(1000 != configTICK_RATE_HZ)
    #error "configTICK_RATE_HZ is not 1000Hz!"
#endif

在编译时遇到错误,因为 #if 不喜欢看到类型转换(编译器不知道 typedef)。我找到了一个关于如何剥离/删除类型转换的 stackoverflow 讨论(link),其中有一个非常有创意的答案,但它还不太适用于我,因为我认为我需要在使用那个有创意的解决方案之前先删除原始宏中最外层的括号。

换句话说,我想让这个 预处理器 输出:

#if(1000 != configTICK_RATE_HZ )

看起来像这样: #if(1000 != 1000)

而不是像这样: #if(1000 != ( ( TickType_t ) 1000 ) )

如果需要中间宏,我不介意,但我不能更改原始的“configTICK_RATE_HZ”定义或 TickType_t 的 typedef。

这是一些最小可复现代码(涵盖我遇到的相同问题的类似问题)。下面的代码失败,因为它不喜欢宏中的类型转换或最外层的括号。

#include <stdio.h>
#define FREQ_HZ ((int)10) //Can not modify this definition

#if( 10 != FREQ_HZ )
#error "Bad frequency value"
#endif

int main(void)
{
    printf("Hello world\n");
}
1个回答

3

你不能使用预处理器来实现这个,但是你可以使用静态断言。

static_assert 声明允许在编译时检查常量表达式,并在条件为 false 时生成带有给定文本的错误。

static_assert(1000 == configTICK_RATE_HZ, "configTICK_RATE_HZ is not 1000Hz!");

能够在编译时完成而非运行时完成这项任务确实非常有用。我真正需要的只是一些宏魔法,可以从原始定义中去掉外层括号。之后删除类型转换应该很容易。 - AamerAirlines
@AamerAirlines 这种情况发生在编译时。这就是 static_assert 的作用。如果条件为假,编译器将生成一个包含给定文本的错误。 - dbush
嗨@dbush,抱歉让你感到困惑。我相信你是正确的(static_assert确实在编译时执行)。当我最初尝试使用它时遇到了错误,这就是为什么我认为它只在运行时起作用的原因。话虽如此,我需要稍微调整一下你的建议才能让它对我有所帮助:_Static_assert(1000 == configTICK_RATE_HZ, "configTICK_RATE_HZ不是1000Hz!"); - AamerAirlines
@AamerAirlines 是的,我把条件反了,而 static_assert_Static_assert 的宏。如果你包含了 #include <assert.h>,那么这个宏应该是可用的。 - dbush
就是这样,我只需要#include <assert.h>就可以让static_assert正常工作了。我注意到一些小的奇怪行为:当条件失败时(例如configTICK_RATE_HZ不是1000),编译器会失败,但它不会打印所需的错误消息。相反,它显示了一个不同的错误:“数组的大小必须大于零”。这很小而且对我来说不是问题,所以我不太在意修复它。谢谢! - AamerAirlines
请注意,static_assert 将在 C23 中成为一个正式的关键字,并且字符串字面值参数将变为可选。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接