- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试编写一个程序,其中某些函数的名称取决于具有如下宏的某个宏变量的值:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
不幸的是,宏NAME()
把它变成了
int some_function_VARIABLE(int a);
而不是
int some_function_3(int a);
所以这显然是错误的做法。幸运的是,VARIABLE 的不同可能值的数量很少,因此我可以简单地执行 #if VARIABLE == n
并分别列出所有情况,但有没有一种聪明的方法来做到这一点?
最佳答案
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
在另一个答案的评论中,Cade Roux asked为什么这需要两级间接。轻率的答案是因为这就是标准要求它工作的方式;您往往会发现您也需要字符串化运算符的等效技巧。
C99 标准的第 6.10.3 节涵盖“宏替换”,第 6.10.3.1 节涵盖“参数替换”。
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a
#
or##
preprocessing token or followed by a##
preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
在调用NAME(mine)
中,参数是“mine”;它完全扩展到“我的”;然后将其替换为替换字符串:
EVALUATOR(mine, VARIABLE)
现在发现了宏 EVALUATOR,并且参数被隔离为“我的”和“变量”;然后后者完全扩展为“3”,并替换为替换字符串:
PASTER(mine, 3)
其他规则涵盖了此操作(6.10.3.3“## 运算符”):
If, in the replacement list of a function-like macro, a parameter is immediately preceded or followed by a
##
preprocessing token, the parameter is replaced by the corresponding argument’s preprocessing token sequence; [...]For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a
##
preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.
因此,替换列表包含 x
后跟 ##
以及 ##
后跟 y
;所以我们有:
mine ## _ ## 3
消除 ##
标记并将两侧的标记连接起来,将“我的”与“_”和“3”组合起来,得到:
mine_3
这就是我们想要的结果。
<小时/>如果我们看一下原来的问题,代码是(调整为使用“mine”而不是“some_function”):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
NAME 的参数显然是“我的”,并且已完全扩展。
根据6.10.3.3的规则,我们发现:
mine ## _ ## VARIABLE
当 ##
运算符被消除时,映射到:
mine_VARIABLE
与问题中报告的完全一样。
<小时/>Is there any way do to this with the traditional C preprocessor which does not have the token pasting operator
##
?
也许,也许不是——这取决于预处理器。标准预处理器的优点之一是它具有可靠工作的功能,而预标准预处理器有不同的实现。一项要求是,当预处理器替换注释时,它不会像 ANSI 预处理器所要求的那样生成空格。 GCC (6.3.0) C 预处理器满足此要求; XCode 8.2.1 中的 Clang 预处理器没有。
当它工作时,这会完成工作(x-paste.c
):
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
请注意,fun,
和 VARIABLE
之间没有空格 - 这很重要,因为如果存在,它会被复制到输出中,最终会得到mine_ 3
作为名称,当然,这在语法上是无效的。 (现在,我可以把头发留回来吗?)
使用 GCC 6.3.0(运行 cpp -traditional x-paste.c
),我得到:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
使用 XCode 8.2.1 中的 Clang,我得到:
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
那些空间破坏了一切。我注意到两个预处理器都是正确的;不同的预标准预处理器表现出这两种行为,这使得在尝试移植代码时 token 粘贴成为一个极其烦人且不可靠的过程。带有 ##
表示法的标准从根本上简化了这一过程。
可能还有其他方法可以做到这一点。但是,这不起作用:
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC 生成:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
接近,但没有骰子。 YMMV,当然,取决于您使用的预标准预处理器。坦率地说,如果您遇到不合作的预处理器,那么安排使用标准 C 预处理器代替预标准预处理器可能会比使用标准 C 预处理器更简单(通常有一种方法可以适当配置编译器)。花费大量时间试图找出完成这项工作的方法。
关于c - 如何与 C 预处理器连接两次并展开宏,如 "arg ## _ ## MACRO"中所示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37722382/
是否可以传递带有宏触发器的字符串作为宏参数?请参阅下面的示例代码: options mprint; %let string5='%abc%def%'; %macro test(string); dat
我意识到我的代码的某个部分由看起来相似的方法组组成(就像我有多个三重奏:一个辅助函数被另外两个为程序员准备的函数调用)。我正在尝试编写一个宏来为我定义这三个函数,这样我需要做的就是调用宏。但我的尝试导
这个问题在这里已经有了答案: What can you do with Lisp macros that you can't do with first-class functions? (8 个回答
在 haxe 宏中,对于每个表达式,我们可以以 http://api.haxe.org/haxe/macro/Position.html 的形式获取它的位置。 : { file:String,
如果我评价 (def ^:macro my-defn1 #'defn) 定义了一个名为“my-defn1”的宏,我可以像使用“defn”一样使用它。 但是,如果我改为求值 (if true (de
我想知道这段代码输出背后的原因。我想不出答案。 #define f(a,b) a##b #define g(a) #a #define h(a) g(a) void main() { print
我正在尝试编写一个宏,该宏扩展为具有解构的 let 形式。我的问题是我想拥有以 let 形式定义的符号列表,包括通过解构获得的符号列表。 用例 我试图排除这种行为,例如验证: (let [a (foo
这段代码: macro FL(message) return @sprintf("%s:%d | %s", @__FILE__, @__LINE__, message) # line 2 en
此宏的目的是创建一个宏,该宏为访问关联列表的某个键提供名称。 (defmacro generate-accessor (key-symbol prefix) (let ((mac-name
在mcpp.exe --help Options available with only -@std (default) option: -@compat Expand recursive ma
鉴于: (define-syntax (test stx) (syntax-case stx () [(_ body ...) (with-syntax ([body0 (pro
Doug Hoyte 在他对 Let Over Lambda 的介绍中将 symb 函数定义为使用宏进行元编程的基本实用程序: 在剪辑中: (defun mkstr (&rest args) (w
我的代码需要两种模式,debug 和 verbose。我在头文件中将它们定义为, #define verbose TRUE #define debug TRUE 到目前为止,在我的代码中,我一直在使用
Set-macro-character 有一个名为 non-terminating-p 的可选参数。好像是用来表示读完宏字符后是否要读另一个字符,但是reader algorithm似乎忽略了这个论点
我一直在搜索,但几乎找不到关于 LibreOffice Basic 的信息 我有点习惯在 excel 中编写宏,但这次需要做一个循环,直到我到达第一个空列并且它需要在 libreoffice 中。 在
我正在尝试编写一个调用某些函数的宏。这些函数只能由宏使用,因此我将它们放在包装宏的 letfn 中。伪代码: (letfn [(fn-a [] ...) (fn-b [] ...)
我发现对于任何在 clojure.tools.macro 中编写类似 defn 的宏的人来说,这将是一个很棒的工具。图书馆:name-with-attributes功能。文档字符串说: To be u
假设: (defmacro testing (&optional var) `(list 'this 'is ,@(when (consp var) `('a 'list)))
在 SBCL 中,我可以使用以下内容获取函数的文档字符串: (documentation #'mapcar t) 但是,我不明白如何获取宏的文档字符串。例如,给定宏: (defmacro with-l
想了解 undef 和将宏定义为 0 之间的区别。谢谢。 最佳答案 #define MACRO 0 定义预处理器标记 MACRO成为文字 0 #undef MACRO 删除预处理器标记 MACRO 的
我是一名优秀的程序员,十分优秀!