We've got i++, why not a "post-assignment" operator ?

J

Jorge

Hi,

We've got i++, which is an expression that assigns i+1 to i, but
evaluates to the original value of i. It's called the postfix
increment, or post-increment operator, isn't it ?

I think that it would have been nice to have too a "post-assignment"
operator, let's say ":=", that would assign a new value, but would
evaluate to the original value:

i := i+1; // emulates an i++

I was writing this the other day:

callBack= queue;
queue= nop;
callBack();

With this "post-assignment" operator I could have written instead:

( queue := nop )(); // which is nicer

It's ideal for swapping as it avoids the need for tmp. vars:

tmp= a;
a= b;
b= tmp;

could be written as :

a= (b := a);

properly optimized, it could even be slightly faster, or not ?

But, my idea must have some problem: why if not it's not in any
language ?
(now me ducks and runs)

Any thoughts ?
Thanks,
 
M

Mark Bluemel

Jorge said:
Hi,

We've got i++, which is an expression that assigns i+1 to i, but
evaluates to the original value of i. It's called the postfix
increment, or post-increment operator, isn't it ?

I think that it would have been nice to have too a "post-assignment"
operator, let's say ":=", that would assign a new value, but would
evaluate to the original value:

i := i+1; // emulates an i++

I was writing this the other day:

callBack= queue;
queue= nop;
callBack();

With this "post-assignment" operator I could have written instead:

( queue := nop )(); // which is nicer

It's ideal for swapping as it avoids the need for tmp. vars:

tmp= a;
a= b;
b= tmp;

could be written as :

a= (b := a);

properly optimized, it could even be slightly faster, or not ?

But, my idea must have some problem: why if not it's not in any
language ?
(now me ducks and runs)

Any thoughts ?
Thanks,


Perhaps a more general mechanism would be a version of the comma
operator where the left-most expression is returned rather than the
right-most. Perhaps we could use a ":" without a corresponding "?".

In this syntax the swap could be :-

a = (b : b=a);

and your queue example :-

(queue : queue = nop)();

I just have a feeling this construction could do more than the
post-assignment one...

(dives into a dugout).
 
P

Paul N

Perhaps a more general mechanism would be a version of the comma
operator where the left-most expression is returned rather than the
right-most.

Ah. I suggested this back in August 2009, see
http://groups.google.co.uk/group/co...6f691b208719938?hl=en&q=gw7rib+comma+operator
.. It would be exaggerating to say the responses were luke-warm.

This may be one of those ideas, such as the ^^ boolean exclusive-or
operator, and the untypedef keyword, which continues to get invented
from time to time but, despite being easy enough to implement, never
catches on ...

Paul.
 
S

Shao Miller

Paul said:
Ah. I suggested this back in August 2009, see
http://groups.google.co.uk/group/co...6f691b208719938?hl=en&q=gw7rib+comma+operator
. It would be exaggerating to say the responses were luke-warm.

This may be one of those ideas, such as the ^^ boolean exclusive-or
operator, and the untypedef keyword, which continues to get invented
from time to time but, despite being easy enough to implement, never
catches on ...

It's a cool idea, in my opinion.

And give me a 'typeof' operator, for Pete's sake.
 
S

Shao Miller

Shao said:
... ... ...
And give me a 'typeof' operator, for Pete's sake.

Before anyone gets too excited, please allow me to clarify:

Agree that a 'typeof' construct could be useful enough to allow someone
writing a nice proposal for it to allow that to be standardized. :)
 
R

robin

| Similarly, I like multiple assignment that can do a swap (a, b := b, a)
| but C is what it is and I don't think any major new group of operators
| are going to be introduced anytime soon.

Try a subroutine SWAP(a, b).
It's simpler.
 
B

Ben Bacarisse

robin said:
| Similarly, I like multiple assignment that can do a swap (a, b := b, a)
| but C is what it is and I don't think any major new group of operators
| are going to be introduced anytime soon.

Try a subroutine SWAP(a, b).
It's simpler.

In that it's the only option, yes, but in C you have to write swap time
and again, once for every type you need[1].

I don't know if you deliberately used the term subroutine to leave open
the option that SWAP might be a macro, but a generic SWAP macro is not
without it problems.

[1] If you need lots of type-specific swap functions you'd probably write
a macro to generate them, but the fact remains that you need one for
each type.
 
