www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - template specialization for arrays

reply J Arrizza <cppgent0 gmail.com> writes:
I have a template that I'd like to have a specialization for arrays.
Initiall I need it to work for byte arrays, but I'd like to make it
eventually work for all arrays. The page
http://d-programming-language.org/template says to use

template TFoo(T : T[]) { ... } // #2


but when I try it, it doesn't quite work:

template abc(T)
  {
    void abc(T parm1)
      {
        writeln("simpleparm: ", parm1);
      }
  }

template abc(T: T[])
  {
    void abc(T parm1)
      {
        writeln("array : ", parm1);
      }
  }


void main(string[] args)
  {
    abc(1);
    abc("str");
    int[] arr = [1, 2];
    abc(arr);
  }


The output is:

simpleparm: 1
simpleparm: str
simpleparm: [1, 2]


Which is not what I want, it needs to be the specialized  template for
arrays. Note, this doesn't work either:

template abc(T: T[])
  {
    void abc(T[] parm1)
      {
        writeln("array : ", parm1);
      }
  }


John
Oct 29 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/29/2011 05:24 PM, J Arrizza wrote:
 I have a template that I'd like to have a specialization for arrays.
 Initiall I need it to work for byte arrays, but I'd like to make it
 eventually work for all arrays. The page
 http://d-programming-language.org/template says to use

     template TFoo(T : T[]) { ... } // #2


 but when I try it, it doesn't quite work:

     template abc(T)
        {
          void abc(T parm1)
            {
              writeln("simpleparm: ", parm1);
            }
        }

     template abc(T: T[])
        {
          void abc(T parm1)
            {
              writeln("array : ", parm1);
            }
        }


     void main(string[] args)
        {
          abc(1);
          abc("str");
          int[] arr = [1, 2];
          abc(arr);
        }


 The output is:

     simpleparm: 1
     simpleparm: str
     simpleparm: [1, 2]


 Which is not what I want, it needs to be the specialized  template for
 arrays. Note, this doesn't work either:

     template abc(T: T[])
        {
          void abc(T[] parm1)
            {
              writeln("array : ", parm1);
            }
        }


 John
Fixed: template abc(T) { void abc(T parm1) { writeln("simpleparm: ", parm1); } } void abc(T:T[])(T[] parm1) { writeln("array : ", parm1); } void main(string[] args) { abc(1); abc!(typeof("str"))("str"); int[] arr = [1, 2]; abc!(int[])(arr); } The important thing to note is that when pattern matching on T[] is done, then T is the element type of the array, not the array type.
Oct 29 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/29/2011 11:32 PM, Timon Gehr wrote:
 On 10/29/2011 05:24 PM, J Arrizza wrote:
 I have a template that I'd like to have a specialization for arrays.
 Initiall I need it to work for byte arrays, but I'd like to make it
 eventually work for all arrays. The page
 http://d-programming-language.org/template says to use

 template TFoo(T : T[]) { ... } // #2


 but when I try it, it doesn't quite work:

 template abc(T)
 {
 void abc(T parm1)
 {
 writeln("simpleparm: ", parm1);
 }
 }

 template abc(T: T[])
 {
 void abc(T parm1)
 {
 writeln("array : ", parm1);
 }
 }


 void main(string[] args)
 {
 abc(1);
 abc("str");
 int[] arr = [1, 2];
 abc(arr);
 }


 The output is:

 simpleparm: 1
 simpleparm: str
 simpleparm: [1, 2]


 Which is not what I want, it needs to be the specialized template for
 arrays. Note, this doesn't work either:

 template abc(T: T[])
 {
 void abc(T[] parm1)
 {
 writeln("array : ", parm1);
 }
 }


 John
