解决有关格式说明符的 C 宏扩展编译错误

问题描述 投票:0回答:1

最近,我正在努力获取和设置 UCI 文件上的一些字段。为了正确地获取失败原因,我决定使用一些宏,如下所示:

#define HANDLE_UCI_ERR(_cond_, _fmt_, ...)                  \
    do {                                                    \
        if (_cond_) {                                       \
            char *err_msg = NULL;                           \
            uci_get_errorstr(g_uci_ctx, &err_msg, _fmt_);   \
            ERR(err_msg, ##__VA_ARGS__);                    \
            free(err_msg);                                  \
            goto bail;                                      \
        }                                                   \
    } while (0)

注意,这与问题无关,但为了编译 uci 代码,我使用here给出的步骤安装了它的库。

但是,它给了我以下编译错误:

gcc trial.c -luci -o trial
trial.c: In function ‘main’:
trial.c:37:11: error: expected ‘)’ before ‘err_msg’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \
      |           ^~~~~~~
trial.c:14:38: note: in definition of macro ‘MY_PRINT’
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
      |                                      ^~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \
      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:10: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat=]
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
      |          ^~~~~~~
trial.c:19:3: note: in expansion of macro ‘MY_PRINT’
   19 |   MY_PRINT("Error  : ", fmt, ##arg)
      |   ^~~~~~~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \
      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:29: note: format string is defined here
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
      |                            ~^
      |                             |
      |                             char *
trial.c:14:10: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
      |          ^~~~~~~
trial.c:19:3: note: in expansion of macro ‘MY_PRINT’
   19 |   MY_PRINT("Error  : ", fmt, ##arg)
      |   ^~~~~~~~
trial.c:37:4: note: in expansion of macro ‘MY_ERR’
   37 |    MY_ERR(err_msg, ##__VA_ARGS__);   \
      |    ^~~~~~
trial.c:72:2: note: in expansion of macro ‘HANDLE_UCI_ERR’
   72 |  HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);
      |  ^~~~~~~~~~~~~~
trial.c:14:32: note: format string is defined here
   14 |   printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
      |                               ~^
      |                                |
      |                                int

Compilation exited abnormally with code 1

说实话,我不确定代码中的问题是什么。因此,我想分享它的可复制版本。这是简化版本:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <uci.h>

#define DEBUG_ERROR_LEVEL 2


int loglevel = 7;


#define MY_PRINT(prefix, fmt, arg...) do {              \
        printf("[DM] " prefix " [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##arg); \
    } while (0)

#define MY_ERR(fmt, arg...)         \
    if (loglevel > DEBUG_ERROR_LEVEL)   \
        MY_PRINT("Error  : " fmt, ##arg)

#define HANDLE_UCI_ERR(_cond_, _fmt_, ...)                              \
    do {                                                            \
        if (_cond_) {                                           \
            char *err_msg = NULL;                           \
            uci_get_errorstr(g_uci_ctx, &err_msg, _fmt_);   \
            MY_ERR(err_msg, ##__VA_ARGS__);         \
            free(err_msg);                                  \
            goto bail;                                      \
        }                                                       \
    } while (0)

static const char *PACKAGE_STR = "package";

static struct uci_context *g_uci_ctx;



__attribute__((constructor))
void init_uci_context() {
    g_uci_ctx = uci_alloc_context();
    if (!g_uci_ctx) {
        exit(EXIT_FAILURE);
    }
    if (uci_set_confdir(g_uci_ctx, "/home/[email protected]/tmp/config/") != 0) {
        exit(EXIT_FAILURE);
    }
}

__attribute__((destructor))
void cleanup_uci_context() {
    if (g_uci_ctx) {
        uci_free_context(g_uci_ctx);
    }
}

int main() {
    int rc = 0;
    struct uci_package *pkg = NULL;

    rc = uci_load(g_uci_ctx, PACKAGE_STR, &pkg);
    HANDLE_UCI_ERR(rc != UCI_OK, "Not able to load package %s", PACKAGE_STR);

bail:
    uci_unload(g_uci_ctx, pkg);
    return rc;
}


要解决这个问题,我可以更新

HANDLE_UCI_ERR
宏,如下所示:

#define HANDLE_UCI_ERR(cond, fmt, arg...)                \
    do {                                                 \
        if (cond) {                                      \
            char *err_msg = NULL;                        \
            uci_get_errorstr(g_uci_ctx, &err_msg, NULL); \
            MY_ERR("%s : " fmt, err_msg, ##arg);         \
            free(err_msg);                               \
            goto bail;                                   \
        }                                                \
    } while (0)

如果有人能告诉我我错过了什么,我真的很感激。

c variadic-macros
1个回答
0
投票

这个...

        MY_PRINT("Error  : " fmt, ##arg)

MY_ERR

 是或扩展为标识符(例如 
fmt
)时,
... 来自
err_msg
宏的扩展为无效 C 代码。您似乎正在尝试执行字符串连接,但这仅适用于字符串文字,并且
err_msg
不是字符串文字。

您提出的解决方案或类似的解决方案是最简单的解决方案,但如果您决定使用可变参数宏,那么您可能应该使用它们的标准形式,使用

__VA_ARGS__
而不是 GCC 风格的命名变量参数。

© www.soinside.com 2019 - 2024. All rights reserved.