www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template parameters that don't affect template type

reply Engine Machine <EM EM.com> writes:
I have the need, in some cases, to pass static information to a 
template class but don't want it to affect its type.

import std.algorithm, core.stdc.stdlib;
struct X(int defaultSize = 100)
{
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
}

If I do

X!100 x;
X!100 y;
X!50 z;

then I can do

x = y;

but not

x = z;

but of course D things these are completely different types. The 
type it self does not depend on the default size.

While one could argue that it can be unsafe, in the my context, 
it is not.

Is there any way to get D to understand I want do not want a 
template parameter to be part of the type comparison?

I use several parameters to pass info to the type that does not 
change affect the type itself. It prevents the "same type" from 
being used with "itself".

another example:

struct s(T1, T2)
{
     T1;
}

then

s!(int, double)

and

s!(int, float)

should really be the same type! The fact that T2 is not used is 
important!

I guess D just can't handle it though?
Aug 11 2016
next sibling parent Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a 
 template class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. 
 The type it self does not depend on the default size.

 While one could argue that it can be unsafe, in the my context, 
 it is not.

 Is there any way to get D to understand I want do not want a 
 template parameter to be part of the type comparison?

 I use several parameters to pass info to the type that does not 
 change affect the type itself. It prevents the "same type" from 
 being used with "itself".

 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used is 
 important!

 I guess D just can't handle it though?
Another good example is if something like template X(bool ClearMemory = false) { } ClearMemory would clearly not affect the type if it just clears memory that is malloc'ed, yet D would treat treat X!true from X!false.
Aug 11 2016
prev sibling next sibling parent sldkf <sldkf sldkf.fr> writes:
On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine wrote:
 Is there any way to get D to understand I want do not want a 
 template parameter to be part of the type comparison?
No. Use a standard run-time parameter. Your problem can be solved by defining a constructor.
Aug 11 2016
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 8/11/16 2:11 PM, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a template
 class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. The type it
 self does not depend on the default size.
And they should be different types. The code generated for the type is different, in this case it's important to declare these are not the same type. For example, if x = y worked, then what should x.foo(5) do?
 While one could argue that it can be unsafe, in the my context, it is not.

 Is there any way to get D to understand I want do not want a template
 parameter to be part of the type comparison?
The type is part of the template, and the instantiations are different. So no.
 I use several parameters to pass info to the type that does not change
 affect the type itself. It prevents the "same type" from being used with
 "itself".
Then define how the compiler can convert from one to another. Or redesign the type to specify the parameters at the right time.
 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used is important!
That's not how template types work. your code is shorthand for: template s(T1, T2) { struct s { T1 t1; // I assume you wanted to put a member here? } } Two different instantiations of s create two different namespaces where the structs are not the same. I'd suggest if you don't use a template parameter, don't declare it. If you use it in a member function only (as in your first example), declare it in the member function as a template parameter.
 I guess D just can't handle it though?
No, it doesn't handle incorrect assumptions very well ;) -Steve
Aug 11 2016
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 8/11/16 2:42 PM, Steven Schveighoffer wrote:
 On 8/11/16 2:11 PM, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a template
 class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. The type it
 self does not depend on the default size.
And they should be different types. The code generated for the type is different, in this case it's important to declare these are not the same type. For example, if x = y worked, then what should x.foo(5) do?
Of course, I meant x = z :) -Steve
Aug 11 2016
prev sibling next sibling parent reply Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 18:42:51 UTC, Steven Schveighoffer 
wrote:
 On 8/11/16 2:11 PM, Engine Machine wrote:
 I have the need, in some cases, to pass static information to 
 a template
 class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. 
 The type it
 self does not depend on the default size.
And they should be different types. The code generated for the type is different, in this case it's important to declare these are not the same type. For example, if x = y worked, then what should x.foo(5) do?
 While one could argue that it can be unsafe, in the my 
 context, it is not.

 Is there any way to get D to understand I want do not want a 
 template
 parameter to be part of the type comparison?
The type is part of the template, and the instantiations are different. So no.
 I use several parameters to pass info to the type that does 
 not change
 affect the type itself. It prevents the "same type" from being 
 used with
 "itself".
Then define how the compiler can convert from one to another. Or redesign the type to specify the parameters at the right time.
 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used 
 is important!
That's not how template types work. your code is shorthand for: template s(T1, T2) { struct s { T1 t1; // I assume you wanted to put a member here? } } Two different instantiations of s create two different namespaces where the structs are not the same.
I don't think this proves anything. You just rewrote the template. T2 is still not used in either case and hence the type does not depend on it. Basically your logic is something like: A = 0*5 is different than B = 0*6. But both are the same, just because they look different doesn't change that.
 I'd suggest if you don't use a template parameter, don't 
 declare it. If you use it in a member function only (as in your 
 first example), declare it in the member function as a template 
 parameter.

 I guess D just can't handle it though?
