www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - passing duck-typed objects and retaining full type information

reply "Adam Taylor" <adam appsorcery.com> writes:
* i apologize in advance, this is my first post -- the code 
formatting probably wont turn out so great...

I have a bunch of duck typed interfaces for "containers" similar 
to what you would find in std.range.

i.e.
template isContainer(C)
{
     enum bool isContainer = is(typeof(
     (inout int = 0)
     {
         C c = C.init;
	...					
     }));
}

template canRemoveFromContainer(C)
{
     enum bool canRemoveFromContainer = isContainer!C && is(typeof(
     (inout int = 0)
     {
         C c = C.init;
         c.remove();
     }));
}

Now what i want to do is pass some such "container" object to an 
interface function:

interface MyInterface
{
void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
}

but of coarse templates and interfaces don't get along so well.  
so...so far as I know i would need to do something like this:


interface MyInterface
{
final void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
{
filterCollisionsImpl(...);
}

void filterCollisionsImpl(...)

}


and pass something to filterCollisionsImpl that is a "proper" 
class or interface type.

So here's the problem:  many of the "duck-typed" interfaces 
cannot be converted to proper interfaces without losing 
something.  So is there ANY way to pass in
a very basic class or interface Container type and call the 
remove function on it?  Here's what i've tried:

interface Container(C)
{
...
}

template ContainerObject(C) if (isContainer!(Unqual!C)) {
     static if (is(C : Container!(ElementType!C))) {
         alias ContainerObject = C;
     } else static if (!is(Unqual!C == C)) {
         alias ContainerObject = ContainerObject!(Unqual!C);
     } else {

     	static if (__traits(compiles, { enum e = C.ValueType; })) {
           alias ValueType = C.ValueType;
	} else {
	  alias ValueType = ElementType!C;
	}
		
         class ContainerObject : Container!ValueType {
             C _container;

             this(C container) {
                 this._container = container;
             }

             static if(canRemoveFromContainer!C) {
               size_t remove()
	      {
	        return _container.remove();
	      }
             }
         }
     }
}

ContainerObject!C containerObject(C)(C container) if 
(isContainer!C) {
     static if (is(C : Container!(ElementType!C))) {
         return container;
     } else {
         return new ContainerObject!C(container);
     }
}


interface MyInterface
{
final void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
{
   auto container = containerObject(collisionCandidates);
   container.remove();  // works just fine -- have the complete 
type info at instantiation site
   filterCollisionsImpl();
}

void filterCollisionsImpl(Container!string collisionCandidates)
collisionCandidates.remove(); // error -- some type info is lost 
here, only have a Container!string which doesn't have a remove 
function.
}
Nov 11 2014
next sibling parent reply "Freddy" <Hexagonalstar64 gmail.com> writes:
On Tuesday, 11 November 2014 at 19:23:39 UTC, Adam Taylor wrote:
 * i apologize in advance, this is my first post -- the code 
 formatting probably wont turn out so great...

 I have a bunch of duck typed interfaces for "containers" 
 similar to what you would find in std.range.

 i.e.
 template isContainer(C)
 {
     enum bool isContainer = is(typeof(
     (inout int = 0)
     {
         C c = C.init;
 	...					
     }));
 }

 template canRemoveFromContainer(C)
 {
     enum bool canRemoveFromContainer = isContainer!C && 
 is(typeof(
     (inout int = 0)
     {
         C c = C.init;
         c.remove();
     }));
 }

 Now what i want to do is pass some such "container" object to 
 an interface function:

 interface MyInterface
 {
 void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
 }

 but of coarse templates and interfaces don't get along so well.
  so...so far as I know i would need to do something like this:


 interface MyInterface
 {
 final void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
 {
 filterCollisionsImpl(...);
 }

 void filterCollisionsImpl(...)

 }


 and pass something to filterCollisionsImpl that is a "proper" 
 class or interface type.

 So here's the problem:  many of the "duck-typed" interfaces 
 cannot be converted to proper interfaces without losing 
 something.  So is there ANY way to pass in
 a very basic class or interface Container type and call the 
 remove function on it?  Here's what i've tried:

 interface Container(C)
 {
 ...
 }

 template ContainerObject(C) if (isContainer!(Unqual!C)) {
     static if (is(C : Container!(ElementType!C))) {
         alias ContainerObject = C;
     } else static if (!is(Unqual!C == C)) {
         alias ContainerObject = ContainerObject!(Unqual!C);
     } else {

     	static if (__traits(compiles, { enum e = C.ValueType; })) {
           alias ValueType = C.ValueType;
 	} else {
 	  alias ValueType = ElementType!C;
 	}
 		
         class ContainerObject : Container!ValueType {
             C _container;

             this(C container) {
                 this._container = container;
             }

             static if(canRemoveFromContainer!C) {
               size_t remove()
 	      {
 	        return _container.remove();
 	      }
             }
         }
     }
 }

 ContainerObject!C containerObject(C)(C container) if 
 (isContainer!C) {
     static if (is(C : Container!(ElementType!C))) {
         return container;
     } else {
         return new ContainerObject!C(container);
     }
 }


 interface MyInterface
 {
 final void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
 {
   auto container = containerObject(collisionCandidates);
   container.remove();  // works just fine -- have the complete 
 type info at instantiation site
   filterCollisionsImpl();
 }

 void filterCollisionsImpl(Container!string collisionCandidates)
 collisionCandidates.remove(); // error -- some type info is 
 lost here, only have a Container!string which doesn't have a 
 remove function.
 }
