D 使用Object.factory,无需强制转换为特定类型

D 使用Object.factory,无需强制转换为特定类型,d,D,我正在尝试创建一个类似于Rails ActionDispatch路由器的路由器,它允许您定义一个类似于 map.get "/foo", :controller => "Foo", :action => "index" 然后将GET/foo路由到FooController\index。有了这个结构,您可以使用如下方法 map.resources :foos map.get "/foo", :controller => "Foo", :action => "index"

我正在尝试创建一个类似于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"
哪一个会调用类似的方法

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
失败。我在任何地方都能看到
对象。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");
}

更新对于现在遇到这个问题的任何人,您可以在这里看到我的最终解决方案:

我这样做的方式是构建自己的工厂功能和动态调度。使用_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", ...]);
我用一个完整的例子更新了这个链接,如果没有看到
moduledynamicstuff,请刷新位于顶部:

循环所有成员,根据运行时字符串调用。通过在ModuleInfo上循环,也可以获得实现接口的所有类的列表。请参见示例文件的底部,了解执行此操作的函数

我的web.d这样做是为了从web自动调用函数。长而凌乱的代码,但它做了很多。以下是包装器函数:

注意ParameterTypeTuple的使用!来自std.traits的func

我在这里放了很多评论,希望他们能回答你的问题。这个例子简要说明:

  • 具有_特征的编译时反射(MyDynamicImplementation)
  • 使用ModuleInfo和ClassInfo(GetAllDynamicClass)的运行时反射
  • 用户定义的属性(isDynamicallyAvailable)
  • 使用动态数据调用方法(MyDynamicImplementation,使用ReturnType、to、ParameterTypeTuple,如果您感兴趣的话,还有用于Variant的注释代码)
  • 多重继承的替代方法,将接口和mixin模板一起使用

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

不过,目前我的d正在分段,我不知道为什么。“现在它在运行时崩溃,说找不到方法”这是因为您上下文中的
C
被推断为
MyDynamic
,而不是
Foo
。对于运行时反射,如果没有某种类型的
ClassInfo
帮助是不可能的。什么类型的
ClassInfo
帮助?
对象。factory
内部使用默认的D方式来进行运行时反射。我最初考虑的是通过检查给定类实例的vtbl来攻击所需的功能。然而,Adam提出的解决方案可以更好地扩展,并且是可靠的类型安全的,所以我不会继续实际的实现尝试;)@你可以看到我在做什么,我用你的例子和解释更新了我的问题,但我仍然缺少一些关键的理解。好吧,我已经解决了编译器的问题,但现在我以前也遇到过同样的问题,我需要
将对象强制转换为正确的类以使其正常工作。是否有方法获取继承自
MyDynamic
的所有类的列表?是的,我更新了此链接:刷新它,现在应该>100行。有一个完整的界面示例,反射和所有类的获取列表。我将编辑答案!非常感谢您花时间详细介绍这么多细节。我需要一段时间来完成这一切,并确保我理解,但如果我还有任何问题,我会在这里留下评论。再次感谢!