使用Object.factory而无需转换为特定类型

时间:2013-12-08 07:56:30

标签: d

我正在尝试为Rails ActionDispatch路由器创建一个类似的路由器,它允许您定义类似于

的路由
map.get "/foo", :controller => "Foo", :action => "index"

然后将GET /foo路由到FooController#index。使用此结构,您可以使用

之类的方法
map.resources :foos

将调用类似

的方法
map.get "/foo", :controller => "Foo", :action => "index"
map.get "/foo/:id", :controller => "Foo", :action => "show"

等等。

在D中,我已经能够找出完成这项工作所需的许多反身代码,但不是全部。在Ruby中,我可以做到:

class Foo
  def bar
    "FOOO BAR!"
  end
end

f = Object.const_get("Foo")
f.new.__send__(:bar) #=> "FOOO BAR!"

我试图将其翻译为

module foo;
import std.stdio;

class Foo {
  void bar() {
    writeln("FOO BAR!");
  }
}

void main() {
  auto foo = Object.factory("foo.Foo");
  __traits(getMember, foo, "bar");
}

但这不起作用,因为编译器不知道foo是什么类型,因此在编译期间对#bar的调用失败。我看到Object.factory使用的所有地方都将它们转换为特定类型,所以

module foo;
import std.stdio;

class Foo {
  void bar() {
    writeln("FOO BAR!");
  }
}

void main() {
  auto foo = cast(Foo) Object.factory("foo.Foo");
  __traits(getMember, foo, "bar");
}

会工作得很好。但是,如果我知道我想要将对象转换为使用Object.factory的好处吗?这对我来说没有任何意义!

更新2 我修复了编译器问题,但现在它在运行时崩溃,说无法找到方法

module foo;
import std.stdio;

class MyDynamic {
  void call(C, T...)(C instance, string method, T args) {
    foreach(member; __traits(allMembers, C)) {
      writeln(member);
      if (member == method) {
        static if (__traits(compiles, __traits(getMember, instance, member)(args))) {
          __traits(getMember, instance, member)(args);
        }
        return;
      }
    }

    assert(0, "No method found");
  }
}

class Foo : MyDynamic {
  void bar() {
    writeln("FOO BAR!");
  }
}

void main() {
  auto foo = cast(MyDynamic) Object.factory("foo.Foo");
  assert(foo !is null);

  foo.call(foo, "bar");
}

更新对于现在提出此问题的任何人,您可以在此处查看我的最终解决方案:https://github.com/jaredonline/action-pack

1 个答案:

答案 0 :(得分:5)

我这样做的方法是建立自己的工厂功能和动态调度。使用__traits(allMembers),遍历所有支持的类并获取方法列表。编写一个包装器模板,它接受泛型参数并将它们转换为函数所需的参数。在关联数组中存储对包装函数的引用,或者在接口中使用调度方法来调用它。

当需要完成工作时,创建类(使用自己的包装器,或者也可以使用Object.factory将其转换为动态调度函数的通用接口),然后使用动态函数。如下所示:

// IMPORTANT: Object.factory needs a full name - includes the module and class name!
auto foo = cast(MyDynamic) Object.factory("mymodule.Foo");
assert(foo !is null); // Object.factory can return null if it didn't find the class
// and cast can also return null if it wasn't actually of that interface type, so gotta check

foo.call("my_method", ["arg", "arg2", ...]);

我使用完整示例更新了此链接,如果您在顶部没有看到module dynamicstuff;则刷新:

http://arsdnet.net/dcode/test46.d

循环allMembers,根据运行时字符串调用。通过循环ModuleInfo,获取实现接口的所有类的列表也是可能的。请参阅示例文件的底部以获取执行此操作的功能。

我的web.d这样做是为了从网络上自动调用功能。冗长,凌乱的代码,但它做了很多。这是包装函数: https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/web.d#L2538

请注意使用std.traits中的ParameterTypeTuple!func。

我在这里发表了很多评论http://arsdnet.net/dcode/test46.d,希望他们能回答你的问题。该示例简要说明:

  • 使用__traits(MyDynamicImplementation)
  • 编译时间反射
  • 使用ModuleInfo和ClassInfo(getAllDynamicClasses)
  • 运行时反射
  • 用户定义的属性(isDynamicallyAvailable)
  • 使用动态数据调用方法(MyDynamicImplementation,如果您感兴趣,可以使用ReturnType,to,ParameterTypeTuple和Variant的注释代码)
  • 多重继承的替代方案,使用接口和mixin模板。

你不一定要使用所有这些东西,但我想我会触及所有这些,因为这些对于这些url路由任务都非常有用。