Jump to page: 1 2 3
Thread overview
How to expand an expression along with a parameter tuple?
Jun 17, 2013
TommiT
Jun 17, 2013
TommiT
Jun 17, 2013
Ali Çehreli
Jun 17, 2013
TommiT
Jun 17, 2013
Artur Skawina
Jun 17, 2013
TommiT
Jun 17, 2013
Artur Skawina
Jun 17, 2013
TommiT
Jun 17, 2013
Artur Skawina
Jun 17, 2013
bearophile
Jun 17, 2013
Artur Skawina
Jun 17, 2013
bearophile
Jun 17, 2013
Artur Skawina
Jun 18, 2013
bearophile
Jun 18, 2013
TommiT
Jun 18, 2013
Artur Skawina
Jun 18, 2013
TommiT
Jul 06, 2013
TommiT
Jun 18, 2013
TommiT
Jun 18, 2013
TommiT
Jun 17, 2013
Ali Çehreli
June 17, 2013
I can't figure out how to do the following C++ code in D:

int arr[] = { 1, 3, 5, 7, 11 };

template <typename... T>
void foo(T... values) { }

template <typename... T>
void bar(T... values)
{
    foo((arr[values] * 10)...);
}

int main()
{
    bar(1, 3, 4); /* calls foo(arr[1] * 10,
                               arr[3] * 10,
                               arr[4] * 10); */
    return 0;
}


This is how I tried to do it in D, but it doesn't work:

import std.conv;
import std.typetuple;

int[5] arr = [ 1, 3, 5, 7, 11 ];

void foo(T...)(T values) { }

void bar(T...)(T values)
{
    foo(expandWrapped!("arr[@] * 10", values));
}

template expandWrapped(string fmt, X...)
{
    string compose()
    {
        string ret = "alias expandWrapped = TypeTuple!(";

        foreach (i; 0 .. X.length)
        {
            auto iStr = to!string(i);

            foreach (ch; fmt)
            {
                if (ch == '@')
                {
                    ret ~= "X[" ~ iStr ~ "]";
                }
                else
                {
                    ret ~= ch;
                }
            }

            if (i != X.length - 1)
            {
                ret ~= ",";
            }
        }
        ret ~= ");";
        return ret;
    }
    mixin(compose()); // [1]
}

void main()
{
    bar(1, 3, 4);
}

1) Error: variable arr cannot be read at compile time
June 17, 2013
Although... now that I think about it, this should really be done as a language feature, and not through some inefficient CTFE trick. So, I should really be able to just write this:

int[5] arr = [ 1, 3, 5, 7, 11 ];

void foo(T...)(T values) { }

void bar(T...)(T values)
{
    foo((arr[values] * 10)...);
}

void main()
{
    bar(1, 3, 4);
}
June 17, 2013
On 06/16/2013 11:19 PM, TommiT wrote:

> I can't figure out how to do the following C++ code in D:
>
> int arr[] = { 1, 3, 5, 7, 11 };
>
> template <typename... T>
> void foo(T... values) { }
>
> template <typename... T>
> void bar(T... values)
> {
>      foo((arr[values] * 10)...);
> }
>
> int main()
> {
>      bar(1, 3, 4); /* calls foo(arr[1] * 10,
>                                 arr[3] * 10,
>                                 arr[4] * 10); */
>      return 0;
> }

The following does not answer the question of expanding but at least foo() receives [30, 70, 110] :)

import std.stdio;
import std.algorithm;
import std.array;
import std.range;

int[] arr = [ 1, 3, 5, 7, 11 ];

void foo(T)(T[] values...)
{
    writeln(values);
}

void bar(T)(T[] values...)
{
    foo(arr
        .indexed(values)
        .map!(a => a * 10)
        .array);
}

void main()
{
    bar(1, 3, 4);
}

Ali

June 17, 2013
On Monday, 17 June 2013 at 07:20:23 UTC, Ali Çehreli wrote:
>
> The following does not answer the question of expanding but at least foo() receives [30, 70, 110] :)
>
> import std.stdio;
> import std.algorithm;
> import std.array;
> import std.range;
>
> int[] arr = [ 1, 3, 5, 7, 11 ];
>
> void foo(T)(T[] values...)
> {
>     writeln(values);
> }
>
> void bar(T)(T[] values...)
> {
>     foo(arr
>         .indexed(values)
>         .map!(a => a * 10)
>         .array);
> }
>
> void main()
> {
>     bar(1, 3, 4);
> }
>
> Ali

Yeah, that would work. I'd hate the overhead though.
June 17, 2013
On 06/17/13 11:32, TommiT wrote:
> On Monday, 17 June 2013 at 07:20:23 UTC, Ali Çehreli wrote:
>>
>> The following does not answer the question of expanding but at least foo() receives [30, 70, 110] :)
>>
>> import std.stdio;
>> import std.algorithm;
>> import std.array;
>> import std.range;
>>
>> int[] arr = [ 1, 3, 5, 7, 11 ];
>>
>> void foo(T)(T[] values...)
>> {
>>     writeln(values);
>> }
>>
>> void bar(T)(T[] values...)
>> {
>>     foo(arr
>>         .indexed(values)
>>         .map!(a => a * 10)
>>         .array);
>> }
>>
>> void main()
>> {
>>     bar(1, 3, 4);
>> }
>>
>> Ali
> 
> Yeah, that would work. I'd hate the overhead though.

   void bar(T...)(T values) {
      T tmp;
      foreach (i, ref v; values)
         tmp[i] = arr[v]*10;
      foo(tmp);
   }

