www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How can I test at compile time whether T is an instance of an

reply wjoe <invalid example.com> writes:
I have some similar functions:

void register(C: IFoo)()
{
   _insert!C();
}

void register(C)() if (behavesLikeFoo!C)
{
   _insert!C();
}

There are more overloads with parameters so I want to merge them

void register(C, ARGS...)(ARGS args) if (behavesLikeFoo!C || 
isInstanceOf!(C, IFoo))
{
   _insert!C(args);
}

I found a lot of information on how to do this at runtime but not 
at compile time.
std.traits: isInstanceOf doesn't work. Neither did anything I 
tried with typeid etc.

The behavesLikeFoo constraint works as expected but it accepts 
any class no matter whether or not it implements the interface.
Sep 23 2020
next sibling parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 18:37:45 UTC, wjoe wrote:
 I have some similar functions:

 void register(C: IFoo)()
 {
   _insert!C();
 }

 void register(C)() if (behavesLikeFoo!C)
 {
   _insert!C();
 }

 There are more overloads with parameters so I want to merge them

 void register(C, ARGS...)(ARGS args) if (behavesLikeFoo!C || 
 isInstanceOf!(C, IFoo))
 {
   _insert!C(args);
 }

 I found a lot of information on how to do this at runtime but 
 not at compile time.
 std.traits: isInstanceOf doesn't work. Neither did anything I 
 tried with typeid etc.

 The behavesLikeFoo constraint works as expected but it accepts 
 any class no matter whether or not it implements the interface.
A class at compile time is it's own static type, OOP polymorphism is a runtime feature not compile time. You have to write your own traits for specific objects to get them to relate to each other using static overloading.
Sep 23 2020
parent reply wjoe <invalid example.com> writes:
On Wednesday, 23 September 2020 at 18:49:28 UTC, data pulverizer 
wrote:
 On Wednesday, 23 September 2020 at 18:37:45 UTC, wjoe wrote:
 [...]
A class at compile time is it's own static type, OOP polymorphism is a runtime feature not compile time. You have to write your own traits for specific objects to get them to relate to each other using static overloading.
It doesn't occur to me that the compiler doesn't know at compile time that interface IFoo{} class Foo: IFoo {} class Foo implements interface IFoo.
Sep 23 2020
parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 18:56:33 UTC, wjoe wrote:
 It doesn't occur to me that the compiler doesn't know at 
 compile time that

 interface IFoo{}
 class Foo: IFoo {}

 class Foo implements interface IFoo.
Didn't think that the compiler didn't know but wasn't aware that you could use that information to statically dispatch. My mistake, I'll shut up now!
Sep 23 2020
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Sep 23, 2020 at 07:08:47PM +0000, data pulverizer via
Digitalmars-d-learn wrote:
 On Wednesday, 23 September 2020 at 18:56:33 UTC, wjoe wrote:
 
 It doesn't occur to me that the compiler doesn't know at compile
 time that
 
 interface IFoo{}
 class Foo: IFoo {}
 
 class Foo implements interface IFoo.
