C语言可变参数宏:能否用单宏替代两类断言包装宏?
Single Variadic Assert Macro for Void/Non-Void Functions in C11 and Earlier
Absolutely! In C11 (and even C99, its predecessor), you can create a single variadic macro that automatically adapts to 1 or 2 input arguments, choosing between a plain return or return ret_ at compile time. This relies on clever preprocessor tricks to detect the number of arguments passed.
Implementation
First, keep your original core assertion macros—they’ll act as the "backends" for our unified macro:
/** * @brief 断言失败时无返回值的断言包装器。 * @param x_: 需测试是否非零的值。 */ #define UTIL_ASSERT_VOID(x_) \ assert_param(x_); \ if (!x_) \ return; /** * @brief 断言失败时带返回值的断言包装器。 * @param x_: 需测试是否非零的值。 * @param ret_: 断言失败时的返回值。 */ #define UTIL_ASSERT_VAL(x_, ret_) \ assert_param(x_); \ if (!x_) \ return ret_;
Next, add helper macros to detect argument count and route to the correct backend:
// Helper macro: Selects the right backend based on argument count #define _UTIL_ASSERT_PICK_BACKEND(_1, _2, SELECTED, ...) SELECTED // Routes calls to UTIL_ASSERT_VOID (1 arg) or UTIL_ASSERT_VAL (2 args) #define _UTIL_ASSERT_DISPATCH(...) \ _UTIL_ASSERT_PICK_BACKEND(__VA_ARGS__, UTIL_ASSERT_VAL, UTIL_ASSERT_VOID, ) // Unified variadic macro for external use #define UTIL_ASSERT(...) _UTIL_ASSERT_DISPATCH(__VA_ARGS__)(__VA_ARGS__)
How It Works
Let’s break down the preprocessor magic:
- When you call
UTIL_ASSERT(ptr != NULL)(1 argument):__VA_ARGS__expands toptr != NULL, so_UTIL_ASSERT_DISPATCHbecomes_UTIL_ASSERT_PICK_BACKEND(ptr != NULL, , UTIL_ASSERT_VAL, UTIL_ASSERT_VOID, )- The macro picks the third positional argument:
UTIL_ASSERT_VOID, then calls it with your original argument.
- When you call
UTIL_ASSERT(ptr != NULL, -1)(2 arguments):__VA_ARGS__expands toptr != NULL, -1, so_UTIL_ASSERT_DISPATCHbecomes_UTIL_ASSERT_PICK_BACKEND(ptr != NULL, -1, UTIL_ASSERT_VAL, UTIL_ASSERT_VOID, )- The macro picks the third positional argument:
UTIL_ASSERT_VAL, then calls it with both your arguments.
Usage Examples
// Void-return function: uses UTIL_ASSERT_VOID under the hood void process_buffer(void* buf) { UTIL_ASSERT(buf != NULL); // ... buffer processing logic } // Int-return function: uses UTIL_ASSERT_VAL under the hood int read_config(int fd) { UTIL_ASSERT(fd >= 0, -EINVAL); // ... read logic return config_value; }
Notes
- This works in C99 and later (including C11) since variadic macros (
__VA_ARGS__) were introduced in C99. - If you pass more than 2 arguments, the preprocessor will throw a compile error—this is intentional, as our assertion logic only supports 1 or 2 arguments.
- Ensure
assert_paramis defined in your codebase (it’s a common macro in embedded systems like STM32, but you can replace it with standardassert()if needed).
内容的提问来源于stack exchange,提问作者Łukasz Przeniosło
相关产品推荐
相关产品推荐

