www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Strange alias behaviour in template arguments

reply "Stefan Frijters" <sfrijters gmail.com> writes:
So this is a strange thing I ran into while trying to streamline 
some templates in my code, where fixed-length arrays are passed 
as runtime arguments. I started out by trying variant fun2(), 
which disappointingly didn't work. fun3() then did its job but I 
was suspicious and tried fun4() and fun(5), which also worked but 
shouldn't. Is this a bug or am I doing something bad?

struct Connectivity(uint _d, uint _q) {
   enum d = _d; // Number of dimensions
   enum q = _q;
}

alias d2q9 = Connectivity!(2,9);

// Stores fixed-size array of base type T, and the length of the 
array is determined by the connectivity.
struct Field(T, alias c) {
   alias conn = c;
   T[conn.d] payload;

   this(in T[conn.d] stuff) {
     payload = stuff;
   }
}

// Ok
void fun(T)(T field) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
}

// cannot deduce function from argument types
void fun2(T)(T field, double[T.conn.d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   field.payload = foo;
}

// Ok!
void fun3(T, alias d = T.conn.d)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // 2, okay
   field.payload = foo;
}

// Huh?
void fun4(T, alias d = T.conn.q)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // expect 9, get 2
   field.payload = foo;
}

// Huh?
void fun5(T, alias d = T.conn)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // don't know what to expect, still 
get 2
   field.payload = foo;
}

void main() {
   double[d2q9.d] foo;
   auto f = Field!(double, d2q9)(foo);

   f.fun();         // Sure, this works
   // f.fun2(foo);  // Won't work without additional alias
   f.fun3(foo);     // Works, so are we happy?
   f.fun4(foo);     // No! This isn't supposed to work...
   f.fun5(foo);     // Nor this...
}

Any thoughts?
Mar 03 2015
parent reply "anonymous" <anonymous example.com> writes:
On Tuesday, 3 March 2015 at 13:42:09 UTC, Stefan Frijters wrote:
 So this is a strange thing I ran into while trying to 
 streamline some templates in my code, where fixed-length arrays 
 are passed as runtime arguments. I started out by trying 
 variant fun2(), which disappointingly didn't work. fun3() then 
 did its job but I was suspicious and tried fun4() and fun(5), 
 which also worked but shouldn't. Is this a bug or am I doing 
 something bad?

 struct Connectivity(uint _d, uint _q) {
   enum d = _d; // Number of dimensions
   enum q = _q;
 }

 alias d2q9 = Connectivity!(2,9);

 // Stores fixed-size array of base type T, and the length of 
 the array is determined by the connectivity.
 struct Field(T, alias c) {
   alias conn = c;
   T[conn.d] payload;

   this(in T[conn.d] stuff) {
     payload = stuff;
   }
 }

 // Ok
 void fun(T)(T field) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
 }

 // cannot deduce function from argument types
 void fun2(T)(T field, double[T.conn.d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   field.payload = foo;
 }

 // Ok!
 void fun3(T, alias d = T.conn.d)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // 2, okay
   field.payload = foo;
 }

 // Huh?
 void fun4(T, alias d = T.conn.q)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // expect 9, get 2
   field.payload = foo;
 }

 // Huh?
 void fun5(T, alias d = T.conn)(T field, double[d] foo) {
   pragma(msg, T);
   pragma(msg, T.conn);
   pragma(msg, T.conn.d);
   pragma(msg, T.conn.q);
   pragma(msg, typeof(foo)); // don't know what to expect, still 
 get 2
   field.payload = foo;
 }

 void main() {
   double[d2q9.d] foo;
   auto f = Field!(double, d2q9)(foo);

   f.fun();         // Sure, this works
   // f.fun2(foo);  // Won't work without additional alias
   f.fun3(foo);     // Works, so are we happy?
   f.fun4(foo);     // No! This isn't supposed to work...
   f.fun5(foo);     // Nor this...
 }

 Any thoughts?
