www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do I simulate variadic parameters for template (range) functions?

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Here's what I can do with a variadic function:

void main()
{
    int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

    process(a[a.countUntil(7) .. $]);
    process(1);
}

void process(int[] vals...)
{
    foreach (val; vals)
    {
    }
}

Very simple, pass one or multiple arguments. But then I thought about
using the `until` template instead of countUntil. However `until`
returns a range. So my next guess was to write:

void main()
{
    int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

    process(a.until(7));  // ok
    process(4);  // error since 4 is not a range
}

void process(Range)(Range vals) if (isInputRange!Range &&
is(ElementType!Range == int))
{
    foreach (val; vals)
    {
    }
}

Is it somehow possible to automatically convert a literal to a range?
I really miss the convenience of variadic functions. I thought about
making an overload that only takes an int and constructing a simple
input range around it so it can be passed to process(), e.g.:

void process(Range)(Range vals) if (isInputRange!Range &&
is(ElementType!Range == int))
{
    foreach (val; vals)
    {
    }
}

void process(int arg)
{
    process(makeInputRange(arg));  // make an input range, pass to
above process()
}

But I can't overload templated and non-templated functions, I think
this is one of those old-standing bugs.
Aug 24 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/24/2011 07:40 PM, Andrej Mitrovic wrote:
 Here's what I can do with a variadic function:

 void main()
 {
      int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

      process(a[a.countUntil(7) .. $]);
      process(1);
 }

 void process(int[] vals...)
 {
      foreach (val; vals)
      {
      }
 }

 Very simple, pass one or multiple arguments. But then I thought about
 using the `until` template instead of countUntil. However `until`
 returns a range. So my next guess was to write:

 void main()
 {
      int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

      process(a.until(7));  // ok
      process(4);  // error since 4 is not a range
 }

 void process(Range)(Range vals) if (isInputRange!Range&&
 is(ElementType!Range == int))
 {
      foreach (val; vals)
      {
      }
 }

 Is it somehow possible to automatically convert a literal to a range?
 I really miss the convenience of variadic functions. I thought about
 making an overload that only takes an int and constructing a simple
 input range around it so it can be passed to process(), e.g.:

 void process(Range)(Range vals) if (isInputRange!Range&&
 is(ElementType!Range == int))
 {
      foreach (val; vals)
      {
      }
 }

 void process(int arg)
 {
      process(makeInputRange(arg));  // make an input range, pass to
 above process()
 }

 But I can't overload templated and non-templated functions, I think
 this is one of those old-standing bugs.

Workaround: You can make it a templated function with no template arguments and wrap a non-templated version, if it is important that the implementation is not a template.
Aug 24 2011
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 24 Aug 2011 13:40:38 -0400, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 Here's what I can do with a variadic function:

 void main()
 {
     int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

     process(a[a.countUntil(7) .. $]);
     process(1);
 }

 void process(int[] vals...)
 {
     foreach (val; vals)
     {
     }
 }

 Very simple, pass one or multiple arguments. But then I thought about
 using the `until` template instead of countUntil. However `until`
 returns a range. So my next guess was to write:

 void main()
 {
     int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

     process(a.until(7));  // ok
     process(4);  // error since 4 is not a range
 }

 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
     foreach (val; vals)
     {
     }
 }

 Is it somehow possible to automatically convert a literal to a range?
 I really miss the convenience of variadic functions. I thought about
 making an overload that only takes an int and constructing a simple
 input range around it so it can be passed to process(), e.g.:

 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
     foreach (val; vals)
     {
     }
 }

 void process(int arg)
 {
     process(makeInputRange(arg));  // make an input range, pass to
 above process()
 }

 But I can't overload templated and non-templated functions, I think
 this is one of those old-standing bugs.

maybe: void process(Range)(Range vals) if (isInputRange!Range && is(ElementType!Range == int)) { ... } void process(Vals...)(Vals vals) if (allValsElementsAreInt) { ... } Note that I'm not sure what to put for allValsElementsAreInt... -Steve
Aug 24 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/24/2011 07:54 PM, Steven Schveighoffer wrote:
 On Wed, 24 Aug 2011 13:40:38 -0400, Andrej Mitrovic
 <andrej.mitrovich gmail.com> wrote:

 Here's what I can do with a variadic function:

 void main()
 {
 int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

 process(a[a.countUntil(7) .. $]);
 process(1);
 }

 void process(int[] vals...)
 {
 foreach (val; vals)
 {
 }
 }

 Very simple, pass one or multiple arguments. But then I thought about
 using the `until` template instead of countUntil. However `until`
 returns a range. So my next guess was to write:

 void main()
 {
 int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

 process(a.until(7)); // ok
 process(4); // error since 4 is not a range
 }

 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
 foreach (val; vals)
 {
 }
 }

 Is it somehow possible to automatically convert a literal to a range?
 I really miss the convenience of variadic functions. I thought about
 making an overload that only takes an int and constructing a simple
 input range around it so it can be passed to process(), e.g.:

 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
 foreach (val; vals)
 {
 }
 }

 void process(int arg)
 {
 process(makeInputRange(arg)); // make an input range, pass to
 above process()
 }

 But I can't overload templated and non-templated functions, I think
 this is one of those old-standing bugs.

