You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 to ptr != NULL, so _UTIL_ASSERT_DISPATCH becomes _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 to ptr != NULL, -1, so _UTIL_ASSERT_DISPATCH becomes _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_param is defined in your codebase (it’s a common macro in embedded systems like STM32, but you can replace it with standard assert() if needed).

内容的提问来源于stack exchange,提问作者Łukasz Przeniosło

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:18:47