www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is this meant to be possible?

reply ted <foo bar.com> writes:
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
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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
parent reply ted <foo bar.com> writes:
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
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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