Fixed: template abc(T) { void abc(T parm1) { writeln("simpleparm: ", parm1); } } void abc(T:T[])(T[] parm1) { writeln("array : ", parm1); } void main(string[] args) { abc(1); abc!(typeof("str"))("str"); int[] arr = [1, 2]; abc!(int[])(arr); } The important thing to note is that when pattern matching on T[] is done, then T is the element type of the array, not the array type.
When using pattern matching, explicit template arguments are necessary. You probably don't want that, the following code does not need them: void abc(T)(T parm1) if(!isDynamicArray!T) { writeln("simpleparm: ", parm1); } void abc(T)(T parm1) if(isDynamicArray!T){ writeln("array : ", parm1); } void main(string[] args) { abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); }
Oct 29 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
This works too:

void abc(T, U=void)(T parm1) {
     writeln("simpleparm: ", parm1);
}

void abc(T:U[],U)(T parm1) {
     writeln("array : ", parm1);
}


void main(string[] args) {
     abc(1);
     abc("str");
     int[] arr = [1, 2];
     abc(arr);
}
Oct 29 2011
prev sibling parent reply J Arrizza <cppgent0 gmail.com> writes:
 When using pattern matching, explicit template arguments are necessary.
 You probably don't want that, the following code does not need them:

 void abc(T)(T parm1) if(!isDynamicArray!T)
 {
    writeln("simpleparm: ", parm1);
 }

 void abc(T)(T parm1) if(isDynamicArray!T){

    writeln("array : ", parm1);
 }
The output is: simpleparm: 1 dynamic array : str dynamic array : [1, 2] which sort of makes sense, but doesn't fit my app. It is possible to treat a string as an array of characters, but in my case I want to treat them as a single entity. The whole purpose of the array specialization is to search/manipulate/compare the individual elements of an array... except for strings. Ok, so I modified a little to take care of strings. But I also added another test for a static array and it's not playing nice anymore. void abc(T, U=void) (T parm1) { writeln("simpleparm: ", parm1); } void abc(T: string) (T parm1) { writeln("string : ", parm1); } void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } void abc(T:U[], U) (T parm1) if (!isDynamicArray!T) //tried isStaticArray as well here { writeln("static array : ", parm1); } void main(string[] args) { writeln("v4"); abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); int[2] arr2 =[3, 4]; writeln("arr2 ", __traits(isStaticArray, arr2)); abc(arr2); } Output is: simpleparm: 1 string : str dynamic array : [1, 2] arr2 true simpleparm: [3, 4] // should be "static array: [3, 4]" John
Oct 29 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/30/2011 12:52 AM, J Arrizza wrote:
     When using pattern matching, explicit template arguments are
     necessary. You probably don't want that, the following code does not
     need them:

     void abc(T)(T parm1) if(!isDynamicArray!T)
     {
         writeln("simpleparm: ", parm1);
     }

     void abc(T)(T parm1) if(isDynamicArray!T){

         writeln("array : ", parm1);
     }


 The output is:

     simpleparm: 1
     dynamic array : str
     dynamic array : [1, 2]


 which sort of makes sense, but doesn't fit my app. It is possible to
 treat a string as an array of characters, but in my case I  want to
 treat them as a single entity. The whole purpose of the array
 specialization is to search/manipulate/compare the individual elements
 of an array... except for strings.

 Ok, so I modified a little to take care of strings. But I also added
 another test for a static array and it's not playing nice anymore.

     void abc(T, U=void) (T parm1)
        {
        writeln("simpleparm: ", parm1);
        }

     void abc(T: string) (T parm1)
        {
        writeln("string : ", parm1);
        }

     void abc(T:U[], U) (T parm1)
        if (isDynamicArray!T)
        {
        writeln("dynamic array : ", parm1);
        }

     void abc(T:U[], U) (T parm1)
        if (!isDynamicArray!T)  //tried isStaticArray as well here
        {
        writeln("static array : ", parm1);
        }

     void main(string[] args)
        {
          writeln("v4");
          abc(1);
          abc("str");
          int[] arr = [1, 2];
          abc(arr);
          int[2] arr2 =[3, 4];
          writeln("arr2 ", __traits(isStaticArray, arr2));
          abc(arr2);
        }


 Output is:

     simpleparm: 1
     string : str
     dynamic array : [1, 2]
     arr2 true
     simpleparm: [3, 4]      // should be "static array: [3, 4]"


 John