artur
June 17, 2013
On Monday, 17 June 2013 at 11:15:24 UTC, Artur Skawina wrote:
> On 06/17/13 11:32, TommiT wrote:
>> On Monday, 17 June 2013 at 07:20:23 UTC, Ali Çehreli wrote:
>>>
>>> The following does not answer the question of expanding but at least foo() receives [30, 70, 110] :)
>>>
>>> import std.stdio;
>>> import std.algorithm;
>>> import std.array;
>>> import std.range;
>>>
>>> int[] arr = [ 1, 3, 5, 7, 11 ];
>>>
>>> void foo(T)(T[] values...)
>>> {
>>>     writeln(values);
>>> }
>>>
>>> void bar(T)(T[] values...)
>>> {
>>>     foo(arr
>>>         .indexed(values)
>>>         .map!(a => a * 10)
>>>         .array);
>>> }
>>>
>>> void main()
>>> {
>>>     bar(1, 3, 4);
>>> }
>>>
>>> Ali
>> 
>> Yeah, that would work. I'd hate the overhead though.
>
>    void bar(T...)(T values) {
>       T tmp;
>       foreach (i, ref v; values)
>          tmp[i] = arr[v]*10;
>       foo(tmp);
>    }
>
> artur

Cool, I didn't know that you could create multiple variables like that (T tmp).
June 17, 2013
On 06/17/13 13:23, TommiT wrote:
> On Monday, 17 June 2013 at 11:15:24 UTC, Artur Skawina wrote:
>>    void bar(T...)(T values) {
>>       T tmp;
>>       foreach (i, ref v; values)
>>          tmp[i] = arr[v]*10;
>>       foo(tmp);
>>    }
> 
> Cool, I didn't know that you could create multiple variables like that (T tmp).

A more correct, but a bit less readable version (the types of 'values' and 'arr' elements do not have to match) would be:

   void bar(T...)(T values) {
      static if (T.length) {
         NTup!(T.length, typeof(arr[T[0].init])) tmp;
         foreach (i, ref v; values)
            tmp[i] = arr[v]*10;
         foo(tmp);
      }
      else
         foo();
   }

   template NTup(size_t N, T...) {
      static if (N>1)
         alias NTup = NTup!(N-1, T, T[$-1]);
      else
         alias NTup = T;
   }

artur
June 17, 2013
On Monday, 17 June 2013 at 12:21:31 UTC, Artur Skawina wrote:
>
> A more correct, but a bit less readable version (the types of 'values' and 'arr'
> elements do not have to match) would be:
>
>    void bar(T...)(T values) {
>       static if (T.length) {
>          NTup!(T.length, typeof(arr[T[0].init])) tmp;
>          foreach (i, ref v; values)
>             tmp[i] = arr[v]*10;
>          foo(tmp);
>       }
>       else
>          foo();
>    }
> 
>    template NTup(size_t N, T...) {
>       static if (N>1)
>          alias NTup = NTup!(N-1, T, T[$-1]);
>       else
>          alias NTup = T;
>    }
>
> artur

Argh, that's a lot of boilerplate. Thanks for pointing this out. I didn't notice in your previous example that the expression types had to match with the parameter types. Now I really do think that we need the C++ ellipsis notation (it's just the ellipsis can be omitted when it would be right next to the tuple):

void bar(T...)(T values)
{
    foo((arr[values] * 10)...);
}
June 17, 2013
On 06/17/13 14:57, TommiT wrote:
> On Monday, 17 June 2013 at 12:21:31 UTC, Artur Skawina wrote:
>>
>> A more correct, but a bit less readable version (the types of 'values' and 'arr' elements do not have to match) would be:
>>
>>    void bar(T...)(T values) {
>>       static if (T.length) {
>>          NTup!(T.length, typeof(arr[T[0].init])) tmp;
>>          foreach (i, ref v; values)
>>             tmp[i] = arr[v]*10;
>>          foo(tmp);
>>       }
>>       else
>>          foo();
>>    }
>>
>>    template NTup(size_t N, T...) {
>>       static if (N>1)
>>          alias NTup = NTup!(N-1, T, T[$-1]);
>>       else
>>          alias NTup = T;
>>    }
> 
> Argh, that's a lot of boilerplate. Thanks for pointing this out. I didn't notice in your previous example that the expression types had to match with the parameter types. Now I really do think that we need the C++ ellipsis notation (it's just the ellipsis can be omitted when it would be right next to the tuple):
> 
> void bar(T...)(T values)
> {
>     foo((arr[values] * 10)...);
> }
> 

Well, the only real difference between these two examples is

   s/T tmp/NTup!(T.length, typeof(arr[T[0].init])) tmp/

'NTup' would be a lib thing, and the empty-args case would
have to handled (or disallowed) in real code anyway. So it's not
/that/ much more boilerplate.


Another solution would be to have the following hidden in some lib:

   struct _ForEach(alias MAP, TS...) {
      NTup!(TS.length, typeof(MAP(TS[0].init))) tuple;

      this(TS values) {
         foreach (i, ref v; values)
            tuple[i] = MAP(v);
      }
   }

   auto ForEach(alias MAP, TS...)(TS ts) {
      return _ForEach!(MAP, TS)(ts);
   }

Then 'bar' becomes just:

   void bar(T...)(T values) {
      foo(ForEach!(a=>arr[a]*10)(values).tuple);
   }

Yes, this is not as concise as '...' would be. But, with a bit more tuple support in the language, the '.tuple' part wouldn't be necessary, and then it's just

      foo(ForEach!(a=>arr[a]*10)(values));
vs
      foo((arr[values] * 10)...);

artur
June 17, 2013
Artur Skawina:

> Yes, this is not as concise as '...' would be. But, with a bit more tuple support in the language, the '.tuple' part wouldn't be
> necessary,

Implicit things are dangerous in languages.

".tuple" can also be written "[]".

Bye,
bearophile
« First   ‹ Prev
1 2 3