目前是否可以跨模块扫描/查询/迭代具有某些属性的所有函数(或类)?
例如:
source/packageA/something.d:
@sillyWalk(10)
void doSomething()
{
}
source/packageB/anotherThing.d:
@sillyWalk(50)
void anotherThing()
{
}
source/main.d:
void main()
{
for (func; /* All @sillWalk ... */) {
...
}
}
发布于 2014-08-29 10:24:58
信不信由你,但是的,这有点...虽然它真的很粗糙,而且有很多漏洞。代码:http://arsdnet.net/d-walk/
运行该命令将打印:
Processing: module main
Processing: module object
Processing: module c
Processing: module attr
test2() has sillyWalk
main() has sillyWalk
您将希望快速查看一下c.d
、b.d
和main.d
以了解其用法。main.d
进程中的onEach
函数找到帮助器函数找到的每个命中,这里只是打印名称。在main
函数中,您将看到一个看起来很疯狂的mixin(__MODULE__)
--这是一个获取当前模块的引用作为迭代起点的技巧。
还要注意,main.d
文件的顶部有一行module project.main;
-如果模块名称只是main
,因为它自动没有声明,那么mixin
黑客会将模块与函数main
混淆。这段代码真的很脆弱!
现在,将您的注意力转移到attr.d
:http://arsdnet.net/d-walk/attr.d
module attr;
struct sillyWalk { int i; }
enum isSillyWalk(alias T) = is(typeof(T) == sillyWalk);
import std.typetuple;
alias hasSillyWalk(alias what) = anySatisfy!(isSillyWalk, __traits(getAttributes, what));
enum hasSillyWalk(what) = false;
alias helper(alias T) = T;
alias helper(T) = T;
void allWithSillyWalk(alias a, alias onEach)() {
pragma(msg, "Processing: " ~ a.stringof);
foreach(memberName; __traits(allMembers, a)) {
// guards against errors from trying to access private stuff etc.
static if(__traits(compiles, __traits(getMember, a, memberName))) {
alias member = helper!(__traits(getMember, a, memberName));
// pragma(msg, "looking at " ~ memberName);
import std.string;
static if(!is(typeof(member)) && member.stringof.startsWith("module ")) {
enum mn = member.stringof["module ".length .. $];
mixin("import " ~ mn ~ ";");
allWithSillyWalk!(mixin(mn), onEach);
}
static if(hasSillyWalk!(member)) {
onEach!member;
}
}
}
}
首先,我们有属性定义和一些帮助器来检测它的存在。如果您以前使用过UDA,那么这里没有什么新的东西--只是扫描我们感兴趣的类型的属性元组。
helper
模板是缩短对__traits(getMember)
的重复调用的技巧-它只是将其命名为更好的名称,同时避免在编译器中出现愚蠢的解析错误。
最后,我们得到了步行者的肉。它循环在D的编译时反射的主力allMembers
上(如果你不熟悉这一点,可以看看我的D Cookbook https://www.packtpub.com/application-development/d-cookbook的示例章节--“免费示例”链接是关于编译时反射的章节)
接下来,第一个static if
只是确保我们可以实际获得我们想要获得的成员。否则,它将在尝试获取自动导入的object
模块的私有成员时抛出错误。
函数的结尾也很简单--它只是在每个元素上调用我们的onEach
。但神奇之处在中间:如果它在遍历中检测到一个模块(非常麻烦,但我只知道怎么做)导入,它就在这里导入它,通过在顶层使用的mixin(module)
技巧获得对它的访问……因此递归通过程序的导入图。
如果你尝试一下,你会发现它实际上是有效的。(在命令行btw上编译所有这些文件以获得最佳结果:dmd main.d attr.d b.d c.d
)
但它也有一些限制:
main
的例子。通过使用带有一些包点的唯一模块名称来解决这个问题,应该是可以的。onEach
是一个具有其作用域的函数。您也许可以将委托的全局关联数组构建为处理程序:用于运行时访问information.的void delegate()[string] handlers; /* ... */ handlers[memberName] = &localHandlerForThis;
类型的处理程序
大多数D代码,而不是试图像这样遍历导入树,只是要求您在使用它的单个聚合或模块中执行mixin UdaHandler!T;
,例如在每个聚合或模块之后执行mixin RegisterSerializableClass!MyClass;
。也许不是非常干燥,但更可靠。
编辑:在最初编写答案时,我没有注意到另一个bug:"module b.d;“实际上没有被选中。将其重命名为"module b;“起作用,但当它包含包时就不起作用了。
哦,因为它被认为是字符串中的"package mod“。它没有成员。也许如果编译器只把它叫做"module foo.bar“而不是"package foo”,我们就可以做生意了。(当然,这对于应用程序编写人员来说是不实用的……这有点破坏了这个技巧在这个时候的用处)
https://stackoverflow.com/questions/25555329
复制相似问题