This works: void abc(T, U=void, size_t N=0) (T parm1) { writeln("simpleparm: ", parm1); } void abc(T: string) (T parm1) { writeln("string : ", parm1); } void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } void abc(T:U[N], U, size_t N) (T parm1) { writeln("static array : ", parm1); } void main(string[] args) { abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); int[2] arr2 =[3, 4]; abc(arr2); }
Oct 29 2011
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/29/11 6:02 PM, Timon Gehr wrote:
 This works:

 void abc(T, U=void, size_t N=0) (T parm1) {
 writeln("simpleparm: ", parm1);
 }

 void abc(T: string) (T parm1) {
 writeln("string : ", parm1);
 }

 void abc(T:U[], U) (T parm1) if (isDynamicArray!T) {
 writeln("dynamic array : ", parm1);
 }

 void abc(T:U[N], U, size_t N) (T parm1) {
 writeln("static array : ", parm1);
 }

 void main(string[] args) {
 abc(1);
 abc("str");
 int[] arr = [1, 2];
 abc(arr);
 int[2] arr2 =[3, 4];
 abc(arr2);
 }
What's wrong with isStaticArray? Also, OP may want to look at isNarrowString. Andrei
Oct 29 2011
next sibling parent reply J Arrizza <cppgent0 gmail.com> writes:
On Sat, Oct 29, 2011 at 4:14 PM, Andrei Alexandrescu <
SeeWebsiteForEmail erdani.org> wrote:

 What's wrong with isStaticArray? Also, OP may want to look at
 isNarrowString.


 Andrei
Tried isStaticArray: void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } void abc(T:U[], U) (T parm1) if (isStaticArray!T) { writeln("static array : ", parm1); } It didn't match. The output was: simpleparm: 1 dynamic array : str dynamic array : [1, 2] simpleparm: [3, 4] isNarrowString isn't in the traits online doc. Looked it up in std/traits.d and I tried it: void abc(T) (T parm1) if (isNarrowString!T) { writeln("string : ", parm1); } void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } and get compiler ambiguity for abc("str") between the two templates above. John
Oct 29 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/29/11 6:44 PM, J Arrizza wrote:
 On Sat, Oct 29, 2011 at 4:14 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:


     What's wrong with isStaticArray? Also, OP may want to look at
     isNarrowString.


     Andrei


 Tried isStaticArray:
[snip] I think you got quite a bit of advice for different approaches, which may get confusing. You should use either std.traits.isXxx systematically, or patterns systematically, but not both at the same time. Personally I prefer isXxx because they foster simple logic to decide what overloads should apply. Also, when posting, you may want to include complete short programs so others can try them quickly. Andrei
Oct 29 2011
parent reply J Arrizza <cppgent0 gmail.com> writes:
 You should use either std.traits.isXxx systematically, or patterns
 systematically, but not both at the same time. Personally I prefer isXxx
 because they foster simple logic to decide what overloads should apply.
