C 如果缺少参数,则将宏扩展为其他默认宏

C 如果缺少参数,则将宏扩展为其他默认宏,c,c-preprocessor,variadic,C,C Preprocessor,Variadic,如果第一个参数不是期望值,是否可以将接受多个参数的宏扩展到另一个宏 例如 如果我知道arg的总数,我可能会尝试过多加载。只需根据需要使用另一个值即可。也许对可变宏有点魔力会有所帮助。 类似于: #define PRINT( print_level , print_string , ... )\ switch( print_level ) \ /* as many syslog cas as needed */ case( 5 ):\ ca

如果第一个参数不是期望值,是否可以将接受多个参数的宏扩展到另一个宏

例如


如果我知道arg的总数,我可能会尝试过多加载。

只需根据需要使用另一个值即可。也许对可变宏有点魔力会有所帮助。

类似于:

#define PRINT( print_level , print_string , ... )\
    switch( print_level ) \
        /* as many syslog cas as needed */
        case( 5 ):\
        case( 4 ):\
        case( 3 ):\
        case( 2 ):\
        case( 2 ):\
        case( 1 ):\
           syslog( print_level , __VA_ARGS__ );\
        break ; \
        default: \
        case( 0 ): \
           printf( __VA_ARGS__ ); \ /* else we simply want to print it */
        break ; 
编辑:
变量宏上的文档:

C宏无法检查其参数。正如你在回复中提到的,有一种偷偷摸摸的方法可以根据争论的数量做不同的事情,但这就是它的范围。如果您在试图执行的重载之外已经有了数量可变的参数,那么这将是不可能的。如果您只需要一个默认级别:

#define PRINTNORM(...) PRINT(3, __VA_ARGS__)
或者随便你怎么称呼它。IMHO,比重载
打印更干净的代码

具有条件宏求值。在这里,您可能会使用类似于
P99\u IF\u EMPTY
的内容

#define PRINT(LEV, ...) my_print(P99_IF_EMPTY(LEV)(3)(LEV), __VA_ARGS__)
对于空参数,仍然需要插入一个
,但可能接近您想要达到的效果。

在其他强制参数之前的可选参数可以通过将它们折叠在括号中来处理:

PRINT((2, "%d%d\n"), i, j);
PRINT("%d%d\n", i, j);
定义
PRINT
如下:

#define PRINT(SL, ...) PRINT_LEVEL(APPLY(CAT(LEVEL, IS_SPLIT(SL)), IDENTITY SL), APPLY(CAT(FSTRING, IS_SPLIT(SL)), IDENTITY SL), __VA_ARGS__)
#define PRINT_LEVEL(LEVEL, ...) syslog(LEVEL, __VA_ARGS__)
PRINT
检测第一个参数是原子(只是格式字符串)还是两个元素的括号列表(printlevel+string),并相应地扩展到实际实现
PRINT\u LEVEL
,从第一个参数提取级别,或提供默认值

的定义为\u SPLIT
,其他帮助程序如下所示:

#define LEVEL_0(_S) 3
#define LEVEL_1(L, S) L
#define FSTRING_0(S) K_##S
#define FSTRING_1(L, S) S

#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A ## B

#define APPLY(F, ...) F(__VA_ARGS__)
#define IDENTITY(...) __VA_ARGS__
#define K_IDENTITY

#define IS_SPLIT(...) IS_SPLIT_1(IDENTITY __VA_ARGS__)
#define IS_SPLIT_1(...) IS_SPLIT_2(__VA_ARGS__, _1, _0, _)
#define IS_SPLIT_2(_X, _Y, R, ...) R

我真的建议为此编写两个单独的宏,就像在C中为两个标志编写两个不同名称的函数一样(我宁愿编写宏来明确地告诉您它们的级别,比如
ERROR(…)
WARNING(…)
等等,而不是引入默认参数。)

也就是说,有两种可能实现你想要的

C11
\u通用
选择
\u Generic
关键字是在C11中引入的。它允许根据参数的类型以类似开关的方式展开宏;罗伯特·甘博有一个好朋友

您需要区分两种情况:第一个参数是字符串,第一个参数是整数。缺点是,在
\u Generic
中,字符串文字不被视为
char*
const char*
,而是被视为
char[size]
。例如,
“%d”
是一个
字符[3]

在您的例子中,我们可以通过将字符串视为非整数的任何内容来解决这个问题。编译器稍后将对所有非字符串、非整数参数进行排序。因此:

#define PRINT(fmt, ...)                              \
    _Generic(fmt,                                    \
        int: syslog(fmt, __VA_ARGS__),               \
        default: syslog(3, fmt, __VA_ARGS__))
缺点是:不能进行单参数调用,因为这样会在调用中留下逗号。(gcc绕过了这一点。)而
\u Generic
关键字尚未得到广泛实施;此解决方案将使您的代码非常不可移植

字符串内省黑客 普通C99宏没有关于其类型的信息。然而,C代码可以做出猜测。下面是一个检查宏参数是否为字符串文字的示例:

#define PRINT(sev, ...)                            \
    if (#sev[0] == '"') syslog(3, sev, __VA_ARGS); \
    else syslog(sev, __VA_ARGS__);
这几乎奏效了。编译器可能会编译掉常量条件,并且只为其中一个分支编译代码。但它无论如何都会解析分支,而死分支将具有错误的函数签名,这将生成警告

您可以通过用C编写可变前端函数来解决此问题。下面是一个有效的示例:

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#define HEAD(X, ...) X
#define STR_(x) #x
#define STR(x) STR_(x)

#define PRINT(...) \
    msg(*STR(HEAD(__VA_ARGS__)) == '"', __VA_ARGS__)

int msg(int dflt, ...)
{
    va_list va;
    int sev = 3;
    const char *fmt;

    va_start(va, dflt);
    if (!dflt) sev = va_arg(va, int);
    fmt = va_arg(va, const char *);

    fprintf(stderr, "[%d] ", sev);
    vfprintf(stderr, fmt, va);
    fprintf(stderr, "\n");

    va_end(va);

    return 0;
}

int main()
{
    PRINT(1, "Incompatible types %s and %s", "Apple", "Orange");
    PRINT("Microphone test: %d, %d, %d, ...", 1, 2, 3);

    return 0;
}
#包括
#包括
#包括
#定义头部(X,…)X
#定义STR_ux(x)#x
#定义STR(x)STR_ux
#定义打印(…)\
msg(*STR(HEAD(_VA_ARGS__))=='''”,_VA_ARGS__)
int msg(int dflt,…)
{
va_列表va;
int-sev=3;
常量字符*fmt;
va_启动(va,dflt);
如果(!dflt)sev=va_arg(va,int);
fmt=va_arg(va,const char*);
fprintf(标准字符,[%d]”,sev);
vfprintf(标准、fmt、va);
fprintf(标准格式,“\n”);
va_端(va);
返回0;
}
int main()
{
打印(1,“不兼容类型%s和%s”、“苹果”、“橙色”);
打印(“麦克风测试:%d,%d,%d,…”,1、2、3);
返回0;
}
此解决方案是危险的,因为
msg
函数只有在由宏生成时才是安全的。并且,只有当格式字符串是以双引号开头的字符串文字时,宏才是安全的。宏将参数向左扩展一个布尔参数,并在可变参数列表中隐藏参数不兼容。


这可能是一个很好的技巧,但最好使用单独的、名称明确的宏。

不。在C@FUZxxl中,条件宏扩展是不可能的,有一些技巧是可以的。可能重复whoa,这对我来说是[聪明和]新闻,这些的来源?@Luis Clearify?所有必需的来源(对于本例)是的,我只是好奇你是从什么地方得到的,还是你自己的发明。@Luis这个问题他们是即兴的,尽管技术非常有名。
is\u SPLIT
基于著名的
NARGS
宏;我甚至不知道最初是谁提出的。
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#define HEAD(X, ...) X
#define STR_(x) #x
#define STR(x) STR_(x)

#define PRINT(...) \
    msg(*STR(HEAD(__VA_ARGS__)) == '"', __VA_ARGS__)

int msg(int dflt, ...)
{
    va_list va;
    int sev = 3;
    const char *fmt;

    va_start(va, dflt);
    if (!dflt) sev = va_arg(va, int);
    fmt = va_arg(va, const char *);

    fprintf(stderr, "[%d] ", sev);
    vfprintf(stderr, fmt, va);
    fprintf(stderr, "\n");

    va_end(va);

    return 0;
}

int main()
{
    PRINT(1, "Incompatible types %s and %s", "Apple", "Orange");
    PRINT("Microphone test: %d, %d, %d, ...", 1, 2, 3);

    return 0;
}