Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #94062 > unrolled thread
| Started by | luser droog <luser.droog@gmail.com> |
|---|---|
| First post | 2016-09-25 16:12 -0700 |
| Last post | 2016-09-26 12:42 -0700 |
| Articles | 12 — 6 participants |
Back to article view | Back to comp.lang.c
lvalues from the ternary op luser droog <luser.droog@gmail.com> - 2016-09-25 16:12 -0700
Re: lvalues from the ternary op "James R. Kuyper" <jameskuyper@verizon.net> - 2016-09-25 19:58 -0400
Re: lvalues from the ternary op Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-26 01:10 +0100
Re: lvalues from the ternary op Tim Rentsch <txr@alumni.caltech.edu> - 2016-09-27 15:01 -0700
Re: lvalues from the ternary op Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-09-28 00:23 +0100
Re: lvalues from the ternary op BartC <bc@freeuk.com> - 2016-09-28 01:35 +0100
Re: lvalues from the ternary op "James R. Kuyper" <jameskuyper@verizon.net> - 2016-09-28 12:39 -0400
Re: lvalues from the ternary op BartC <bc@freeuk.com> - 2016-09-28 18:23 +0100
Re: lvalues from the ternary op Tim Rentsch <txr@alumni.caltech.edu> - 2016-10-08 15:37 -0700
Re: lvalues from the ternary op supercat@casperkitty.com - 2016-09-25 18:14 -0700
Re: lvalues from the ternary op BartC <bc@freeuk.com> - 2016-09-26 10:51 +0100
Re: lvalues from the ternary op luser droog <luser.droog@gmail.com> - 2016-09-26 12:42 -0700
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2016-09-25 16:12 -0700 |
| Subject | lvalues from the ternary op |
| Message-ID | <c8677aed-7589-4ba4-9aa0-8b20b00c0981@googlegroups.com> |
Just came up with this. Probably foolish nonsense as usual. I was looking over the (obfuscated) code of my 8086 emulator and my eyes fell upon this statement. https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112 d?y+=*fl&CF:(x+=*fl&CF); And I thought: There's a lot of repetition in the two cases. So what about: *(d?&y:&x)+=F(CF); where F() was already written and does the masking: #define F(f) !!(*fl&f) Pointers! Yay! -- tldr; the answer was *pointers
[toc] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-09-25 19:58 -0400 |
| Message-ID | <63e5a957-02b6-3ddd-e993-f028bed0abc7@verizon.net> |
| In reply to | #94062 |
On 09/25/2016 07:12 PM, luser droog wrote:
...
> #define F(f) !!(*fl&f)
As a general rule, if you write a macro intended to be used as an
expression, you should parenthesize the expansion of that macro. If it's
a function-like macro whose arguments are intended to be expressions,
you should also generally parenthesize each use of the arguments:
#define F(f) (!!(*fl&(f)))
You might know for certain that it won't matter in this particular case,
but unless you went out of your way to check, I'd recommend following
the general rule.
In this case, your use of binary & implies that the values of *fl, f,
and *fl&f are all integers. Combined with the fact that the outermost
operator is a unary operator, that limits, but does not eliminate, the
possibilities for confusion. With your definition,
F(a)[array]
would parse as
!!((*fl&a)[array])
equivalent to !!array[*fl&a]
rather than as
(!!(*fl&s))[array]
equivalent to array[!!(*fl&a)]
In the unlikely case that you used this macro in that fashion, which of
those two expansions would you have intended?
As far as parenthesizing the arguments, that's easier to justify. Given
your definition,
F(a|b)
would parse as
!!((*fl&a)|b)
Similar results apply for &&, ||, any of the assignment operators, and
the comma operator. Is that what you'd want to happen in those cases?
I may have made a mistake performing this analysis. However, if I did,
that merely emphasizes the importance of parenthesizing the arguments
and the results of macros unless you're certain that you don't need to
do so.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-26 01:10 +0100 |
| Message-ID | <8737kned1h.fsf@bsb.me.uk> |
| In reply to | #94062 |
luser droog <luser.droog@gmail.com> writes: > Just came up with this. Probably foolish nonsense as usual. No, I think it's a reasonable thing to do. C++ thought so too, to the extent of making it easier. But it's not technically "lvalues from the ternary op". It's just lvalues from the * op -- the result of a * expression is always an lvalue. > I was looking over the (obfuscated) code of my 8086 emulator > and my eyes fell upon this statement. > > https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112 > d?y+=*fl&CF:(x+=*fl&CF); > > And I thought: There's a lot of repetition in the two cases. > So what about: > > *(d?&y:&x)+=F(CF); C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure why C doesn't, but it seems a shame to me that it doesn't. -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-09-27 15:01 -0700 |
| Message-ID | <kfnk2dxc89h.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #94068 |
Ben Bacarisse <ben.usenet@bsb.me.uk> writes: > luser droog <luser.droog@gmail.com> writes: > >> Just came up with this. Probably foolish nonsense as usual. > > No, I think its a reasonable thing to do. C++ thought so too, to the > extent of making it easier. But its not technically "lvalues from the > ternary op". It's just lvalues from the * op -- the result of a * > expression is always an lvalue. > >> I was looking over the (obfuscated) code of my 8086 emulator >> and my eyes fell upon this statement. >> >> https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112 >> d?y+=*fl&CF:(x+=*fl&CF); >> >> And I thought: There's a lot of repetition in the two cases. >> So what about: >> >> *(d?&y:&x)+=F(CF); > > C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure > why C doesn't, but it seems a shame to me that it doesn't. Does it? The pattern is one that IME hardly ever comes up (not never, but certainly rarely). Adding the two &'s and one * is not very burdensome (and might be done using a macro to make the idiom stand out more, if that is desired). I don't see how a form like this generalizes to other cases (to emphasize, in C - in C++ it might be different). To me it looks like the two cost/benefit ratios weigh in favor of how C is now. So perhaps I don't understand what you consider the respective costs and benefits are in the two cases.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-09-28 00:23 +0100 |
| Message-ID | <8737klapvv.fsf@bsb.me.uk> |
| In reply to | #94172 |
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
>> luser droog <luser.droog@gmail.com> writes:
>>
>>> Just came up with this. Probably foolish nonsense as usual.
>>
>> No, I think its a reasonable thing to do. C++ thought so too, to the
>> extent of making it easier. But its not technically "lvalues from the
>> ternary op". It's just lvalues from the * op -- the result of a *
>> expression is always an lvalue.
>>
>>> I was looking over the (obfuscated) code of my 8086 emulator
>>> and my eyes fell upon this statement.
>>>
>>> https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112
>>> d?y+=*fl&CF:(x+=*fl&CF);
>>>
>>> And I thought: There's a lot of repetition in the two cases.
>>> So what about:
>>>
>>> *(d?&y:&x)+=F(CF);
>>
>> C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure
>> why C doesn't, but it seems a shame to me that it doesn't.
>
> Does it?
Yes it does.
> The pattern is one that IME hardly ever comes up (not
> never, but certainly rarely). Adding the two &'s and one * is
> not very burdensome (and might be done using a macro to make the
> idiom stand out more, if that is desired). I don't see how a
> form like this generalizes to other cases (to emphasize, in C -
> in C++ it might be different). To me it looks like the two
> cost/benefit ratios weigh in favor of how C is now. So perhaps
> I don't understand what you consider the respective costs and
> benefits are in the two cases.
I consider special cases to be a sort of intellectual cost. But my
remark about it being a shame is not intended to be a considered
weighing-up of hard engineering costs but simply a sad observation on
there being yet another exception I would not have expected. If I did
not know better, I would assume that that any expression that could
reasonably be a lvalue would be an lvalue.
I would have liked (in some alternate universe where the engineering
costs had already been incurred) to see the result of assignments be
lvalues (as they are in C++) as well as the result of a comma operator
(as it is in C++) and prefix -- and ++ (as they are in C++[1]).
I don't really know how to evaluate the costs, but I would probably
still consider it a shame, even if I were sure it were so for sound
economic reasons.
[1] Only to a first approximation. C++ has a more complex menagerie of
value kinds than C does.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-28 01:35 +0100 |
| Message-ID | <nsf37j$9ko$1@dont-email.me> |
| In reply to | #94175 |
On 28/09/2016 00:23, Ben Bacarisse wrote: > Tim Rentsch <txr@alumni.caltech.edu> writes: >> The pattern is one that IME hardly ever comes up (not >> never, but certainly rarely). Adding the two &'s and one * is >> not very burdensome (and might be done using a macro to make the >> idiom stand out more, if that is desired). I don't see how a >> form like this generalizes to other cases (to emphasize, in C - >> in C++ it might be different). To me it looks like the two >> cost/benefit ratios weigh in favor of how C is now. So perhaps >> I don't understand what you consider the respective costs and >> benefits are in the two cases. It's for completeness. You allow A, *A, A.m, A[i] etc as both rvalues and lvalues. So why not a ternary op that can yield one of two potential lvalues? > I would have liked (in some alternate universe where the engineering > costs had already been incurred) to see the result of assignments be > lvalues (as they are in C++) as well as the result of a comma operator > (as it is in C++) and prefix -- and ++ (as they are in C++[1]). > > I don't really know how to evaluate the costs, but I would probably > still consider it a shame, even if I were sure it were so for sound > economic reasons. I looked at my language and that allows ternary ops (or equivalent) as the lhs of an assignment. But not the equivalent of the comma operator. Not until 20 minutes later anyway. When targeted at C, the results looked like this: Source lang: C (a|b|c) := d => (*(a?&b:&c)) = d; (++a; --b ;c) := d => (*((++a),(--b),&c)) = d; LHS expressions can be nested too. So the costs are not to do with the technical side. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-09-28 12:39 -0400 |
| Message-ID | <2bb1903b-4f4d-235e-dd9c-ff8e559b83bb@verizon.net> |
| In reply to | #94175 |
On 09/27/2016 07:23 PM, Ben Bacarisse wrote: > Tim Rentsch <txr@alumni.caltech.edu> writes: > >> Ben Bacarisse <ben.usenet@bsb.me.uk> writes: ... >>> C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure >>> why C doesn't, but it seems a shame to me that it doesn't. >> >> Does it? > > Yes it does. > >> The pattern is one that IME hardly ever comes up (not >> never, but certainly rarely). Adding the two &'s and one * is >> not very burdensome (and might be done using a macro to make the >> idiom stand out more, if that is desired). I don't see how a >> form like this generalizes to other cases (to emphasize, in C - >> in C++ it might be different). The key feature of C++ that makes this work is relatively simple, and could be translated into the C context without much trouble. In C terms, it basically says that if both the second and third operands of ?: are lvalues of the same type, then the result is an lvalue of that type. There's some tricky issues with the wording, since the C++ standard talks in terms of lvalue to rvalue conversions, whereas C deals with lvalues a bit differently, but there's no insurmountable barriers to adopting such a rule in C. Doing so would be a fully backward-compatible change, since it only affects the behavior of constructs that currently are constraint violations.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-28 18:23 +0100 |
| Message-ID | <nsgua2$jom$1@dont-email.me> |
| In reply to | #94194 |
On 28/09/2016 17:39, James R. Kuyper wrote: > On 09/27/2016 07:23 PM, Ben Bacarisse wrote: >> Tim Rentsch <txr@alumni.caltech.edu> writes: >> >>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes: > ... >>>> C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure >>>> why C doesn't, but it seems a shame to me that it doesn't. >>> >>> Does it? >> >> Yes it does. >> >>> The pattern is one that IME hardly ever comes up (not >>> never, but certainly rarely). Adding the two &'s and one * is >>> not very burdensome (and might be done using a macro to make the >>> idiom stand out more, if that is desired). I don't see how a >>> form like this generalizes to other cases (to emphasize, in C - >>> in C++ it might be different). > > The key feature of C++ that makes this work is relatively simple, and > could be translated into the C context without much trouble. In C terms, > it basically says that if both the second and third operands of ?: are > lvalues of the same type, then the result is an lvalue of that type. That isn't much different from this: int *p; int cond; int a,b; p = (cond ? &a : &b); The two pointer expressions need to match. -- bartc
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-10-08 15:37 -0700 |
| Message-ID | <kfnshs68o2f.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #94175 |
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> luser droog <luser.droog@gmail.com> writes:
>>>
>>>> Just came up with this. Probably foolish nonsense as usual.
>>>
>>> No, I think its a reasonable thing to do. C++ thought so too, to the
>>> extent of making it easier. But its not technically "lvalues from the
>>> ternary op". It's just lvalues from the * op -- the result of a *
>>> expression is always an lvalue.
>>>
>>>> I was looking over the (obfuscated) code of my 8086 emulator
>>>> and my eyes fell upon this statement.
>>>>
>>>> https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112
>>>> d?y+=*fl&CF:(x+=*fl&CF);
>>>>
>>>> And I thought: There's a lot of repetition in the two cases.
>>>> So what about:
>>>>
>>>> *(d?&y:&x)+=F(CF);
>>>
>>> C++ allows you to write (d ? y : x) += F(CF); directly. I'm not sure
>>> why C doesn't, but it seems a shame to me that it doesn't.
>>
>> Does it?
>
> Yes it does.
Sorry, my question may have come across as snide or sarcastic,
which is not how I meant it. I think I was hoping for some sort
of elaboration, which you basically give below.
>> The pattern is one that IME hardly ever comes up (not
>> never, but certainly rarely). Adding the two &'s and one * is
>> not very burdensome (and might be done using a macro to make the
>> idiom stand out more, if that is desired). I don't see how a
>> form like this generalizes to other cases (to emphasize, in C -
>> in C++ it might be different). To me it looks like the two
>> cost/benefit ratios weigh in favor of how C is now. So perhaps
>> I don't understand what you consider the respective costs and
>> benefits are in the two cases.
>
> I consider special cases to be a sort of intellectual cost. But my
> remark about it being a shame is not intended to be a considered
> weighing-up of hard engineering costs but simply a sad observation on
> there being yet another exception I would not have expected.
Reading these comments confused me. I had to re-read several
times before I got what you were saying. (Note that this is as
much a comment on my state of mind as it is on what words were
used.)
> If I did
> not know better, I would assume that that any expression that could
> reasonably be a lvalue would be an lvalue.
>
> I would have liked (in some alternate universe where the engineering
> costs had already been incurred) to see the result of assignments be
> lvalues (as they are in C++) as well as the result of a comma operator
> (as it is in C++) and prefix -- and ++ (as they are [to a first
> approximation] in C++).
For conditional expressions, my reaction is basically just the
opposite -- it would seem weird, or exceptional, if certain kinds
of conditional expressions were lvalues. In most cases whether
an expression is an lvalue or not is a syntactic property, not a
semantic property. That is not quite true for identifiers,
because an identifier can be an enumeration constant, which is
not an lvalue; it is also not quite true for the * indirection
operator, which can designate a function rather than designate an
object (but a function designator can be thought of as just a
different sort of lvalue, eg, it can have its address taken).
Conditional expressions are different: only some of them would
qualify as lvalues, depending (presumably) on the types of the
second and third operands. The symmetry is not really symmetric.
Looking at a broader rule -- "any expression that could reasonably
be a lvalue would be an lvalue" -- I can't help the feeling that
this would lead to strained or artificial rules in an effort to
make different operators "more consistent". If prefix ++ and the
comma operator can give lvalues, why not casts:
(int) x = 7;
Or, how about functions:
int foo(){ if( something ) return x; return y; }
foo() = 7;
Or, how about the && operator:
a && b = 7; /* sets 'a' if a==0, otherwise sets 'b' */
Each of these operators could be (and probably has been, in some
programming language or other) taken to supply an lvalue (or
whatever the moral equivalent is in the other language). Should
these expressions be allowed as lvalues, or should they be ruled
out? Either way, on what basis?
> I don't really know how to evaluate the costs, but I would probably
> still consider it a shame, even if I were sure it were so for sound
> economic reasons.
Generally I am in favor of unifying the underlying principles of a
programming language, but here (ie, considering C as the context) it
mostly strikes me as uniformity purely for the sake of uniformity,
which by itself doesn't provide much value.
I should say explicitly, all the foregoing is just my own opinions,
and is not meant to change or invalidate your views, which I thank
you for expressing.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-09-25 18:14 -0700 |
| Message-ID | <9d0058c6-ae61-474e-910b-5ce47f01d769@googlegroups.com> |
| In reply to | #94062 |
On Sunday, September 25, 2016 at 6:12:20 PM UTC-5, luser droog wrote: > And I thought: There's a lot of repetition in the two cases. > So what about: > > *(d?&y:&x)+=F(CF); > > where F() was already written and does the masking: > #define F(f) !!(*fl&f) > > Pointers! Yay! In some platforms, loading a pointer and using it will be slower than using a variable direcctly, and on such platforms the optimal code for a construct like that would likely select one of two "store direct" instructions. When using a simple compiler for such a platform code like yours would best be written as: int temp = value; if (d) y=temp; else x=temp; On other platforms, accessing a global variable would require first loading its address into a register, and on those platforms code which uses pointers may be more efficient. If the variables are globals, and "d" is known to be either 0 or 1, having a static array with the addresses of both globals may be most efficient.
[toc] | [prev] | [next] | [standalone]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2016-09-26 10:51 +0100 |
| Message-ID | <nsar29$9gs$1@dont-email.me> |
| In reply to | #94062 |
On 26/09/2016 00:12, luser droog wrote: > Just came up with this. Probably foolish nonsense as usual. > > I was looking over the (obfuscated) code of my 8086 emulator > and my eyes fell upon this statement. > > https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112 > d?y+=*fl&CF:(x+=*fl&CF); > > And I thought: There's a lot of repetition in the two cases. > So what about: > > *(d?&y:&x)+=F(CF); I don't quite understand. You seem to be adding *fl&CF to either x or y depending on d. So why not: *(d?&y:&x) += (&fl&CF); Why is it necessary to introduce F(CF)? Or is it also about repetition of *fl&CF within that set of macros? (Which doesn't appear too serious to me as they are just a set of macros; the repetition isn't in the actual code.) BTW if you're that keen on macros then why not: #define SETC(a,b,c) *((a)?&(b):&(c)) SETC(d,x,y) += (&fl&CF); At least SETC (or whatever more suitable name is used) is of more general use. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2016-09-26 12:42 -0700 |
| Message-ID | <51cc127e-ca89-49f6-ab26-f6174f41e68d@googlegroups.com> |
| In reply to | #94073 |
On Monday, September 26, 2016 at 4:51:24 AM UTC-5, Bart wrote: > On 26/09/2016 00:12, luser droog wrote: > > Just came up with this. Probably foolish nonsense as usual. > > > > I was looking over the (obfuscated) code of my 8086 emulator > > and my eyes fell upon this statement. > > > > https://github.com/luser-dr00g/8086/blob/b7bfb134c1146cbe2ac7f1b80dd521e0ddb2349c/a8086.c#L112 > > > d?y+=*fl&CF:(x+=*fl&CF); > > > > And I thought: There's a lot of repetition in the two cases. > > So what about: > > > > *(d?&y:&x)+=F(CF); > > I don't quite understand. You seem to be adding *fl&CF to either x or y > depending on d. So why not: > > *(d?&y:&x) += (&fl&CF); Yes, that would do it. As I said, the F() macro was already written and used in other parts of the code. I was perhaps a mistake to further complicate my presentation by introducing it here. But it allowed me to create my snippets by simple cut-and-paste from my source. > Why is it necessary to introduce F(CF)? Or is it also about repetition > of *fl&CF within that set of macros? (Which doesn't appear too serious > to me as they are just a set of macros; the repetition isn't in the > actual code.) The F() macro also normalizes the flag-mask test with !!. Not strictly necessary for the Carry Flag which is bit0 already, but using F() is the idiom I have chosen for testing flags in this program. So, just following through for consistency. > BTW if you're that keen on macros then why not: > > #define SETC(a,b,c) *((a)?&(b):&(c)) > > SETC(d,x,y) += (&fl&CF); > > At least SETC (or whatever more suitable name is used) is of more > general use. > That is pretty cool, thanks.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web