No, it doesn't handle incorrect assumptions very well ;) -Steve
You are misunderstanding what I am asking or saying. The code may be different, but one reference can store the value of another. The type itself does not depend on the parameter. X!100 x; X!50 y; 50 for y does not change anything to the outside world about y. So, for all practical purposes, we have X x; X y; in which case we can do x = y; with no problem. You are confusing the general case with the specific case. Sure, in general, it doesn't work, we know that. But not all types are dependent on their type parameter. Just because such parameters are specified does not necessarily mean they should be different types. My example is not good because the types are different. If I did something like void foo(int size) { Size = max(size, defaultSize, 100); p = cast(int*)malloc(Size); } Then it would be different. in this case X!50 and X!100 would be the same. X!101 would not.
Aug 11 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 8/11/16 4:15 PM, Engine Machine wrote:
 On Thursday, 11 August 2016 at 18:42:51 UTC, Steven Schveighoffer wrote:
 On 8/11/16 2:11 PM, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a template
 class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. The type it
 self does not depend on the default size.
And they should be different types. The code generated for the type is different, in this case it's important to declare these are not the same type. For example, if x = y worked, then what should x.foo(5) do?
 While one could argue that it can be unsafe, in the my context, it is
 not.

 Is there any way to get D to understand I want do not want a template
 parameter to be part of the type comparison?
The type is part of the template, and the instantiations are different. So no.
 I use several parameters to pass info to the type that does not change
 affect the type itself. It prevents the "same type" from being used with
 "itself".
Then define how the compiler can convert from one to another. Or redesign the type to specify the parameters at the right time.
 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used is
 important!
That's not how template types work. your code is shorthand for: template s(T1, T2) { struct s { T1 t1; // I assume you wanted to put a member here? } } Two different instantiations of s create two different namespaces where the structs are not the same.
I don't think this proves anything. You just rewrote the template. T2 is still not used in either case and hence the type does not depend on it.
OK, I'll use a different example: struct S(int N) { int x; } S!1 s1; S!2 s2 = s1; // error This is very similar to what you are asking for. This can be reimagined like so: struct S1 { int x; } struct S2 { int x; } S1 s1; S2 s2 = s1; // error The names end in a different number. They have the same types and structure, but the compiler considers them to be different types. Each instantiation of a template defines a DIFFERENT namespace. The only time the namespaces are identical are when the template parameters are identical. The reason templates are so useful is because I don't HAVE to write both S1 and S2. I can write it once and not have to repeat myself. But the template itself is not a namespace. Only the instantiated template is one.
 Basically your logic is something like: A = 0*5 is different than B =
 0*6. But both are the same, just because they look different doesn't
 change that.
Not at all. This is way off.
 You are misunderstanding what I am asking or saying.

 The code may be different, but one reference can store the value of
 another. The type itself does not depend on the parameter.
The namespace in which the type is declared depends on the parameter. Templates depend on this. For instance: template Foo(string name) { static int x; } Foo!"blah".x = 5; auto y = Foo!"blar".x; The namespace I've declared depends on the name I've passed the template. I want to fetch a *different* x when I declare a *different* namespace. That's the point! If the compiler said "hey, you aren't using the name string, so I'm just going to assume all these are the same", that's going to screw up the entire purpose of this construct.
 X!100 x;
 X!50 y;

 50 for y does not change anything to the outside world about y.

 So, for all practical purposes, we have

 X x;
 X y;
No, you have X100 x; X50 y; Where X100 and X50 have the exact same structure. But they are different types because the names are different.
 You are confusing the general case with the specific case. Sure, in
 general, it doesn't work, we know that. But not all types are dependent
 on their type parameter. Just because such parameters are specified does
 not necessarily mean they should be different types.
Again, not how templates work. You are looking for a different mechanism. -Steve
Aug 11 2016
prev sibling parent Engine Machine <EM EM.com> writes:
Also, what if we use a class instead of a struct?

in this case they are both references to the same thing.

I see a problem with reflection though, as one could get the 
template parameter value and it would wrong on conversion. D 
takes the easy way out of just preventing complex and potentially 
ill-defined behavior, I guess that is ultimately a good thing, 
even if it makes it a pain in some cases.
Aug 11 2016
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 08/11/2016 11:11 AM, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a template
 class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types.
The reason they are different types is the fact that the code generated for X!100 and X!50 are different. If x=z were allowed, which foo() would you expect to be executed for x.foo()? If it would change at run time depending on the actual type, we need a run-time expression, which cannot be a template parameter.
 The type it
 self does not depend on the default size.

 While one could argue that it can be unsafe, in the my context, it is 
