digitalmars.D.learn - Is this meant to be possible?
- ted (63/63) Feb 17 2014 I've been using D (at a primitive level) for a while now at work (a larg...
- Adam D. Ruppe (24/28) Feb 17 2014 This will cause the linker problem because templates cannot be
- ted (5/41) Feb 17 2014 Fantastic !! (thanks !)
- Adam D. Ruppe (4/8) Feb 19 2014 Yeah, that's good as long as you have a no-arg constructor on the
I've been using D (at a primitive level) for a while now at work (a large test-harness that exercises our main code. The harness launches and monitors multiple processes with multiple threads and performs actions on those processes to ensure correct behaviour). I am wanting to stretch my 'D' wings. I'm having some problems with trying to do the following (reduced case) - and I'm wanting to know if D will even (ever) allow it. Main reason is that I'm porting 'Ash' - a component/entity system written in actionscript, which uses this type of construct. The key issue is that if the line in main() is uncommented - a linker error occurs. Clearly, there is a 'visibility' issue of 'provider' regarding 'TestClass'. However, even if I add 'import main:TestClass' into module 'provider' - (and introduce a compile-time circularity that I absolutely do not want) - it still causes the link error - I guess the necessary information is lost through the IProvider interface. ------------------------------------- module IProvider; public interface IProvider { string providedType(); T createInstance(T)(); } ------------------------------------- module provider; import IProvider; public class Provider(T): IProvider { string providedType() { return T.classinfo.stringof;} public T createInstance(T)() { return new T; } } ------------------------------------- module manager; import provider; import IProvider; public class Manager { private { IProvider[ClassInfo] mProviders; } public void add(T)() { mProviders[T.classinfo] = new Provider!T(); } public IProvider get(T)() { if ( T.classinfo in mProviders ) return mProviders[ T.classinfo ]; else return null; } } ------------------------------------- import manager; import IProvider; import std.stdio; void main() { auto mgr = new Manager(); mgr.add!TestClass(); IProvider provider = mgr.get!TestClass(); writeln("managed type: ", provider.providedType); //auto tmp = provider.createInstance!TestClass(); // Linker error if above line is uncommented } class TestClass { public int value; } -------------------------------------
Feb 17 2014
On Tuesday, 18 February 2014 at 00:31:22 UTC, ted wrote:public interface IProvider { string providedType(); T createInstance(T)();This will cause the linker problem because templates cannot be virtual. This is declaring a final method in the interface that is never implemented. The reason they can't be virtual is that an interface consists of an array of function pointers. Since templates might form multiple functions based on their compile-time arguments, the compiler can't know how many slots to reserve in that array for it. What you can do is something like this: interface IProvider { // this is a final method with an implementation right here T createInstance(T)() { auto i = cast(T) createDynamicInstance(typeid(T)); if(i is null) throw new Exception("Couldn't create " ~ T.stringof); return i; } string providedType(); // virtual function Object createDynamicInstance(ClassInfo type); // virtual } Then in the class, implement createDynamicInstance based on the classinfo instead of the template. Your other code for add and get should continue to work.
Feb 17 2014
Fantastic !! (thanks !) so is: createDynamicInstance(ClassInfo type) { return type.create(); } the correct implementation ?? - it certainly seems to work... Adam D. Ruppe wrote:On Tuesday, 18 February 2014 at 00:31:22 UTC, ted wrote:public interface IProvider { string providedType(); T createInstance(T)();This will cause the linker problem because templates cannot be virtual. This is declaring a final method in the interface that is never implemented. The reason they can't be virtual is that an interface consists of an array of function pointers. Since templates might form multiple functions based on their compile-time arguments, the compiler can't know how many slots to reserve in that array for it. What you can do is something like this: interface IProvider { // this is a final method with an implementation right here T createInstance(T)() { auto i = cast(T) createDynamicInstance(typeid(T)); if(i is null) throw new Exception("Couldn't create " ~ T.stringof); return i; } string providedType(); // virtual function Object createDynamicInstance(ClassInfo type); // virtual } Then in the class, implement createDynamicInstance based on the classinfo instead of the template. Your other code for add and get should continue to work.
Feb 17 2014
On Tuesday, 18 February 2014 at 00:59:24 UTC, ted wrote:so is: createDynamicInstance(ClassInfo type) { return type.create(); } the correct implementation ?? - it certainly seems to work...Yeah, that's good as long as you have a no-arg constructor on the class. (the create function never passes arguments to the constructor)
Feb 19 2014