digitalmars.D.learn - Template constraints: opCmp and opUnary!"++"
- Francesco Cattoglio (21/21) Dec 20 2013 I'm trying to experiment a bit around the iota function.
- Philippe Sigaud (9/16) Dec 20 2013 You could put the target code inside a anonymous block and use
- monarch_dodra (10/24) Dec 20 2013 That's news to me.
- Timon Gehr (3/6) Dec 20 2013 is(typeof((T v){ /+ use v +/ }))
-
Francesco Cattoglio
(3/12)
Dec 20 2013
Is there any difference between "is(typeof(
))" and - Philippe Sigaud (4/6) Dec 20 2013 I find the latter cleaner: its intent is more apparent. I use
- monarch_dodra (4/10) Dec 20 2013 AFAIK, there is no real difference, but "is(typeof())" is more
- Timon Gehr (9/20) Dec 20 2013 I used is(typeof(...)) as that was used in the original post, but I
- Timon Gehr (3/6) Dec 20 2013 Actually, it seems that the behaviour of DMD has changed in this
- Jakob Ovrum (9/11) Dec 20 2013 Most uses of `is(typeof())` were written before the `compiles`
- monarch_dodra (39/48) Dec 20 2013 I dunno. Being forced to declare a scope and a list of variables
- Francesco Cattoglio (13/21) Dec 20 2013 It seems strange to me too, but: page 69 on the PDF:
- Philippe Sigaud (8/19) Dec 20 2013 Well I wrote this, so I should explain :-)
I'm trying to experiment a bit around the iota function. If I try to impose the following constraits: auto my_iota(B, E)(B begin, E end) if (is (typeof(++begin)) && is (typeof(begin < end))) {} Everything works as it should, but according to "D Templates: A Tutorial" book, you should not use arguments in constraints. If I try doing something like: auto my_iota(B, E)(B begin, E end) if (is (typeof(++B.init)) && is (typeof(B.init < E.init))) {} the code stops compiling for integers. On the other hand the code auto my_iota(B, E)(B begin, E end) if (is (typeof(++B)) && is (typeof(B < E))) {} fails to compile for both integers and my defined types. I read the "D Templates: A Tutorial" book and as far as I can tell "++B.init" and "B.init < E.init" doesn't look too much wrong, but I've not seen any constraint of this kind in phobos (using variables instead of types) so I was wondering if doing something like this is actually bad or even really bad. (And I also wonder how to properly setting those constraints directly on types)
Dec 20 2013
fails to compile for both integers and my defined types. I read the "D Templates: A Tutorial" book and as far as I can tell "++B.init" and "B.init < E.init" doesn't look too much wrong, but I've not seen any constraint of this kind in phobos (using variables instead of types) so I was wondering if doing something like this is actually bad or even really bad. (And I also wonder how to properly setting those constraints directly on types)You could put the target code inside a anonymous block and use __traits(compiles, ...), like this: if (__traits(compiles, { B begin; E end; ++begin; bool stop = begin < end; })) I never hade any problem by using Type.init, that's strange.
Dec 20 2013
On Friday, 20 December 2013 at 15:38:56 UTC, Francesco Cattoglio wrote:I'm trying to experiment a bit around the iota function. If I try to impose the following constraits:Everything works as it should, but according to "D Templates: A Tutorial" book, you should not use arguments in constraints.That's news to me.If I try doing something like: auto my_iota(B, E)(B begin, E end) if (is (typeof(++B.init)) && is (typeof(B.init < E.init))) {} the code stops compiling for integers.That's normal, because "T.init" is not an lvalue. If you need an lvalue, we have `std.traits.lvalueOf!T` which you can use. That said:auto my_iota(B, E)(B begin, E end) if (is (typeof(++begin)) && is (typeof(begin < end))) {}Seems perfectly legit to me.On the other hand the code auto my_iota(B, E)(B begin, E end) if (is (typeof(++B)) && is (typeof(B < E))) {} fails to compile for both integers and my defined types."B" is a type, so "++B" will always resolve to "__error", unless you've implemented a static operator (not sure if even legal?).
Dec 20 2013
On 12/20/2013 05:40 PM, monarch_dodra wrote:That's normal, because "T.init" is not an lvalue. If you need an lvalue, we have `std.traits.lvalueOf!T` which you can use.is(typeof((T v){ /+ use v +/ })) I think this is a lot cleaner.
Dec 20 2013
On Friday, 20 December 2013 at 17:18:01 UTC, Timon Gehr wrote:On 12/20/2013 05:40 PM, monarch_dodra wrote:Is there any difference between "is(typeof(<somecode>))" and __traits(compiles, <somecode>)?That's normal, because "T.init" is not an lvalue. If you need an lvalue, we have `std.traits.lvalueOf!T` which you can use.is(typeof((T v){ /+ use v +/ })) I think this is a lot cleaner.
Dec 20 2013
On Fri, Dec 20, 2013 at 6:33 PM, Francesco Cattoglio <francesco.cattoglio gmail.com> wrote:Is there any difference between "is(typeof(<somecode>))" and __traits(compiles, <somecode>)?I find the latter cleaner: its intent is more apparent. I use is(typeof()) only for really testing for type existence.
Dec 20 2013
On Friday, 20 December 2013 at 17:48:03 UTC, Philippe Sigaud wrote:On Fri, Dec 20, 2013 at 6:33 PM, Francesco Cattoglio <francesco.cattoglio gmail.com> wrote:AFAIK, there is no real difference, but "is(typeof())" is more "idiomatic" in phobos.Is there any difference between "is(typeof(<somecode>))" and __traits(compiles, <somecode>)?I find the latter cleaner: its intent is more apparent. I use is(typeof()) only for really testing for type existence.
Dec 20 2013
On 12/20/2013 09:42 PM, monarch_dodra wrote:On Friday, 20 December 2013 at 17:48:03 UTC, Philippe Sigaud wrote:I used is(typeof(...)) as that was used in the original post, but I think it is actually better to use __traits(compiles,...). The difference is that typeof will not check if all referenced symbols are indeed accessible in the given scope. (Currently __traits(compiles, ...) won't either, but I assume this is a bug.) Most non-trivial templates that use is(typeof(...)) in the constraint can be broken. (In the sense that it is possible to instantiate them even though their body does not compile.)On Fri, Dec 20, 2013 at 6:33 PM, Francesco Cattoglio <francesco.cattoglio gmail.com> wrote:AFAIK, there is no real difference, but "is(typeof())" is more "idiomatic" in phobos.Is there any difference between "is(typeof(<somecode>))" and __traits(compiles, <somecode>)?I find the latter cleaner: its intent is more apparent. I use is(typeof()) only for really testing for type existence.
Dec 20 2013
On 12/20/2013 10:57 PM, Timon Gehr wrote:Most non-trivial templates that use is(typeof(...)) in the constraint can be broken. (In the sense that it is possible to instantiate them even though their body does not compile.)Actually, it seems that the behaviour of DMD has changed in this respect. It does not seem to be an issue any longer.
Dec 20 2013
On Friday, 20 December 2013 at 20:42:13 UTC, monarch_dodra wrote:AFAIK, there is no real difference, but "is(typeof())" is more "idiomatic" in phobos.Most uses of `is(typeof())` were written before the `compiles` trait was introduced. Add the cargo cult to that, and it's no wonder that Phobos uses `is(typeof())` so liberally, but I think the `compiles` trait shows intent better and benefits from fewer parentheses. However, there is the problem that uses of `__traits` are supposed to be internal, not user-facing. We haven't made much headway on that one.
Dec 20 2013
On Friday, 20 December 2013 at 17:18:01 UTC, Timon Gehr wrote:On 12/20/2013 05:40 PM, monarch_dodra wrote:I dunno. Being forced to declare a scope and a list of variables just to have access to an lvalue instance seems a bit verbose to me. I *think* doing this can lead to issues if done inside an inout context (not certain about this. It's a buggy behavior anywyas, Kenji told me, so not a real argument). For example: enum isAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); vs enum isAssignable(Lhs, Rhs = Lhs) = __traits(compiles, (Lhs lhs, Rhs rhs){lhs = rhs}); Hum... Actually, I'm not sure which is cleanest. You do bring up a good point. Plus, it solves the whole "initialization issue" we've been having. Why haven't e been using this up to now...? For example, std.range has a lot of: template isInputRange(R) { enum bool isInputRange = is(typeof( (inout int = 0) { R r = void; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range })); } The line "R r = void;" has led to problems before. Why haven't we just used: template isInputRange(R) { enum bool isInputRange = is(typeof( (R r, inout int = 0) { if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range })); }That's normal, because "T.init" is not an lvalue. If you need an lvalue, we have `std.traits.lvalueOf!T` which you can use.is(typeof((T v){ /+ use v +/ })) I think this is a lot cleaner.
Dec 20 2013
On Friday, 20 December 2013 at 16:40:23 UTC, monarch_dodra wrote:It seems strange to me too, but: page 69 on the PDF: "Do not use argument in your constraint. If you need a value of type T, use T.init." Since I don't know D really well, I thought something was wrong with it.Everything works as it should, but according to "D Templates: A Tutorial" book, you should not use arguments in constraints.That's news to me.That's normal, because "T.init" is not an lvalue.Right! I was suspecting something like this.That said: Seems perfectly legit to me.Then I'll probably stick to it. It's simple, easy to understand, and works. Or maybe the __traits(compiles) actually looks even better."B" is a type, so "++B" will always resolve to "__error", unless you've implemented a static operator (not sure if even legal?).Best part is: ++B actually works, it's the B < E that fails. But they both smelled bad. Perhaps ++B is some kind of accept-invalid bug then?
Dec 20 2013
On Fri, Dec 20, 2013 at 6:31 PM, Francesco Cattoglio <francesco.cattoglio gmail.com> wrote:On Friday, 20 December 2013 at 16:40:23 UTC, monarch_dodra wrote:Well I wrote this, so I should explain :-) I'm pretty sure at one time, using direct symbols led to strange behavior. And I'm still not sure symbol have values when used in template constraints. What happens when you try ++a on such a symbol? But then, if people tell me using these values directly is perfectly OK, I'll update the text, of course.It seems strange to me too, but: page 69 on the PDF: "Do not use argument in your constraint. If you need a value of type T, use T.init." Since I don't know D really well, I thought something was wrong with it.Everything works as it should, but according to "D Templates: A Tutorial" book, you should not use arguments in constraints.That's news to me.
Dec 20 2013