Also, when posting, you may want to include complete short programs so others can try them quickly.
Andrei, I thought I had posted the entire program. Here it is again using only traits as you recommend: import std.stdio; import std.traits; void abc(T) (T parm1) if (isNarrowString!T || (!isStaticArray!T && !isDynamicArray!T)) { writeln("simpleparm: ", parm1); } void abc(T) (T parm1) if (!isNarrowString!T && (isDynamicArray!T || isStaticArray!T) ) { writeln("array : ", parm1); } void main(string[] args) { writeln("v4"); abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); int[2] arr2 = [3, 4]; abc(arr2); } And it does work, here's the output: simpleparm: 1 simpleparm: str array : [1, 2] array : [3, 4] Note my original intent was to differentiate between arrays and non-arrays only (lumping strings into non-array). As for using only patterns, I can't get the compiler to disambiguate between non-arrays and arrays: import std.stdio; import std.traits; void abc(T, U = void, size_t N = 0) (T parm1) //line 3 { writeln("simpleparm: ", parm1); } void abc(T: U[N], U = char, size_t N) (T parm1) { writeln("string : ", parm1); } void abc(T) (T parm1) { writeln("dynamic array : ", parm1); } void abc(T: U[N], U, size_t N) (T parm1) //line 15 { writeln("static array : ", parm1); } void main(string[] args) { writeln("v4"); abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); int[2] arr2 = [3, 4]; abc(arr2); //line 27 } Here's the compiler error: dtest.d(27): Error: template dtest.abc(T,U = void,ulong N = 0) abc(T,U = void,ulong N = 0) matches more than one template declaration, dtest.d(3):abc(T,U = void,ulong N = 0) and dtest.d(15):abc(T : U[N],U,ulong N)
Oct 30 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/30/11 5:16 AM, J Arrizza wrote:
         You should use either std.traits.isXxx systematically, or
         patterns systematically, but not both at the same time.
         Personally I prefer isXxx because they foster simple logic to
         decide what overloads should apply.


     Also, when posting, you may want to include complete short programs
     so others can try them quickly.


 Andrei, I thought I had posted the entire program. Here it is again
 using only traits as you recommend:

     import std.stdio;
     import std.traits;
     void abc(T) (T parm1)
        if (isNarrowString!T || (!isStaticArray!T && !isDynamicArray!T))
        {
        writeln("simpleparm: ", parm1);
        }
     void abc(T) (T parm1)
        if (!isNarrowString!T && (isDynamicArray!T || isStaticArray!T) )
        {
        writeln("array : ", parm1);
        }
     void main(string[] args)
        {
          writeln("v4");
          abc(1);
          abc("str");
          int[] arr = [1, 2];
          abc(arr);
          int[2] arr2 = [3, 4];
          abc(arr2);
        }
Thanks, sorry for having missed that. The code as above is canonical. I think restricted templates are the way to go for most code. Pattern matching on types is rather arcane and should be let to a few advanced uses (such as implementing traits themselves). Andrei
Oct 30 2011
next sibling parent J Arrizza <cppgent0 gmail.com> writes:
On Sun, Oct 30, 2011 at 7:36 AM, Andrei Alexandrescu <
SeeWebsiteForEmail erdani.org> wrote:

 The code as above is canonical. I think restricted templates are the way
 to go for most code.
Yes, they are much simpler to use. I went back to traits.d to see how isDynamicArray and isStaticArray were built mostly to figure out the patterns used for them. I found a couple more isArray!T and isSomeString which simplify and generalize the code just a little more: import std.stdio; import std.traits; void abc(T) (T parm1) if (isSomeString!T || !isArray!T) { writeln("simpleparm: ", parm1); } void abc(T) (T parm1) if (!isSomeString!T && isArray!T) { writeln("array : ", parm1); } void main(string[] args) { writeln("v4"); abc(1); abc("str"); int[] arr = [1, 2]; abc(arr); int[2] arr2 = [3, 4]; abc(arr2); } Another one that looked promising is isIterable() for the array. All of this begs the question, where do I find the latest doc? Since these are not showing up in the online doc but are clearly in traits.d. http://d-programming-language.org/traits.html Thanks again for your help, John
Oct 30 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, October 30, 2011 08:41:47 J Arrizza wrote:
 All of this begs the question, where do I find the latest doc? Since these
 are not showing up in the online doc but are clearly in traits.d.
 http://d-programming-language.org/traits.html
