我想根据参数数量选择两个函数之一:
nargs = 0
----> f1 nargs > 0
----> f2。宏执行以下操作:获取第一个参数,然后如果未提供任何参数,它将添加两个逗号“ ,NULL,NULL
”。然后它将从返回的参数列表中选择第二个参数。
例如:
f("Hello, world%i%s", x , s)
---->“ Hello, world%i%s
” ----> void
f()
----> ,NULL,NULL
----> NULL
所以我可以根据参数的数量获得null或void。
这是宏:
#define FIRST(a, ...) a
#define COMMA_IF_PARENS(...) ,NULL,NULL
#define IS_EMPTY(...) COMMA_IF_PARENS __VA_ARGS__ ()
#define RM_FIRST(x, ...) __VA_ARGS__
#define CHOOSE(...) IS_EMPTY(FIRST(__VA_ARGS__))
#define ff_(...)() CHOOSE (__VA_ARGS__)
#define FF(...)() ff_(__VA_ARGS__) ()
#define FX(...) RM_FIRST (FF(__VA_ARGS__) ())
输出:
FF()
----> ,((void*)0),((void*)0);
FF("Hello, world%i%s")
----> COMMA_IF_PARENS "Hello, world%i%s" ();
输出:
FX()
---> void
FX("Hello, world%i%s")
----> void
预期的FX输出:
FX()
----> NULL
FX("Hello, world%i%s")
----> void
问题是从CHOOSE返回的,NULL,NULL
被视为单个参数!
问题:
,NULL,NULL
视为单个参数?CHOOSE
的结果作为用逗号分隔而不是单个参数的参数列表?注意:
答案 0 :(得分:1)
在我看来,您正在将直觉从C语言本身传递回C预处理程序,而这些直觉正在困扰您,因为CPP的工作方式不同。通常在C语言中,函数将类型化的值作为参数。表达式不是类型化的值;他们得到评估以给予这些东西。因此,当您链接事物时,最终得到的是一种由内而外的评估。这会影响您的直觉。例如,在评估f(g(h(),h()),m())
时,向f传递了两个参数,但它对g(h(),h())
无效。必须对其求值,结果是一个值,这是传递给f的参数。假设h返回1,m返回7,g返回总和,f返回乘积。然后g评估值 1和1。f评估 values 2和7。大多数C编码使用这种语言,并且您习惯了这些内部表达式求值,并将结果值传递给函数。但这不是宏的工作原理。
在宏调用的怪异世界中(谨慎地措辞;我有意忽略条件指令),您的函数不会采用类型化的值;他们采用令牌序列。 CPP确实为您匹配了括号,这意味着F(())
是使用参数F
调用()
,而不是使用参数(
后跟{{ 1}}令牌。但是在宏区域中,)
用两个参数调用F(G(H(),H()),M())
。参数1是令牌序列F
;参数2是令牌序列G(H(),H())
。我们不评估表达式M()
以获得类型化的值,因为没有类型化的值。只有令牌序列。
针对宏之类的函数的宏调用步骤始于(6.10.3.1)参数替换(a.s.)。在A.s.中,CPP首先查看被调用宏的定义,并注意宏的参数在其替换列表中提到的位置。对于未进行串化且未参与粘贴的任何此类提及,CPP都会评估相应的自变量,并且其评估结果将替换替换列表中参数的这些合格提及。接下来,CPP将(6.10.3.2)字符串化,并以不特定的顺序粘贴(6.10.3.3)。完成所有这些操作后,对生成的替换列表(6.10.3.4)进行重新扫描,然后进行进一步替换(r.a.f.r),顾名思义,对其进行重新扫描以进行进一步替换;在此重新扫描过程中,特定的宏被暂时禁用(根据6.10.3.4p2,“涂成蓝色”)。
因此,让我们逐步了解一下;我会忽略这样一个事实,即您使用的是语言扩展(gcc?clang?),其中您在调用可变参数宏且参数数量不足(无论如何,您都没有故意这样做)。我们从以下内容开始:
G
这将调用FX()
,它的单个参数是一个空的令牌列表(请注意,对于CPP,零参数仅在您使用零参数定义宏时才有意义; FX
被调用为一个空参数,就像F()
被两个空参数调用)。所以然后发生,这将F(,)
的替换列表从...转换为:
FX
跳过字符串化/粘贴,因为没有,所以我们做r.a.f.r。这会将RM_FIRST (FF(__VA_ARGS__) ()) => RM_FIRST (FF() ())
识别为宏。 RM_FIRST
有一个参数:RM_FIRST
。因此,我们进入了递归级别2 ...调用FF() ()
。
RM_FIRST
本身的调用以a.s开始。假设可变参数部分被视为空白,我们将参数x与RM_FIRST
关联,但这就是您的直觉真正失败的地方。替换列表中未提及x,因此FF() ()
没有任何反应。那是一个为了你。按照适用于任何FF() ()
的扩展名,就好像它是空的一样,我们得到以下信息:
__VA_ARGS__
... IOW,这里什么也没有了。这样我们就完成了。
我猜你是在用C函数实现这种功能;这样做时,您期望__VA_ARGS__ =>
进行求值,并将结果作为参数传递给FF() ()
,但这不是宏求值的方式。
但是,您可以通过间接操作使它发生。如果您改为这样做:
RM_FIRST
...然后我们回到调用#define RM_FIRST(...) RM_FIRST_I(__VA_ARGS__)
#define RM_FIRST_I(x,...) __VA_ARGS__
时,我们有一个不同的故事。在这里,RM_FIRST
是可变参数列表的一部分,并且提到了FF() ()
。所以在那步骤,我们将得到:
__VA_ARGS__
(只是字面意思……我猜想多余的垃圾是您诊断的一部分;但是我很确定您知道从哪里删除多余的()。)然后,在r.a.f.r.期间,我们看到RM_FIRST_I(__VA_ARGS__) => RM_FIRST_I( () () ,NULL,NULL ())
被调用,故事就这样了。