S

sha0.miller

Ben said:
robin said:
| Similarly, I like multiple assignment that can do a swap (a, b := b, a)
| but C is what it is and I don't think any major new group of operators
| are going to be introduced anytime soon.

Try a subroutine SWAP(a, b).
It's simpler.

In that it's the only option, yes, but in C you have to write swap time
and again, once for every type you need[1].

I don't know if you deliberately used the term subroutine to leave open
the option that SWAP might be a macro, but a generic SWAP macro is not
without it problems.

[1] If you need lots of type-specific swap functions you'd probably write
a macro to generate them, but the fact remains that you need one for
each type.

The 'SWAP_STMNT()' below is one reason why I thought a standardized

'typeof' construct might be handy.

#include <stdio.h>

/* Internal. */
#define SWAP_BUDDY(ident) \
swap_buddy_ ## ident

/* Internal. */
#define SWAP_TYPE(ident) \
swap_type_ ## ident

/**
* Establish 'ident' for use with 'SWAP()' and 'SWAP_STMNT()'.
* This is a declaration and belongs wherever declarations are allowed.
*/
#define DECL_SWAPPABLE(ident, type) \
type SWAP_BUDDY(ident); typedef type SWAP_TYPE(ident)

/* Swap 'x' and 'y' via 'tmp', return 'x'. */
#define SWAP_TMP(tmp, x, y) \
(((tmp) = (x)), ((x) = (y)), ((y) = (tmp)))

/* Swap 'x' and 'y', return 'x'. Requires 'DECL_SWAPPABLE(x)'. */
#define SWAP(x, y) \
(SWAP_TMP(SWAP_BUDDY(x), x, y))

/* A statement which swaps 'x' and 'y'. Requires 'DECL_SWAPPABLE(x)'. */
#define SWAP_STMNT(x, y) \
do { \
SWAP_TYPE(x) swap_stmnt_tmp = (x); \
(x) = (y); \
(y) = swap_stmnt_tmp; \
} while (0)

int main(void) {
int x = 3, y = 5;
DECL_SWAPPABLE(x, int);
DECL_SWAPPABLE(y, int);
double d = 3.3, e = 5.5;
DECL_SWAPPABLE(d, double);
DECL_SWAPPABLE(e, double);

/* Initial swap for 'int's via swap statement. */
if (1)
SWAP_STMNT(x, y);
else
SWAP_STMNT(y, x);
/* Restore via swap statement. */
SWAP_STMNT(x, y);

/* Swap 'int's via swap expression. */
if ((SWAP(x, y), 1) && x == 5 && y == 3)
puts("Success for 'int'.");
else
puts("Failure for 'int'.");

/* Initial swap for 'double's via swap statement. */
if (1)
SWAP_STMNT(d, e);
else
SWAP_STMNT(e, d);
/* Restore via swap statement. */
SWAP_STMNT(d, e);

/* Swap 'double's via swap expression. */
if ((SWAP(d, e), 1) && d == 5.5 && e == 3.3)
puts("Success for 'double'.");
else
puts("Failure for 'double'.");

return 0;
}
 
S

Shao Miller

Shao said:
Ben said:
robin said:
| Similarly, I like multiple assignment that can do a swap (a, b :=
b, a)
| but C is what it is and I don't think any major new group of operators
| are going to be introduced anytime soon.

Try a subroutine SWAP(a, b).
It's simpler.

In that it's the only option, yes, but in C you have to write swap time
and again, once for every type you need[1].

I don't know if you deliberately used the term subroutine to leave open
the option that SWAP might be a macro, but a generic SWAP macro is not
without it problems.

[1] If you need lots of type-specific swap functions you'd probably write
a macro to generate them, but the fact remains that you need one for
each type.

The 'SWAP_STMNT()' below is one reason why I thought a standardized
'typeof' construct might be handy.

[Snipped code for 'SWAP()' and 'SWAP_STMNT()' macros.]

Or, in an attempt to "generalize" to the left-hand comma operator that
was mentioned by both Mr. M. Bluemel and Mr. P. N.:

#include <stdio.h>

/* Internal. */
#define LH_COMMA_BUDDY(ident) \
lh_comma_buddy_ ## ident

/**
* Establish 'ident' for use with 'LH_COMMA()'.
* This belongs wherever declarations are allowed.
*/
#define DECL_LH_COMMA(ident, type) \
type LH_COMMA_BUDDY(ident)

