D:查找具有特定属性的所有功能 [英] D: finding all functions with certain attribute

查看:78
本文介绍了D:查找具有特定属性的所有功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


例如:




源/程序包A / something.d:

  @sillyWalk(10)
void doSomething()
{
}




来源/程序包B / anotherThing.d:

  @sillyWalk(50)
void anotherThing()
{
}




source / main.d:

  void main()
{
for(func; / *全部@sillWalk ... * /){
...
}
}


解决方案

信不信由你,但是的,它有点...虽然它确实很hacky并且有很多漏洞。代码: http://arsdnet.net/d-walk/


将打印的运行:

 处理:模块主
处理:模块对象
处理:模块c
处理:模块attr
test2()具有sillyWalk
main()具有sillyWalk

您需要快速浏览 cd bd main.d。 查看用法。 main.d 进程中的 onEach 函数每次都找到帮助程序函数,此处仅打印名称。在 main 函数中,您会看到一个看上去很疯狂的 mixin(__ MODULE __)-这是一个很棘手的技巧


还要注意, main.d 文件的 module project.main; 排在最前面-如果模块名称只是 main ,因为它自动不带该声明,则 mixin hack将使模块混淆功能 main 。这段代码真的很脆弱!


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

  module attr; 

结构sillyWalk {int i; }

枚举isSillyWalk(alias T)= is(typeof(T)== sillyWalk);

导入std.typetuple;
别名hasSillyWalk(alias what)= anySatisfy!(isSillyWalk,__traits(getAttributes,what));
枚举hasSillyWalk(what)= false;

别名助手(alias T)= T;
别名helper(T)= T;

void allWithSillyWalk(alias a,alias onEach)(){
pragma(msg, Processing:〜a.stringof);
foreach(memberName; __traits(allMembers,a)){
//防止尝试访问私有内容等错误。
static if(__ traits(compiles,__traits(getMember,a, memberName))){
别名member = helper!(__ traits(getMember,a,memberName));

// pragma(msg,"看着"〜memberName);
import std.string;
静态if(!is(typeof(member))&& member.stringof.startsWith(模块)){
枚举mn = member.stringof [模块。长度.. $];
mixin( import〜mn〜;);
allWithSillyWalk!(mixin(mn),onEach);
}

静态if(hasSillyWalk!(member)){
onEach!member;
}
}
}
}

首先,我们有了属性定义和一些帮助程序来检测其存在。如果您以前使用过UDA,那么这里没有什么真正的新鲜事物-只需扫描属性元组以查找我们感兴趣的类型。


helper 模板是避免重复调用 __ traits(getMember)的技巧-它只是将其别名为更好的名称,同时避免了编译器中的愚蠢的解析错误。 allMembers (如果您不熟悉此内容,请细读我的D Cookbook的示例章节 https://www.packtpub.com/application-development/d-cookbook -免费示例链接是有关编译时间反射的章节)


接下来,第一个 static if 只是确保我们可以实际获得该成员我们想要得到。否则,尝试获取自动导入的 object 模块的私有成员会引发错误。


该函数的结尾是也很简单-只需在每个元素上调用 onEach 即可。但是中间的地方是魔术:如果它检测到在步行中导入的模块(太糟糕了,但我唯一知道的方法),它将在这里导入,并通过 mixin访问它(模块)技巧在顶层使用...因此可以遍历程序的导入图。


如果玩转,您会发现它实际上有点作品。 (为了获得最佳效果,请在命令行上将所有这些文件一起编译: dmd main.d attr.d bd cd


但是它也有很多限制:



  • 可以进入类/结构成员,但此处未实现。但是,这非常简单:如果成员是一个类,则也可以递归地进入它。



  • 如果模块共享一个带有成员的名称,例如上面提到的 main 的示例。也可以通过使用带有一些包点的唯一模块名称来解决,这应该没问题。



  • 它不会降级为函数本地导入,这意味着可以在程序中使用该技巧无法使用的功能。即使您愿意使用该语言中的所有技巧,我今天也不知道D中有任何解决方案。



  • 使用UDA添加代码总是很棘手,但在这里要加倍,因为 onEach 是具有作用域的函数。您也许可以在这些事件的处理程序中建立委托的全局关联数组: void委托()[字符串]处理程序; / * ... * / handlers [memberName] =& localHandlerForThis; 这种东西,用于在运行时访问信息。



  • 我相信它也将无法在更复杂的内容上进行编译,我现在只是将其打成一个玩具,以此作为概念证明。




大多数D代码不是试图像这样遍历导入树,只是要求您 mixin UdaHandler!T; 单独或使用它的模块,例如 mixin RegisterSerializableClass!MyClass; 每一个。也许不是超级DRY,而是更可靠。


编辑:
最初编写答案时,我没有注意到另一个错误: module b.d;模块。并没有真正被捡起。将其重命名为模块b;。


ooooh cuz,它被视为 package mod,在stringof ....中,没有成员。也许编译器只是将其称为 module foo.bar。而不是 package foo我们会做生意。 (当然,这对于应用程序编写者来说并不实用...这有点破坏了该技巧的实用性)


Is it currently possible to scan/query/iterate all functions (or classes) with some attribute across modules?

For example:


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 ... */) {
        ...
    }
}

