May 08, 2016
On Sunday, 8 May 2016 at 14:11:31 UTC, Ali Çehreli wrote:
>         E front() {
>             final switch (index) {
>                 /* static */ foreach (i, arg; Args) {
>                     case i:
>                         return arg;
>                 }
>             }
>         }

AFAIK, this will do funny things with referencing stack if arguments are variables and not literals.
May 08, 2016
On Sunday, 8 May 2016 at 22:37:44 UTC, Dicebot wrote:
> On Sunday, 8 May 2016 at 14:11:31 UTC, Ali Çehreli wrote:
>>         E front() {
>>             final switch (index) {
>>                 /* static */ foreach (i, arg; Args) {
>>                     case i:
>>                         return arg;
>>                 }
>>             }
>>         }
>
> AFAIK, this will do funny things with referencing stack if arguments are variables and not literals.

Thanks! The static array version works for me too.   It would be good to understand more about what is going on.  It looks like the cost of the static array is an extra copy for each element.  Maybe there is still a way to avoid that.

May 08, 2016
On 05/08/2016 04:48 PM, Erik Smith wrote:
> On Sunday, 8 May 2016 at 22:37:44 UTC, Dicebot wrote:
>> On Sunday, 8 May 2016 at 14:11:31 UTC, Ali Çehreli wrote:
>>>         E front() {
>>>             final switch (index) {
>>>                 /* static */ foreach (i, arg; Args) {
>>>                     case i:
>>>                         return arg;
>>>                 }
>>>             }
>>>         }
>>
>> AFAIK, this will do funny things with referencing stack if arguments
>> are variables and not literals.
>
> Thanks! The static array version works for me too.   It would be good to
> understand more about what is going on.  It looks like the cost of the
> static array is an extra copy for each element. Maybe there is still a
> way to avoid that.
>

I had to change one line of your test code. Dicebot's code work with it:

auto toInputRange (T...) (T args) @nogc
{
    import std.traits : CommonType;
    alias E = CommonType!T;

    struct Range
    {
        E[T.length] args;
        size_t index;

        E front () { return args[index]; }
        void popFront () { ++index; }
        bool empty () { return index >= args.length; }
    }

    Range range;
    foreach (i, ref arg; args)
        range.args[i] = arg;
    return range;
}

static void foo(Args...)(Args args) {
    import std.container.array;
    auto array = Array!int(toInputRange(args));  // <-- HERE

    foreach(a; array) {
        import std.stdio : writeln;
        writeln("e: ", a);
    }
}

void main ( )
{
    import std.stdio;
    writeln(toInputRange(1, 2, 3));

    foo(1,2,3);
}

It used to be

    toInputRange!(args)

Ali

May 09, 2016
On Sunday, 8 May 2016 at 23:49:40 UTC, Ali Çehreli wrote:
> On 05/08/2016 04:48 PM, Erik Smith wrote:
>> On Sunday, 8 May 2016 at 22:37:44 UTC, Dicebot wrote:
>>> On Sunday, 8 May 2016 at 14:11:31 UTC, Ali Çehreli wrote:
>>>>         E front() {
>>>>             final switch (index) {
>>>>                 /* static */ foreach (i, arg; Args) {
>>>>                     case i:
>>>>                         return arg;
>>>>                 }
>>>>             }
>>>>         }
>>>
>>> AFAIK, this will do funny things with referencing stack if arguments
>>> are variables and not literals.
>>
>> Thanks! The static array version works for me too.   It would be good to
>> understand more about what is going on.  It looks like the cost of the
>> static array is an extra copy for each element. Maybe there is still a
>> way to avoid that.
>>
>
> I had to change one line of your test code. Dicebot's code work with it:
>
> auto toInputRange (T...) (T args) @nogc
> {
>     import std.traits : CommonType;
>     alias E = CommonType!T;
>
>     struct Range
>     {
>         E[T.length] args;
>         size_t index;
>
>         E front () { return args[index]; }
>         void popFront () { ++index; }
>         bool empty () { return index >= args.length; }
>     }
>
>     Range range;
>     foreach (i, ref arg; args)
>         range.args[i] = arg;
>     return range;
> }
>
> static void foo(Args...)(Args args) {
>     import std.container.array;
>     auto array = Array!int(toInputRange(args));  // <-- HERE
>
>     foreach(a; array) {
>         import std.stdio : writeln;
>         writeln("e: ", a);
>     }
> }
>
> void main ( )
> {
>     import std.stdio;
>     writeln(toInputRange(1, 2, 3));
>
>     foo(1,2,3);
> }
>
> It used to be
>
>     toInputRange!(args)
>
> Ali