not. Understood but not possible with the template definition of D.
 Is there any way to get D to understand I want do not want a template
 parameter to be part of the type comparison?
It must involve runtime.
 I use several parameters to pass info to the type that does not change
 affect the type itself. It prevents the "same type" from being used with
 "itself".

 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used is 
important! It's not used in this module but another module may be using __traits (or std.traits) to inspect that second argument.
 I guess D just can't handle it though?
By design, no. (Another language that behaves this way is C++.) Ali
Aug 11 2016
prev sibling next sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine wrote:
 [...]
If, in your case, it is possible to use one type as the other, then specify it. I mean, implement a templated opAssign that allows you to assign values of one instantiation to values of another. While doing so, remember that this will not change the behaviour of the assigned-to variable, but will only transfer the runtime state from one variable to the other. struct S(T1, T2) { T1 t; void opAssign(T)(auto ref S!(T1, T) other) { t = other.t; } } unittest { S!(int, float) x(1); S!(int, char) y(3); x = y; assert(x.t == 3); // the value of x is changed static assert(is(typeof(x) == S!(int, float))) // the type of x isn't changed }
Aug 11 2016
parent Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 19:28:47 UTC, Lodovico Giaretta 
wrote:
 On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine 
 wrote:
 [...]
If, in your case, it is possible to use one type as the other, then specify it. I mean, implement a templated opAssign that allows you to assign values of one instantiation to values of another. While doing so, remember that this will not change the behaviour of the assigned-to variable, but will only transfer the runtime state from one variable to the other. struct S(T1, T2) { T1 t; void opAssign(T)(auto ref S!(T1, T) other) { t = other.t; } } unittest { S!(int, float) x(1); S!(int, char) y(3); x = y; assert(x.t == 3); // the value of x is changed static assert(is(typeof(x) == S!(int, float))) // the type of x isn't changed }
Ok, well, my point is that in some cases, the OpAssign is trivially since everything is copied. I guess being explicit is not a bad thing in this case though.
Aug 11 2016
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine wrote:
 I have the need, in some cases, to pass static information to a 
 template class but don't want it to affect its type.

 import std.algorithm, core.stdc.stdlib;
 struct X(int defaultSize = 100)
 {
    int Size;
    int* p;
    void foo(int size)
    {
     Size = max(size, defaultSize);
     p = cast(int*)malloc(Size);
    }
 }

 If I do

 X!100 x;
 X!100 y;
 X!50 z;

 then I can do

 x = y;

 but not

 x = z;

 but of course D things these are completely different types. 
 The type it self does not depend on the default size.

 While one could argue that it can be unsafe, in the my context, 
 it is not.

 Is there any way to get D to understand I want do not want a 
 template parameter to be part of the type comparison?

 I use several parameters to pass info to the type that does not 
 change affect the type itself. It prevents the "same type" from 
 being used with "itself".

 another example:

 struct s(T1, T2)
 {
     T1;
 }

 then

 s!(int, double)

 and

 s!(int, float)

 should really be the same type! The fact that T2 is not used is 
 important!

 I guess D just can't handle it though?
It can be done, but you have to be explicit and should think very carefully if this is really a good design. struct X(int defaultSize = 100) { int size; int* p; void foo(int size) { size = max(size, defaultSize); p = cast(int*)malloc(size); } X opAssign(X2: X!n, int n)(X2 other) { //Do whatever you want here } X2 changeDefaultSize(int n)() { auto newX = X!n(n, p); p = null; //Release ownership of p return newX; } } void takesX50(X!50 x) { //... } void main() { X!100 n; X!100 m; X!50 o; n = m; o = m; takesX50(n); //Error takesX50(n.changeDefaultSize!50); //Okay } Really though this problem is properly solved by runtime polymorphism.
Aug 11 2016
parent Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 20:43:13 UTC, Meta wrote:
 On Thursday, 11 August 2016 at 18:11:30 UTC, Engine Machine 
 wrote:
 [...]
It can be done, but you have to be explicit and should think very carefully if this is really a good design. struct X(int defaultSize = 100) { int size; int* p; void foo(int size) { size = max(size, defaultSize); p = cast(int*)malloc(size); } X opAssign(X2: X!n, int n)(X2 other) { //Do whatever you want here } X2 changeDefaultSize(int n)() { auto newX = X!n(n, p); p = null; //Release ownership of p return newX; } } void takesX50(X!50 x) { //... } void main() { X!100 n; X!100 m; X!50 o; n = m; o = m; takesX50(n); //Error takesX50(n.changeDefaultSize!50); //Okay } Really though this problem is properly solved by runtime polymorphism.
Thanks. I am using templates and in some cases use template arguments to set compile time properties, hence runtime won't work. These arguments don't affect the type itself, as far as the functionality/behavior as I use them.
Aug 12 2016