/* Evaluate 'left' and 'right', return 'left'. */
#define LH_COMMA(left, right) \
((LH_COMMA_BUDDY(left) = (left)), (right), LH_COMMA_BUDDY(left))

#define DUMMY_FUNC(ident) \
void (ident)(void)
typedef DUMMY_FUNC(*dummy_func);

static DUMMY_FUNC(foo) {
puts("foo()");
return;
}

static DUMMY_FUNC(bar) {
puts("bar()");
return;
}

int main(void) {
dummy_func baz = foo;
DECL_LH_COMMA(baz, dummy_func);

/* 'baz' points to 'foo'. */
baz();
/* We point 'baz' to 'bar', but use the previous value of 'baz'. */
LH_COMMA(baz, baz = bar)();
/* 'baz' points to 'bar'. */
baz();
return 0;
}
 
J

Jorge

Jorge said:
We've got i++, which is an expression that assigns i+1 to i, but
evaluates to the original value of i. It's called the postfix
increment, or post-increment operator, isn't it ?
I think that it would have been nice to have too a "post-assignment"
operator, let's say ":=", that would assign a new value, but would
evaluate to the original value:
i := i+1; // emulates an i++
I was writing this the other day:
callBack= queue;
queue= nop;
callBack();

With this "post-assignment" operator I could have written instead:
( queue := nop )(); // which is nicer

It's ideal for swapping as it avoids the need for tmp. vars:
tmp= a;
a= b;
b= tmp;
could be written as :
a= (b := a);
(...)

Perhaps a more general mechanism would be a version of the comma
operator where the left-most expression is returned rather than the
right-most. Perhaps we could use a ":" without a corresponding "?".

In this syntax the swap could be :-

a = (b : b=a);

and your queue example :-

(queue : queue = nop)();

I just have a feeling this construction could do more than the
post-assignment one...

(dives into a dugout).


Yep. That's very likely. Nice one!. But say you had:

newItem.nextItem= ( item.nextItem := newItem ) // post-assignment
operator (1)
newItem.nextItem= ( item.nextItem : item.nextItem= newItem ) // left-
most "comma" operator (2)

Isn't it that (1) could be slightly faster than (2) ? If so, we'd
probably want to have both :)
 
J

Jorge

Algol 68C had what it called displacement operators: variations on the
assignment operators :)=, +:= and so on) that return the prior value.  I
used to like them a lot -- they were useful all over the place.

Good to know. But why have they been dropped ever since ALGOL ?
Similarly, I like multiple assignment that can do a swap (a, b := b, a)

Yes. Me too.
but C is what it is and I don't think any major new group of operators
are going to be introduced anytime soon.

Sure :-/

What would D. Ritchie say of this operators ? I'd love to know.
 
B

Ben Bacarisse

Ben Bacarisse wrote:
[...] SWAP might be a macro, but a generic SWAP macro is not
without it problems.
The 'SWAP_STMNT()' below is one reason why I thought a standardized

'typeof' construct might be handy.

#include <stdio.h>

/* Internal. */
#define SWAP_BUDDY(ident) \
swap_buddy_ ## ident

/* Internal. */
#define SWAP_TYPE(ident) \
swap_type_ ## ident

/**
* Establish 'ident' for use with 'SWAP()' and 'SWAP_STMNT()'.
* This is a declaration and belongs wherever declarations are allowed.
*/
#define DECL_SWAPPABLE(ident, type) \
type SWAP_BUDDY(ident); typedef type SWAP_TYPE(ident)

/* Swap 'x' and 'y' via 'tmp', return 'x'. */
#define SWAP_TMP(tmp, x, y) \
(((tmp) = (x)), ((x) = (y)), ((y) = (tmp)))

/* Swap 'x' and 'y', return 'x'. Requires 'DECL_SWAPPABLE(x)'. */
#define SWAP(x, y) \
(SWAP_TMP(SWAP_BUDDY(x), x, y))

/* A statement which swaps 'x' and 'y'. Requires 'DECL_SWAPPABLE(x)'. */
#define SWAP_STMNT(x, y) \
do { \
SWAP_TYPE(x) swap_stmnt_tmp = (x); \
(x) = (y); \
(y) = swap_stmnt_tmp; \
} while (0)

