Thread overview | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
December 20, 2013 Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
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) |
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Francesco Cattoglio | > 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.
|
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Francesco Cattoglio | 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?). |
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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.
|
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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? |
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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>)?
|
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Francesco Cattoglio | 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. |
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Francesco Cattoglio | 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. |
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Philippe Sigaud | 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.
|
December 20, 2013 Re: Template constraints: opCmp and opUnary!"++" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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
}));
}
|
Copyright © 1999-2021 by the D Language Foundation