I did notice that but forgot to mention it - thanks for clarifying.  Again it definitely works but it would be nice to find a non-copy solution.  I tried to form a static array of pointers to the args (see below).  It compiled but the output was bad.  I would expect that taking the address of the arguments should be safe as long as you are in the called function.


auto toInputRange (T...) (T args) @nogc {
    import std.traits : CommonType;
    alias E = CommonType!T;

    struct Range {
        alias P = E*;
        P[T.length] args;
        size_t index;

        E front () { return *args[index]; }
        void popFront () { ++index; }
        bool empty () { return index >= args.length; }
    }

    Range range;
    foreach (i, ref arg; args) range.args[i] = &arg;
    return range;
}


May 09, 2016
On Sunday, 8 May 2016 at 23:48:01 UTC, Erik Smith wrote:
> Thanks! The static array version works for me too.   It would be good to understand more about what is going on.  It looks like the cost of the static array is an extra copy for each element.  Maybe there is still a way to avoid that.

The difference is quite simple: my latest version does save copy of arguments in static array which means you can freely pass it around with referenced data being valid. But it may become expensive to copy with large argument lists if compiler can't optimize it away. Ali version generates a compile-time switch for index that returns either literals or external variables by referencing their symbol directly. If this gets used to reference external stack variables which later get out of scope, range may either start iterating over garbage memory or silently allocate a closure (don't remember which is the case for latest compiler).

In practice, if you use good inlining compiler, like LDC, I'd say static array version is strictly better. It is guaranteed to be safe and RVO allows compiler to completely get rid of additional stack copies if usage scope is limited.
May 09, 2016
On 05/09/2016 03:44 AM, Dicebot wrote:

> Ali version generates a compile-time switch for index

I think it's a run-time switch, generated by a compile-time foreach:

E front() {
    final switch (index) {                    // <-- RUNTIME
        /* static */ foreach (i, arg; Args) { // <-- COMPILE TIME
            case i:                           // <-- RUNTIME
                return arg;                   // <-- RUNTIME
        }
    }
}

Quite confusing. :)

> that returns either literals or external variables by
> referencing their symbol directly.

I see. So, a parameter pack (AliasSeq) uses 'alias' template parameters for symbols. Makes sense...

Ali

May 09, 2016
On Monday, 9 May 2016 at 12:36:00 UTC, Ali Çehreli wrote:
> On 05/09/2016 03:44 AM, Dicebot wrote:
>
> > Ali version generates a compile-time switch for index
>
> I think it's a run-time switch, generated by a compile-time foreach:
>
> E front() {
>     final switch (index) {                    // <-- RUNTIME
>         /* static */ foreach (i, arg; Args) { // <-- COMPILE TIME
>             case i:                           // <-- RUNTIME
>                 return arg;                   // <-- RUNTIME
>         }
>     }
> }
>
> Quite confusing. :)

It becomes even more confusing if you keep in mind that it is a final switch on an index range known at compile-time - which means that as long as whole template can be inlined and actual index is know at CT, compiler can eliminate all runtime indexing checks and just replace it with matching literal/symbol.


May 09, 2016
On Sunday, 8 May 2016 at 14:11:31 UTC, Ali Çehreli wrote:
> I like Alex Parrill's only() solution but it allocates a dynamic array as well by doing the equivalent of [args] in the guts of its implementation.

No it does not.

The constructor does `this.data = [values];`, but `this.data` is a fixed-sized array, which is stored in the structure itself. No allocation needs to happen (it should be the same as the foreach loop in your implementation).

You guys are just re-inventing `only`.
1 2
Next ›   Last »