Didn't think that the compiler didn't know but wasn't aware that you could use that information to statically dispatch. My mistake, I'll shut up now!
Of course the compiler knows. And of course it can use this information for static dispatch. That's why D is so awesome at metaprogramming. ;-) What the compiler *doesn't* know is whether a variable of some supertype of Foo (say Object) implements IFoo, because that's something that can only be determined at runtime (effectively, you need to downcast to Foo / IFoo and test if it's null). But that's not what the OP is asking for in this case. T -- Error: Keyboard not attached. Press F1 to continue. -- Yoon Ha Lee, CONLANG
Sep 23 2020
parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 19:16:13 UTC, H. S. Teoh wrote:
 Of course the compiler knows. And of course it can use this 
 information for static dispatch. That's why D is so awesome at 
 metaprogramming. ;-)

 What the compiler *doesn't* know is whether a variable of some 
 supertype of Foo (say Object) implements IFoo, because that's 
 something that can only be determined at runtime (effectively, 
 you need to downcast to Foo / IFoo and test if it's null).  But 
 that's not what the OP is asking for in this case.


 T
This has prompted me to write a data structure that I thought would be impossible until now. It's a data structure with a Start node with a link to next, then any number of Middle nodes with previous and next links, and an End node with a previous link all inheriting from a common interface Node. This is of course possible using runtime polymorphism, but I wanted it available for static dispatch rather than everything being listed as "Node" static type. The implementation is below and works. Interestingly when I wrote it down with a pen and paper I thought that it would lead to infinitely recursive unwriteable data type and so didn't even bother trying to implement it. But D actually forms the correct static type, it prints the gobbledygook type I expect at compile time but the actual static type at runtime. Amazing! I have been using structs with tuples because I thought this data structure was impossible! ``` import std.stdio: writeln; interface Node{} class Start: Node { Node next; } class Middle: Node { Node prev; Node next; } class End: Node { Node prev; } auto makeChain(Args...)(Args args) if(Args.length > 3) { args[0].next = args[1]; static foreach(i; 1..(Args.length - 1)) { args[i].prev = args[i - 1]; args[i].next = args[i + 1]; } args[$ - 1].prev = args[$ - 2]; return args[0]; } void main() { static const x = makeChain(new Start(), new Middle(), new Middle(), new Middle(), new Middle(), new End()); pragma(msg, "x.next: ", x.next, "\n"); writeln("x.next: ", x.next); } ``` output: ``` x.next: Middle(Start(Middle(<recursion>)), Middle(Middle(Start(Middle(<recursion>)), Middle(<recursion>)), Middle(Middle(Middle(Start(Middle(<recursion>)), Middle(<recursion>)), Middle(<recursion>)), Middle(Middle(Middle(Middle(Start(Middle(<recursion>)), Middle(<recursion>)), Middle(<recursion>)), Middle(<recursion>)), End(Middle(Middle(Middle(Middle(Start(Middle(<recursion>)), Middle(<recursion>)), Middle(<recursion>)), Middle(<recursion>)), End(<recursion>))))))) x.next: node.Middle ```
Sep 23 2020
next sibling parent data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 20:19:04 UTC, data pulverizer 
wrote:
 This has prompted me to write a data structure that I thought 
 would be impossible until now. [...SNIP...]
Here is the function with the correct template constraint: ``` auto makeChain(Args...)(Args args) if(Args.length > 2 && is(Args[0] == Start) && is(Args[Args.length - 1] == End)) { args[0].next = args[1]; static foreach(i; 1..(Args.length - 1)) { args[i].prev = args[i - 1]; args[i].next = args[i + 1]; } args[$ - 1].prev = args[$ - 2]; return args[0]; } ```
Sep 23 2020
prev sibling next sibling parent data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 20:19:04 UTC, data pulverizer 
wrote:
 This has prompted me to write a data structure that I thought 
 would be impossible until now....
False alarm: ``` writeln("typeof(x.next): ", typeof(x.next).stringof); ``` gives: ``` typeof(x.next): const(Node) ``` Oh well.
Sep 23 2020
prev sibling parent data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 20:19:04 UTC, data pulverizer 
wrote:
Thinking more about it this ...

 class Middle: Node
 {
   Node prev;
   Node next;
 }
won't work because the I've told the compiler that the static type is "Node", my original design was something like this: ``` struct Middle(P, N) { P prev; N next; } ``` which also won't work because it can't actually be instantiated (recursive), and the start and end nodes must be different types (and you can't substitute the middle node for the start and end node). It's the simplest case because there could be more than one type of middle node. More generally, correct me if I'm wrong, but I don't think these kinds of statically multityped tree structures can be formed in static languages so I've been using tuples - at least for the simple case I outlined.
Sep 24 2020
prev sibling parent reply wjoe <invalid example.com> writes:
On Wednesday, 23 September 2020 at 19:08:47 UTC, data pulverizer 
wrote:
 On Wednesday, 23 September 2020 at 18:56:33 UTC, wjoe wrote:
 [...]
Didn't think that the compiler didn't know but wasn't aware that you could use that information to statically dispatch. My mistake, I'll shut up now!
Appologies if you took offense. Your replies are very much appreciated.
Sep 23 2020
parent data pulverizer <data.pulverizer gmail.com> writes:
On Wednesday, 23 September 2020 at 19:27:13 UTC, wjoe wrote:
 Appologies if you took offense. Your replies are very much 
 appreciated.
No offense taken.
Sep 23 2020
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
Try this:

	interface I {}
	class C : I {}
	class D {}
	struct S {}

	pragma(msg, is(C : I));	// true
	pragma(msg, is(D : I));	// false
	pragma(msg, is(S : I));	// false

So probably what you want is something like this:

	void register(C, ARGS...)(ARGS args)
		if (behavesLikeFoo!C || is(C : IFoo))
		...


T

-- 
Change is inevitable, except from a vending machine.
Sep 23 2020
parent wjoe <invalid example.com> writes:
On Wednesday, 23 September 2020 at 18:50:28 UTC, H. S. Teoh wrote:
 Try this:

 	interface I {}
 	class C : I {}
 	class D {}
 	struct S {}

 	pragma(msg, is(C : I));	// true
 	pragma(msg, is(D : I));	// false
 	pragma(msg, is(S : I));	// false

 So probably what you want is something like this:

 	void register(C, ARGS...)(ARGS args)
 		if (behavesLikeFoo!C || is(C : IFoo))
 		...


 T
Yes, that's it. Thanks :)
Sep 23 2020
prev sibling parent =?UTF-8?B?T3TDoXZpbw==?= Augusto <otavio.porto fatec.sp.gov.br> writes:
On Wednesday, 23 September 2020 at 18:37:45 UTC, wjoe wrote:
 I have some similar functions:

 void register(C: IFoo)()
 {
   _insert!C();
 }

 void register(C)() if (behavesLikeFoo!C)
 {
   _insert!C();
 }

 There are more overloads with parameters so I want to merge them

 void register(C, ARGS...)(ARGS args) if (behavesLikeFoo!C || 
 isInstanceOf!(C, IFoo))
 {
   _insert!C(args);
 }

 I found a lot of information on how to do this at runtime but 
 not at compile time.
 std.traits: isInstanceOf doesn't work. Neither did anything I 
 tried with typeid etc.

 The behavesLikeFoo constraint works as expected but it accepts 
 any class no matter whether or not it implements the interface.
see if "import std.traits: isImplicitlyConvertible" helps you.
Sep 23 2020