int main(void) {
int x = 3, y = 5;
DECL_SWAPPABLE(x, int);
DECL_SWAPPABLE(y, int);
double d = 3.3, e = 5.5;
DECL_SWAPPABLE(d, double);
DECL_SWAPPABLE(e, double);

/* Initial swap for 'int's via swap statement. */
if (1)
SWAP_STMNT(x, y);
else
SWAP_STMNT(y, x);
/* Restore via swap statement. */
SWAP_STMNT(x, y);

/* Swap 'int's via swap expression. */
if ((SWAP(x, y), 1) && x == 5 && y == 3)
puts("Success for 'int'.");
else
puts("Failure for 'int'.");

/* Initial swap for 'double's via swap statement. */
if (1)
SWAP_STMNT(d, e);
else
SWAP_STMNT(e, d);
/* Restore via swap statement. */
SWAP_STMNT(d, e);

/* Swap 'double's via swap expression. */
if ((SWAP(d, e), 1) && d == 5.5 && e == 3.3)
puts("Success for 'double'.");
else
puts("Failure for 'double'.");

return 0;
}

I am not sure this is worth the effort. It seems only able to swap
objects named by simple identifiers -- no SWAP(a, a[j]) or
SWAP(a.one, a.two).

I'd do this:

#define SWAP(a, b) \
do { char temp[sizeof (a)]; \
memcpy(temp, &(a), sizeof (a)); \
memcpy(&(a), &(b), sizeof (a)); \
memcpy(&(b), temp, sizeof (a)); \
} while (0)

and hope that memcpy is special-cased for small objects. I think it is
in gcc for example.
 
J

Jorge

Ah. I suggested this back in August 2009, seehttp://groups.google.co.uk/group/comp.lang.c/browse_frm/thread/3d3bab...
. It would be exaggerating to say the responses were luke-warm.

This may be one of those ideas, such as the ^^ boolean exclusive-or
operator, and the untypedef keyword, which continues to get invented
from time to time but, despite being easy enough to implement, never
catches on ...

And it's a pity, isn't it ?
 
B

Ben Bacarisse

Jorge said:
Good to know. But why have they been dropped ever since ALGOL ?


Yes. Me too.


Sure :-/

What would D. Ritchie say of this operators ? I'd love to know.

So would it but I am pretty sure that I can guess what he'd say about
why C did not have them originally. The same lab was working with Algol
68 and they had great trouble getting the compiler to fit into the
memory of the small machines that were the target of their research. C
was kept small very deliberately.

At the time "real" programming was done with big languages -- PL/I,
Algol 68, COBOL and so on, but there was a growing interest in small
machines and the software they would need. One of C's predecessors,
BCPL, was itself a cut down "basic" version of another big language,
Stachey's[1] CPL. If you worked with small systems (literally a few
dozens of kilobytes) you were very wary of feature creep in language
design and C was already big for its purpose (B and BCPL have no types,
for example). I image that a feature really would have to carry its
weight be to be considered.

In case it needs saying, pretty much any feature that was not in the
original C was not there quite deliberately.

[1] I should say "et al" since it was a team effort.
 
B

BartC

Ben Bacarisse said:
if ((SWAP(d, e), 1) && d == 5.5 && e == 3.3)
puts("Success for 'double'.");
else
puts("Failure for 'double'.");

return 0;
}

I am not sure this is worth the effort. It seems only able to swap
objects named by simple identifiers -- no SWAP(a, a[j]) or
SWAP(a.one, a.two).

I'd do this:

#define SWAP(a, b) \
do { char temp[sizeof (a)]; \
memcpy(temp, &(a), sizeof (a)); \
memcpy(&(a), &(b), sizeof (a)); \
memcpy(&(b), temp, sizeof (a)); \
} while (0)

and hope that memcpy is special-cased for small objects. I think it is
in gcc for example.


The advantage of having an intrinsic function for swap is that the compiler
can optimise it more easily it for both small and large swaps.

Your macro for example might involve 6 memory ops, instead of just 4 (read
a,b, write a,b), unless optimisation is particularly good.

Also size- and type-checking becomes more practical.

(I used such an operator in another language (using a:=:b or a swap b), and
I used it all the time, when I had to write a bubble-sort. Actually it was
quite useful and a very easy and painless feature to add to a language.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top