Not entirly sure of what you asking for,but have you tried inhertance? ---- interface Base(C){ /+...+/ } interface Derived(C):Base!C{ /+...+/ } ----
Nov 11 2014
parent reply "Adam Taylor" <adam appsorcery.com> writes:
 Not entirly sure of what you asking for,but have you tried
 inhertance?
 ----
 interface Base(C){
      /+...+/
 }

 interface Derived(C):Base!C{
      /+...+/
 }
 ----
No, i'm specifically looking for a solution that is NOT inheritance based. I've been looking at solutions based on opDispatch or template mixins -- but so far haven't come up with anything.
Nov 11 2014
parent reply "Adam Taylor" <adam appsorcery.com> writes:
 No, i'm specifically looking for a solution that is NOT 
 inheritance based.  I've been looking at solutions based on 
 opDispatch or template mixins -- but so far haven't come up 
 with anything.
For example in this thread: http://forum.dlang.org/thread/mailman.410.1319536838.24802.digitalmars-d puremagic.com?page=2 Adam Ruppe has an interesting example: DynamicObject delegate(DynamicObject[] args) dynamicFunctions; DynamicObject opDispatch(string name, T...)(T t) { if(name !in dynamicFunctions) throw new MethodNotFoundException(name); DynamicObject[] args; foreach(arg; t) args ~= new DynamicObject(arg); return dynamicFunctions[name](args); }
Nov 11 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 12 November 2014 at 01:50:07 UTC, Adam Taylor wrote:
 Adam Ruppe has an interesting example:
What that does is defer the type work to runtime, so a lot of type information is lost there too. The big magic is that the wrapper object is a template, specialized for the compile time type, but once it is used it is still through an interface. This is similar to how the inputRangeObject+InputRange interface work in phobos http://dlang.org/phobos/std_range.html#inputRangeObject Plain interface, templated implementation. I don't really understand your question though....
Nov 11 2014
prev sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Tuesday, 11 November 2014 at 19:23:39 UTC, Adam Taylor wrote:
 Now what i want to do is pass some such "container" object to 
 an interface function:

 interface MyInterface
 {
 void filterCollisions(S)(S collisionCandidates)
     if(canRemoveFromContainer!S)
 }
You can't. This is one of the major restrictions of the template approach. Generics in C# allow for this, which I believe is reliant its JIT compiler. Once you introduce the need for a vTable (which interfaces do) it is no longer possible to require implementation of a "phantom" function. Templates don't exist until their use, so it is not possible to require an entry in the vTable when the number of possible combinations is infinite.
Nov 12 2014