解决方案

Believe it or not, but yes, it kinda is... though it is REALLY hacky and has a lot of holes. Code: http://arsdnet.net/d-walk/

Running that will print:

Processing: module main
Processing: module object
Processing: module c
Processing: module attr
test2() has sillyWalk
main() has sillyWalk

You'll want to take a quick look at c.d, b.d, and main.d to see the usage. The onEach function in main.d processes each hit the helper function finds, here just printing the name. In the main function, you'll see a crazy looking mixin(__MODULE__) - this is a hacky trick to get a reference to the current module as a starting point for our iteration.

Also notice that the main.d file has a module project.main; line up top - if the module name was just main as it is automatically without that declaration, the mixin hack would confuse the module for the function main. This code is really brittle!

Now, direct your attention to 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;
            }
        }
    }
}

First, we have the attribute definition and some helpers to detect its presence. If you've used UDAs before, nothing really new here - just scanning the attributes tuple for the type we're interested in.

The helper templates are a trick to abbreviate repeated calls to __traits(getMember) - it just aliases it to a nicer name while avoiding a silly parse error in the compiler.

Finally, we have the meat of the walker. It loops over allMembers, D's compile time reflection's workhorse (if you aren't familiar with this, take a gander at the sample chapter of my D Cookbook https://www.packtpub.com/application-development/d-cookbook - the "Free Sample" link is the chapter on compile time reflection)

Next, the first static if just makes sure we can actually get the member we want to get. Without that, it would throw errors on trying to get private members of the automatically imported object module.

The end of the function is simple too - it just calls our onEach thing on each element. But the middle is where the magic is: if it detects a module (sooo hacky btw but only way I know to do it) import in the walk, it imports it here, gaining access to it via the mixin(module) trick used at the top level... thus recursing through the program's import graph.

If you play around, you'll see it actually kinda works. (Compile all those files together on the command line btw for best results: dmd main.d attr.d b.d c.d)

But it also has a number of limitations:

  • Going into class/struct members is possible, but not implemented here. Pretty straightforward though: if the member is a class, just descend into it recursively too.

  • It is liable to break if a module shares a name with a member, such as the example with main mentioned above. Work around by using unique module names with some package dots too, should be ok.

  • It will not descend into function-local imports, meaning it is possible to use a function in the program that will not be picked up by this trick. I'm not aware of any solution to this in D today, not even if you're willing to use every hack in the language.

  • Adding code with UDAs is always tricky, but doubly so here because the onEach is a function with its on scope. You could perhaps build up a global associative array of delegates into handlers for the things though: void delegate()[string] handlers; /* ... */ handlers[memberName] = &localHandlerForThis; kind of thing for runtime access to the information.

  • I betcha it will fail to compile on more complex stuff too, I just slapped this together now as a toy proof of concept.

Most D code, instead of trying to walk the import tree like this, just demands that you mixin UdaHandler!T; in the individual aggregate or module where it is used, e.g. mixin RegisterSerializableClass!MyClass; after each one. Maybe not super DRY, but way more reliable.

edit: There's another bug I didn't notice when writing the answer originally: the "module b.d;" didn't actually get picked up. Renaming it to "module b;" works, but not when it includes the package.

ooooh cuz it is considered "package mod" in stringof.... which has no members. Maybe if the compiler just called it "module foo.bar" instead of "package foo" we'd be in business though. (of course this isn't practical for application writers... which kinda ruins the trick's usefulness at this time)

这篇关于D:查找具有特定属性的所有功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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