【问题标题】:C: passing arguments from variadic function to variadic macroC:将参数从可变参数函数传递到可变参数宏
【发布时间】:2020-03-25 14:03:09
【问题描述】:

我的 C 代码中内置了一个标准的日志记录 API,一个不错的简单 logF(const char *pFormat, ...) 东西,到目前为止,它一直被映射到 vprintf(),即:

void logF(const char *pFormat, ...)
{
    va_list args;
    va_start(args, pFormat);
    vprintf(pFormat, args);
    va_end(args);
}

此 API 上方的 C 代码必须在多个嵌入式平台上工作。我刚刚到达一个平台(Nordic NRF52840),我必须使用的底层日志接口显示为NRF_LOG_INFO(...) 形式的可变参数宏。

问题:如何正确地将fn(const char *pFormat, ...) 传递给BLAH(...) 宏?我的脑袋好痛……

这适用于 GCC 4.9.3,但如果有一个对 C 编译器版本相对宽松的解决方案会很好。

编辑 1:注意到我可以将我的 logF() 函数重新定义为可变参数宏并将其映射到那里,问题是我将拥有一个特定于平台的头文件而不是通用头文件,我将不得不将其移至平台代码中,并为每个代码提供一个。并非不可能,但更混乱。

编辑 2:有人问我 NRF_LOG_INFO() 如何扩展的踪迹。这是预处理器的相关输出:

#define NRF_LOG_INFO(...) NRF_LOG_INTERNAL_INFO( __VA_ARGS__)

#define NRF_LOG_INTERNAL_INFO(...) NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, __VA_ARGS__)

#define NRF_LOG_INTERNAL_MODULE(level,level_id,...) if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && (level <= NRF_LOG_DEFAULT_LEVEL)) { if (NRF_LOG_FILTER >= level) { LOG_INTERNAL(LOG_SEVERITY_MOD_ID(level_id), __VA_ARGS__); } }

#define LOG_INTERNAL(type,...) LOG_INTERNAL_X(NUM_VA_ARGS_LESS_1( __VA_ARGS__), type, __VA_ARGS__)

#define LOG_INTERNAL_X(N,...) CONCAT_2(LOG_INTERNAL_, N) (__VA_ARGS__)

Then depending on number of args, anything up to:

#define LOG_INTERNAL_6(type,str,arg0,arg1,arg2,arg3,arg4,arg5) nrf_log_frontend_std_6(type, str, (uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3), (uint32_t)(arg4), (uint32_t)(arg5))

void nrf_log_frontend_std_6(uint32_t severity_mid,
                            char const * const p_str,
                            uint32_t val0,
                            uint32_t val1,
                            uint32_t val2,
                            uint32_t val3,
                            uint32_t val4,
                            uint32_t val5);

【问题讨论】:

  • 您可以重命名您的可变参数函数并使用可变参数封装宏来调用您自己的函数或平台特定的宏。
  • @MOehm: 反过来,我在一个接收到const char pFormat, ...的函数里面,我怎么把它推下来BLAH(...)
  • 你看过可变参数宏在里面做了什么吗?可以发一下吗?
  • NRF_LOG_INFO 扩展为什么?
  • @Bodo,同意,尽管头文件是 API 的一部分并且应该与平台无关,但我试图避免这种情况。为此,我需要将标题向下移动到平台代码中,并为每个平台设置一个。并非不可能,只是更混乱。

标签: c macros variadic


【解决方案1】:

不能将参数从可变参数函数传递到可变参数宏。

由于您想从 API 标头中隐藏特定于平台的宏调用,您可以使用 vsnprintf 而不是 vprintf 处理函数参数,并使用格式 "%s" 和结果字符串缓冲区调用日志记录宏。

void logF(const char *pFormat, ...)
{
    va_list args;
    /* Choose a reasonable size or replace with dynamic allocation based on the return value of vsnprintf */
    /* This could also be a static variable or a global variable to avoid allocation of a big buffer on the stack. */
    char buffer[1024];

    va_start(args, pFormat);
    vsnprintf(buffer, sizeof(buffer), pFormat, args);

    NRF_LOG_INFO("%s", buffer);

    va_end(args);
}

请注意,您可能必须在缓冲区超出范围或被覆盖之前调用NRF_LOG_FLUSH。见https://devzone.nordicsemi.com/f/nordic-q-a/22647/nrf_log_info-how-to-print-log-with-string-parameter

【讨论】:

  • 好帖子。我现在唯一要问自己的是我是否能负担得起缓冲空间。可能会避免malloc(),因为我没有太多内存可以玩,它可能会碎片化。
猜你喜欢
  • 1970-01-01
  • 2014-10-20
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
  • 1970-01-01
相关资源
最近更新 更多