首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >D:查找具有特定属性的所有函数

D:查找具有特定属性的所有函数
EN

Stack Overflow用户
提问于 2014-08-29 02:10:29
回答 1查看 1K关注 0票数 17

目前是否可以跨模块扫描/查询/迭代具有某些属性的所有函数(或类)?

例如:

source/packageA/something.d:

代码语言:javascript
复制
@sillyWalk(10)
void doSomething()
{
}

source/packageB/anotherThing.d:

代码语言:javascript
复制
@sillyWalk(50)
void anotherThing()
{
}

source/main.d:

代码语言:javascript
复制
void main()
{
    for (func; /* All @sillWalk ... */) {
        ...
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-29 10:24:58

信不信由你,但是的,这有点...虽然它真的很粗糙,而且有很多漏洞。代码:http://arsdnet.net/d-walk/

运行该命令将打印:

代码语言:javascript
复制
Processing: module main
Processing: module object
Processing: module c
Processing: module attr
test2() has sillyWalk
main() has sillyWalk

您将希望快速查看一下c.db.dmain.d以了解其用法。main.d进程中的onEach函数找到帮助器函数找到的每个命中,这里只是打印名称。在main函数中,您将看到一个看起来很疯狂的mixin(__MODULE__) --这是一个获取当前模块的引用作为迭代起点的技巧。

还要注意,main.d文件的顶部有一行module project.main; -如果模块名称只是main,因为它自动没有声明,那么mixin黑客会将模块与函数main混淆。这段代码真的很脆弱!

现在,将您的注意力转移到attr.dhttp://arsdnet.net/d-walk/attr.d

代码语言:javascript
复制
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的例子。通过使用带有一些包点的唯一模块名称来解决这个问题,应该是可以的。

  • It不会下降到函数本地导入,这意味着可以在程序中使用不会被此技巧拾取的函数。我现在还没有意识到在D中有任何解决方案,即使你愿意使用这门语言中的每一个技巧。

  • 使用UDA添加代码总是很棘手,但在这里更是如此,因为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”,我们就可以做生意了。(当然,这对于应用程序编写人员来说是不实用的……这有点破坏了这个技巧在这个时候的用处)

票数 38
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25555329

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档