That page is talking about __traits (which is built into the compiler), not std.traits. For std.traits, you want http://www.d-programming-language.org/phobos/std_traits.html Depending on what you're doing, you may need both, but generally all you need is std.traits unless you're getting fancy. - Jonathan M Davis
Oct 30 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 30 Oct 2011 10:36:26 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 10/30/11 5:16 AM, J Arrizza wrote:
         You should use either std.traits.isXxx systematically, or
         patterns systematically, but not both at the same time.
         Personally I prefer isXxx because they foster simple logic to
         decide what overloads should apply.


     Also, when posting, you may want to include complete short programs
     so others can try them quickly.


 Andrei, I thought I had posted the entire program. Here it is again
 using only traits as you recommend:

     import std.stdio;
     import std.traits;
     void abc(T) (T parm1)
        if (isNarrowString!T || (!isStaticArray!T && !isDynamicArray!T))
        {
        writeln("simpleparm: ", parm1);
        }
     void abc(T) (T parm1)
        if (!isNarrowString!T && (isDynamicArray!T || isStaticArray!T) )
        {
        writeln("array : ", parm1);
        }
     void main(string[] args)
        {
          writeln("v4");
          abc(1);
          abc("str");
          int[] arr = [1, 2];
          abc(arr);
          int[2] arr2 = [3, 4];
          abc(arr2);
        }
Thanks, sorry for having missed that. The code as above is canonical. I think restricted templates are the way to go for most code. Pattern matching on types is rather arcane and should be let to a few advanced uses (such as implementing traits themselves).
The attraction of using specializations instead of constraints (aside from the readability) is that specializations do not require modifying the non-specialized templates. For example: void abc(T)(T parm1) {...} void abc(T:int)(T parm1) {...} vs: void abc(T)(T parm1) if (!is(T : int)) {...} void abc(T)(T parm1) if (is(T: int)) {...} Note that the if(!is(T :int)) is required in the base case. This can quickly get out of hand if you have lots of specializations (see any phobos modules for lots of examples). It might be nice if constrained templates took precedence over non-constrained ones. -Steve
Oct 31 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, October 29, 2011 16:44:16 J Arrizza wrote:
 On Sat, Oct 29, 2011 at 4:14 PM, Andrei Alexandrescu <
 
 SeeWebsiteForEmail erdani.org> wrote:
 What's wrong with isStaticArray? Also, OP may want to look at
 isNarrowString.
 
 
 Andrei
Tried isStaticArray: void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } void abc(T:U[], U) (T parm1) if (isStaticArray!T) { writeln("static array : ", parm1); } It didn't match. The output was: simpleparm: 1 dynamic array : str dynamic array : [1, 2] simpleparm: [3, 4] isNarrowString isn't in the traits online doc. Looked it up in std/traits.d and I tried it: void abc(T) (T parm1) if (isNarrowString!T) { writeln("string : ", parm1); } void abc(T:U[], U) (T parm1) if (isDynamicArray!T) { writeln("dynamic array : ", parm1); } and get compiler ambiguity for abc("str") between the two templates above.
Of course you do. A narrow string is a dynamic array, so it matches both. You need to change the second constraint to if(isDynamicArray!T && !isNarrowString!T), then narrow strings won't match both. - Jonathan M Davis
Oct 29 2011
prev sibling parent J Arrizza <cppgent0 gmail.com> writes:
On Sat, Oct 29, 2011 at 5:16 PM, Jonathan M Davis <jmdavisProg gmx.com>wrote:

 and get compiler ambiguity for abc("str") between the two templates
above. Of course you do. A narrow string is a dynamic array, so it matches both. You need to change the second constraint to if(isDynamicArray!T && !isNarrowString!T), then narrow strings won't match both. - Jonathan M Davis
Yup, just tried it and works fine.
Oct 29 2011
prev sibling parent reply J Arrizza <cppgent0 gmail.com> writes:
Thank you Timon,  I very much appreciate your (and others) help.

