- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在开发一个使用第三方UI库的程序,其函数的格式为Vbox(void *first, ...)
。这些函数用作布局函数并接受任意数量的参数。列表的结尾由检测到的第一个空值定义。这意味着我需要记住以空结束我的列表,这是我经常做不到的事情。
因此,我创建了一些辅助宏,这些宏应该扩展到用null附加我的列表。
其形式如下:
#define UtlVbox(first, ...) Vbox(first, ##__VA_ARGS__, NULL)
##
前的
__VA_ARGS__
用于在
__VA_ARGS
为空时去掉前一个逗号。
first
):在这些情况下,用户必须显式地添加空值,因为我不能去掉
Vbox(NULL)
之后的
,
(因为
__VA_ARGS__
hack只在逗号位于
##
之前而不是之后时才起作用),所以用户必须给出显式的空值,这将导致以下扩展:
##
,这是一个有点多余,但很好。
// expand.c
void* Vbox(void* first, ...);
void* Hbox(void* first, ...);
#define UtlVbox(first, ...) Vbox(first, ##__VA_ARGS__, NULL)
#define UtlHbox(first, ...) Hbox(first, ##__VA_ARGS__, NULL)
static void* Test()
{
return UtlHbox(
Foo,
UtlVbox(
UtlHbox(Bar)));
}
Vbox(NULL, NULL)
,将得到以下输出:
# 1 "expand.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "expand.c"
void* Vbox(void* first, ...);
void* Hbox(void* first, ...);
static void* Test()
{
return Hbox(Foo, Vbox(UtlHbox(Bar), NULL), NULL);
}
gcc -E expand.c
操作的不同含义之间的冲突吗?有什么办法解决这个问题吗?
#define UtlZbox(first, ...) Zbox(first , ##__VA_ARGS__, NULL)
static void* Test()
{
return Hbox(Foo, Vbox(Zbox(Bar, NULL), NULL), NULL);
}
#define UtlVbox(first, ...) V(first,##__VA_ARGS__)
#define UtlHbox(first, ...) H(first,##__VA_ARGS__)
int main()
{
// HHH
UtlHbox(UtlHbox(UtlHbox(1)));
UtlHbox(UtlHbox(UtlHbox(2, 1)));
UtlHbox(UtlHbox(2, UtlHbox(1)));
UtlHbox(2, UtlHbox(UtlHbox(1)));
UtlHbox(3, UtlHbox(2, UtlHbox(1)));
// HHV
UtlHbox(UtlHbox(UtlVbox(1)));
UtlHbox(UtlHbox(UtlVbox(2, 1)));
UtlHbox(UtlHbox(2, UtlVbox(1)));
UtlHbox(2, UtlHbox(UtlVbox(1)));
UtlHbox(3, UtlHbox(2, UtlVbox(1)));
// HVH
UtlHbox(UtlVbox(UtlHbox(1)));
UtlHbox(UtlVbox(UtlHbox(2, 1)));
UtlHbox(UtlVbox(2, UtlHbox(1)));
UtlHbox(2, UtlVbox(UtlHbox(1)));
UtlHbox(3, UtlVbox(2, UtlHbox(1)));
// VHH
UtlVbox(UtlHbox(UtlHbox(1)));
UtlVbox(UtlHbox(UtlHbox(2, 1)));
UtlVbox(UtlHbox(2, UtlHbox(1)));
UtlVbox(2, UtlHbox(UtlHbox(1)));
UtlVbox(3, UtlHbox(2, UtlHbox(1)));
return 0;
}
##
)是所有先前失败的情况的重复,只将任何未能扩展的宏替换为UtlZbox。这样做在每一种情况下都会导致适当的扩展,除了一个UtlZbox放在另一个UtlZbox的变量参数中的情况。
最佳答案
这不是虫子,这是“蓝色颜料”。
在VC12(Visual Studio 2013)中,一切编译得很好。
只是说。。。Visual Studio的预处理器是非标准的。
我遇到了一个我不太明白的奇怪情况。
……在这里我可以帮忙。首先,让我们回顾一下预处理器如何工作的一般规则。
大纲
类似于函数的宏的扩展在许多步骤中发生,我们可以调用
参数识别
参数替换
架线粘贴
重新扫描和进一步更换
在参数标识期间,只需将形式参数与调用的参数匹配。对于可变参数宏,标准要求可变参数本身具有一个或多个调用的参数。
…作为gnu扩展(您正在使用),我们可以将变化部分映射为无参数。我要把这个称为空。注意,这与empty(和占位符标记)不同;特别是,如果我们#define FOO(x,...)
,则调用FOO(z)
将__VA_ARGS__
设置为空;相反,FOO(z,)
将其设置为空。
在参数替换期间,应用替换列表;在替换列表中,可以用调用的参数替换形式参数。在执行此操作之前,任何未进行字符串化且未参与粘贴运算符(粘贴的左侧或右侧)的调用参数都将完全展开。
紧绷和粘贴应用下一步,以任何顺序。
执行上述步骤后,在重新扫描和进一步更换步骤期间,将再次进行最终扫描。作为一个特殊规则,在扫描特定宏的过程中,不再允许扩展同一宏。这方面的标准行话是“蓝色油漆”;宏被标记(或“蓝色油漆”)用于此扩展。整个扫描完成后,宏将“未绘制”。
解释
让我们举你的第一个例子,但我要稍微改变一下:
#define UtlVbox(first, ...) Vbox(first, ##__VA_ARGS__, NULL)
#define UtlHbox(first, ...) Hbox(first, ##__VA_ARGS__, NULL)
#define foomacro Foo
UtlHbox(foomacro,UtlVbox(UtlHbox(Bar)))
foomacro
以突出显示某些内容。下面是UtlHbox调用的扩展方式。
UtlHbox
具有形式参数
first
和
...
;调用具有参数
foomacro
和
UtlVbox(UtlHbox(Bar))
。所以
first
是
foomacro
而
__VA_ARGS__
是
UtlVbox(UtlHbox(Bar))
。
Hbox(first, ##__VA_ARGS__
,空)
……因此,在扩展后,我们将first
替换为foomacro
,并将foomacro
替换为__VA_ARGS__
。后一种情况是不同的,因为在这个替换列表中,UtlVbox(UtlHbox(Bar))
是粘贴运算符的参与者(即,右手边);因此,它不会被扩展。所以我们得到这个:
Hbox(Foo, ## UtlVbox(UtlHbox(Bar)))
接下来,我们执行串接和粘贴,得到:
Hbox(Foo, UtlVbox(UtlHbox(Bar)))
接下来,我们对__VA_ARGS__
应用重新扫描和进一步替换。所以我们画蓝色,然后计算字符串。你可能已经看到你自己在这里遇到麻烦了,但为了完成我会继续。
在重新扫描和进一步替换过程中,我们发现UtlHbox
,这是另一个宏。这将产生宏UtlHbox
的第二级求值。
在第二级参数标识中,UtlVbox
为UtlVbox
;first
为空。
在第二级参数替换中,我们查看UtlHbox(Bar)
的替换列表,它是:
Vbox(first, ##__VA_ARGS__, NULL)
由于__VA_ARGS__
不是stringified或pasted,因此在替换它之前,我们对调用的参数UtlVbox
进行求值。但由于UtlHbox被涂成蓝色,我们无法将其识别为宏。同时,first
为空。所以我们得到:
UtlHbox(Bar)
在粘贴的第二个级别中,我们将一个放置标记粘贴到逗号的右侧,该标记为空;这将触发逗号省略规则的gnu扩展,因此生成的粘贴将删除逗号,我们将得到:
Vbox(UtlHbox(Bar), NULL)
在第二级重扫描和替换中,我们将__VA_ARGS__
涂成蓝色,然后再次重扫描该片。因为Vbox(UtlHbox(Bar), ## null, NULL)
仍然被涂成蓝色,所以它仍然不能被识别为宏。因为没有其他东西是宏,所以扫描完成。
所以退一级,我们得到的结论是:
Hbox(Foo, Vbox(UtlHbox(Bar), NULL))
…在继续之前,对每个进行重新扫描和替换,我们将取消绘制UtlVbox
和UtlHbox
。
解决方案
有什么办法解决这个问题吗?
好吧,请注意有两个扩展级别:一个发生在参数替换期间,另一个发生在重新扫描和替换期间。前者发生在蓝色油漆应用之前,它可以无限期地重复:
#define BRACIFY(NAME_) { NAME_ }
BRACIFY(BRACIFY(BRACIFY(BRACIFY(BRACIFY(Z)))) BRACIFY(X))
…将很高兴地扩展到:
{ { { { { Z } } } } { X } }
这看起来像你想做的。但是“参数替换”求值只在参数没有串接或粘贴时发生。因此,真正让您丧命的是gnu逗号省略特性;您对它的使用涉及将paste运算符应用于UtlVbox
;这将取消您在参数替换期间扩展的各种参数的资格。相反,它们只能在重新扫描和替换期间展开,在该阶段,宏被涂成蓝色。
所以解决方法就是避免省略逗号。对你来说,这其实很简单。让我们仔细看看:
#define UtlVbox(first, ...) Vbox(first, ##__VA_ARGS__, NULL)
#define UtlHbox(first, ...) Hbox(first, ##__VA_ARGS__, NULL)
所以你希望UtlHbox
变成__VA_ARGS__
,而UtlVbox(a)
变成Vbox(a, NULL)
。那就这么做怎么样?
#define UtlVbox(...) Vbox(__VA_ARGS__, NULL)
#define UtlHbox(...) Hbox(__VA_ARGS__, NULL)
现在这个:
UtlHbox(UtlHbox(UtlHbox(1)));
UtlHbox(UtlHbox(UtlHbox(2, 1)));
UtlHbox(UtlHbox(2, UtlHbox(1)));
UtlHbox(2, UtlHbox(UtlHbox(1)));
UtlHbox(3, UtlHbox(2, UtlHbox(1)));
UtlHbox(UtlHbox(UtlVbox(1)));
UtlHbox(UtlHbox(UtlVbox(2, 1)));
UtlHbox(UtlHbox(2, UtlVbox(1)));
UtlHbox(2, UtlHbox(UtlVbox(1)));
UtlHbox(3, UtlHbox(2, UtlVbox(1)));
UtlHbox(UtlVbox(UtlHbox(1)));
UtlHbox(UtlVbox(UtlHbox(2, 1)));
UtlHbox(UtlVbox(2, UtlHbox(1)));
UtlHbox(2, UtlVbox(UtlHbox(1)));
UtlHbox(3, UtlVbox(2, UtlHbox(1)));
UtlVbox(UtlHbox(UtlHbox(1)));
UtlVbox(UtlHbox(UtlHbox(2, 1)));
UtlVbox(UtlHbox(2, UtlHbox(1)));
UtlVbox(2, UtlHbox(UtlHbox(1)));
UtlVbox(3, UtlHbox(2, UtlHbox(1)));
…扩展到:
Hbox(Hbox(Hbox(1, NULL), NULL), NULL);
Hbox(Hbox(Hbox(2, 1, NULL), NULL), NULL);
Hbox(Hbox(2, Hbox(1, NULL), NULL), NULL);
Hbox(2, Hbox(Hbox(1, NULL), NULL), NULL);
Hbox(3, Hbox(2, Hbox(1, NULL), NULL), NULL);
Hbox(Hbox(Vbox(1, NULL), NULL), NULL);
Hbox(Hbox(Vbox(2, 1, NULL), NULL), NULL);
Hbox(Hbox(2, Vbox(1, NULL), NULL), NULL);
Hbox(2, Hbox(Vbox(1, NULL), NULL), NULL);
Hbox(3, Hbox(2, Vbox(1, NULL), NULL), NULL);
Hbox(Vbox(Hbox(1, NULL), NULL), NULL);
Hbox(Vbox(Hbox(2, 1, NULL), NULL), NULL);
Hbox(Vbox(2, Hbox(1, NULL), NULL), NULL);
Hbox(2, Vbox(Hbox(1, NULL), NULL), NULL);
Hbox(3, Vbox(2, Hbox(1, NULL), NULL), NULL);
Vbox(Hbox(Hbox(1, NULL), NULL), NULL);
Vbox(Hbox(Hbox(2, 1, NULL), NULL), NULL);
Vbox(Hbox(2, Hbox(1, NULL), NULL), NULL);
Vbox(2, Hbox(Hbox(1, NULL), NULL), NULL);
Vbox(3, Hbox(2, Hbox(1, NULL), NULL), NULL);
关于c - gcc无法扩展某些宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44183934/
我是 magento 的新手,目前我在 magento 安装期间遇到“必须加载 PHP 扩展 curl ”错误。你能帮帮我吗? 最佳答案 如果您的服务器上没有安装 curl,您可以键入以下命令之一来安
我在 macOS Mojave/macOS Big Sur/macOS Monterey/macOS Ventura 上使用最新的 php 版本 7.2 并收到类似错误 $composer requ
这个问题已经有答案了: Why generic type is not applicable for argument extends super class for both? (5 个回答) 已关
我正在使用 NightWatch.js 并进行一些 UI 测试,我想用一些额外的 desiredCapabilities 启动默认浏览器实例(即启用扩展并应用一些特定值)。 p> 注意:我可以执行这些
有人知道为什么我在 java 8 中使用此代码时没有服务器扩展名称吗: try { URL url = new URL(urlString); URLC
扩展提供给我的类(class)。为现有的类提供新功能。或扩展现有的mixin s 或虚拟类,任何东西都可以工作。 也许是这样的: class FlatButton {} // maybe no
我有一个关于使用 c 代码和 mod_wsgi 扩展 python 的问题。 我在 apache 服务器中有一个 django 应用程序,它查询 postgresql 数据库以生成报告。在某些报告中,
testcafe支持在Chrome浏览器中加载crx扩展吗? 如果是这样,请告诉我需要尝试什么方法。 我尝试了下面的代码,但没有成功 await t.eval(new Function(fs.read
这个问题已经有答案了: What is a raw type and why shouldn't we use it? (16 个回答) 已关闭 3 年前。 有什么区别: // 1 class A c
我正在编写一个 chrome 扩展来记录单击开始按钮后触发的请求。 这是我的文件:1. list .json { "manifest_version": 2, "name": "recorde
扩展是将较短的文本,例如一组提示或主题列表,输入到大型语言模型中,让模型生成更长的文本。我们可以利用这个特性让大语言模型生成基于某个主题的电子邮件或小论文。通过这种方式使用大语言模型,可以为工作与生活
我每天都在使用 vim 和 perforce 现在我的问题是,如果我想查看 perforce 文件修订版,则从命令模式下的 vim :!p4 打印文件#1 vim 试图让我获得缓冲区 #1。有没有办法
大家好,我有一个关于 NUnit 扩展(2.5.10)的问题。 我想做的是向 数据库。为此,我使用 Event 创建了 NUnit 扩展 听众。 我遇到的问题是公共(public)无效 TestFin
我有弹出窗口,而不是模态窗口。 如何通过单击页面的其他部分(不在窗口中)来关闭此窗口? 最佳答案 像这样的东西: function closeWin(e, t) { var el = win.
我通常非常谨慎地使用扩展方法。当我确实觉得有必要编写一个扩展方法时,有时我想重载该方法。我的问题是,您对调用其他扩展方法的扩展方法有何看法?不好的做法?感觉不对,但我无法真正定义原因。 例如,第二个
扩展 Ant Ant带有一组预定义的任务,但是你可以创建自己的任务,如下面的例子所示。 定制Ant 任务应扩展 org.apache.tools.ant.Task 类,同时也应该拓展 execut
我想要一个重定向所有请求的扩展: http://website.com/foo.js 到: http://localhost/myfoo.js 我无法使用主机文件将主机从 website.com 编辑
对于为什么 QChartView 放在 QTabWidget 中时会扩展,我有点迷惑。 这是 QChartView 未展开(因为它被隐藏)时应用程序的图片。 应用程序的黑色部分是 QOpenGLWid
如果在连接条件中使用 OR 运算符,如何优化以下查询以避免 SQL 调优方面的 OR 扩展? SELECT t1.A, t2.B, t1.C, t1.D, t2.E FROM t1 LEFT J
一旦加载插件的问题得到解决(在 .NET 中通过 MEF 的情况下),下一步要解决的是与它们的通信。简单的方法是实现一个接口(interface),使用插件实现,但有时插件只需要扩展应用程序的工作方式
我是一名优秀的程序员,十分优秀!