Use Object.factory without needing to cast to a specific type
d
Solution
The way I do this is to build my own factory function and dynamic dispatches. Using __traits(allMembers), loop through all the supported classes and get a list of methods. Write a wrapper template that takes generic arguments and converts them to the arguments the function needs. Store a reference to the wrapper function in an associative array, or have a dispatch method in your interface to call it.
When it is time to do the work, create the class (with your own wrapper, or you could also use Object.factory and cast it to some generic interface with your dynamic dispatch function), then use the dynamic function. So something like:
// 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", ...]);
I updated this link with a full example, refresh if you don't see `module dynamicstuff;` at the top:
http://arsdnet.net/dcode/test46.d
loop allMembers, invoke based on a runtime string. Getting a list of all classes that implement an interface is possible too, by looping over ModuleInfo. See the bottom of the example file for a function to do it.
My web.d does this to do automatic calling of functions from the web. Long, messy code, but it does a lot. Here's the wrapper function: https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/web.d#L2538
Note the use of ParameterTypeTuple!func from std.traits.
I put a lot of comments in here http://arsdnet.net/dcode/test46.d so hopefully they will answer your questions. That example briefly demonstrates:
- compile time reflection with __traits (MyDynamicImplementation)
- run time reflection with ModuleInfo and ClassInfo (getAllDynamicClasses)
- User-defined attributes (isDynamicallyAvailable)
- Calling a method with dynamic data (MyDynamicImplementation, uses ReturnType, to, ParameterTypeTuple, and there's commented code for Variant if you are interested)
- An alternative to multiple inheritance, using an interface and mixin template together.
You don't necessarily have to use all that stuff, but I figured I'd touch upon all of it since these can all be pretty useful for these url routing tasks.
Problem
I'm trying to create a similar router to Rails ActionDispatch router, which allows you to define a route similar to ``` map.get "/foo", :controller => "Foo", :action => "index" ``` which will then route `GET /foo` to `FooController#index`. With this structure in place you can use methods like ``` map.resources :foos ``` which will call methods like ``` map.get "/foo", :controller => "Foo", :action => "index" map.get "/foo/:id", :controller => "Foo", :action => "show" ``` and so on. In D, I've been able to figure out a lot of the reflexive code required to make this work, but not all of it. In Ruby I can do: ``` class Foo def bar "FOOO BAR!" end end f = Object.const_get("Foo") f.new.__send__(:bar) #=> "FOOO BAR!" ``` Which I've tried to translate to ``` module foo; import std.stdio; class Foo { void bar() { writeln("FOO BAR!"); } } void main() { auto foo = Object.factory("foo.Foo"); __traits(getMember, foo, "bar"); } ``` But that doesn't work because the compiler doesn't know what type `foo` is, so the call to `#bar` fails during compile. Everywhere I've seen `Object.factory` used they cast it to a specific type, so ``` 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"); } ``` would work just fine. But if I know what I want to cast the object to what good is using `Object.factory`? That doesn't make any sense to me! UPDATE 2 I've fixed compiler issues, but now it's crashing at runtime, saying it couldn't find the method ``` 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"); } ``` Update For anyone coming to this question now, you can see my final solution here: https://github.com/jaredonline/action-pack