I don't know if there's a reason why fun2 doesn't work. I don't see one. fun4 and fun5 work correctly. They are the same as fun3, just with other default values for d. Those default values are not used, because d is inferred from the argument to be 2. If you pass a double[3], d is inferred to be 3 (and the compiler complains on `field.payload = foo`). You can use a static assert or a template constraint to work around fun2 not working: void fun6(T, size_t d)(T field, double[d] foo) { static assert(d == T.conn.d); ... } void fun7(T, size_t d)(T field, double[d] foo) if(d == T.conn.d) { ... }
Mar 03 2015
parent "Stefan Frijters" <sfrijters gmail.com> writes:
On Tuesday, 3 March 2015 at 14:40:45 UTC, anonymous wrote:
 On Tuesday, 3 March 2015 at 13:42:09 UTC, Stefan Frijters wrote:
 So this is a strange thing I ran into while trying to 
 streamline some templates in my code, where fixed-length 
 arrays are passed as runtime arguments. I started out by 
 trying variant fun2(), which disappointingly didn't work. 
 fun3() then did its job but I was suspicious and tried fun4() 
 and fun(5), which also worked but shouldn't. Is this a bug or 
 am I doing something bad?

 struct Connectivity(uint _d, uint _q) {
  enum d = _d; // Number of dimensions
  enum q = _q;
 }

 alias d2q9 = Connectivity!(2,9);

 // Stores fixed-size array of base type T, and the length of 
 the array is determined by the connectivity.
 struct Field(T, alias c) {
  alias conn = c;
  T[conn.d] payload;

  this(in T[conn.d] stuff) {
    payload = stuff;
  }
 }

 // Ok
 void fun(T)(T field) {
  pragma(msg, T);
  pragma(msg, T.conn);
  pragma(msg, T.conn.d);
  pragma(msg, T.conn.q);
 }

 // cannot deduce function from argument types
 void fun2(T)(T field, double[T.conn.d] foo) {
  pragma(msg, T);
  pragma(msg, T.conn);
  pragma(msg, T.conn.d);
  pragma(msg, T.conn.q);
  field.payload = foo;
 }

 // Ok!
 void fun3(T, alias d = T.conn.d)(T field, double[d] foo) {
  pragma(msg, T);
  pragma(msg, T.conn);
  pragma(msg, T.conn.d);
  pragma(msg, T.conn.q);
  pragma(msg, typeof(foo)); // 2, okay
  field.payload = foo;
 }

 // Huh?
 void fun4(T, alias d = T.conn.q)(T field, double[d] foo) {
  pragma(msg, T);
  pragma(msg, T.conn);
  pragma(msg, T.conn.d);
  pragma(msg, T.conn.q);
  pragma(msg, typeof(foo)); // expect 9, get 2
  field.payload = foo;
 }

 // Huh?
 void fun5(T, alias d = T.conn)(T field, double[d] foo) {
  pragma(msg, T);
  pragma(msg, T.conn);
  pragma(msg, T.conn.d);
  pragma(msg, T.conn.q);
  pragma(msg, typeof(foo)); // don't know what to expect, still 
 get 2
  field.payload = foo;
 }

 void main() {
  double[d2q9.d] foo;
  auto f = Field!(double, d2q9)(foo);

  f.fun();         // Sure, this works
  // f.fun2(foo);  // Won't work without additional alias
  f.fun3(foo);     // Works, so are we happy?
  f.fun4(foo);     // No! This isn't supposed to work...
  f.fun5(foo);     // Nor this...
 }

 Any thoughts?
I don't know if there's a reason why fun2 doesn't work. I don't see one. fun4 and fun5 work correctly. They are the same as fun3, just with other default values for d. Those default values are not used, because d is inferred from the argument to be 2. If you pass a double[3], d is inferred to be 3 (and the compiler complains on `field.payload = foo`). You can use a static assert or a template constraint to work around fun2 not working: void fun6(T, size_t d)(T field, double[d] foo) { static assert(d == T.conn.d); ... } void fun7(T, size_t d)(T field, double[d] foo) if(d == T.conn.d) { ... }
Ah, yes, I was misinterpreting the alias. fun7() would work for me, although it's a shame fun2() doesn't... Cheers.
Mar 03 2015