I will look all this up in the online docs and the book.

There is still some very odd things in how these fit together. For example,
if I comment out the last 3 function templates, I get all "simpleparms",
which is expected.

But if I add in the fourth, I get a compilation error saying the call to
abc(arr2) is ambiguous. So even though the first one specifies "U = void"
and "size_t N = 0", and the fourth specifies something very different,
there is ambiguity between them.

Adding in the third resolves the ambiguity between the first and fourth.

John

On Sat, Oct 29, 2011 at 4:02 PM, Timon Gehr <timon.gehr gmx.ch> wrote:

 This works:

 void abc(T, U=void, size_t N=0) (T parm1) {
    writeln("simpleparm: ", parm1);
 }

 void abc(T: string) (T parm1) {
    writeln("string : ", parm1);
 }

 void abc(T:U[], U) (T parm1) if (isDynamicArray!T) {
    writeln("dynamic array : ", parm1);
  }

 void abc(T:U[N], U, size_t N) (T parm1) {
    writeln("static array : ", parm1);

 }

 void main(string[] args) {
    abc(1);
    abc("str");
    int[] arr = [1, 2];
    abc(arr);
    int[2] arr2 =[3, 4];
    abc(arr2);
 }
Oct 29 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/30/2011 01:34 AM, J Arrizza wrote:
 Thank you Timon,  I very much appreciate your (and others) help.

 I will look all this up in the online docs and the book.

 There is still some very odd things in how these fit together. For
 example, if I comment out the last 3 function templates, I get all
 "simpleparms", which is expected.

 But if I add in the fourth, I get a compilation error saying the call to
 abc(arr2) is ambiguous. So even though the first one specifies "U =
 void" and "size_t N = 0", and the fourth specifies something very
 different, there is ambiguity between them.

 Adding in the third resolves the ambiguity between the first and fourth.
 John
I cannot reproduce that behaviour. If I comment out the second and the third, I get 3 "simpleparm"s and 1 "static array"
 On Sat, Oct 29, 2011 at 4:02 PM, Timon Gehr <timon.gehr gmx.ch
 <mailto:timon.gehr gmx.ch>> wrote:


     This works:

     void abc(T, U=void, size_t N=0) (T parm1) {
         writeln("simpleparm: ", parm1);
     }

     void abc(T: string) (T parm1) {
         writeln("string : ", parm1);
     }

     void abc(T:U[], U) (T parm1) if (isDynamicArray!T) {
         writeln("dynamic array : ", parm1);
       }

     void abc(T:U[N], U, size_t N) (T parm1) {
         writeln("static array : ", parm1);

     }

     void main(string[] args) {
         abc(1);
         abc("str");
         int[] arr = [1, 2];
         abc(arr);
         int[2] arr2 =[3, 4];
         abc(arr2);
     }
Whoops, just noticed that I accidentally left the if(isDynamicArray!T) in there. That is not necessary, if the pattern matches it will always evaluate to true. void abc(T, U=void, size_t N=0) (T parm1) { writeln("simpleparm: ", parm1); } //void abc(T: string) (T parm1) { // writeln("string : ", parm1); //} //void abc(T:U[], U) (T parm1){ // no constraint necessary // writeln("dynamic array : ", parm1); //} void abc(T:U[N], U, size_t N) (T parm1) { writeln("static array : ", parm1); } void main(string[] args) { abc(1); // simpleparm abc("str"); // simpleparm int[] arr = [1, 2]; abc(arr); // simpleparm int[2] arr2 =[3, 4]; abc(arr2); // static array } What compiler version are you using?
Oct 30 2011
parent J Arrizza <cppgent0 gmail.com> writes:
On Sun, Oct 30, 2011 at 3:13 AM, Timon Gehr <timon.gehr gmx.ch> wrote:

 What compiler version are you using?
$ dmd -v DMD64 D Compiler v2.055
Oct 30 2011