digitalmars.D - D's metaprogramming could be flawed
- maik klein (98/98) Jan 22 2016 This will be a small rant that might be completely unjustified
- rsw0x (3/4) Jan 22 2016 You're looking for AliasSeq in std.meta, it's a tup—er, finite
- maik klein (4/8) Jan 22 2016 I am already aware of AliasSeq as I have written above. But I
- rsw0x (19/28) Jan 22 2016 Sorry, I must have skipped that.
- maik klein (28/57) Jan 23 2016 I think that should work but it only works because you do an
- maik klein (5/10) Jan 23 2016 I forgot to show what `tref` was in my last comment.
- Timon Gehr (3/13) Jan 22 2016 Custom expansions can be built with string mixins.
This will be a small rant that might be completely unjustified and it is not meant to talk ill about D. I am new to D and I run into a road blocking issue https://issues.dlang.org/show_bug.cgi?id=10541 So I set myself to fixing it. Asking around the irc, it seems that the biggest problem was that tuples always copy and we need something that can hold references. I was going to create a TupleRef but I run into some issues. At first I created something similar to an std::reference_wrapper which I am also not sure was needed in the first place but here it is. struct RefWrapper(T){ T* value; this(ref T v){ value = &v; } ref T get(){ return *value; } } auto refWrapper(T)(ref T t){ return RefWrapper!(T)(t); } RefWrapper is probably flawed, but let's pretend it works. The general idea was to have a tuple of type Tuple!(RefWrapper(T1), RefWrapper(T2), ... , RefWrapper(TN)); It would probably work that way, but D has its own reference semantics with `ref`. I thought it would be nicer if you can reuse `ref` notation. For example: foreach(t; something){ t.get(); } //vs foreach(ref t; something){ } That meant I needed to create a wrapper that would automatically unbox every RefWrapper. struct TupleRef(Ts...){ import std.meta; alias RefTs = staticMap!(RefWrapper, Ts); Tuple!RefTs refTs; this(ref Ts ts){ refTs = ???? } } Now we are at the problem that I constantly run into since I started with D. It seems that D doesn't have a way to express C++'s fold expression/template expansions. For example in C++ I could write std::make_tuple(std::ref(ts)...); and it would expand like std::make_tuple(std::ref(t1), std::ref(t2), ... , std::ref(tn)); I would love to use staticMap! but it seems to only work for types, it could be altered slightly so that it would also work with normal functions but the values would still have to be read at compile time. For this case I used my little helper function: auto mapToTuple(alias f,T...)(){ import std.typecons; static if(T.length == 0) { return tuple(); } else{ return tuple(f(T[0]), mapToTuple!(f, T[1..$]).expand); } } TupleRef then became struct TupleRef(Ts...){ import std.meta; alias RefTs = staticMap!(RefWrapper, Ts); Tuple!RefTs refTs; this(Ts ts){ refTs = mapToTuple!(refWrapper, ts); } } In this case everything seems to work nicely but the problem still exists. For example I need to be able to expand TupleRef like this f(someTupleRef.expand); f(t[0].get(), t[1].get(), ... , t[n].get()); //f takes the arguments by ref This time I can not use a tuple because it would copy the value. I also can not use `AliasSeq` because it needs it values at compile time. Another small problem for TupleRef is opIndex. It seems that the [index] notation with opIndex doesn't allow me to express the use of compile time values. It seems that tuple somehow manages this with struct Tuple{ ... alias expand this; ... } But I don't think I can use the same technique here. I thought I would write a bigger post that explains my thoughts instead of asking in the irc. I hope it didn't sound too harsh which was not my intent.
Jan 22 2016
On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:...You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
Jan 22 2016
On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?...You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
Jan 22 2016
On Friday, 22 January 2016 at 13:28:00 UTC, maik klein wrote:On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:Sorry, I must have skipped that. Is there a reason you're explicitly using tuples? Unless I'm misunderstanding you, you're looking for something like... struct Baz(V...) { V vals; } which can be used like... void foo(int, int, int) { } void main(){ auto i = Baz!(int, int, int)(); foo(i.vals); } or am I way off base? If so, is this similar to what you're looking for? http://dpaste.dzfl.pl/cbae4c4ed7af Sorry if I'm nowhere near what you meant.On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?...You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
Jan 22 2016
On Friday, 22 January 2016 at 14:19:30 UTC, rsw0x wrote:On Friday, 22 January 2016 at 13:28:00 UTC, maik klein wrote:I think that should work but it only works because you do an implicit conversion with get which is quite nice. But I was also looking for a more general solution. I think the mixin solution could be quite nice. static template unpack(alias f){ pragma(inline) auto into(alias target, Args...)(ref Args args){ import std.conv; enum s = `target(`~iota(Args.length).map!(i=>text(`f(args[`,i,`])`)).join(",")~`)`; return mixin(s); } } and use it like: auto r = unpack!(i => i * 2).into!((a, b) => a + b)(1,2); The only semi weird thing is that I can use this directly with my version of TupleRef like this: void foo(ref int, ref int){ } unpack!((r)=> r.get()).into!(foo)(tref.refTs.expand); I think that is because "lambda/delegates" can not express ref return types? So I think I need to do this: ref auto get(R)(R r){ return r.get(); } unpack!(get).into!(foo)(tref.refTs.expand);On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:Sorry, I must have skipped that. Is there a reason you're explicitly using tuples? Unless I'm misunderstanding you, you're looking for something like... struct Baz(V...) { V vals; } which can be used like... void foo(int, int, int) { } void main(){ auto i = Baz!(int, int, int)(); foo(i.vals); } or am I way off base? If so, is this similar to what you're looking for? http://dpaste.dzfl.pl/cbae4c4ed7af Sorry if I'm nowhere near what you meant.On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?...You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
Jan 23 2016
On Saturday, 23 January 2016 at 12:13:16 UTC, maik klein wrote:On Friday, 22 January 2016 at 14:19:30 UTC, rsw0x wrote:I forgot to show what `tref` was in my last comment. int i = 1; int i2 = 2 auto tref = TupleRef!(int,int)(i, i2);[...]I think that should work but it only works because you do an implicit conversion with get which is quite nice. [...]
Jan 23 2016
On 01/22/2016 01:57 PM, maik klein wrote:It seems that D doesn't have a way to express C++'s fold expression/template expansions. For example in C++ I could write std::make_tuple(std::ref(ts)...); and it would expand like std::make_tuple(std::ref(t1), std::ref(t2), ... , std::ref(tn)); ... For example I need to be able to expand TupleRef like this f(someTupleRef.expand); f(t[0].get(), t[1].get(), ... , t[n].get());Custom expansions can be built with string mixins. mixin(`f(`~iota(t.length).map!(i=>text(`t[`,i,`].get()`)).join(",")~`);`);
Jan 22 2016