Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #94062 > unrolled thread

lvalues from the ternary op

Started byluser droog <luser.droog@gmail.com>
First post2016-09-25 16:12 -0700
Last post2016-09-26 12:42 -0700
Articles 12 — 6 participants

Back to article view | Back to comp.lang.c


Contents

  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

#94062 — lvalues from the ternary op

Fromluser droog <luser.droog@gmail.com>
Date2016-09-25 16:12 -0700
Subjectlvalues 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]


#94067

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2016-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]


#94068

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2016-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]


#94172

FromTim Rentsch <txr@alumni.caltech.edu>
Date2016-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]


#94175

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2016-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]


#94179

FromBartC <bc@freeuk.com>
Date2016-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]


#94194

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2016-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]


#94200

FromBartC <bc@freeuk.com>
Date2016-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]


#94630

FromTim Rentsch <txr@alumni.caltech.edu>
Date2016-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]


#94069

Fromsupercat@casperkitty.com
Date2016-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]


#94073

FromBartC <bc@freeuk.com>
Date2016-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]


#94102

Fromluser droog <luser.droog@gmail.com>
Date2016-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