如何使用可选和可变参数编写宏

时间:2017-08-06 09:15:25

标签: c macros

我有一个名为PRINT(...)的宏,我在我的代码中使用它,它获取可变数量的参数,行为类似于printf(获取格式和参数)。它的定义如下:

#define PRINT(...) PRINT(__VA_ARGS__)                     

现在我想修改它,以便它有一个可选参数,比如它的名字是number,它会为打印添加一个数字前缀。例如:

PRINT("%s", "hi") - >将打印hi
PRINT(1, "%s", "hi") - >将打印1: hi

如何更改PRINT宏以支持此功能?
重要的是,我不希望从我的代码中更改对此宏的任何现有调用(在示例中,如果我调用PRINT("%s", "hi") - 它在更改后需要保持不变)。<登记/> 此外,我无法为此目的创建新的宏 - 必须使用现有的PRINT宏来实现此目的(但当然我可以更改它的arguemnts定义)。
知道我该怎么办?

编辑:我看到这个post关于可变参数宏 - 但它与我在这里要求的不同,因为参数number需要是一个公认的变量,它将在实现中被处理如果对PRINT的调用不包含-1参数(PRINT将是打印无数字的指示符),则number-1,否则将{}打印数字前缀。

3 个答案:

答案 0 :(得分:2)

从C11开始,您可以使用_Generic关键字。这允许您检查任何值或变量的类型。根据{{​​3}},_Generic的行为因编译器而异。 this document使用This answer提供了一个简单的解决方案。

#define PRINT(fst, ...) \
( \
    _Generic((0, fst), char *: 1, default: 0) ? \
    PRINTNL(fst, __VA_ARGS__) : \
    PRINTL(fst, __VA_ARGS__) \
)

PRINTNL打印时没有数字,PRINTL打印数字。

其余代码:

#define PRINTNL(...) printf(__VA_ARGS__)
#define PRINTL (n, ...) ({ \
    printf("%d: ", n); \
    printf(__VA_ARGS__); \
})

答案 1 :(得分:1)

由于您在撰写本文时已经知道第一个参数是否是数字前缀,因此请使用其他名称创建一个宏,以便为该数字添加前缀。在此,我假设PRINT(...)扩展为printf(__VA_ARGS__)

#define PRINT(...) printf(__VA_ARGS__)

所以定义一个调用NPRINT两次的宏printf,一次用数字输出前缀,用格式输出一次:

#define NPRINT(number, fmt, ...) (printf("%d: ", number), printf(fmt, __VA_ARGS__))

用法

#include <stdio.h>

int main(void) {
    NPRINT(1, "%s\n", "hi");
}

当然,如果对printf的调用应该是 atomic ,那么这不起作用 - 现在如果格式字符串始终是 literal 字符串,然后你可以使用字符串连接:

#define NPRINT(number, fmt, ...) (printf("%d " fmt, number, __VA_ARGS__))

如果它可以是一个变量,并且只允许对PRINT进行一次调用,那么我唯一可以看到的便携式方式就是创建一个构建格式的函数。

对于没有数字参数的最新编辑,-1:应该加上前缀,这只会变成:

#define PRINT(...) NPRINT(-1, __VA_ARGS__)

答案 2 :(得分:0)

请查看##__VA_ARGS__ macro。还要检查下面为日志函数编写的代码。

在头文件中

/** #include <libgen.h> */
void APP_LOGX(const char * file, int num, const char* message, ...)
{
    va_list ap;
    int length;
    char * tfilename = NULL;
    tfilename = strdup(file);
    
    length = strlen(message);
    if(length>0){
        va_start(ap, message);
        vprintf(message, ap);
        printf(" | File: %s Line %d ", basename(tfilename), num);
        va_end(ap);

        /* add newline if nessasary */
        if(message[length -1] != '\n'){
            printf("\n");
        }
    }

    free(tfilename);
}

在实施中

 APP_LOG("app display init")

申请

APP_LOG("Value of x acceleration %.2f", g);
APP_ERROR("Something bad happened!")

category_orders={'m4':d}