July 06, 2013
On Saturday, 6 July 2013 at 12:21:29 UTC, Max Strakhov wrote:
> Artur, if i use your solution like
>
> printf("...", ForEach!(val => conv(val).c_str())(values).tuple);
>
> Than i would get a crash, because all the tuple elements would be char*'s, pointing to already freed memory, as std::string's destructor gets called each time right after alias function exits. Ths is what c++ unpacking operator for: i actually return a packed list of std::string's, apply .c_str() to each and than unpack the list to current context.

I thought about this a bit more and realised you're right. The following should work though:

printf( "...", ForEach!(tmp => tmp.c_str())(ForEach!(val => conv(val))(values).tuple).tuple );

It's a defect in ForEach though, and gives me more reason to ask for the C++ style ellipsis expansion operator for D.
July 06, 2013
> The following should work though:
>
> printf( "...", ForEach!(tmp => tmp.c_str())(ForEach!(val => conv(val))(values).tuple).tuple );

I had the same idea too, but couldn't make it work: compiler complains about failed CTFE or something. On the other hand i might do somethong wrong.

> It's a defect in ForEach though, and gives me more reason to ask for the C++ style ellipsis expansion operator for D.

Yeap, i personally find (as * 2)... awesome feature of c++.
July 06, 2013
On Saturday, 6 July 2013 at 17:29:36 UTC, Max Strakhov wrote:
>> The following should work though:
>>
>> printf( "...", ForEach!(tmp => tmp.c_str())(ForEach!(val => conv(val))(values).tuple).tuple );
>
> I had the same idea too, but couldn't make it work: compiler complains about failed CTFE or something. On the other hand i might do somethong wrong.

There were a couple of typos in _TypeMap. That's a correct one:

template _TypeMap(alias MAP, size_t N, TS...) {
  static if (N<TS.length)
      alias _TypeMap = _TypeMap!(MAP, N+1, TS[0..N], typeof(MAP(TS[N].init)), TS[N+1..$]);
  else
      alias _TypeMap = TS;
}

> Yeap, i personally find (as * 2)... awesome feature of c++.

Me too.
July 06, 2013
On Saturday, 6 July 2013 at 15:23:40 UTC, Artur Skawina wrote:
> On 07/06/13 14:21, Max Strakhov wrote:
>> Artur, if i use your solution like
>> 
>> printf("...", ForEach!(val => conv(val).c_str())(values).tuple);
>> 
>> Than i would get a crash, because all the tuple elements would be char*'s, pointing to already freed memory, as std::string's destructor gets called each time right after alias function exits. Ths is what c++ unpacking operator for: i actually return a packed list of std::string's, apply .c_str() to each and than unpack the list to current context.
>
> Can you show some simple code that exhibits the problem?
>
> artur

import std.stdio;

template _TypeMap(alias MAP, size_t N, TS...) {
  static if (N<TS.length)
      alias _TypeMap = _TypeMap!(MAP,
                                 N+1,
                                 TS[0..N],
                                 typeof(MAP(TS[N].init)),
                                 TS[N+1..$]);
  else
      alias _TypeMap = TS;
}
template TypeMap(alias MAP, TS...) {
 alias TypeMap = _TypeMap!(MAP, 0, TS);
}

struct _ForEach(alias MAP, TS...)
{
  TypeMap!(MAP, TS) 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);
}


int[2] getArray(int n)
{
    int[2] data = n;
    return data;
}

void foo(R...)(R ranges)
{
    foreach (range; ranges)
        foreach (value; range)
            write(value, " ");
}

void main()
{
    // Expecting this to print: 1 1 2 2 3 3
    foo(ForEach!(a => getArray(a)[])(1, 2, 3).tuple);
    // ... but it prints random garbage
}

Notice that there were a couple of typos in _TypeMap.
July 06, 2013
> There were a couple of typos in _TypeMap.
Yeah, i fixed them myself. Still, there were some problems.
I like my solution better anyway, because it behaves exactly like c++ ... operator and just expands some code for all items in tuple.
July 06, 2013
On Saturday, 6 July 2013 at 15:23:40 UTC, Artur Skawina wrote:
> ...

Can you tell me why this isn't working though?

import std.stdio, std.typetuple;

template _TypeMap(alias MAP, size_t N, TS...) {
  static if (N<TS.length)
      alias _TypeMap = _TypeMap!(MAP,
                                 N+1,
                                 TS[0..N],
                                 typeof(MAP(TS[N].init)),
                                 TS[N+1..$]);
  else
      alias _TypeMap = TS;
}
template TypeMap(alias MAP, TS...) {
 alias TypeMap = _TypeMap!(MAP, 0, TS);
}

