Signature restriction for generic types

struct S(int a, int b) { } void fun(T)(T t) { } 

I want fun work only with S What will the signature restriction look like?

I can not make fun member of S , but with void fun(T)(T t) if(is(T : S)) { } I get Error: struct t1.S(int a,int b) is used as a type

+4
source share
3 answers

S not a type. This is a template for a type. S!(5, 4) is a type. It is possible that different instances of S generate a completely different code, so the definition of S!(5, 4) may be completely different from S!(2, 5) . For example, S may be

 struct S(int a, int b) { static if(a > 3) string foo; static if(b == 4) int boz = 17; else float boz = 2.1; } 

Note that the number and types of member variables differ so that you cannot use S!(5, 4) instead of S!(2, 5) . They could also be structures with the names U and V , which were not subjected to standardization in general for all relations that they really have to each other.

Now, different instances of a particular template are usually similar to their API (or they probably would not be executed with the same template), but from the point of view of the compiler, they have nothing to do with each other. Thus, the normal way to deal with this is to use restrictions only on the type API, and not on its name or in which template it was created.

So, if you expect S to have the functions foo , bar and foozle , and you want your fun use these functions, you will create a constraint that checks that the type provided by fun has these functions and works as expected . For instance,

 void fun(T)(T t) if(is({ auto a = t.foo(); t.bar(a); int i = t.foozle("hello", 22);})) {} 

Then any type that has a function called foo that returns a value with the name bar , which may or may not return a value that accepts the result foo , and a function called foozle that takes string and int , and returns int , compiles with fun . So fun much more flexible than if you insisted that it only accepts S instances. In most cases, such restrictions are highlighted in separate templates of the same name (for example, isForwardRange or isDynamicArray ) instead of putting the source code in is expression so that they are reused (and more convenient for the user), but such expressions are what are of the same name patterns are used internally.

Now, if you really insist on the fun constraint so that it only works with S instances, then there are two options that I know of.

1. Add a declaration of some type that S always has, and you do not expect any other type to have. For instance,

 struct S(int a, int b) { enum isS = true; } void fun(T)(T t) if(is(typeof(T.isS))) {} 

Please note that the actual value of the declaration does not matter (and does not have its own type). It is a simple fact that it exists for testing.

2. A more elegant (but much less obvious solution) is as follows:

 struct S(int a, int b) { } void fun(T)(T t) if(is(T u : S!(i, j), int i, int j)) {} 

is expressions tend to border on voodoo when they get very complex, but the comma version is exactly what you need. T u is the type that you are testing and the identifier; : S!(i, j) provides a template specialization in which you want T be an instance; and the rest are TemplateParameterList , declaring characters that are used in the material on the left, but which were not previously declared - in this case, i and j .

+10
source

I think the other answers have some little red herring. You can use pattern matching to determine if T is some instance of S, as follows.

The easiest way is to simply combine the template with the argument itself:

 void fun(int a, int b)(S!(a, b) t) { } 

More generally, you can map patterns when split within a template constraint:

 void fun(T)(T t) if (is(TU == S!(a, b), int a, int b)) { } 

In both cases, you have access to the instantiation arguments.

+7
source

“Working with S only” does not really make sense in D, because S not a type, it's a pattern.

The template itself is "something" in D, unlike other languages.

What you wrote is a string for:

 template S(int a, int b) { struct S { } } 

So the full name is of type S(a, b).S , for any a or b that you use. There is no way to make this a “generic” reference to S

If you need to put a restriction like this, I suggest putting something personal inside S and checking that T has the same element.

+3
source

All Articles