C预处理程序宏不解析逗号分隔的标记吗? [英] C preprocessor macro doesn't parse comma separated tokens?

查看:84
本文介绍了C预处理程序宏不解析逗号分隔的标记吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想根据参数的数量选择两个函数之一:

I want to choose one of two functions depending on the number of arguments:

  • nargs = 0 ----> f1
  • nargs > 0 ----> f2.
  • nargs = 0 ----> f1
  • nargs > 0 ----> f2.

宏执行以下操作:获取第一个参数,然后,如果未提供任何参数,它将添加两个逗号",NULL,NULL".然后它将从返回的参数列表中选择第二个参数.

Macros do the following: get the first argument, then if no argument supplied ,it would add two commas ",NULL,NULL". Then it would select the second argument from the returned list of arguments.

例如:

  • f("Hello, world%i%s", x , s) ---->"Hello, world%i%s" ----> void
  • f() ----> ,NULL,NULL ----> NULL
  • f("Hello, world%i%s", x , s) ----> "Hello, world%i%s" ----> void
  • f() ----> ,NULL,NULL ----> NULL

所以我可以根据参数的数量获得null或void.

so I can get null or void depending the on number of arguments.

这是宏:

#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宏的
  • 输出:

    • output for FF macro:

      • FF() ----> ,((void*)0),((void*)0);
      • FF("Hello, world%i%s") ----> COMMA_IF_PARENS "Hello, world%i%s" ();
      • 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() ---> void
      • FX("Hello, world%i%s") ----> void

      预期的FX输出:

      • FX() ----> NULL
      • FX("Hello, world%i%s") ----> void
      • FX() ----> NULL
      • FX("Hello, world%i%s") ----> void

      问题是从CHOOSE返回的,NULL,NULL被视为单个参数!

      The problem is that ,NULL,NULL which is returned from CHOOSE is treated as a single parameter!

      问题:

      1. 为什么C预处理程序将,NULL,NULL视为单个参数?
      2. 如何使C预处理器将CHOOSE的结果作为用逗号分隔而不是单个参数的参数列表?
      1. Why does C preprocessor treat ,NULL,NULL as a single argument?
      2. How to make C preprocessor treat the result from CHOOSE as a list of arguments separated by comma rather than a single parameter?

      注意:

      • 我想知道为什么C预处理器不能按我期望的那样工作.

      推荐答案

      在我看来,您正在将直觉从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编码使用这种语言,并且您习惯了这些内部表达式求值,并将结果值传递给函数.但这不是宏的工作原理.

      It sounds to me like you're carrying over intuitions from the C language itself back to the C preprocessor, and those intuitions are biting you because the CPP doesn't work the same way. Generically in C, functions take typed values as arguments. Expressions are not typed values; they get evaluated to give those things. So what you wind up with when you chain things is a type of inner-out evaluation; and this shapes your intuitions. For example, in evaluating f(g(h(),h()),m()), f is passed two arguments, but it can't do anything with g(h(),h()); that has to be evaluated, and the result is a value, and that's the argument passed to f. Say h returns 1, m returns 7, g returns a sum, and f a product. Then g evaluates on the values 1 and 1. f evaluates on the values 2 and 7. Most of C coding uses this language, and you get used to the idea that these inner expressions evaluate, and the resulting values get passed to the functions. But that's not how macros work.

      在宏调用的怪异世界中(谨慎地措辞;我有意忽略条件指令),您的函数不会采用类型化的值;他们采用令牌序列. CPP确实为您匹配了括号,这意味着F(())是使用参数()调用的F,而不是使用参数(后跟)标记的调用.但是在宏区域中,F(G(H(),H()),M())用两个参数调用F.参数1是令牌序列G(H(),H());参数2是令牌序列M().我们不评估表达式G以获得类型化的值,因为没有类型化的值.只有令牌序列.

      In the weird world of macro invocations (phrased carefully; I'm intentionally ignoring conditional directives), your functions don't take typed values; they take token sequences. The CPP does match parentheses for you, meaning F(()) is an invocation of F with the argument (), as opposed to an invocation with the argument ( followed by a ) token. But in macro land, F(G(H(),H()),M()) invokes F with two arguments. Argument 1 is the token sequence G(H(),H()); and argument 2 is the token sequence M(). We don't evaluate the expression G to get a typed value, because there aren't typed values; there's only token sequences.

      针对宏之类的函数的宏调用步骤始于(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,涂成蓝色").

      The steps of macro invocation for a function like macro begins with (6.10.3.1) argument substitution (a.s.). During a.s., the CPP looks first at the definition of the macro being called, and notes where the macro's parameters are mentioned in its replacement list. For any such mentions that are not being stringified, and not participating in a paste, the CPP evaluates the corresponding argument, and its evaluated result replaces these qualifying mentions of the parameter in the replacement list. Next, the CPP stringifies (6.10.3.2) and pastes (6.10.3.3) in no particular order. Once all of that is done, the resulting replacement list (6.10.3.4) undergoes rescan and further replacement (r.a.f.r) where it is, as the name suggests, rescanned for further replacements; during this rescanning the particular macro is temporarily disabled ("painted blue", as per 6.10.3.4p2).

      因此,让我们逐步了解一下;我会忽略这样一个事实,即您使用的是语言扩展(gcc?clang?),其中您在调用的可变参数宏中使用的参数数量不足(无论如何,您并不是故意这样做的).我们从以下内容开始:

      So let's walk through this; I'll ignore the fact that you're using a language extension (gcc? clang?) where you're invoking a variadic macro with an insufficient number of arguments (you're not doing that intentionally anyway). We start with:

      FX()
      

      这将调用FX,它的单个参数是一个空的令牌列表(请注意,对于CPP,零参数仅在您使用零参数定义宏时才有意义; F()仅使用空参数被调用因为F(,)被两个空的调用).所以然后发生,这将FX的替换列表从...转换为:

      That invokes FX, with a single argument that is an empty token list (note that to the CPP, zero arguments only make sense if you define the macro with zero parameters; F() is called with an empty argument just as F(,) is called with two empty ones). So then a.s. happens, which transforms FX's replacement list from this... to this:

      RM_FIRST (FF(__VA_ARGS__) ())  => RM_FIRST (FF() ())
      

      跳过字符串化/粘贴,因为没有,所以我们做r.a.f.r.这会将RM_FIRST识别为宏. RM_FIRST具有一个参数:FF() ().因此,我们进入了递归级别2 ...调用RM_FIRST.

      Skipping stringification/pastes since there are none, we then do r.a.f.r. That recognizes RM_FIRST as a macro. RM_FIRST has one argument: FF() (). So we jump into a recursion level 2... invoking RM_FIRST.

      RM_FIRST本身的调用以a.s开始.假设可变参数部分被视为空,我们将参数x与FF() ()关联,但这是您的直觉真正失败的地方.替换列表中未提及x,因此FF() ()没有任何反应.那是一个为你.按照适用于任何扩展名的__VA_ARGS__对待它,就好像它是空的一样,我们得到以下信息:

      That invocation of RM_FIRST itself begins with a.s. Assuming the variadic part is treated as empty, we have the parameter x associated with FF() (), but here's where your intuition really fails. x isn't mentioned in the replacement list, so nothing happens to FF() (). That's a.s. for you. Treating as per whatever extension applies __VA_ARGS__ as if it's empty, we just get this:

      __VA_ARGS__ => 
      

      ... IOW,这里什么也没有了.这样我们就完成了.

      ...IOW, there's nothing there any more. So we're basically done.

      我猜你是在用C函数实现这种功能;这样做时,您期望FF() ()进行求值,并将结果作为参数传递给RM_FIRST,但这不是宏求值的方式.

      I'm guessing you were C-function-intuiting this; in doing so, you were expecting FF() () to evaluate, and the result to be passed into RM_FIRST as an argument, but that's not how macros evaluate.

      但是,您可以通过间接操作使它发生.如果您改为这样做:

      You can, however, get that to happen with indirection. If you did this instead:

      #define RM_FIRST(...) RM_FIRST_I(__VA_ARGS__)
      #define RM_FIRST_I(x,...) __VA_ARGS__
      

      ...然后回溯到调用RM_FIRST时,我们有一个不同的故事.在这里,FF() ()是可变参数列表的一部分,并且提到了__VA_ARGS__.所以在那个步骤,我们将得到:

      ...and we go back to when RM_FIRST is invoked, we have a different story. Here, FF() () is part of your variadic list, and __VA_ARGS__ is mentioned. So at that a.s. step, we would get:

      RM_FIRST_I(__VA_ARGS__) => RM_FIRST_I( () () ,NULL,NULL ())
      

      (只是字面意思……我猜想多余的垃圾是您诊断的一部分;但是我很确定您知道从哪里删除多余的().)然后,在r.a.f.r.期间,我们看到RM_FIRST_I被调用,所以故事就这样了.

      (Just being literal... I'm guessing the extra litter is part of your diagnostic; but I'm pretty sure you know where to remove the redundant ()'s). Then, during r.a.f.r., we see RM_FIRST_I being invoked, and so the story goes.

      这篇关于C预处理程序宏不解析逗号分隔的标记吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