www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template constraints: opCmp and opUnary!"++"

reply "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
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
next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
 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
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
next sibling parent reply "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Friday, 20 December 2013 at 17:18:01 UTC, Timon Gehr wrote:
 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.
Is there any difference between "is(typeof(<somecode>))" and __traits(compiles, <somecode>)?
Dec 20 2013
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
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
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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:

 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.
AFAIK, there is no real difference, but "is(typeof())" is more "idiomatic" in phobos.
Dec 20 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/20/2013 09:42 PM, monarch_dodra wrote:
 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:

 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.
AFAIK, there is no real difference, but "is(typeof())" is more "idiomatic" in phobos.
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.)
Dec 20 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
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
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 20 December 2013 at 17:18:01 UTC, Timon Gehr wrote:
 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.
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 })); }
Dec 20 2013
prev sibling parent reply "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Friday, 20 December 2013 at 16:40:23 UTC, monarch_dodra wrote:
 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.
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.
 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
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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:
 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.
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.
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.
Dec 20 2013