在编译时验证宏参数的大小

Ale*_*op. 4 c sizeof bit-fields c11

假设我有一个宏(更多细节为何,以下是在PS部分)

void my_macro_impl(uint32_t arg0, uint32_t arg1, uint32_t arg2);

...

#define MY_MACRO(arg0, arg1, arg2)       my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2))
Run Code Online (Sandbox Code Playgroud)

将要在其上使用该宏的硬件是小尾数法,并使用32位体系结构,以便所有指针的宽度最大为(包括)32位宽度。我的目标是在用户通过uint64_tint64_t错误输入参数时警告用户。

我想使用sizeof这样的

#define MY_MACRO(arg0, arg1, arg2)       do \
                                         {  \
                                             static_assert(sizeof(arg0) <= sizeof(uint32_t));  \
                                             static_assert(sizeof(arg1) <= sizeof(uint32_t));  \
                                             static_assert(sizeof(arg2) <= sizeof(uint32_t));  \
                                             my_macro_impl((uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2));  \
                                         } while (0)
Run Code Online (Sandbox Code Playgroud)

但是用户可以使用MY_MACRO位域,然后我的代码无法编译:

错误:“ sizeof”对位字段的无效应用

问题:是否可以选择在编译时检测宏参数的大小是否大于(例如)uint32_t


聚苯乙烯

它的MY_MACRO作用类似于printf实时嵌入式环境。该环境有一个硬件记录器,最多可以接收5个参数,每个参数应为32位。目的是保留的标准格式printf。格式字符串是脱机解析的,并且解析器很清楚每个参数都是32位,因此它将根据%...格式字符串中的进行强制转换。可能的用法如下。

不需要的用法:

uint64_t time = systime_get();
MY_MACRO_2("Starting execution at systime %llx", time); // WRONG! only the low 32 bits are printed. I want to detect it and fail the compilation.
Run Code Online (Sandbox Code Playgroud)

预期用途:

uint64_t time = systime_get();
MY_MACRO_3("Starting execution at systime %x%x", (uint32_t)(time >> 32), (uint32_t)time); // OK! 
Run Code Online (Sandbox Code Playgroud)

Ale*_*op. 5

以下方法可满足此需求:

#define CHECK_ARG(arg)                  _Generic((arg), \
                                                 int64_t  : (arg),  \
                                                 uint64_t : (arg),  \
                                                 default  : (uint32_t)(arg))
Run Code Online (Sandbox Code Playgroud)

然后,MY_MACRO可以定义为

#define MY_MACRO(a0, a1, a2)       do \
                                   {  \
                                       uint32_t arg1 = CHECK_ARG(a0);  \
                                       uint32_t arg2 = CHECK_ARG(a1);  \
                                       uint32_t arg3 = CHECK_ARG(a2);  \
                                       my_macro_impl(arg1, arg2, arg3);\
                                   } while (0)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,例如通过时uint64_t,会发出警告:

警告:隐式转换会失去整数精度:将'uint64_t'(又名'unsigned long long')转换为'uint32_t'(又名'unsigned int')[-Wshorten-64-to-32]

注意:

其他类型(如double128/256位类型)也可以类似地处理。

应启用适当的警告。

编辑:

受到Lundin的评论和回答的启发,上述提议的解决方案可以轻松地修改为可移植的版本,这将导致编译错误,而不仅仅是编译器警告。

#define CHECK_ARG(arg)          _Generic((arg),         \
                                         int64_t  : 0,  \
                                         uint64_t : 0,  \
                                         default  : 1)
Run Code Online (Sandbox Code Playgroud)

因此MY_MACRO可以修改为

#define MY_MACRO(a0, a1, a2)       do \
                                   {  \
                                       _Static_assert(CHECK_ARG(a1) && \
                                                      CHECK_ARG(a2) && \
                                                      CHECK_ARG(a3),   \
                                                      "64 bit parameters are not supported!"); \
                                       my_macro_impl((uint32_t)(a1), (uint32_t)(a2), (uint32_t)(a3)); \
                                   } while (0)
Run Code Online (Sandbox Code Playgroud)

这次,当传递uint64_t参数时MY_MACRO(1ULL, 0, -1),编译失败并显示错误

错误:由于要求'_Generic((1ULL),long long:0,unsigned long long:0,默认值:1)&&(_Generic((0),long long:0,unsigned long long:0,默认值: 1)&& _Generic((-1),long long:0,unsigned long long:0,默认值:1))' “不支持64位参数!”