maybe: void process(Range)(Range vals) if (isInputRange!Range && is(ElementType!Range == int)) { ... } void process(Vals...)(Vals vals) if (allValsElementsAreInt) { ... } Note that I'm not sure what to put for allValsElementsAreInt... -Steve

This should do (although it would probably be even better to have a forAll template and a predicate template). template allElementsAreInt(T...){ static if(T.length==0) enum allElementsAreInt=true; else enum allElementsAreInt=is(typeof(T[0])==int) && allElementsAreInt!(T[1..$]); }
Aug 24 2011
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, August 24, 2011 11:00 Timon Gehr wrote:
 On 08/24/2011 07:54 PM, Steven Schveighoffer wrote:
 On Wed, 24 Aug 2011 13:40:38 -0400, Andrej Mitrovic
 
 <andrej.mitrovich gmail.com> wrote:
 Here's what I can do with a variadic function:
 
 void main()
 {
 int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];
 
 process(a[a.countUntil(7) .. $]);
 process(1);
 }
 
 void process(int[] vals...)
 {
 foreach (val; vals)
 {
 }
 }
 
 Very simple, pass one or multiple arguments. But then I thought about
 using the `until` template instead of countUntil. However `until`
 returns a range. So my next guess was to write:
 
 void main()
 {
 int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];
 
 process(a.until(7)); // ok
 process(4); // error since 4 is not a range
 }
 
 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
 foreach (val; vals)
 {
 }
 }
 
 Is it somehow possible to automatically convert a literal to a range?
 I really miss the convenience of variadic functions. I thought about
 making an overload that only takes an int and constructing a simple
 input range around it so it can be passed to process(), e.g.:
 
 void process(Range)(Range vals) if (isInputRange!Range &&
 is(ElementType!Range == int))
 {
 foreach (val; vals)
 {
 }
 }
 
 void process(int arg)
 {
 process(makeInputRange(arg)); // make an input range, pass to
 above process()
 }
 
 But I can't overload templated and non-templated functions, I think
 this is one of those old-standing bugs.

maybe: void process(Range)(Range vals) if (isInputRange!Range && is(ElementType!Range == int)) { ... } void process(Vals...)(Vals vals) if (allValsElementsAreInt) { ... } Note that I'm not sure what to put for allValsElementsAreInt... -Steve

This should do (although it would probably be even better to have a forAll template and a predicate template). template allElementsAreInt(T...){ static if(T.length==0) enum allElementsAreInt=true; else enum allElementsAreInt=is(typeof(T[0])==int) && allElementsAreInt!(T[1..$]); }

std.typetuple has anySatisfy and allSatisfy (though that's a rather bizarre place to put them IMHO). - Jonathan M Davis
Aug 24 2011
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-08-24 19:40, Andrej Mitrovic wrote:
 Here's what I can do with a variadic function:

 void main()
 {
      int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

      process(a[a.countUntil(7) .. $]);
      process(1);
 }

 void process(int[] vals...)
 {
      foreach (val; vals)
      {
      }
 }

 Very simple, pass one or multiple arguments. But then I thought about
 using the `until` template instead of countUntil. However `until`
 returns a range. So my next guess was to write:

 void main()
 {
      int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];

      process(a.until(7));  // ok
      process(4);  // error since 4 is not a range
 }

 void process(Range)(Range vals) if (isInputRange!Range&&
 is(ElementType!Range == int))
 {
      foreach (val; vals)
      {
      }
 }

 Is it somehow possible to automatically convert a literal to a range?
 I really miss the convenience of variadic functions. I thought about
 making an overload that only takes an int and constructing a simple
 input range around it so it can be passed to process(), e.g.:

 void process(Range)(Range vals) if (isInputRange!Range&&
 is(ElementType!Range == int))
 {
      foreach (val; vals)
      {
      }
 }

 void process(int arg)
 {
      process(makeInputRange(arg));  // make an input range, pass to
 above process()
 }

 But I can't overload templated and non-templated functions, I think
 this is one of those old-standing bugs.

Use a variadic template function and check if the first argument is a range or not. -- /Jacob Carlborg
Aug 24 2011