struct _ForEach(alias MAP, TS...)
{
  TypeMap!(MAP, TS) tuple;

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

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


int[2] getArray(int n)
{
    int[2] data = n;
    return data;
}

void foo(R...)(R ranges)
{
    foreach (range; ranges)
        foreach (value; range)
            write(value, " ");
}

void main()
{
    alias pack = TypeTuple!(1, 2, 3);

    // prints random garbage
    foo(ForEach!(tmp => tmp[])
                (ForEach!(a => getArray(a))(pack).tuple).tuple);
}
July 06, 2013
On 07/06/13 19:42, TommiT wrote:
>                                  TS[N+1..$]);

> Notice that there were a couple of typos in _TypeMap.

Thanks for catching that.


> int[2] getArray(int n)
> {
>     int[2] data = n;
>     return data;
> }
> 
> void foo(R...)(R ranges)
> {
>     foreach (range; ranges)
>         foreach (value; range)
>             write(value, " ");
> }
> 
> void main()
> {
>     // Expecting this to print: 1 1 2 2 3 3
>     foo(ForEach!(a => getArray(a)[])(1, 2, 3).tuple);
>     // ... but it prints random garbage
> }

This is slicing a local static array and returning that slice, which points to the stack and lives only until the lambda returns. Moving the data to the heap or simply returning it directly (ie by value) will work:

>     foo(ForEach!(a => getArray(a).dup[])(1, 2, 3).tuple);
>     foo(ForEach!(a => getArray(a))(1, 2, 3).tuple);


I like Max's solution better than mine too, btw. Why didn't i think of that... :)

artur


July 06, 2013
On Saturday, 6 July 2013 at 18:17:34 UTC, TommiT wrote:
> Can you tell me why this isn't working though?

I can get this so far that it prints: 1 1 2 0 <random> <random>

import std.stdio, std.typetuple;

template _TypeMap(alias MAP, size_t N, TS...) {
  static if (N<TS.length)
      alias _TypeMap = _TypeMap!(MAP,
                                 N+1,
                                 TS[0..N],
                                 typeof(MAP([TS[N]][0])),
                                 TS[N+1..$]);
  else
      alias _TypeMap = TS;
}
template TypeMap(alias MAP, TS...) {
 alias TypeMap = _TypeMap!(MAP, 0, TS);
}

struct _ForEach(alias MAP, TS...)
{
  TypeMap!(MAP, TS) tuple;

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

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


int[2] getArray(int n)
{
    int[2] data = n;
    return data;
}

void foo(R...)(R ranges)
{
    foreach (range; ranges)
        foreach (value; range)
            write(value, " ");
}

void main()
{
    alias pack = TypeTuple!(1, 2, 3);

    // prints random garbage
    foo(ForEach!((ref int[2] tmp) { return tmp[]; })
                (ForEach!(a => getArray(a))(pack).tuple).tuple);
}
July 06, 2013
On Saturday, 6 July 2013 at 18:26:28 UTC, Artur Skawina wrote:
> This is slicing a local static array and returning that slice,
> which points to the stack and lives only until the lambda
> returns.

Yes, but certainly simple parameter pack expansion shouldn't be this brittle. I want to be able to write:

foo(getArray(v)[]...); // v is a parameter pack (1, 2, 3)

And be confident in that it gets simply re-written as:

foo(getArray(v[0])[], getArray(v[1])[], getArray(v[2])[]);

It's easy to conceptualise and easy to get it right.
July 06, 2013
> foo(getArray(v[0])[], getArray(v[1])[], getArray(v[2])[]);
>
> It's easy to conceptualise and easy to get it right.

I wrote a template, that can do something like that:

template Expand(string code, string Args, string args, Ts...) {
	const hasType = std.string.indexOf(code, "%Arg") != -1;
	const hasVal  = std.string.indexOf(code, "%arg") != -1;

	static if(hasType)
		const code1 = std.array.replace(code, "%Arg", Args ~ "[%d]");
	else
		const code1 = code;

	static if(hasVal)
		const code2 = std.array.replace(code1, "%arg", args ~ "[%d]");
	else
		const code2 = code1;

	template Fun(size_t i, T) {
		static if(!hasType && !hasVal)
			alias Fun = TypeTuple!(code2);
		else static if(hasType && hasVal)
			alias Fun = TypeTuple!(std.string.format(code2, i, i));
		else
			alias Fun = TypeTuple!(std.string.format(code2, i));
	}

	template ConcatArgs() {
		const Val = "";
	}

	template ConcatArgs(T, As...) if(!is(T == string)) {
		static assert(T);
	}

	template ConcatArgs(string s, As...) if(As.length == 0) {
		const Val = s;
	}

	template ConcatArgs(string s, As...) if(As.length != 0) {
		const Val = s ~ ", " ~ ConcatArgs!As.Val;
	}

	const Val = ConcatArgs!(staticIndexedMap!(Fun, Ts)).Val;
}

Usage:

writeln(Expand!("getArray(%arg)[]", "", "v", string, int, int).Val); // == getArray(v[0])[], getArray(v[1])[], getArray(v[2])[]

If anyine has any ideas how make this implementation simplier, bring it on :)